Use JavaScript Standard Style
Introduce JavaScript Standard Style as project style rule, and fixed all fail on backend code.
This commit is contained in:
		
							parent
							
								
									8f1c97f4a4
								
							
						
					
					
						commit
						4889e9732d
					
				
							
								
								
									
										194
									
								
								lib/auth.js
									
									
									
									
									
								
							
							
						
						
									
										194
									
								
								lib/auth.js
									
									
									
									
									
								
							@ -1,24 +1,24 @@
 | 
				
			|||||||
//auth
 | 
					// auth
 | 
				
			||||||
//external modules
 | 
					// external modules
 | 
				
			||||||
var passport = require('passport');
 | 
					var passport = require('passport')
 | 
				
			||||||
var FacebookStrategy = require('passport-facebook').Strategy;
 | 
					var FacebookStrategy = require('passport-facebook').Strategy
 | 
				
			||||||
var TwitterStrategy = require('passport-twitter').Strategy;
 | 
					var TwitterStrategy = require('passport-twitter').Strategy
 | 
				
			||||||
var GithubStrategy = require('passport-github').Strategy;
 | 
					var GithubStrategy = require('passport-github').Strategy
 | 
				
			||||||
var GitlabStrategy = require('passport-gitlab2').Strategy;
 | 
					var GitlabStrategy = require('passport-gitlab2').Strategy
 | 
				
			||||||
var DropboxStrategy = require('passport-dropbox-oauth2').Strategy;
 | 
					var DropboxStrategy = require('passport-dropbox-oauth2').Strategy
 | 
				
			||||||
var GoogleStrategy = require('passport-google-oauth20').Strategy;
 | 
					var GoogleStrategy = require('passport-google-oauth20').Strategy
 | 
				
			||||||
var LdapStrategy = require('passport-ldapauth');
 | 
					var LdapStrategy = require('passport-ldapauth')
 | 
				
			||||||
var LocalStrategy = require('passport-local').Strategy;
 | 
					var LocalStrategy = require('passport-local').Strategy
 | 
				
			||||||
var validator = require('validator');
 | 
					var validator = require('validator')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//core
 | 
					// core
 | 
				
			||||||
var config = require('./config.js');
 | 
					var config = require('./config.js')
 | 
				
			||||||
var logger = require("./logger.js");
 | 
					var logger = require('./logger.js')
 | 
				
			||||||
var models = require("./models");
 | 
					var models = require('./models')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function callback(accessToken, refreshToken, profile, done) {
 | 
					function callback (accessToken, refreshToken, profile, done) {
 | 
				
			||||||
    //logger.info(profile.displayName || profile.username);
 | 
					  // logger.info(profile.displayName || profile.username);
 | 
				
			||||||
    var stringifiedProfile = JSON.stringify(profile);
 | 
					  var stringifiedProfile = JSON.stringify(profile)
 | 
				
			||||||
  models.User.findOrCreate({
 | 
					  models.User.findOrCreate({
 | 
				
			||||||
    where: {
 | 
					    where: {
 | 
				
			||||||
      profileid: profile.id.toString()
 | 
					      profileid: profile.id.toString()
 | 
				
			||||||
@ -30,89 +30,88 @@ function callback(accessToken, refreshToken, profile, done) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }).spread(function (user, created) {
 | 
					  }).spread(function (user, created) {
 | 
				
			||||||
    if (user) {
 | 
					    if (user) {
 | 
				
			||||||
            var needSave = false;
 | 
					      var needSave = false
 | 
				
			||||||
            if (user.profile != stringifiedProfile) {
 | 
					      if (user.profile !== stringifiedProfile) {
 | 
				
			||||||
                user.profile = stringifiedProfile;
 | 
					        user.profile = stringifiedProfile
 | 
				
			||||||
                needSave = true;
 | 
					        needSave = true
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
            if (user.accessToken != accessToken) {
 | 
					      if (user.accessToken !== accessToken) {
 | 
				
			||||||
                user.accessToken = accessToken;
 | 
					        user.accessToken = accessToken
 | 
				
			||||||
                needSave = true;
 | 
					        needSave = true
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
            if (user.refreshToken != refreshToken) {
 | 
					      if (user.refreshToken !== refreshToken) {
 | 
				
			||||||
                user.refreshToken = refreshToken;
 | 
					        user.refreshToken = refreshToken
 | 
				
			||||||
                needSave = true;
 | 
					        needSave = true
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      if (needSave) {
 | 
					      if (needSave) {
 | 
				
			||||||
        user.save().then(function () {
 | 
					        user.save().then(function () {
 | 
				
			||||||
                    if (config.debug)
 | 
					          if (config.debug) { logger.info('user login: ' + user.id) }
 | 
				
			||||||
                        logger.info('user login: ' + user.id);
 | 
					          return done(null, user)
 | 
				
			||||||
                    return done(null, user);
 | 
					        })
 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
                if (config.debug)
 | 
					        if (config.debug) { logger.info('user login: ' + user.id) }
 | 
				
			||||||
                    logger.info('user login: ' + user.id);
 | 
					        return done(null, user)
 | 
				
			||||||
                return done(null, user);
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }).catch(function (err) {
 | 
					  }).catch(function (err) {
 | 
				
			||||||
        logger.error('auth callback failed: ' + err);
 | 
					    logger.error('auth callback failed: ' + err)
 | 
				
			||||||
        return done(err, null);
 | 
					    return done(err, null)
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//facebook
 | 
					function registerAuthMethod () {
 | 
				
			||||||
if (config.facebook) {
 | 
					// facebook
 | 
				
			||||||
    module.exports = passport.use(new FacebookStrategy({
 | 
					  if (config.facebook) {
 | 
				
			||||||
 | 
					    passport.use(new FacebookStrategy({
 | 
				
			||||||
      clientID: config.facebook.clientID,
 | 
					      clientID: config.facebook.clientID,
 | 
				
			||||||
      clientSecret: config.facebook.clientSecret,
 | 
					      clientSecret: config.facebook.clientSecret,
 | 
				
			||||||
      callbackURL: config.serverurl + '/auth/facebook/callback'
 | 
					      callbackURL: config.serverurl + '/auth/facebook/callback'
 | 
				
			||||||
    }, callback));
 | 
					    }, callback))
 | 
				
			||||||
}
 | 
					  }
 | 
				
			||||||
//twitter
 | 
					// twitter
 | 
				
			||||||
if (config.twitter) {
 | 
					  if (config.twitter) {
 | 
				
			||||||
    passport.use(new TwitterStrategy({
 | 
					    passport.use(new TwitterStrategy({
 | 
				
			||||||
      consumerKey: config.twitter.consumerKey,
 | 
					      consumerKey: config.twitter.consumerKey,
 | 
				
			||||||
      consumerSecret: config.twitter.consumerSecret,
 | 
					      consumerSecret: config.twitter.consumerSecret,
 | 
				
			||||||
      callbackURL: config.serverurl + '/auth/twitter/callback'
 | 
					      callbackURL: config.serverurl + '/auth/twitter/callback'
 | 
				
			||||||
    }, callback));
 | 
					    }, callback))
 | 
				
			||||||
}
 | 
					  }
 | 
				
			||||||
//github
 | 
					// github
 | 
				
			||||||
if (config.github) {
 | 
					  if (config.github) {
 | 
				
			||||||
    passport.use(new GithubStrategy({
 | 
					    passport.use(new GithubStrategy({
 | 
				
			||||||
      clientID: config.github.clientID,
 | 
					      clientID: config.github.clientID,
 | 
				
			||||||
      clientSecret: config.github.clientSecret,
 | 
					      clientSecret: config.github.clientSecret,
 | 
				
			||||||
      callbackURL: config.serverurl + '/auth/github/callback'
 | 
					      callbackURL: config.serverurl + '/auth/github/callback'
 | 
				
			||||||
    }, callback));
 | 
					    }, callback))
 | 
				
			||||||
}
 | 
					  }
 | 
				
			||||||
//gitlab
 | 
					// gitlab
 | 
				
			||||||
if (config.gitlab) {
 | 
					  if (config.gitlab) {
 | 
				
			||||||
    passport.use(new GitlabStrategy({
 | 
					    passport.use(new GitlabStrategy({
 | 
				
			||||||
      baseURL: config.gitlab.baseURL,
 | 
					      baseURL: config.gitlab.baseURL,
 | 
				
			||||||
      clientID: config.gitlab.clientID,
 | 
					      clientID: config.gitlab.clientID,
 | 
				
			||||||
      clientSecret: config.gitlab.clientSecret,
 | 
					      clientSecret: config.gitlab.clientSecret,
 | 
				
			||||||
      callbackURL: config.serverurl + '/auth/gitlab/callback'
 | 
					      callbackURL: config.serverurl + '/auth/gitlab/callback'
 | 
				
			||||||
    }, callback));
 | 
					    }, callback))
 | 
				
			||||||
}
 | 
					  }
 | 
				
			||||||
//dropbox
 | 
					// dropbox
 | 
				
			||||||
if (config.dropbox) {
 | 
					  if (config.dropbox) {
 | 
				
			||||||
    passport.use(new DropboxStrategy({
 | 
					    passport.use(new DropboxStrategy({
 | 
				
			||||||
      apiVersion: '2',
 | 
					      apiVersion: '2',
 | 
				
			||||||
      clientID: config.dropbox.clientID,
 | 
					      clientID: config.dropbox.clientID,
 | 
				
			||||||
      clientSecret: config.dropbox.clientSecret,
 | 
					      clientSecret: config.dropbox.clientSecret,
 | 
				
			||||||
      callbackURL: config.serverurl + '/auth/dropbox/callback'
 | 
					      callbackURL: config.serverurl + '/auth/dropbox/callback'
 | 
				
			||||||
    }, callback));
 | 
					    }, callback))
 | 
				
			||||||
}
 | 
					  }
 | 
				
			||||||
//google
 | 
					// google
 | 
				
			||||||
if (config.google) {
 | 
					  if (config.google) {
 | 
				
			||||||
    passport.use(new GoogleStrategy({
 | 
					    passport.use(new GoogleStrategy({
 | 
				
			||||||
      clientID: config.google.clientID,
 | 
					      clientID: config.google.clientID,
 | 
				
			||||||
      clientSecret: config.google.clientSecret,
 | 
					      clientSecret: config.google.clientSecret,
 | 
				
			||||||
      callbackURL: config.serverurl + '/auth/google/callback'
 | 
					      callbackURL: config.serverurl + '/auth/google/callback'
 | 
				
			||||||
    }, callback));
 | 
					    }, callback))
 | 
				
			||||||
}
 | 
					  }
 | 
				
			||||||
// ldap
 | 
					// ldap
 | 
				
			||||||
if (config.ldap) {
 | 
					  if (config.ldap) {
 | 
				
			||||||
    passport.use(new LdapStrategy({
 | 
					    passport.use(new LdapStrategy({
 | 
				
			||||||
      server: {
 | 
					      server: {
 | 
				
			||||||
        url: config.ldap.url || null,
 | 
					        url: config.ldap.url || null,
 | 
				
			||||||
@ -122,9 +121,9 @@ if (config.ldap) {
 | 
				
			|||||||
        searchFilter: config.ldap.searchFilter || null,
 | 
					        searchFilter: config.ldap.searchFilter || null,
 | 
				
			||||||
        searchAttributes: config.ldap.searchAttributes || null,
 | 
					        searchAttributes: config.ldap.searchAttributes || null,
 | 
				
			||||||
        tlsOptions: config.ldap.tlsOptions || null
 | 
					        tlsOptions: config.ldap.tlsOptions || null
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    },
 | 
					    function (user, done) {
 | 
				
			||||||
    function(user, done) {
 | 
					 | 
				
			||||||
      var profile = {
 | 
					      var profile = {
 | 
				
			||||||
        id: 'LDAP-' + user.uidNumber,
 | 
					        id: 'LDAP-' + user.uidNumber,
 | 
				
			||||||
        username: user.uid,
 | 
					        username: user.uid,
 | 
				
			||||||
@ -132,59 +131,62 @@ if (config.ldap) {
 | 
				
			|||||||
        emails: user.mail ? [user.mail] : [],
 | 
					        emails: user.mail ? [user.mail] : [],
 | 
				
			||||||
        avatarUrl: null,
 | 
					        avatarUrl: null,
 | 
				
			||||||
        profileUrl: null,
 | 
					        profileUrl: null,
 | 
				
			||||||
            provider: 'ldap',
 | 
					        provider: 'ldap'
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
        var stringifiedProfile = JSON.stringify(profile);
 | 
					      var stringifiedProfile = JSON.stringify(profile)
 | 
				
			||||||
      models.User.findOrCreate({
 | 
					      models.User.findOrCreate({
 | 
				
			||||||
        where: {
 | 
					        where: {
 | 
				
			||||||
          profileid: profile.id.toString()
 | 
					          profileid: profile.id.toString()
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        defaults: {
 | 
					        defaults: {
 | 
				
			||||||
                profile: stringifiedProfile,
 | 
					          profile: stringifiedProfile
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }).spread(function (user, created) {
 | 
					      }).spread(function (user, created) {
 | 
				
			||||||
        if (user) {
 | 
					        if (user) {
 | 
				
			||||||
                var needSave = false;
 | 
					          var needSave = false
 | 
				
			||||||
                if (user.profile != stringifiedProfile) {
 | 
					          if (user.profile !== stringifiedProfile) {
 | 
				
			||||||
                    user.profile = stringifiedProfile;
 | 
					            user.profile = stringifiedProfile
 | 
				
			||||||
                    needSave = true;
 | 
					            needSave = true
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
          if (needSave) {
 | 
					          if (needSave) {
 | 
				
			||||||
            user.save().then(function () {
 | 
					            user.save().then(function () {
 | 
				
			||||||
                        if (config.debug)
 | 
					              if (config.debug) { logger.info('user login: ' + user.id) }
 | 
				
			||||||
                            logger.info('user login: ' + user.id);
 | 
					              return done(null, user)
 | 
				
			||||||
                        return done(null, user);
 | 
					            })
 | 
				
			||||||
                    });
 | 
					 | 
				
			||||||
          } else {
 | 
					          } else {
 | 
				
			||||||
                    if (config.debug)
 | 
					            if (config.debug) { logger.info('user login: ' + user.id) }
 | 
				
			||||||
                        logger.info('user login: ' + user.id);
 | 
					            return done(null, user)
 | 
				
			||||||
                    return done(null, user);
 | 
					 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }).catch(function (err) {
 | 
					      }).catch(function (err) {
 | 
				
			||||||
            logger.error('ldap auth failed: ' + err);
 | 
					        logger.error('ldap auth failed: ' + err)
 | 
				
			||||||
            return done(err, null);
 | 
					        return done(err, null)
 | 
				
			||||||
        });
 | 
					      })
 | 
				
			||||||
    }));
 | 
					    }))
 | 
				
			||||||
}
 | 
					  }
 | 
				
			||||||
// email
 | 
					// email
 | 
				
			||||||
if (config.email) {
 | 
					  if (config.email) {
 | 
				
			||||||
    passport.use(new LocalStrategy({
 | 
					    passport.use(new LocalStrategy({
 | 
				
			||||||
      usernameField: 'email'
 | 
					      usernameField: 'email'
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    function(email, password, done) {
 | 
					    function (email, password, done) {
 | 
				
			||||||
        if (!validator.isEmail(email)) return done(null, false);
 | 
					      if (!validator.isEmail(email)) return done(null, false)
 | 
				
			||||||
      models.User.findOne({
 | 
					      models.User.findOne({
 | 
				
			||||||
        where: {
 | 
					        where: {
 | 
				
			||||||
          email: email
 | 
					          email: email
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }).then(function (user) {
 | 
					      }).then(function (user) {
 | 
				
			||||||
            if (!user) return done(null, false);
 | 
					        if (!user) return done(null, false)
 | 
				
			||||||
            if (!user.verifyPassword(password)) return done(null, false);
 | 
					        if (!user.verifyPassword(password)) return done(null, false)
 | 
				
			||||||
            return done(null, user);
 | 
					        return done(null, user)
 | 
				
			||||||
      }).catch(function (err) {
 | 
					      }).catch(function (err) {
 | 
				
			||||||
            logger.error(err);
 | 
					        logger.error(err)
 | 
				
			||||||
            return done(err);
 | 
					        return done(err)
 | 
				
			||||||
        });
 | 
					      })
 | 
				
			||||||
    }));
 | 
					    }))
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = {
 | 
				
			||||||
 | 
					  registerAuthMethod: registerAuthMethod
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										204
									
								
								lib/config.js
									
									
									
									
									
								
							
							
						
						
									
										204
									
								
								lib/config.js
									
									
									
									
									
								
							@ -1,118 +1,117 @@
 | 
				
			|||||||
// external modules
 | 
					// external modules
 | 
				
			||||||
var fs = require('fs');
 | 
					var fs = require('fs')
 | 
				
			||||||
var path = require('path');
 | 
					var path = require('path')
 | 
				
			||||||
var fs = require('fs');
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// configs
 | 
					// configs
 | 
				
			||||||
var env = process.env.NODE_ENV || 'development';
 | 
					var env = process.env.NODE_ENV || 'development'
 | 
				
			||||||
var config = require(path.join(__dirname, '..', 'config.json'))[env];
 | 
					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'));
 | 
					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
 | 
					// Create function that reads docker secrets but fails fast in case of a non docker environment
 | 
				
			||||||
var handleDockerSecret = fs.existsSync('/run/secrets/') ? function(secret) {
 | 
					var handleDockerSecret = fs.existsSync('/run/secrets/') ? function (secret) {
 | 
				
			||||||
    return fs.existsSync('/run/secrets/' + secret) ? fs.readFileSync('/run/secrets/' + secret) : null;
 | 
					  return fs.existsSync('/run/secrets/' + secret) ? fs.readFileSync('/run/secrets/' + secret) : null
 | 
				
			||||||
} : function() {
 | 
					} : function () {
 | 
				
			||||||
  return null
 | 
					  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;
 | 
					// url
 | 
				
			||||||
defaultpermission = permissions.indexOf(defaultpermission) != -1 ? defaultpermission : 'editable';
 | 
					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
 | 
					// db
 | 
				
			||||||
var dburl = process.env.HMD_DB_URL || process.env.DATABASE_URL || config.dburl;
 | 
					var dburl = process.env.HMD_DB_URL || process.env.DATABASE_URL || config.dburl
 | 
				
			||||||
var db = config.db || {};
 | 
					var db = config.db || {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ssl path
 | 
					// ssl path
 | 
				
			||||||
var sslkeypath = (fs.existsSync('/run/secrets/key.pem') ? '/run/secrets/key.pem' : null) || config.sslkeypath || '';
 | 
					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 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 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 || '';
 | 
					var dhparampath = (fs.existsSync('/run/secrets/dhparam.pem') ? '/run/secrets/dhparam.pem' : null) || config.dhparampath || ''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// other path
 | 
					// other path
 | 
				
			||||||
var tmppath = config.tmppath || './tmp';
 | 
					var tmppath = config.tmppath || './tmp'
 | 
				
			||||||
var defaultnotepath = config.defaultnotepath || './public/default.md';
 | 
					var defaultnotepath = config.defaultnotepath || './public/default.md'
 | 
				
			||||||
var docspath = config.docspath || './public/docs';
 | 
					var docspath = config.docspath || './public/docs'
 | 
				
			||||||
var indexpath = config.indexpath || './public/views/index.ejs';
 | 
					var indexpath = config.indexpath || './public/views/index.ejs'
 | 
				
			||||||
var hackmdpath = config.hackmdpath || './public/views/hackmd.ejs';
 | 
					var hackmdpath = config.hackmdpath || './public/views/hackmd.ejs'
 | 
				
			||||||
var errorpath = config.errorpath || './public/views/error.ejs';
 | 
					var errorpath = config.errorpath || './public/views/error.ejs'
 | 
				
			||||||
var prettypath = config.prettypath || './public/views/pretty.ejs';
 | 
					var prettypath = config.prettypath || './public/views/pretty.ejs'
 | 
				
			||||||
var slidepath = config.slidepath || './public/views/slide.ejs';
 | 
					var slidepath = config.slidepath || './public/views/slide.ejs'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// session
 | 
					// session
 | 
				
			||||||
var sessionname = config.sessionname || 'connect.sid';
 | 
					var sessionname = config.sessionname || 'connect.sid'
 | 
				
			||||||
var sessionsecret = handleDockerSecret('sessionsecret') || config.sessionsecret || 'secret';
 | 
					var sessionsecret = handleDockerSecret('sessionsecret') || config.sessionsecret || 'secret'
 | 
				
			||||||
var sessionlife = config.sessionlife || 14 * 24 * 60 * 60 * 1000; //14 days
 | 
					var sessionlife = config.sessionlife || 14 * 24 * 60 * 60 * 1000 // 14 days
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// static files
 | 
					// static files
 | 
				
			||||||
var staticcachetime = config.staticcachetime || 1 * 24 * 60 * 60 * 1000; // 1 day
 | 
					var staticcachetime = config.staticcachetime || 1 * 24 * 60 * 60 * 1000 // 1 day
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// socket.io
 | 
					// socket.io
 | 
				
			||||||
var heartbeatinterval = config.heartbeatinterval || 5000;
 | 
					var heartbeatinterval = config.heartbeatinterval || 5000
 | 
				
			||||||
var heartbeattimeout = config.heartbeattimeout || 10000;
 | 
					var heartbeattimeout = config.heartbeattimeout || 10000
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// document
 | 
					// document
 | 
				
			||||||
var documentmaxlength = config.documentmaxlength || 100000;
 | 
					var documentmaxlength = config.documentmaxlength || 100000
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// image upload setting, available options are imgur/s3/filesystem
 | 
					// image upload setting, available options are imgur/s3/filesystem
 | 
				
			||||||
var imageUploadType = process.env.HMD_IMAGE_UPLOAD_TYPE || config.imageUploadType || 'imgur';
 | 
					var imageUploadType = process.env.HMD_IMAGE_UPLOAD_TYPE || config.imageUploadType || 'imgur'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
config.s3 = config.s3 || {};
 | 
					config.s3 = config.s3 || {}
 | 
				
			||||||
var s3 = {
 | 
					var s3 = {
 | 
				
			||||||
  accessKeyId: handleDockerSecret('s3_acccessKeyId') || process.env.HMD_S3_ACCESS_KEY_ID || config.s3.accessKeyId,
 | 
					  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,
 | 
					  secretAccessKey: handleDockerSecret('s3_secretAccessKey') || process.env.HMD_S3_SECRET_ACCESS_KEY || config.s3.secretAccessKey,
 | 
				
			||||||
  region: process.env.HMD_S3_REGION || config.s3.region
 | 
					  region: process.env.HMD_S3_REGION || config.s3.region
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
var s3bucket = process.env.HMD_S3_BUCKET || config.s3.bucket;
 | 
					var s3bucket = process.env.HMD_S3_BUCKET || config.s3.bucket
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// auth
 | 
					// 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')) ? {
 | 
					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,
 | 
					  clientID: handleDockerSecret('facebook_clientID') || process.env.HMD_FACEBOOK_CLIENTID,
 | 
				
			||||||
  clientSecret: handleDockerSecret('facebook_clientSecret') || process.env.HMD_FACEBOOK_CLIENTSECRET
 | 
					  clientSecret: handleDockerSecret('facebook_clientSecret') || process.env.HMD_FACEBOOK_CLIENTSECRET
 | 
				
			||||||
} : config.facebook || false;
 | 
					} : 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')) ? {
 | 
					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,
 | 
					  consumerKey: handleDockerSecret('twitter_consumerKey') || process.env.HMD_TWITTER_CONSUMERKEY,
 | 
				
			||||||
  consumerSecret: handleDockerSecret('twitter_consumerSecret') || process.env.HMD_TWITTER_CONSUMERSECRET
 | 
					  consumerSecret: handleDockerSecret('twitter_consumerSecret') || process.env.HMD_TWITTER_CONSUMERSECRET
 | 
				
			||||||
} : config.twitter || false;
 | 
					} : 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')) ? {
 | 
					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,
 | 
					  clientID: handleDockerSecret('github_clientID') || process.env.HMD_GITHUB_CLIENTID,
 | 
				
			||||||
  clientSecret: handleDockerSecret('github_clientSecret') || process.env.HMD_GITHUB_CLIENTSECRET
 | 
					  clientSecret: handleDockerSecret('github_clientSecret') || process.env.HMD_GITHUB_CLIENTSECRET
 | 
				
			||||||
} : config.github || false;
 | 
					} : 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')) ? {
 | 
					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,
 | 
					  baseURL: process.env.HMD_GITLAB_BASEURL,
 | 
				
			||||||
  clientID: handleDockerSecret('gitlab_clientID') || process.env.HMD_GITLAB_CLIENTID,
 | 
					  clientID: handleDockerSecret('gitlab_clientID') || process.env.HMD_GITLAB_CLIENTID,
 | 
				
			||||||
  clientSecret: handleDockerSecret('gitlab_clientSecret') || process.env.HMD_GITLAB_CLIENTSECRET
 | 
					  clientSecret: handleDockerSecret('gitlab_clientSecret') || process.env.HMD_GITLAB_CLIENTSECRET
 | 
				
			||||||
} : config.gitlab || false;
 | 
					} : 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'))) ? {
 | 
					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,
 | 
					  clientID: handleDockerSecret('dropbox_clientID') || process.env.HMD_DROPBOX_CLIENTID,
 | 
				
			||||||
  clientSecret: handleDockerSecret('dropbox_clientSecret') || process.env.HMD_DROPBOX_CLIENTSECRET
 | 
					  clientSecret: handleDockerSecret('dropbox_clientSecret') || process.env.HMD_DROPBOX_CLIENTSECRET
 | 
				
			||||||
} : (config.dropbox && config.dropbox.clientID && config.dropbox.clientSecret && config.dropbox) || false;
 | 
					} : (config.dropbox && config.dropbox.clientID && config.dropbox.clientSecret && config.dropbox) || false
 | 
				
			||||||
var google = ((process.env.HMD_GOOGLE_CLIENTID && process.env.HMD_GOOGLE_CLIENTSECRET)
 | 
					var google = ((process.env.HMD_GOOGLE_CLIENTID && process.env.HMD_GOOGLE_CLIENTSECRET) ||
 | 
				
			||||||
              || (fs.existsSync('/run/secrets/google_clientID') && fs.existsSync('/run/secrets/google_clientSecret'))) ? {
 | 
					              (fs.existsSync('/run/secrets/google_clientID') && fs.existsSync('/run/secrets/google_clientSecret'))) ? {
 | 
				
			||||||
                clientID: handleDockerSecret('google_clientID') || process.env.HMD_GOOGLE_CLIENTID,
 | 
					                clientID: handleDockerSecret('google_clientID') || process.env.HMD_GOOGLE_CLIENTID,
 | 
				
			||||||
                clientSecret: handleDockerSecret('google_clientSecret') || process.env.HMD_GOOGLE_CLIENTSECRET
 | 
					                clientSecret: handleDockerSecret('google_clientSecret') || process.env.HMD_GOOGLE_CLIENTSECRET
 | 
				
			||||||
} : (config.google && config.google.clientID && config.google.clientSecret && config.google) || false;
 | 
					              } : (config.google && config.google.clientID && config.google.clientSecret && config.google) || false
 | 
				
			||||||
var ldap = config.ldap || ((
 | 
					var ldap = config.ldap || ((
 | 
				
			||||||
    process.env.HMD_LDAP_URL ||
 | 
					    process.env.HMD_LDAP_URL ||
 | 
				
			||||||
    process.env.HMD_LDAP_BINDDN ||
 | 
					    process.env.HMD_LDAP_BINDDN ||
 | 
				
			||||||
@ -123,59 +122,50 @@ var ldap = config.ldap || ((
 | 
				
			|||||||
    process.env.HMD_LDAP_SEARCHATTRIBUTES ||
 | 
					    process.env.HMD_LDAP_SEARCHATTRIBUTES ||
 | 
				
			||||||
    process.env.HMD_LDAP_TLS_CA ||
 | 
					    process.env.HMD_LDAP_TLS_CA ||
 | 
				
			||||||
    process.env.HMD_LDAP_PROVIDERNAME
 | 
					    process.env.HMD_LDAP_PROVIDERNAME
 | 
				
			||||||
) ? {} : false);
 | 
					) ? {} : false)
 | 
				
			||||||
if (process.env.HMD_LDAP_URL)
 | 
					if (process.env.HMD_LDAP_URL) { ldap.url = 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_BINDDN)
 | 
					if (process.env.HMD_LDAP_BINDCREDENTIALS) { ldap.bindCredentials = process.env.HMD_LDAP_BINDCREDENTIALS }
 | 
				
			||||||
    ldap.bindDn = process.env.HMD_LDAP_BINDDN;
 | 
					if (process.env.HMD_LDAP_TOKENSECRET) { ldap.tokenSecret = process.env.HMD_LDAP_TOKENSECRET }
 | 
				
			||||||
if (process.env.HMD_LDAP_BINDCREDENTIALS)
 | 
					if (process.env.HMD_LDAP_SEARCHBASE) { ldap.searchBase = process.env.HMD_LDAP_SEARCHBASE }
 | 
				
			||||||
    ldap.bindCredentials = process.env.HMD_LDAP_BINDCREDENTIALS;
 | 
					if (process.env.HMD_LDAP_SEARCHFILTER) { ldap.searchFilter = process.env.HMD_LDAP_SEARCHFILTER }
 | 
				
			||||||
if (process.env.HMD_LDAP_TOKENSECRET)
 | 
					if (process.env.HMD_LDAP_SEARCHATTRIBUTES) { ldap.searchAttributes = process.env.HMD_LDAP_SEARCHATTRIBUTES }
 | 
				
			||||||
    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) {
 | 
					if (process.env.HMD_LDAP_TLS_CA) {
 | 
				
			||||||
  var ca = {
 | 
					  var ca = {
 | 
				
			||||||
    ca: process.env.HMD_LDAP_TLS_CA.split(',')
 | 
					    ca: process.env.HMD_LDAP_TLS_CA.split(',')
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
    ldap.tlsOptions = ldap.tlsOptions ? Object.assign(ldap.tlsOptions, ca) : ca;
 | 
					  ldap.tlsOptions = ldap.tlsOptions ? Object.assign(ldap.tlsOptions, ca) : ca
 | 
				
			||||||
  if (Array.isArray(ldap.tlsOptions.ca) && ldap.tlsOptions.ca.length > 0) {
 | 
					  if (Array.isArray(ldap.tlsOptions.ca) && ldap.tlsOptions.ca.length > 0) {
 | 
				
			||||||
        var i, len, results;
 | 
					    var i, len, results
 | 
				
			||||||
        results = [];
 | 
					    results = []
 | 
				
			||||||
    for (i = 0, len = ldap.tlsOptions.ca.length; i < len; i++) {
 | 
					    for (i = 0, len = ldap.tlsOptions.ca.length; i < len; i++) {
 | 
				
			||||||
            results.push(fs.readFileSync(ldap.tlsOptions.ca[i], 'utf8'));
 | 
					      results.push(fs.readFileSync(ldap.tlsOptions.ca[i], 'utf8'))
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
        ldap.tlsOptions.ca = results;
 | 
					    ldap.tlsOptions.ca = results
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
if (process.env.HMD_LDAP_PROVIDERNAME) {
 | 
					if (process.env.HMD_LDAP_PROVIDERNAME) {
 | 
				
			||||||
    ldap.providerName = 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 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 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);
 | 
					var allowemailregister = process.env.HMD_ALLOW_EMAIL_REGISTER ? (process.env.HMD_ALLOW_EMAIL_REGISTER === 'true') : ((typeof config.allowemailregister === 'boolean') ? config.allowemailregister : true)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function getserverurl() {
 | 
					function getserverurl () {
 | 
				
			||||||
    var url = '';
 | 
					  var url = ''
 | 
				
			||||||
  if (domain) {
 | 
					  if (domain) {
 | 
				
			||||||
        var protocol = protocolusessl ? 'https://' : 'http://';
 | 
					    var protocol = protocolusessl ? 'https://' : 'http://'
 | 
				
			||||||
        url = protocol + domain;
 | 
					    url = protocol + domain
 | 
				
			||||||
        if (urladdport && ((usessl && port != 443) || (!usessl && port != 80)))
 | 
					    if (urladdport && ((usessl && port !== 443) || (!usessl && port !== 80))) { url += ':' + port }
 | 
				
			||||||
            url += ':' + port;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
    if (urlpath)
 | 
					  if (urlpath) { url += '/' + urlpath }
 | 
				
			||||||
        url += '/' + urlpath;
 | 
					  return url
 | 
				
			||||||
    return url;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var version = '0.5.0';
 | 
					var version = '0.5.0'
 | 
				
			||||||
var minimumCompatibleVersion = '0.5.0';
 | 
					var minimumCompatibleVersion = '0.5.0'
 | 
				
			||||||
var maintenance = true;
 | 
					var maintenance = true
 | 
				
			||||||
var cwd = path.join(__dirname, '..');
 | 
					var cwd = path.join(__dirname, '..')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = {
 | 
					module.exports = {
 | 
				
			||||||
  version: version,
 | 
					  version: version,
 | 
				
			||||||
@ -225,4 +215,4 @@ module.exports = {
 | 
				
			|||||||
  imageUploadType: imageUploadType,
 | 
					  imageUploadType: imageUploadType,
 | 
				
			||||||
  s3: s3,
 | 
					  s3: s3,
 | 
				
			||||||
  s3bucket: s3bucket
 | 
					  s3bucket: s3bucket
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										187
									
								
								lib/history.js
									
									
									
									
									
								
							
							
						
						
									
										187
									
								
								lib/history.js
									
									
									
									
									
								
							@ -1,42 +1,44 @@
 | 
				
			|||||||
//history
 | 
					// history
 | 
				
			||||||
//external modules
 | 
					// external modules
 | 
				
			||||||
var async = require('async');
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
//core
 | 
					// core
 | 
				
			||||||
var config = require("./config.js");
 | 
					var config = require('./config.js')
 | 
				
			||||||
var logger = require("./logger.js");
 | 
					var logger = require('./logger.js')
 | 
				
			||||||
var response = require("./response.js");
 | 
					var response = require('./response.js')
 | 
				
			||||||
var models = require("./models");
 | 
					var models = require('./models')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//public
 | 
					// public
 | 
				
			||||||
var History = {
 | 
					var History = {
 | 
				
			||||||
  historyGet: historyGet,
 | 
					  historyGet: historyGet,
 | 
				
			||||||
  historyPost: historyPost,
 | 
					  historyPost: historyPost,
 | 
				
			||||||
  historyDelete: historyDelete,
 | 
					  historyDelete: historyDelete,
 | 
				
			||||||
  updateHistory: updateHistory
 | 
					  updateHistory: updateHistory
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function getHistory(userid, callback) {
 | 
					function getHistory (userid, callback) {
 | 
				
			||||||
  models.User.findOne({
 | 
					  models.User.findOne({
 | 
				
			||||||
    where: {
 | 
					    where: {
 | 
				
			||||||
      id: userid
 | 
					      id: userid
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }).then(function (user) {
 | 
					  }).then(function (user) {
 | 
				
			||||||
        if (!user)
 | 
					    if (!user) {
 | 
				
			||||||
            return callback(null, null);
 | 
					      return callback(null, null)
 | 
				
			||||||
        var history = {};
 | 
					    }
 | 
				
			||||||
        if (user.history)
 | 
					    var history = {}
 | 
				
			||||||
            history = parseHistoryToObject(JSON.parse(user.history));
 | 
					    if (user.history) {
 | 
				
			||||||
        if (config.debug)
 | 
					      history = parseHistoryToObject(JSON.parse(user.history))
 | 
				
			||||||
            logger.info('read history success: ' + user.id);
 | 
					    }
 | 
				
			||||||
        return callback(null, history);
 | 
					    if (config.debug) {
 | 
				
			||||||
 | 
					      logger.info('read history success: ' + user.id)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return callback(null, history)
 | 
				
			||||||
  }).catch(function (err) {
 | 
					  }).catch(function (err) {
 | 
				
			||||||
        logger.error('read history failed: ' + err);
 | 
					    logger.error('read history failed: ' + err)
 | 
				
			||||||
        return callback(err, null);
 | 
					    return callback(err, null)
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function setHistory(userid, history, callback) {
 | 
					function setHistory (userid, history, callback) {
 | 
				
			||||||
  models.User.update({
 | 
					  models.User.update({
 | 
				
			||||||
    history: JSON.stringify(parseHistoryToArray(history))
 | 
					    history: JSON.stringify(parseHistoryToArray(history))
 | 
				
			||||||
  }, {
 | 
					  }, {
 | 
				
			||||||
@ -44,129 +46,130 @@ function setHistory(userid, history, callback) {
 | 
				
			|||||||
      id: userid
 | 
					      id: userid
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }).then(function (count) {
 | 
					  }).then(function (count) {
 | 
				
			||||||
        return callback(null, count);
 | 
					    return callback(null, count)
 | 
				
			||||||
  }).catch(function (err) {
 | 
					  }).catch(function (err) {
 | 
				
			||||||
        logger.error('set history failed: ' + err);
 | 
					    logger.error('set history failed: ' + err)
 | 
				
			||||||
        return callback(err, null);
 | 
					    return callback(err, null)
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function updateHistory(userid, noteId, document, time) {
 | 
					function updateHistory (userid, noteId, document, time) {
 | 
				
			||||||
  if (userid && noteId && typeof document !== 'undefined') {
 | 
					  if (userid && noteId && typeof document !== 'undefined') {
 | 
				
			||||||
    getHistory(userid, function (err, history) {
 | 
					    getHistory(userid, function (err, history) {
 | 
				
			||||||
            if (err || !history) return;
 | 
					      if (err || !history) return
 | 
				
			||||||
      if (!history[noteId]) {
 | 
					      if (!history[noteId]) {
 | 
				
			||||||
                history[noteId] = {};
 | 
					        history[noteId] = {}
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
            var noteHistory = history[noteId];
 | 
					      var noteHistory = history[noteId]
 | 
				
			||||||
            var noteInfo = models.Note.parseNoteInfo(document);
 | 
					      var noteInfo = models.Note.parseNoteInfo(document)
 | 
				
			||||||
            noteHistory.id = noteId;
 | 
					      noteHistory.id = noteId
 | 
				
			||||||
            noteHistory.text = noteInfo.title;
 | 
					      noteHistory.text = noteInfo.title
 | 
				
			||||||
            noteHistory.time = time || Date.now();
 | 
					      noteHistory.time = time || Date.now()
 | 
				
			||||||
            noteHistory.tags = noteInfo.tags;
 | 
					      noteHistory.tags = noteInfo.tags
 | 
				
			||||||
      setHistory(userid, history, function (err, count) {
 | 
					      setHistory(userid, history, function (err, count) {
 | 
				
			||||||
                return;
 | 
					        if (err) {
 | 
				
			||||||
            });
 | 
					          logger.log(err)
 | 
				
			||||||
        });
 | 
					        }
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function parseHistoryToArray(history) {
 | 
					function parseHistoryToArray (history) {
 | 
				
			||||||
    var _history = [];
 | 
					  var _history = []
 | 
				
			||||||
  Object.keys(history).forEach(function (key) {
 | 
					  Object.keys(history).forEach(function (key) {
 | 
				
			||||||
        var item = history[key];
 | 
					    var item = history[key]
 | 
				
			||||||
        _history.push(item);
 | 
					    _history.push(item)
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
    return _history;
 | 
					  return _history
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function parseHistoryToObject(history) {
 | 
					function parseHistoryToObject (history) {
 | 
				
			||||||
    var _history = {};
 | 
					  var _history = {}
 | 
				
			||||||
  for (var i = 0, l = history.length; i < l; i++) {
 | 
					  for (var i = 0, l = history.length; i < l; i++) {
 | 
				
			||||||
        var item = history[i];
 | 
					    var item = history[i]
 | 
				
			||||||
        _history[item.id] = item;
 | 
					    _history[item.id] = item
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
    return _history;
 | 
					  return _history
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function historyGet(req, res) {
 | 
					function historyGet (req, res) {
 | 
				
			||||||
  if (req.isAuthenticated()) {
 | 
					  if (req.isAuthenticated()) {
 | 
				
			||||||
    getHistory(req.user.id, function (err, history) {
 | 
					    getHistory(req.user.id, function (err, history) {
 | 
				
			||||||
            if (err) return response.errorInternalError(res);
 | 
					      if (err) return response.errorInternalError(res)
 | 
				
			||||||
            if (!history) return response.errorNotFound(res);
 | 
					      if (!history) return response.errorNotFound(res)
 | 
				
			||||||
      res.send({
 | 
					      res.send({
 | 
				
			||||||
        history: parseHistoryToArray(history)
 | 
					        history: parseHistoryToArray(history)
 | 
				
			||||||
            });
 | 
					      })
 | 
				
			||||||
        });
 | 
					    })
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
        return response.errorForbidden(res);
 | 
					    return response.errorForbidden(res)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function historyPost(req, res) {
 | 
					function historyPost (req, res) {
 | 
				
			||||||
  if (req.isAuthenticated()) {
 | 
					  if (req.isAuthenticated()) {
 | 
				
			||||||
        var noteId = req.params.noteId;
 | 
					    var noteId = req.params.noteId
 | 
				
			||||||
    if (!noteId) {
 | 
					    if (!noteId) {
 | 
				
			||||||
            if (typeof req.body['history'] === 'undefined') return response.errorBadRequest(res);
 | 
					      if (typeof req.body['history'] === 'undefined') return response.errorBadRequest(res)
 | 
				
			||||||
            if (config.debug)
 | 
					      if (config.debug) { logger.info('SERVER received history from [' + req.user.id + ']: ' + req.body.history) }
 | 
				
			||||||
                logger.info('SERVER received history from [' + req.user.id + ']: ' + req.body.history);
 | 
					 | 
				
			||||||
      try {
 | 
					      try {
 | 
				
			||||||
                var history = JSON.parse(req.body.history);
 | 
					        var history = JSON.parse(req.body.history)
 | 
				
			||||||
      } catch (err) {
 | 
					      } catch (err) {
 | 
				
			||||||
                return response.errorBadRequest(res);
 | 
					        return response.errorBadRequest(res)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      if (Array.isArray(history)) {
 | 
					      if (Array.isArray(history)) {
 | 
				
			||||||
        setHistory(req.user.id, history, function (err, count) {
 | 
					        setHistory(req.user.id, history, function (err, count) {
 | 
				
			||||||
                    if (err) return response.errorInternalError(res);
 | 
					          if (err) return response.errorInternalError(res)
 | 
				
			||||||
                    res.end();
 | 
					          res.end()
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
                return response.errorBadRequest(res);
 | 
					        return response.errorBadRequest(res)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
            if (typeof req.body['pinned'] === 'undefined') return response.errorBadRequest(res);
 | 
					      if (typeof req.body['pinned'] === 'undefined') return response.errorBadRequest(res)
 | 
				
			||||||
      getHistory(req.user.id, function (err, history) {
 | 
					      getHistory(req.user.id, function (err, history) {
 | 
				
			||||||
                if (err) return response.errorInternalError(res);
 | 
					        if (err) return response.errorInternalError(res)
 | 
				
			||||||
                if (!history) return response.errorNotFound(res);
 | 
					        if (!history) return response.errorNotFound(res)
 | 
				
			||||||
                if (!history[noteId]) return response.errorNotFound(res);
 | 
					        if (!history[noteId]) return response.errorNotFound(res)
 | 
				
			||||||
        if (req.body.pinned === 'true' || req.body.pinned === 'false') {
 | 
					        if (req.body.pinned === 'true' || req.body.pinned === 'false') {
 | 
				
			||||||
                    history[noteId].pinned = (req.body.pinned === 'true');
 | 
					          history[noteId].pinned = (req.body.pinned === 'true')
 | 
				
			||||||
          setHistory(req.user.id, history, function (err, count) {
 | 
					          setHistory(req.user.id, history, function (err, count) {
 | 
				
			||||||
                        if (err) return response.errorInternalError(res);
 | 
					            if (err) return response.errorInternalError(res)
 | 
				
			||||||
                        res.end();
 | 
					            res.end()
 | 
				
			||||||
                    });
 | 
					          })
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
                    return response.errorBadRequest(res);
 | 
					          return response.errorBadRequest(res)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
            });
 | 
					      })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
        return response.errorForbidden(res);
 | 
					    return response.errorForbidden(res)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function historyDelete(req, res) {
 | 
					function historyDelete (req, res) {
 | 
				
			||||||
  if (req.isAuthenticated()) {
 | 
					  if (req.isAuthenticated()) {
 | 
				
			||||||
        var noteId = req.params.noteId;
 | 
					    var noteId = req.params.noteId
 | 
				
			||||||
    if (!noteId) {
 | 
					    if (!noteId) {
 | 
				
			||||||
      setHistory(req.user.id, [], function (err, count) {
 | 
					      setHistory(req.user.id, [], function (err, count) {
 | 
				
			||||||
                if (err) return response.errorInternalError(res);
 | 
					        if (err) return response.errorInternalError(res)
 | 
				
			||||||
                res.end();
 | 
					        res.end()
 | 
				
			||||||
            });
 | 
					      })
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      getHistory(req.user.id, function (err, history) {
 | 
					      getHistory(req.user.id, function (err, history) {
 | 
				
			||||||
                if (err) return response.errorInternalError(res);
 | 
					        if (err) return response.errorInternalError(res)
 | 
				
			||||||
                if (!history) return response.errorNotFound(res);
 | 
					        if (!history) return response.errorNotFound(res)
 | 
				
			||||||
                delete history[noteId];
 | 
					        delete history[noteId]
 | 
				
			||||||
        setHistory(req.user.id, history, function (err, count) {
 | 
					        setHistory(req.user.id, history, function (err, count) {
 | 
				
			||||||
                    if (err) return response.errorInternalError(res);
 | 
					          if (err) return response.errorInternalError(res)
 | 
				
			||||||
                    res.end();
 | 
					          res.end()
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
            });
 | 
					      })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
        return response.errorForbidden(res);
 | 
					    return response.errorForbidden(res)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = History;
 | 
					module.exports = History
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,23 @@
 | 
				
			|||||||
"use strict";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// external modules
 | 
					// external modules
 | 
				
			||||||
var randomcolor = require('randomcolor');
 | 
					var randomcolor = require('randomcolor')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// core
 | 
					// core
 | 
				
			||||||
module.exports = function(name) {
 | 
					module.exports = function (name) {
 | 
				
			||||||
  var color = randomcolor({
 | 
					  var color = randomcolor({
 | 
				
			||||||
    seed: name,
 | 
					    seed: name,
 | 
				
			||||||
    luminosity: 'dark'
 | 
					    luminosity: 'dark'
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
    var letter = name.substring(0, 1).toUpperCase();
 | 
					  var letter = name.substring(0, 1).toUpperCase()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var svg = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>';
 | 
					  var svg = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>'
 | 
				
			||||||
    svg += '<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="96" width="96" version="1.1" viewBox="0 0 96 96">';
 | 
					  svg += '<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="96" width="96" version="1.1" viewBox="0 0 96 96">'
 | 
				
			||||||
    svg += '<g>';
 | 
					  svg += '<g>'
 | 
				
			||||||
    svg += '<rect width="96" height="96" fill="' + color + '" />';
 | 
					  svg += '<rect width="96" height="96" fill="' + color + '" />'
 | 
				
			||||||
    svg += '<text font-size="64px" font-family="sans-serif" text-anchor="middle" fill="#ffffff">';
 | 
					  svg += '<text font-size="64px" font-family="sans-serif" text-anchor="middle" fill="#ffffff">'
 | 
				
			||||||
    svg += '<tspan x="48" y="72" stroke-width=".26458px" fill="#ffffff">' + letter + '</tspan>';
 | 
					  svg += '<tspan x="48" y="72" stroke-width=".26458px" fill="#ffffff">' + letter + '</tspan>'
 | 
				
			||||||
    svg += '</text>';
 | 
					  svg += '</text>'
 | 
				
			||||||
    svg += '</g>';
 | 
					  svg += '</g>'
 | 
				
			||||||
    svg += '</svg>';
 | 
					  svg += '</svg>'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return 'data:image/svg+xml;base64,' + new Buffer(svg).toString('base64');
 | 
					  return 'data:image/svg+xml;base64,' + new Buffer(svg).toString('base64')
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
var winston = require('winston');
 | 
					var winston = require('winston')
 | 
				
			||||||
winston.emitErrs = true;
 | 
					winston.emitErrs = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var logger = new winston.Logger({
 | 
					var logger = new winston.Logger({
 | 
				
			||||||
  transports: [
 | 
					  transports: [
 | 
				
			||||||
@ -12,11 +12,11 @@ var logger = new winston.Logger({
 | 
				
			|||||||
    })
 | 
					    })
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
  exitOnError: false
 | 
					  exitOnError: false
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = logger;
 | 
					module.exports = logger
 | 
				
			||||||
module.exports.stream = {
 | 
					module.exports.stream = {
 | 
				
			||||||
    write: function(message, encoding){
 | 
					  write: function (message, encoding) {
 | 
				
			||||||
        logger.info(message);
 | 
					    logger.info(message)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,15 +1,11 @@
 | 
				
			|||||||
"use strict";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
module.exports = {
 | 
					module.exports = {
 | 
				
			||||||
  up: function (queryInterface, Sequelize) {
 | 
					  up: function (queryInterface, Sequelize) {
 | 
				
			||||||
        queryInterface.addColumn('Users', 'accessToken', Sequelize.STRING);
 | 
					    queryInterface.addColumn('Users', 'accessToken', Sequelize.STRING)
 | 
				
			||||||
        queryInterface.addColumn('Users', 'refreshToken', Sequelize.STRING);
 | 
					    queryInterface.addColumn('Users', 'refreshToken', Sequelize.STRING)
 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  down: function (queryInterface, Sequelize) {
 | 
					  down: function (queryInterface, Sequelize) {
 | 
				
			||||||
        queryInterface.removeColumn('Users', 'accessToken');
 | 
					    queryInterface.removeColumn('Users', 'accessToken')
 | 
				
			||||||
        queryInterface.removeColumn('Users', 'refreshToken');
 | 
					    queryInterface.removeColumn('Users', 'refreshToken')
 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,8 +1,6 @@
 | 
				
			|||||||
'use strict';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
module.exports = {
 | 
					module.exports = {
 | 
				
			||||||
  up: function (queryInterface, Sequelize) {
 | 
					  up: function (queryInterface, Sequelize) {
 | 
				
			||||||
    queryInterface.addColumn('Notes', 'savedAt', Sequelize.DATE);
 | 
					    queryInterface.addColumn('Notes', 'savedAt', Sequelize.DATE)
 | 
				
			||||||
    queryInterface.createTable('Revisions', {
 | 
					    queryInterface.createTable('Revisions', {
 | 
				
			||||||
      id: {
 | 
					      id: {
 | 
				
			||||||
        type: Sequelize.UUID,
 | 
					        type: Sequelize.UUID,
 | 
				
			||||||
@ -15,13 +13,11 @@ module.exports = {
 | 
				
			|||||||
      length: Sequelize.INTEGER,
 | 
					      length: Sequelize.INTEGER,
 | 
				
			||||||
      createdAt: Sequelize.DATE,
 | 
					      createdAt: Sequelize.DATE,
 | 
				
			||||||
      updatedAt: Sequelize.DATE
 | 
					      updatedAt: Sequelize.DATE
 | 
				
			||||||
    });
 | 
					    })
 | 
				
			||||||
    return;
 | 
					 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  down: function (queryInterface, Sequelize) {
 | 
					  down: function (queryInterface, Sequelize) {
 | 
				
			||||||
    queryInterface.dropTable('Revisions');
 | 
					    queryInterface.dropTable('Revisions')
 | 
				
			||||||
    queryInterface.removeColumn('Notes', 'savedAt');
 | 
					    queryInterface.removeColumn('Notes', 'savedAt')
 | 
				
			||||||
    return;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,9 +1,7 @@
 | 
				
			|||||||
'use strict';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
module.exports = {
 | 
					module.exports = {
 | 
				
			||||||
  up: function (queryInterface, Sequelize) {
 | 
					  up: function (queryInterface, Sequelize) {
 | 
				
			||||||
    queryInterface.addColumn('Notes', 'authorship', Sequelize.TEXT);
 | 
					    queryInterface.addColumn('Notes', 'authorship', Sequelize.TEXT)
 | 
				
			||||||
    queryInterface.addColumn('Revisions', 'authorship', Sequelize.TEXT);
 | 
					    queryInterface.addColumn('Revisions', 'authorship', Sequelize.TEXT)
 | 
				
			||||||
    queryInterface.createTable('Authors', {
 | 
					    queryInterface.createTable('Authors', {
 | 
				
			||||||
      id: {
 | 
					      id: {
 | 
				
			||||||
        type: Sequelize.INTEGER,
 | 
					        type: Sequelize.INTEGER,
 | 
				
			||||||
@ -15,14 +13,12 @@ module.exports = {
 | 
				
			|||||||
      userId: Sequelize.UUID,
 | 
					      userId: Sequelize.UUID,
 | 
				
			||||||
      createdAt: Sequelize.DATE,
 | 
					      createdAt: Sequelize.DATE,
 | 
				
			||||||
      updatedAt: Sequelize.DATE
 | 
					      updatedAt: Sequelize.DATE
 | 
				
			||||||
    });
 | 
					    })
 | 
				
			||||||
    return;
 | 
					 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  down: function (queryInterface, Sequelize) {
 | 
					  down: function (queryInterface, Sequelize) {
 | 
				
			||||||
    queryInterface.dropTable('Authors');
 | 
					    queryInterface.dropTable('Authors')
 | 
				
			||||||
    queryInterface.removeColumn('Revisions', 'authorship');
 | 
					    queryInterface.removeColumn('Revisions', 'authorship')
 | 
				
			||||||
    queryInterface.removeColumn('Notes', 'authorship');
 | 
					    queryInterface.removeColumn('Notes', 'authorship')
 | 
				
			||||||
    return;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,11 +1,9 @@
 | 
				
			|||||||
'use strict';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
module.exports = {
 | 
					module.exports = {
 | 
				
			||||||
  up: function (queryInterface, Sequelize) {
 | 
					  up: function (queryInterface, Sequelize) {
 | 
				
			||||||
    queryInterface.addColumn('Notes', 'deletedAt', Sequelize.DATE);
 | 
					    queryInterface.addColumn('Notes', 'deletedAt', Sequelize.DATE)
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  down: function (queryInterface, Sequelize) {
 | 
					  down: function (queryInterface, Sequelize) {
 | 
				
			||||||
    queryInterface.removeColumn('Notes', 'deletedAt');
 | 
					    queryInterface.removeColumn('Notes', 'deletedAt')
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,13 +1,11 @@
 | 
				
			|||||||
'use strict';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
module.exports = {
 | 
					module.exports = {
 | 
				
			||||||
  up: function (queryInterface, Sequelize) {
 | 
					  up: function (queryInterface, Sequelize) {
 | 
				
			||||||
    queryInterface.addColumn('Users', 'email', Sequelize.TEXT);
 | 
					    queryInterface.addColumn('Users', 'email', Sequelize.TEXT)
 | 
				
			||||||
    queryInterface.addColumn('Users', 'password', Sequelize.TEXT);
 | 
					    queryInterface.addColumn('Users', 'password', Sequelize.TEXT)
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  down: function (queryInterface, Sequelize) {
 | 
					  down: function (queryInterface, Sequelize) {
 | 
				
			||||||
    queryInterface.removeColumn('Users', 'email');
 | 
					    queryInterface.removeColumn('Users', 'email')
 | 
				
			||||||
    queryInterface.removeColumn('Users', 'password');
 | 
					    queryInterface.removeColumn('Users', 'password')
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,13 +1,8 @@
 | 
				
			|||||||
"use strict";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// external modules
 | 
					// external modules
 | 
				
			||||||
var Sequelize = require("sequelize");
 | 
					var Sequelize = require('sequelize')
 | 
				
			||||||
 | 
					 | 
				
			||||||
// core
 | 
					 | 
				
			||||||
var logger = require("../logger.js");
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = function (sequelize, DataTypes) {
 | 
					module.exports = function (sequelize, DataTypes) {
 | 
				
			||||||
    var Author = sequelize.define("Author", {
 | 
					  var Author = sequelize.define('Author', {
 | 
				
			||||||
    id: {
 | 
					    id: {
 | 
				
			||||||
      type: Sequelize.INTEGER,
 | 
					      type: Sequelize.INTEGER,
 | 
				
			||||||
      primaryKey: true,
 | 
					      primaryKey: true,
 | 
				
			||||||
@ -26,18 +21,17 @@ module.exports = function (sequelize, DataTypes) {
 | 
				
			|||||||
    classMethods: {
 | 
					    classMethods: {
 | 
				
			||||||
      associate: function (models) {
 | 
					      associate: function (models) {
 | 
				
			||||||
        Author.belongsTo(models.Note, {
 | 
					        Author.belongsTo(models.Note, {
 | 
				
			||||||
                    foreignKey: "noteId",
 | 
					          foreignKey: 'noteId',
 | 
				
			||||||
                    as: "note",
 | 
					          as: 'note',
 | 
				
			||||||
          constraints: false
 | 
					          constraints: false
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
        Author.belongsTo(models.User, {
 | 
					        Author.belongsTo(models.User, {
 | 
				
			||||||
                    foreignKey: "userId",
 | 
					          foreignKey: 'userId',
 | 
				
			||||||
                    as: "user",
 | 
					          as: 'user',
 | 
				
			||||||
          constraints: false
 | 
					          constraints: false
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
    
 | 
					  return Author
 | 
				
			||||||
    return Author;
 | 
					}
 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -1,57 +1,55 @@
 | 
				
			|||||||
"use strict";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// external modules
 | 
					// external modules
 | 
				
			||||||
var fs = require("fs");
 | 
					var fs = require('fs')
 | 
				
			||||||
var path = require("path");
 | 
					var path = require('path')
 | 
				
			||||||
var Sequelize = require("sequelize");
 | 
					var Sequelize = require('sequelize')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// core
 | 
					// core
 | 
				
			||||||
var config = require('../config.js');
 | 
					var config = require('../config.js')
 | 
				
			||||||
var logger = require("../logger.js");
 | 
					var logger = require('../logger.js')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var dbconfig = config.db;
 | 
					var dbconfig = config.db
 | 
				
			||||||
dbconfig.logging = config.debug ? logger.info : false;
 | 
					dbconfig.logging = config.debug ? logger.info : false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var sequelize = null;
 | 
					var sequelize = null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Heroku specific
 | 
					// Heroku specific
 | 
				
			||||||
if (config.dburl)
 | 
					if (config.dburl) {
 | 
				
			||||||
    sequelize = new Sequelize(config.dburl, dbconfig);
 | 
					  sequelize = new Sequelize(config.dburl, dbconfig)
 | 
				
			||||||
else
 | 
					} else {
 | 
				
			||||||
    sequelize = new Sequelize(dbconfig.database, dbconfig.username, dbconfig.password, dbconfig);
 | 
					  sequelize = new Sequelize(dbconfig.database, dbconfig.username, dbconfig.password, dbconfig)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// [Postgres] Handling NULL bytes
 | 
					// [Postgres] Handling NULL bytes
 | 
				
			||||||
// https://github.com/sequelize/sequelize/issues/6485
 | 
					// https://github.com/sequelize/sequelize/issues/6485
 | 
				
			||||||
function stripNullByte(value) {
 | 
					function stripNullByte (value) {
 | 
				
			||||||
    return value ? value.replace(/\u0000/g, "") : value;
 | 
					  return value ? value.replace(/\u0000/g, '') : value
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
sequelize.stripNullByte = stripNullByte;
 | 
					sequelize.stripNullByte = stripNullByte
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function processData(data, _default, process) {
 | 
					function processData (data, _default, process) {
 | 
				
			||||||
    if (data === undefined) return data;
 | 
					  if (data === undefined) return data
 | 
				
			||||||
    else return data === null ? _default : (process ? process(data) : data);
 | 
					  else return data === null ? _default : (process ? process(data) : data)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
sequelize.processData = processData;
 | 
					sequelize.processData = processData
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var db = {};
 | 
					var db = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fs
 | 
					fs.readdirSync(__dirname)
 | 
				
			||||||
    .readdirSync(__dirname)
 | 
					 | 
				
			||||||
    .filter(function (file) {
 | 
					    .filter(function (file) {
 | 
				
			||||||
        return (file.indexOf(".") !== 0) && (file !== "index.js");
 | 
					      return (file.indexOf('.') !== 0) && (file !== 'index.js')
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
    .forEach(function (file) {
 | 
					    .forEach(function (file) {
 | 
				
			||||||
        var model = sequelize.import(path.join(__dirname, file));
 | 
					      var model = sequelize.import(path.join(__dirname, file))
 | 
				
			||||||
        db[model.name] = model;
 | 
					      db[model.name] = model
 | 
				
			||||||
    });
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Object.keys(db).forEach(function (modelName) {
 | 
					Object.keys(db).forEach(function (modelName) {
 | 
				
			||||||
    if ("associate" in db[modelName]) {
 | 
					  if ('associate' in db[modelName]) {
 | 
				
			||||||
        db[modelName].associate(db);
 | 
					    db[modelName].associate(db)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db.sequelize = sequelize;
 | 
					db.sequelize = sequelize
 | 
				
			||||||
db.Sequelize = Sequelize;
 | 
					db.Sequelize = Sequelize
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = db;
 | 
					module.exports = db
 | 
				
			||||||
 | 
				
			|||||||
@ -1,32 +1,30 @@
 | 
				
			|||||||
"use strict";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// external modules
 | 
					// external modules
 | 
				
			||||||
var fs = require('fs');
 | 
					var fs = require('fs')
 | 
				
			||||||
var path = require('path');
 | 
					var path = require('path')
 | 
				
			||||||
var LZString = require('lz-string');
 | 
					var LZString = require('lz-string')
 | 
				
			||||||
var md = require('markdown-it')();
 | 
					var md = require('markdown-it')()
 | 
				
			||||||
var metaMarked = require('meta-marked');
 | 
					var metaMarked = require('meta-marked')
 | 
				
			||||||
var cheerio = require('cheerio');
 | 
					var cheerio = require('cheerio')
 | 
				
			||||||
var shortId = require('shortid');
 | 
					var shortId = require('shortid')
 | 
				
			||||||
var Sequelize = require("sequelize");
 | 
					var Sequelize = require('sequelize')
 | 
				
			||||||
var async = require('async');
 | 
					var async = require('async')
 | 
				
			||||||
var moment = require('moment');
 | 
					var moment = require('moment')
 | 
				
			||||||
var DiffMatchPatch = require('diff-match-patch');
 | 
					var DiffMatchPatch = require('diff-match-patch')
 | 
				
			||||||
var dmp = new DiffMatchPatch();
 | 
					var dmp = new DiffMatchPatch()
 | 
				
			||||||
var S = require('string');
 | 
					var S = require('string')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// core
 | 
					// core
 | 
				
			||||||
var config = require("../config.js");
 | 
					var config = require('../config.js')
 | 
				
			||||||
var logger = require("../logger.js");
 | 
					var logger = require('../logger.js')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//ot
 | 
					// ot
 | 
				
			||||||
var ot = require("../ot/index.js");
 | 
					var ot = require('../ot/index.js')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// permission types
 | 
					// permission types
 | 
				
			||||||
var permissionTypes = ["freely", "editable", "limited", "locked", "protected", "private"];
 | 
					var permissionTypes = ['freely', 'editable', 'limited', 'locked', 'protected', 'private']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = function (sequelize, DataTypes) {
 | 
					module.exports = function (sequelize, DataTypes) {
 | 
				
			||||||
    var Note = sequelize.define("Note", {
 | 
					  var Note = sequelize.define('Note', {
 | 
				
			||||||
    id: {
 | 
					    id: {
 | 
				
			||||||
      type: DataTypes.UUID,
 | 
					      type: DataTypes.UUID,
 | 
				
			||||||
      primaryKey: true,
 | 
					      primaryKey: true,
 | 
				
			||||||
@ -54,28 +52,28 @@ module.exports = function (sequelize, DataTypes) {
 | 
				
			|||||||
    title: {
 | 
					    title: {
 | 
				
			||||||
      type: DataTypes.TEXT,
 | 
					      type: DataTypes.TEXT,
 | 
				
			||||||
      get: function () {
 | 
					      get: function () {
 | 
				
			||||||
                return sequelize.processData(this.getDataValue('title'), "");
 | 
					        return sequelize.processData(this.getDataValue('title'), '')
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      set: function (value) {
 | 
					      set: function (value) {
 | 
				
			||||||
                this.setDataValue('title', sequelize.stripNullByte(value));
 | 
					        this.setDataValue('title', sequelize.stripNullByte(value))
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    content: {
 | 
					    content: {
 | 
				
			||||||
      type: DataTypes.TEXT,
 | 
					      type: DataTypes.TEXT,
 | 
				
			||||||
      get: function () {
 | 
					      get: function () {
 | 
				
			||||||
                return sequelize.processData(this.getDataValue('content'), "");
 | 
					        return sequelize.processData(this.getDataValue('content'), '')
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      set: function (value) {
 | 
					      set: function (value) {
 | 
				
			||||||
                this.setDataValue('content', sequelize.stripNullByte(value));
 | 
					        this.setDataValue('content', sequelize.stripNullByte(value))
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    authorship: {
 | 
					    authorship: {
 | 
				
			||||||
      type: DataTypes.TEXT,
 | 
					      type: DataTypes.TEXT,
 | 
				
			||||||
      get: function () {
 | 
					      get: function () {
 | 
				
			||||||
                return sequelize.processData(this.getDataValue('authorship'), [], JSON.parse);
 | 
					        return sequelize.processData(this.getDataValue('authorship'), [], JSON.parse)
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      set: function (value) {
 | 
					      set: function (value) {
 | 
				
			||||||
                this.setDataValue('authorship', JSON.stringify(value));
 | 
					        this.setDataValue('authorship', JSON.stringify(value))
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    lastchangeAt: {
 | 
					    lastchangeAt: {
 | 
				
			||||||
@ -89,39 +87,36 @@ module.exports = function (sequelize, DataTypes) {
 | 
				
			|||||||
    classMethods: {
 | 
					    classMethods: {
 | 
				
			||||||
      associate: function (models) {
 | 
					      associate: function (models) {
 | 
				
			||||||
        Note.belongsTo(models.User, {
 | 
					        Note.belongsTo(models.User, {
 | 
				
			||||||
                    foreignKey: "ownerId",
 | 
					          foreignKey: 'ownerId',
 | 
				
			||||||
                    as: "owner",
 | 
					          as: 'owner',
 | 
				
			||||||
          constraints: false
 | 
					          constraints: false
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
        Note.belongsTo(models.User, {
 | 
					        Note.belongsTo(models.User, {
 | 
				
			||||||
                    foreignKey: "lastchangeuserId",
 | 
					          foreignKey: 'lastchangeuserId',
 | 
				
			||||||
                    as: "lastchangeuser",
 | 
					          as: 'lastchangeuser',
 | 
				
			||||||
          constraints: false
 | 
					          constraints: false
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
        Note.hasMany(models.Revision, {
 | 
					        Note.hasMany(models.Revision, {
 | 
				
			||||||
                    foreignKey: "noteId",
 | 
					          foreignKey: 'noteId',
 | 
				
			||||||
          constraints: false
 | 
					          constraints: false
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
        Note.hasMany(models.Author, {
 | 
					        Note.hasMany(models.Author, {
 | 
				
			||||||
                    foreignKey: "noteId",
 | 
					          foreignKey: 'noteId',
 | 
				
			||||||
                    as: "authors",
 | 
					          as: 'authors',
 | 
				
			||||||
          constraints: false
 | 
					          constraints: false
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      checkFileExist: function (filePath) {
 | 
					      checkFileExist: function (filePath) {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
                    return fs.statSync(filePath).isFile();
 | 
					          return fs.statSync(filePath).isFile()
 | 
				
			||||||
        } catch (err) {
 | 
					        } catch (err) {
 | 
				
			||||||
                    return false;
 | 
					          return false
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      checkNoteIdValid: function (id) {
 | 
					      checkNoteIdValid: function (id) {
 | 
				
			||||||
                var uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
 | 
					        var uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i
 | 
				
			||||||
                var result = id.match(uuidRegex);
 | 
					        var result = id.match(uuidRegex)
 | 
				
			||||||
                if (result && result.length == 1)
 | 
					        if (result && result.length === 1) { return true } else { return false }
 | 
				
			||||||
                    return true;
 | 
					 | 
				
			||||||
                else
 | 
					 | 
				
			||||||
                    return false;
 | 
					 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      parseNoteId: function (noteId, callback) {
 | 
					      parseNoteId: function (noteId, callback) {
 | 
				
			||||||
        async.series({
 | 
					        async.series({
 | 
				
			||||||
@ -133,15 +128,15 @@ module.exports = function (sequelize, DataTypes) {
 | 
				
			|||||||
              }
 | 
					              }
 | 
				
			||||||
            }).then(function (note) {
 | 
					            }).then(function (note) {
 | 
				
			||||||
              if (note) {
 | 
					              if (note) {
 | 
				
			||||||
                                var filePath = path.join(config.docspath, noteId + '.md');
 | 
					                let filePath = path.join(config.docspath, noteId + '.md')
 | 
				
			||||||
                if (Note.checkFileExist(filePath)) {
 | 
					                if (Note.checkFileExist(filePath)) {
 | 
				
			||||||
                  // if doc in filesystem have newer modified time than last change time
 | 
					                  // if doc in filesystem have newer modified time than last change time
 | 
				
			||||||
                  // then will update the doc in db
 | 
					                  // then will update the doc in db
 | 
				
			||||||
                                    var fsModifiedTime = moment(fs.statSync(filePath).mtime);
 | 
					                  var fsModifiedTime = moment(fs.statSync(filePath).mtime)
 | 
				
			||||||
                                    var dbModifiedTime = moment(note.lastchangeAt || note.createdAt);
 | 
					                  var dbModifiedTime = moment(note.lastchangeAt || note.createdAt)
 | 
				
			||||||
                                    var body = fs.readFileSync(filePath, 'utf8');
 | 
					                  var body = fs.readFileSync(filePath, 'utf8')
 | 
				
			||||||
                                    var contentLength = body.length;
 | 
					                  var contentLength = body.length
 | 
				
			||||||
                                    var title = Note.parseNoteTitle(body);
 | 
					                  var title = Note.parseNoteTitle(body)
 | 
				
			||||||
                  if (fsModifiedTime.isAfter(dbModifiedTime) && note.content !== body) {
 | 
					                  if (fsModifiedTime.isAfter(dbModifiedTime) && note.content !== body) {
 | 
				
			||||||
                    note.update({
 | 
					                    note.update({
 | 
				
			||||||
                      title: title,
 | 
					                      title: title,
 | 
				
			||||||
@ -149,61 +144,58 @@ module.exports = function (sequelize, DataTypes) {
 | 
				
			|||||||
                      lastchangeAt: fsModifiedTime
 | 
					                      lastchangeAt: fsModifiedTime
 | 
				
			||||||
                    }).then(function (note) {
 | 
					                    }).then(function (note) {
 | 
				
			||||||
                      sequelize.models.Revision.saveNoteRevision(note, function (err, revision) {
 | 
					                      sequelize.models.Revision.saveNoteRevision(note, function (err, revision) {
 | 
				
			||||||
                                                if (err) return _callback(err, null);
 | 
					                        if (err) return _callback(err, null)
 | 
				
			||||||
                        // update authorship on after making revision of docs
 | 
					                        // update authorship on after making revision of docs
 | 
				
			||||||
                                                var patch = dmp.patch_fromText(revision.patch);
 | 
					                        var patch = dmp.patch_fromText(revision.patch)
 | 
				
			||||||
                                                var operations = Note.transformPatchToOperations(patch, contentLength);
 | 
					                        var operations = Note.transformPatchToOperations(patch, contentLength)
 | 
				
			||||||
                                                var authorship = note.authorship;
 | 
					                        var authorship = note.authorship
 | 
				
			||||||
                                                for (var i = 0; i < operations.length; i++) {
 | 
					                        for (let i = 0; i < operations.length; i++) {
 | 
				
			||||||
                                                    authorship = Note.updateAuthorshipByOperation(operations[i], null, authorship);
 | 
					                          authorship = Note.updateAuthorshipByOperation(operations[i], null, authorship)
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        note.update({
 | 
					                        note.update({
 | 
				
			||||||
                          authorship: JSON.stringify(authorship)
 | 
					                          authorship: JSON.stringify(authorship)
 | 
				
			||||||
                        }).then(function (note) {
 | 
					                        }).then(function (note) {
 | 
				
			||||||
                                                    return callback(null, note.id);
 | 
					                          return callback(null, note.id)
 | 
				
			||||||
                        }).catch(function (err) {
 | 
					                        }).catch(function (err) {
 | 
				
			||||||
                                                    return _callback(err, null);
 | 
					                          return _callback(err, null)
 | 
				
			||||||
                                                });
 | 
					                        })
 | 
				
			||||||
                                            });
 | 
					                      })
 | 
				
			||||||
                    }).catch(function (err) {
 | 
					                    }).catch(function (err) {
 | 
				
			||||||
                                            return _callback(err, null);
 | 
					                      return _callback(err, null)
 | 
				
			||||||
                                        });
 | 
					                    })
 | 
				
			||||||
                  } else {
 | 
					                  } else {
 | 
				
			||||||
                                        return callback(null, note.id);
 | 
					                    return callback(null, note.id)
 | 
				
			||||||
                  }
 | 
					                  }
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                                    return callback(null, note.id);
 | 
					                  return callback(null, note.id)
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
              } else {
 | 
					              } else {
 | 
				
			||||||
                                var filePath = path.join(config.docspath, noteId + '.md');
 | 
					                var filePath = path.join(config.docspath, noteId + '.md')
 | 
				
			||||||
                if (Note.checkFileExist(filePath)) {
 | 
					                if (Note.checkFileExist(filePath)) {
 | 
				
			||||||
                  Note.create({
 | 
					                  Note.create({
 | 
				
			||||||
                    alias: noteId,
 | 
					                    alias: noteId,
 | 
				
			||||||
                    owner: null,
 | 
					                    owner: null,
 | 
				
			||||||
                    permission: 'locked'
 | 
					                    permission: 'locked'
 | 
				
			||||||
                  }).then(function (note) {
 | 
					                  }).then(function (note) {
 | 
				
			||||||
                                        return callback(null, note.id);
 | 
					                    return callback(null, note.id)
 | 
				
			||||||
                  }).catch(function (err) {
 | 
					                  }).catch(function (err) {
 | 
				
			||||||
                                        return _callback(err, null);
 | 
					                    return _callback(err, null)
 | 
				
			||||||
                                    });
 | 
					                  })
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                                    return _callback(null, null);
 | 
					                  return _callback(null, null)
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
            }).catch(function (err) {
 | 
					            }).catch(function (err) {
 | 
				
			||||||
                            return _callback(err, null);
 | 
					              return _callback(err, null)
 | 
				
			||||||
                        });
 | 
					            })
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
          parseNoteIdByLZString: function (_callback) {
 | 
					          parseNoteIdByLZString: function (_callback) {
 | 
				
			||||||
            // try to parse note id by LZString Base64
 | 
					            // try to parse note id by LZString Base64
 | 
				
			||||||
            try {
 | 
					            try {
 | 
				
			||||||
                            var id = LZString.decompressFromBase64(noteId);
 | 
					              var id = LZString.decompressFromBase64(noteId)
 | 
				
			||||||
                            if (id && Note.checkNoteIdValid(id))
 | 
					              if (id && Note.checkNoteIdValid(id)) { return callback(null, id) } else { return _callback(null, null) }
 | 
				
			||||||
                                return callback(null, id);
 | 
					 | 
				
			||||||
                            else
 | 
					 | 
				
			||||||
                                return _callback(null, null);
 | 
					 | 
				
			||||||
            } catch (err) {
 | 
					            } catch (err) {
 | 
				
			||||||
                            return _callback(err, null);
 | 
					              return _callback(err, null)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
          parseNoteIdByShortId: function (_callback) {
 | 
					          parseNoteIdByShortId: function (_callback) {
 | 
				
			||||||
@ -215,321 +207,318 @@ module.exports = function (sequelize, DataTypes) {
 | 
				
			|||||||
                    shortid: noteId
 | 
					                    shortid: noteId
 | 
				
			||||||
                  }
 | 
					                  }
 | 
				
			||||||
                }).then(function (note) {
 | 
					                }).then(function (note) {
 | 
				
			||||||
                                    if (!note) return _callback(null, null);
 | 
					                  if (!note) return _callback(null, null)
 | 
				
			||||||
                                    return callback(null, note.id);
 | 
					                  return callback(null, note.id)
 | 
				
			||||||
                }).catch(function (err) {
 | 
					                }).catch(function (err) {
 | 
				
			||||||
                                    return _callback(err, null);
 | 
					                  return _callback(err, null)
 | 
				
			||||||
                                });
 | 
					                })
 | 
				
			||||||
              } else {
 | 
					              } else {
 | 
				
			||||||
                                return _callback(null, null);
 | 
					                return _callback(null, null)
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
            } catch (err) {
 | 
					            } catch (err) {
 | 
				
			||||||
                            return _callback(err, null);
 | 
					              return _callback(err, null)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }, function (err, result) {
 | 
					        }, function (err, result) {
 | 
				
			||||||
          if (err) {
 | 
					          if (err) {
 | 
				
			||||||
                        logger.error(err);
 | 
					            logger.error(err)
 | 
				
			||||||
                        return callback(err, null);
 | 
					            return callback(err, null)
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
                    return callback(null, null);
 | 
					          return callback(null, null)
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      parseNoteInfo: function (body) {
 | 
					      parseNoteInfo: function (body) {
 | 
				
			||||||
                var parsed = Note.extractMeta(body);
 | 
					        var parsed = Note.extractMeta(body)
 | 
				
			||||||
                var $ = cheerio.load(md.render(parsed.markdown));
 | 
					        var $ = cheerio.load(md.render(parsed.markdown))
 | 
				
			||||||
        return {
 | 
					        return {
 | 
				
			||||||
          title: Note.extractNoteTitle(parsed.meta, $),
 | 
					          title: Note.extractNoteTitle(parsed.meta, $),
 | 
				
			||||||
          tags: Note.extractNoteTags(parsed.meta, $)
 | 
					          tags: Note.extractNoteTags(parsed.meta, $)
 | 
				
			||||||
                };
 | 
					        }
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      parseNoteTitle: function (body) {
 | 
					      parseNoteTitle: function (body) {
 | 
				
			||||||
                var parsed = Note.extractMeta(body);
 | 
					        var parsed = Note.extractMeta(body)
 | 
				
			||||||
                var $ = cheerio.load(md.render(parsed.markdown));
 | 
					        var $ = cheerio.load(md.render(parsed.markdown))
 | 
				
			||||||
                return Note.extractNoteTitle(parsed.meta, $);
 | 
					        return Note.extractNoteTitle(parsed.meta, $)
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      extractNoteTitle: function (meta, $) {
 | 
					      extractNoteTitle: function (meta, $) {
 | 
				
			||||||
                var title = "";
 | 
					        var title = ''
 | 
				
			||||||
                if (meta.title && (typeof meta.title == "string" || typeof meta.title == "number")) {
 | 
					        if (meta.title && (typeof meta.title === 'string' || typeof meta.title === 'number')) {
 | 
				
			||||||
                    title = meta.title;
 | 
					          title = meta.title
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
                    var h1s = $("h1");
 | 
					          var h1s = $('h1')
 | 
				
			||||||
                    if (h1s.length > 0 && h1s.first().text().split('\n').length == 1)
 | 
					          if (h1s.length > 0 && h1s.first().text().split('\n').length === 1) { title = S(h1s.first().text()).stripTags().s }
 | 
				
			||||||
                        title = S(h1s.first().text()).stripTags().s;
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
                if (!title) title = "Untitled";
 | 
					        if (!title) title = 'Untitled'
 | 
				
			||||||
                return title;
 | 
					        return title
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      generateDescription: function (markdown) {
 | 
					      generateDescription: function (markdown) {
 | 
				
			||||||
                return markdown.substr(0, 100).replace(/(?:\r\n|\r|\n)/g, ' ');
 | 
					        return markdown.substr(0, 100).replace(/(?:\r\n|\r|\n)/g, ' ')
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      decodeTitle: function (title) {
 | 
					      decodeTitle: function (title) {
 | 
				
			||||||
                return title ? title : 'Untitled';
 | 
					        return title || 'Untitled'
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      generateWebTitle: function (title) {
 | 
					      generateWebTitle: function (title) {
 | 
				
			||||||
                title = !title || title == "Untitled" ? "HackMD - Collaborative markdown notes" : title + " - HackMD";
 | 
					        title = !title || title === 'Untitled' ? 'HackMD - Collaborative markdown notes' : title + ' - HackMD'
 | 
				
			||||||
                return title;
 | 
					        return title
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      extractNoteTags: function (meta, $) {
 | 
					      extractNoteTags: function (meta, $) {
 | 
				
			||||||
                var tags = [];
 | 
					        var tags = []
 | 
				
			||||||
                var rawtags = [];
 | 
					        var rawtags = []
 | 
				
			||||||
                if (meta.tags && (typeof meta.tags == "string" || typeof meta.tags == "number")) {
 | 
					        if (meta.tags && (typeof meta.tags === 'string' || typeof meta.tags === 'number')) {
 | 
				
			||||||
                    var metaTags = ('' + meta.tags).split(',');
 | 
					          var metaTags = ('' + meta.tags).split(',')
 | 
				
			||||||
                    for (var i = 0; i < metaTags.length; i++) {
 | 
					          for (let i = 0; i < metaTags.length; i++) {
 | 
				
			||||||
                        var text = metaTags[i].trim();
 | 
					            var text = metaTags[i].trim()
 | 
				
			||||||
                        if (text) rawtags.push(text);
 | 
					            if (text) rawtags.push(text)
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
                    var h6s = $("h6");
 | 
					          var h6s = $('h6')
 | 
				
			||||||
          h6s.each(function (key, value) {
 | 
					          h6s.each(function (key, value) {
 | 
				
			||||||
            if (/^tags/gmi.test($(value).text())) {
 | 
					            if (/^tags/gmi.test($(value).text())) {
 | 
				
			||||||
                            var codes = $(value).find("code");
 | 
					              var codes = $(value).find('code')
 | 
				
			||||||
                            for (var i = 0; i < codes.length; i++) {
 | 
					              for (let i = 0; i < codes.length; i++) {
 | 
				
			||||||
                                var text = S($(codes[i]).text().trim()).stripTags().s;
 | 
					                var text = S($(codes[i]).text().trim()).stripTags().s
 | 
				
			||||||
                                if (text) rawtags.push(text);
 | 
					                if (text) rawtags.push(text)
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
                    });
 | 
					          })
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
                for (var i = 0; i < rawtags.length; i++) {
 | 
					        for (let i = 0; i < rawtags.length; i++) {
 | 
				
			||||||
                    var found = false;
 | 
					          var found = false
 | 
				
			||||||
                    for (var j = 0; j < tags.length; j++) {
 | 
					          for (let j = 0; j < tags.length; j++) {
 | 
				
			||||||
                        if (tags[j] == rawtags[i]) {
 | 
					            if (tags[j] === rawtags[i]) {
 | 
				
			||||||
                            found = true;
 | 
					              found = true
 | 
				
			||||||
                            break;
 | 
					              break
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
                    if (!found)
 | 
					          if (!found) { tags.push(rawtags[i]) }
 | 
				
			||||||
                        tags.push(rawtags[i]);
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
                return tags;
 | 
					        return tags
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      extractMeta: function (content) {
 | 
					      extractMeta: function (content) {
 | 
				
			||||||
 | 
					        var obj = null
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
                    var obj = metaMarked(content);
 | 
					          obj = metaMarked(content)
 | 
				
			||||||
                    if (!obj.markdown) obj.markdown = "";
 | 
					          if (!obj.markdown) obj.markdown = ''
 | 
				
			||||||
                    if (!obj.meta) obj.meta = {};
 | 
					          if (!obj.meta) obj.meta = {}
 | 
				
			||||||
        } catch (err) {
 | 
					        } catch (err) {
 | 
				
			||||||
                    var obj = {
 | 
					          obj = {
 | 
				
			||||||
            markdown: content,
 | 
					            markdown: content,
 | 
				
			||||||
            meta: {}
 | 
					            meta: {}
 | 
				
			||||||
                    };
 | 
					 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
                return obj;
 | 
					        }
 | 
				
			||||||
 | 
					        return obj
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      parseMeta: function (meta) {
 | 
					      parseMeta: function (meta) {
 | 
				
			||||||
                var _meta = {};
 | 
					        var _meta = {}
 | 
				
			||||||
        if (meta) {
 | 
					        if (meta) {
 | 
				
			||||||
                    if (meta.title && (typeof meta.title == "string" || typeof meta.title == "number"))
 | 
					          if (meta.title && (typeof meta.title === 'string' || typeof meta.title === 'number')) { _meta.title = meta.title }
 | 
				
			||||||
                        _meta.title = meta.title;
 | 
					          if (meta.description && (typeof meta.description === 'string' || typeof meta.description === 'number')) { _meta.description = meta.description }
 | 
				
			||||||
                    if (meta.description && (typeof meta.description == "string" || typeof meta.description == "number"))
 | 
					          if (meta.robots && (typeof meta.robots === 'string' || typeof meta.robots === 'number')) { _meta.robots = meta.robots }
 | 
				
			||||||
                        _meta.description = meta.description;
 | 
					          if (meta.GA && (typeof meta.GA === 'string' || typeof meta.GA === 'number')) { _meta.GA = meta.GA }
 | 
				
			||||||
                    if (meta.robots && (typeof meta.robots == "string" || typeof meta.robots == "number"))
 | 
					          if (meta.disqus && (typeof meta.disqus === 'string' || typeof meta.disqus === 'number')) { _meta.disqus = meta.disqus }
 | 
				
			||||||
                        _meta.robots = meta.robots;
 | 
					          if (meta.slideOptions && (typeof meta.slideOptions === 'object')) { _meta.slideOptions = meta.slideOptions }
 | 
				
			||||||
                    if (meta.GA && (typeof meta.GA == "string" || typeof meta.GA == "number"))
 | 
					 | 
				
			||||||
                        _meta.GA = meta.GA;
 | 
					 | 
				
			||||||
                    if (meta.disqus && (typeof meta.disqus == "string" || typeof meta.disqus == "number"))
 | 
					 | 
				
			||||||
                        _meta.disqus = meta.disqus;
 | 
					 | 
				
			||||||
                    if (meta.slideOptions && (typeof meta.slideOptions == "object"))
 | 
					 | 
				
			||||||
                        _meta.slideOptions = meta.slideOptions;
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
                return _meta;
 | 
					        return _meta
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      updateAuthorshipByOperation: function (operation, userId, authorships) {
 | 
					      updateAuthorshipByOperation: function (operation, userId, authorships) {
 | 
				
			||||||
                var index = 0;
 | 
					        var index = 0
 | 
				
			||||||
                var timestamp = Date.now();
 | 
					        var timestamp = Date.now()
 | 
				
			||||||
                for (var i = 0; i < operation.length; i++) {
 | 
					        for (let i = 0; i < operation.length; i++) {
 | 
				
			||||||
                    var op = operation[i];
 | 
					          var op = operation[i]
 | 
				
			||||||
          if (ot.TextOperation.isRetain(op)) {
 | 
					          if (ot.TextOperation.isRetain(op)) {
 | 
				
			||||||
                        index += op;
 | 
					            index += op
 | 
				
			||||||
          } else if (ot.TextOperation.isInsert(op)) {
 | 
					          } else if (ot.TextOperation.isInsert(op)) {
 | 
				
			||||||
                        var opStart = index;
 | 
					            let opStart = index
 | 
				
			||||||
                        var opEnd = index + op.length;
 | 
					            let opEnd = index + op.length
 | 
				
			||||||
                        var inserted = false;
 | 
					            var inserted = false
 | 
				
			||||||
            // authorship format: [userId, startPos, endPos, createdAt, updatedAt]
 | 
					            // authorship format: [userId, startPos, endPos, createdAt, updatedAt]
 | 
				
			||||||
                        if (authorships.length <= 0) authorships.push([userId, opStart, opEnd, timestamp, timestamp]);
 | 
					            if (authorships.length <= 0) authorships.push([userId, opStart, opEnd, timestamp, timestamp])
 | 
				
			||||||
            else {
 | 
					            else {
 | 
				
			||||||
                            for (var j = 0; j < authorships.length; j++) {
 | 
					              for (let j = 0; j < authorships.length; j++) {
 | 
				
			||||||
                                var authorship = authorships[j];
 | 
					                let authorship = authorships[j]
 | 
				
			||||||
                if (!inserted) {
 | 
					                if (!inserted) {
 | 
				
			||||||
                                    var nextAuthorship = authorships[j + 1] || -1;
 | 
					                  let nextAuthorship = authorships[j + 1] || -1
 | 
				
			||||||
                                    if (nextAuthorship != -1 && nextAuthorship[1] >= opEnd || j >= authorships.length - 1) {
 | 
					                  if ((nextAuthorship !== -1 && nextAuthorship[1] >= opEnd) || j >= authorships.length - 1) {
 | 
				
			||||||
                    if (authorship[1] < opStart && authorship[2] > opStart) {
 | 
					                    if (authorship[1] < opStart && authorship[2] > opStart) {
 | 
				
			||||||
                      // divide
 | 
					                      // divide
 | 
				
			||||||
                                            var postLength = authorship[2] - opStart;
 | 
					                      let postLength = authorship[2] - opStart
 | 
				
			||||||
                                            authorship[2] = opStart;
 | 
					                      authorship[2] = opStart
 | 
				
			||||||
                                            authorship[4] = timestamp;
 | 
					                      authorship[4] = timestamp
 | 
				
			||||||
                                            authorships.splice(j + 1, 0, [userId, opStart, opEnd, timestamp, timestamp]);
 | 
					                      authorships.splice(j + 1, 0, [userId, opStart, opEnd, timestamp, timestamp])
 | 
				
			||||||
                                            authorships.splice(j + 2, 0, [authorship[0], opEnd, opEnd + postLength, authorship[3], timestamp]);
 | 
					                      authorships.splice(j + 2, 0, [authorship[0], opEnd, opEnd + postLength, authorship[3], timestamp])
 | 
				
			||||||
                                            j += 2;
 | 
					                      j += 2
 | 
				
			||||||
                                            inserted = true;
 | 
					                      inserted = true
 | 
				
			||||||
                    } else if (authorship[1] >= opStart) {
 | 
					                    } else if (authorship[1] >= opStart) {
 | 
				
			||||||
                                            authorships.splice(j, 0, [userId, opStart, opEnd, timestamp, timestamp]);
 | 
					                      authorships.splice(j, 0, [userId, opStart, opEnd, timestamp, timestamp])
 | 
				
			||||||
                                            j += 1;
 | 
					                      j += 1
 | 
				
			||||||
                                            inserted = true;
 | 
					                      inserted = true
 | 
				
			||||||
                    } else if (authorship[2] <= opStart) {
 | 
					                    } else if (authorship[2] <= opStart) {
 | 
				
			||||||
                                            authorships.splice(j + 1, 0, [userId, opStart, opEnd, timestamp, timestamp]);
 | 
					                      authorships.splice(j + 1, 0, [userId, opStart, opEnd, timestamp, timestamp])
 | 
				
			||||||
                                            j += 1;
 | 
					                      j += 1
 | 
				
			||||||
                                            inserted = true;
 | 
					                      inserted = true
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                  }
 | 
					                  }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                if (authorship[1] >= opStart) {
 | 
					                if (authorship[1] >= opStart) {
 | 
				
			||||||
                                    authorship[1] += op.length;
 | 
					                  authorship[1] += op.length
 | 
				
			||||||
                                    authorship[2] += op.length;
 | 
					                  authorship[2] += op.length
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
                        index += op.length;
 | 
					            index += op.length
 | 
				
			||||||
          } else if (ot.TextOperation.isDelete(op)) {
 | 
					          } else if (ot.TextOperation.isDelete(op)) {
 | 
				
			||||||
                        var opStart = index;
 | 
					            let opStart = index
 | 
				
			||||||
                        var opEnd = index - op;
 | 
					            let opEnd = index - op
 | 
				
			||||||
                        if (operation.length == 1) {
 | 
					            if (operation.length === 1) {
 | 
				
			||||||
                            authorships = [];
 | 
					              authorships = []
 | 
				
			||||||
            } else if (authorships.length > 0) {
 | 
					            } else if (authorships.length > 0) {
 | 
				
			||||||
                            for (var j = 0; j < authorships.length; j++) {
 | 
					              for (let j = 0; j < authorships.length; j++) {
 | 
				
			||||||
                                var authorship = authorships[j];
 | 
					                let authorship = authorships[j]
 | 
				
			||||||
                if (authorship[1] >= opStart && authorship[1] <= opEnd && authorship[2] >= opStart && authorship[2] <= opEnd) {
 | 
					                if (authorship[1] >= opStart && authorship[1] <= opEnd && authorship[2] >= opStart && authorship[2] <= opEnd) {
 | 
				
			||||||
                                    authorships.splice(j, 1);
 | 
					                  authorships.splice(j, 1)
 | 
				
			||||||
                                    j -= 1;
 | 
					                  j -= 1
 | 
				
			||||||
                } else if (authorship[1] < opStart && authorship[1] < opEnd && authorship[2] > opStart && authorship[2] > opEnd) {
 | 
					                } else if (authorship[1] < opStart && authorship[1] < opEnd && authorship[2] > opStart && authorship[2] > opEnd) {
 | 
				
			||||||
                                    authorship[2] += op;
 | 
					                  authorship[2] += op
 | 
				
			||||||
                                    authorship[4] = timestamp;
 | 
					                  authorship[4] = timestamp
 | 
				
			||||||
                } else if (authorship[2] >= opStart && authorship[2] <= opEnd) {
 | 
					                } else if (authorship[2] >= opStart && authorship[2] <= opEnd) {
 | 
				
			||||||
                                    authorship[2] = opStart;
 | 
					                  authorship[2] = opStart
 | 
				
			||||||
                                    authorship[4] = timestamp;
 | 
					                  authorship[4] = timestamp
 | 
				
			||||||
                } else if (authorship[1] >= opStart && authorship[1] <= opEnd) {
 | 
					                } else if (authorship[1] >= opStart && authorship[1] <= opEnd) {
 | 
				
			||||||
                                    authorship[1] = opEnd;
 | 
					                  authorship[1] = opEnd
 | 
				
			||||||
                                    authorship[4] = timestamp;
 | 
					                  authorship[4] = timestamp
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                if (authorship[1] >= opEnd) {
 | 
					                if (authorship[1] >= opEnd) {
 | 
				
			||||||
                                    authorship[1] += op;
 | 
					                  authorship[1] += op
 | 
				
			||||||
                                    authorship[2] += op;
 | 
					                  authorship[2] += op
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
                        index += op;
 | 
					            index += op
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        // merge
 | 
					        // merge
 | 
				
			||||||
                for (var j = 0; j < authorships.length; j++) {
 | 
					        for (let j = 0; j < authorships.length; j++) {
 | 
				
			||||||
                    var authorship = authorships[j];
 | 
					          let authorship = authorships[j]
 | 
				
			||||||
                    for (var k = j + 1; k < authorships.length; k++) {
 | 
					          for (let k = j + 1; k < authorships.length; k++) {
 | 
				
			||||||
                        var nextAuthorship = authorships[k];
 | 
					            let nextAuthorship = authorships[k]
 | 
				
			||||||
            if (nextAuthorship && authorship[0] === nextAuthorship[0] && authorship[2] === nextAuthorship[1]) {
 | 
					            if (nextAuthorship && authorship[0] === nextAuthorship[0] && authorship[2] === nextAuthorship[1]) {
 | 
				
			||||||
                            var minTimestamp = Math.min(authorship[3], nextAuthorship[3]);
 | 
					              let minTimestamp = Math.min(authorship[3], nextAuthorship[3])
 | 
				
			||||||
                            var maxTimestamp = Math.max(authorship[3], nextAuthorship[3]);
 | 
					              let maxTimestamp = Math.max(authorship[3], nextAuthorship[3])
 | 
				
			||||||
                            authorships.splice(j, 1, [authorship[0], authorship[1], nextAuthorship[2], minTimestamp, maxTimestamp]);
 | 
					              authorships.splice(j, 1, [authorship[0], authorship[1], nextAuthorship[2], minTimestamp, maxTimestamp])
 | 
				
			||||||
                            authorships.splice(k, 1);
 | 
					              authorships.splice(k, 1)
 | 
				
			||||||
                            j -= 1;
 | 
					              j -= 1
 | 
				
			||||||
                            break;
 | 
					              break
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        // clear
 | 
					        // clear
 | 
				
			||||||
                for (var j = 0; j < authorships.length; j++) {
 | 
					        for (let j = 0; j < authorships.length; j++) {
 | 
				
			||||||
                    var authorship = authorships[j];
 | 
					          let authorship = authorships[j]
 | 
				
			||||||
          if (!authorship[0]) {
 | 
					          if (!authorship[0]) {
 | 
				
			||||||
                        authorships.splice(j, 1);
 | 
					            authorships.splice(j, 1)
 | 
				
			||||||
                        j -= 1;
 | 
					            j -= 1
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
                return authorships;
 | 
					        return authorships
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      transformPatchToOperations: function (patch, contentLength) {
 | 
					      transformPatchToOperations: function (patch, contentLength) {
 | 
				
			||||||
                var operations = [];
 | 
					        var operations = []
 | 
				
			||||||
        if (patch.length > 0) {
 | 
					        if (patch.length > 0) {
 | 
				
			||||||
          // calculate original content length
 | 
					          // calculate original content length
 | 
				
			||||||
                    for (var j = patch.length - 1; j >= 0; j--) {
 | 
					          for (let j = patch.length - 1; j >= 0; j--) {
 | 
				
			||||||
                        var p = patch[j];
 | 
					            var p = patch[j]
 | 
				
			||||||
                        for (var i = 0; i < p.diffs.length; i++) {
 | 
					            for (let i = 0; i < p.diffs.length; i++) {
 | 
				
			||||||
                            var diff = p.diffs[i];
 | 
					              var diff = p.diffs[i]
 | 
				
			||||||
                            switch(diff[0]) {
 | 
					              switch (diff[0]) {
 | 
				
			||||||
                case 1: // insert
 | 
					                case 1: // insert
 | 
				
			||||||
                                    contentLength -= diff[1].length;
 | 
					                  contentLength -= diff[1].length
 | 
				
			||||||
                                break;
 | 
					                  break
 | 
				
			||||||
                case -1: // delete
 | 
					                case -1: // delete
 | 
				
			||||||
                                    contentLength += diff[1].length;
 | 
					                  contentLength += diff[1].length
 | 
				
			||||||
                                break;
 | 
					                  break
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
          // generate operations
 | 
					          // generate operations
 | 
				
			||||||
                    var bias = 0;
 | 
					          var bias = 0
 | 
				
			||||||
                    var lengthBias = 0;
 | 
					          var lengthBias = 0
 | 
				
			||||||
                    for (var j = 0; j < patch.length; j++) {
 | 
					          for (let j = 0; j < patch.length; j++) {
 | 
				
			||||||
                        var operation = [];
 | 
					            var operation = []
 | 
				
			||||||
                        var p = patch[j];
 | 
					            let p = patch[j]
 | 
				
			||||||
                        var currIndex = p.start1;
 | 
					            var currIndex = p.start1
 | 
				
			||||||
                        var currLength = contentLength - bias;
 | 
					            var currLength = contentLength - bias
 | 
				
			||||||
                        for (var i = 0; i < p.diffs.length; i++) {
 | 
					            for (let i = 0; i < p.diffs.length; i++) {
 | 
				
			||||||
                            var diff = p.diffs[i];
 | 
					              let diff = p.diffs[i]
 | 
				
			||||||
                            switch(diff[0]) {
 | 
					              switch (diff[0]) {
 | 
				
			||||||
                case 0: // retain
 | 
					                case 0: // retain
 | 
				
			||||||
                                    if (i == 0) // first
 | 
					                  if (i === 0) {
 | 
				
			||||||
                                        operation.push(currIndex + diff[1].length);
 | 
					                    // first
 | 
				
			||||||
                                    else if (i != p.diffs.length - 1) // mid
 | 
					                    operation.push(currIndex + diff[1].length)
 | 
				
			||||||
                                        operation.push(diff[1].length);
 | 
					                  } else if (i !== p.diffs.length - 1) {
 | 
				
			||||||
                                    else // last
 | 
					                    // mid
 | 
				
			||||||
                                        operation.push(currLength + lengthBias - currIndex);
 | 
					                    operation.push(diff[1].length)
 | 
				
			||||||
                                    currIndex += diff[1].length;
 | 
					                  } else {
 | 
				
			||||||
                                break;
 | 
					                    // last
 | 
				
			||||||
 | 
					                    operation.push(currLength + lengthBias - currIndex)
 | 
				
			||||||
 | 
					                  }
 | 
				
			||||||
 | 
					                  currIndex += diff[1].length
 | 
				
			||||||
 | 
					                  break
 | 
				
			||||||
                case 1: // insert
 | 
					                case 1: // insert
 | 
				
			||||||
                                    operation.push(diff[1]);
 | 
					                  operation.push(diff[1])
 | 
				
			||||||
                                    lengthBias += diff[1].length;
 | 
					                  lengthBias += diff[1].length
 | 
				
			||||||
                                    currIndex += diff[1].length;
 | 
					                  currIndex += diff[1].length
 | 
				
			||||||
                                break;
 | 
					                  break
 | 
				
			||||||
                case -1: // delete
 | 
					                case -1: // delete
 | 
				
			||||||
                                    operation.push(-diff[1].length);
 | 
					                  operation.push(-diff[1].length)
 | 
				
			||||||
                                    bias += diff[1].length;
 | 
					                  bias += diff[1].length
 | 
				
			||||||
                                    currIndex += diff[1].length;
 | 
					                  currIndex += diff[1].length
 | 
				
			||||||
                                break;
 | 
					                  break
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
                        operations.push(operation);
 | 
					            operations.push(operation)
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
                return operations;
 | 
					        return operations
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    hooks: {
 | 
					    hooks: {
 | 
				
			||||||
      beforeCreate: function (note, options, callback) {
 | 
					      beforeCreate: function (note, options, callback) {
 | 
				
			||||||
        // if no content specified then use default note
 | 
					        // if no content specified then use default note
 | 
				
			||||||
        if (!note.content) {
 | 
					        if (!note.content) {
 | 
				
			||||||
                    var body = null;
 | 
					          var body = null
 | 
				
			||||||
                    var filePath = null;
 | 
					          let filePath = null
 | 
				
			||||||
          if (!note.alias) {
 | 
					          if (!note.alias) {
 | 
				
			||||||
                        filePath = config.defaultnotepath;
 | 
					            filePath = config.defaultnotepath
 | 
				
			||||||
          } else {
 | 
					          } else {
 | 
				
			||||||
                        filePath = path.join(config.docspath, note.alias + '.md');
 | 
					            filePath = path.join(config.docspath, note.alias + '.md')
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
          if (Note.checkFileExist(filePath)) {
 | 
					          if (Note.checkFileExist(filePath)) {
 | 
				
			||||||
                        var fsCreatedTime = moment(fs.statSync(filePath).ctime);
 | 
					            var fsCreatedTime = moment(fs.statSync(filePath).ctime)
 | 
				
			||||||
                        body = fs.readFileSync(filePath, 'utf8');
 | 
					            body = fs.readFileSync(filePath, 'utf8')
 | 
				
			||||||
                        note.title = Note.parseNoteTitle(body);
 | 
					            note.title = Note.parseNoteTitle(body)
 | 
				
			||||||
                        note.content = body;
 | 
					            note.content = body
 | 
				
			||||||
            if (filePath !== config.defaultnotepath) {
 | 
					            if (filePath !== config.defaultnotepath) {
 | 
				
			||||||
                            note.createdAt = fsCreatedTime;
 | 
					              note.createdAt = fsCreatedTime
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        // if no permission specified and have owner then give default permission in config, else default permission is freely
 | 
					        // if no permission specified and have owner then give default permission in config, else default permission is freely
 | 
				
			||||||
        if (!note.permission) {
 | 
					        if (!note.permission) {
 | 
				
			||||||
          if (note.ownerId) {
 | 
					          if (note.ownerId) {
 | 
				
			||||||
                        note.permission = config.defaultpermission;
 | 
					            note.permission = config.defaultpermission
 | 
				
			||||||
          } else {
 | 
					          } else {
 | 
				
			||||||
                        note.permission = "freely";
 | 
					            note.permission = 'freely'
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
                return callback(null, note);
 | 
					        return callback(null, note)
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      afterCreate: function (note, options, callback) {
 | 
					      afterCreate: function (note, options, callback) {
 | 
				
			||||||
        sequelize.models.Revision.saveNoteRevision(note, function (err, revision) {
 | 
					        sequelize.models.Revision.saveNoteRevision(note, function (err, revision) {
 | 
				
			||||||
                    callback(err, note);
 | 
					          callback(err, note)
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return Note;
 | 
					  return Note
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,58 +1,56 @@
 | 
				
			|||||||
"use strict";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// external modules
 | 
					// external modules
 | 
				
			||||||
var Sequelize = require("sequelize");
 | 
					var Sequelize = require('sequelize')
 | 
				
			||||||
var async = require('async');
 | 
					var async = require('async')
 | 
				
			||||||
var moment = require('moment');
 | 
					var moment = require('moment')
 | 
				
			||||||
var childProcess = require('child_process');
 | 
					var childProcess = require('child_process')
 | 
				
			||||||
var shortId = require('shortid');
 | 
					var shortId = require('shortid')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// core
 | 
					// core
 | 
				
			||||||
var config = require("../config.js");
 | 
					var config = require('../config.js')
 | 
				
			||||||
var logger = require("../logger.js");
 | 
					var logger = require('../logger.js')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var dmpWorker = createDmpWorker();
 | 
					var dmpWorker = createDmpWorker()
 | 
				
			||||||
var dmpCallbackCache = {};
 | 
					var dmpCallbackCache = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function createDmpWorker() {
 | 
					function createDmpWorker () {
 | 
				
			||||||
    var worker = childProcess.fork("./lib/workers/dmpWorker.js", {
 | 
					  var worker = childProcess.fork('./lib/workers/dmpWorker.js', {
 | 
				
			||||||
    stdio: 'ignore'
 | 
					    stdio: 'ignore'
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
    if (config.debug) logger.info('dmp worker process started');
 | 
					  if (config.debug) logger.info('dmp worker process started')
 | 
				
			||||||
  worker.on('message', function (data) {
 | 
					  worker.on('message', function (data) {
 | 
				
			||||||
    if (!data || !data.msg || !data.cacheKey) {
 | 
					    if (!data || !data.msg || !data.cacheKey) {
 | 
				
			||||||
            return logger.error('dmp worker error: not enough data on message');
 | 
					      return logger.error('dmp worker error: not enough data on message')
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
        var cacheKey = data.cacheKey;
 | 
					    var cacheKey = data.cacheKey
 | 
				
			||||||
        switch(data.msg) {
 | 
					    switch (data.msg) {
 | 
				
			||||||
      case 'error':
 | 
					      case 'error':
 | 
				
			||||||
                dmpCallbackCache[cacheKey](data.error, null);
 | 
					        dmpCallbackCache[cacheKey](data.error, null)
 | 
				
			||||||
                break;
 | 
					        break
 | 
				
			||||||
      case 'check':
 | 
					      case 'check':
 | 
				
			||||||
                dmpCallbackCache[cacheKey](null, data.result);
 | 
					        dmpCallbackCache[cacheKey](null, data.result)
 | 
				
			||||||
                break;
 | 
					        break
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
        delete dmpCallbackCache[cacheKey];
 | 
					    delete dmpCallbackCache[cacheKey]
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
  worker.on('close', function (code) {
 | 
					  worker.on('close', function (code) {
 | 
				
			||||||
        dmpWorker = null;
 | 
					    dmpWorker = null
 | 
				
			||||||
        if (config.debug) logger.info('dmp worker process exited with code ' + code);
 | 
					    if (config.debug) logger.info('dmp worker process exited with code ' + code)
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
    return worker;
 | 
					  return worker
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function sendDmpWorker(data, callback) {
 | 
					function sendDmpWorker (data, callback) {
 | 
				
			||||||
    if (!dmpWorker) dmpWorker = createDmpWorker();
 | 
					  if (!dmpWorker) dmpWorker = createDmpWorker()
 | 
				
			||||||
    var cacheKey = Date.now() + '_' + shortId.generate();
 | 
					  var cacheKey = Date.now() + '_' + shortId.generate()
 | 
				
			||||||
    dmpCallbackCache[cacheKey] = callback;
 | 
					  dmpCallbackCache[cacheKey] = callback
 | 
				
			||||||
  data = Object.assign(data, {
 | 
					  data = Object.assign(data, {
 | 
				
			||||||
    cacheKey: cacheKey
 | 
					    cacheKey: cacheKey
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
    dmpWorker.send(data);
 | 
					  dmpWorker.send(data)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = function (sequelize, DataTypes) {
 | 
					module.exports = function (sequelize, DataTypes) {
 | 
				
			||||||
    var Revision = sequelize.define("Revision", {
 | 
					  var Revision = sequelize.define('Revision', {
 | 
				
			||||||
    id: {
 | 
					    id: {
 | 
				
			||||||
      type: DataTypes.UUID,
 | 
					      type: DataTypes.UUID,
 | 
				
			||||||
      primaryKey: true,
 | 
					      primaryKey: true,
 | 
				
			||||||
@ -61,28 +59,28 @@ module.exports = function (sequelize, DataTypes) {
 | 
				
			|||||||
    patch: {
 | 
					    patch: {
 | 
				
			||||||
      type: DataTypes.TEXT,
 | 
					      type: DataTypes.TEXT,
 | 
				
			||||||
      get: function () {
 | 
					      get: function () {
 | 
				
			||||||
                return sequelize.processData(this.getDataValue('patch'), "");
 | 
					        return sequelize.processData(this.getDataValue('patch'), '')
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      set: function (value) {
 | 
					      set: function (value) {
 | 
				
			||||||
                this.setDataValue('patch', sequelize.stripNullByte(value));
 | 
					        this.setDataValue('patch', sequelize.stripNullByte(value))
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    lastContent: {
 | 
					    lastContent: {
 | 
				
			||||||
      type: DataTypes.TEXT,
 | 
					      type: DataTypes.TEXT,
 | 
				
			||||||
      get: function () {
 | 
					      get: function () {
 | 
				
			||||||
                return sequelize.processData(this.getDataValue('lastContent'), "");
 | 
					        return sequelize.processData(this.getDataValue('lastContent'), '')
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      set: function (value) {
 | 
					      set: function (value) {
 | 
				
			||||||
                this.setDataValue('lastContent', sequelize.stripNullByte(value));
 | 
					        this.setDataValue('lastContent', sequelize.stripNullByte(value))
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    content: {
 | 
					    content: {
 | 
				
			||||||
      type: DataTypes.TEXT,
 | 
					      type: DataTypes.TEXT,
 | 
				
			||||||
      get: function () {
 | 
					      get: function () {
 | 
				
			||||||
                return sequelize.processData(this.getDataValue('content'), "");
 | 
					        return sequelize.processData(this.getDataValue('content'), '')
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      set: function (value) {
 | 
					      set: function (value) {
 | 
				
			||||||
                this.setDataValue('content', sequelize.stripNullByte(value));
 | 
					        this.setDataValue('content', sequelize.stripNullByte(value))
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    length: {
 | 
					    length: {
 | 
				
			||||||
@ -91,20 +89,20 @@ module.exports = function (sequelize, DataTypes) {
 | 
				
			|||||||
    authorship: {
 | 
					    authorship: {
 | 
				
			||||||
      type: DataTypes.TEXT,
 | 
					      type: DataTypes.TEXT,
 | 
				
			||||||
      get: function () {
 | 
					      get: function () {
 | 
				
			||||||
                return sequelize.processData(this.getDataValue('authorship'), [], JSON.parse);
 | 
					        return sequelize.processData(this.getDataValue('authorship'), [], JSON.parse)
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      set: function (value) {
 | 
					      set: function (value) {
 | 
				
			||||||
                this.setDataValue('authorship', value ? JSON.stringify(value) : value);
 | 
					        this.setDataValue('authorship', value ? JSON.stringify(value) : value)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }, {
 | 
					  }, {
 | 
				
			||||||
    classMethods: {
 | 
					    classMethods: {
 | 
				
			||||||
      associate: function (models) {
 | 
					      associate: function (models) {
 | 
				
			||||||
        Revision.belongsTo(models.Note, {
 | 
					        Revision.belongsTo(models.Note, {
 | 
				
			||||||
                    foreignKey: "noteId",
 | 
					          foreignKey: 'noteId',
 | 
				
			||||||
                    as: "note",
 | 
					          as: 'note',
 | 
				
			||||||
          constraints: false
 | 
					          constraints: false
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      getNoteRevisions: function (note, callback) {
 | 
					      getNoteRevisions: function (note, callback) {
 | 
				
			||||||
        Revision.findAll({
 | 
					        Revision.findAll({
 | 
				
			||||||
@ -113,18 +111,18 @@ module.exports = function (sequelize, DataTypes) {
 | 
				
			|||||||
          },
 | 
					          },
 | 
				
			||||||
          order: '"createdAt" DESC'
 | 
					          order: '"createdAt" DESC'
 | 
				
			||||||
        }).then(function (revisions) {
 | 
					        }).then(function (revisions) {
 | 
				
			||||||
                    var data = [];
 | 
					          var data = []
 | 
				
			||||||
          for (var i = 0, l = revisions.length; i < l; i++) {
 | 
					          for (var i = 0, l = revisions.length; i < l; i++) {
 | 
				
			||||||
                        var revision = revisions[i];
 | 
					            var revision = revisions[i]
 | 
				
			||||||
            data.push({
 | 
					            data.push({
 | 
				
			||||||
              time: moment(revision.createdAt).valueOf(),
 | 
					              time: moment(revision.createdAt).valueOf(),
 | 
				
			||||||
              length: revision.length
 | 
					              length: revision.length
 | 
				
			||||||
                        });
 | 
					            })
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
                    callback(null, data);
 | 
					          callback(null, data)
 | 
				
			||||||
        }).catch(function (err) {
 | 
					        }).catch(function (err) {
 | 
				
			||||||
                    callback(err, null);
 | 
					          callback(err, null)
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      getPatchedNoteRevisionByTime: function (note, time, callback) {
 | 
					      getPatchedNoteRevisionByTime: function (note, time, callback) {
 | 
				
			||||||
        // find all revisions to prepare for all possible calculation
 | 
					        // find all revisions to prepare for all possible calculation
 | 
				
			||||||
@ -134,7 +132,7 @@ module.exports = function (sequelize, DataTypes) {
 | 
				
			|||||||
          },
 | 
					          },
 | 
				
			||||||
          order: '"createdAt" DESC'
 | 
					          order: '"createdAt" DESC'
 | 
				
			||||||
        }).then(function (revisions) {
 | 
					        }).then(function (revisions) {
 | 
				
			||||||
                    if (revisions.length <= 0) return callback(null, null);
 | 
					          if (revisions.length <= 0) return callback(null, null)
 | 
				
			||||||
          // measure target revision position
 | 
					          // measure target revision position
 | 
				
			||||||
          Revision.count({
 | 
					          Revision.count({
 | 
				
			||||||
            where: {
 | 
					            where: {
 | 
				
			||||||
@ -145,28 +143,28 @@ module.exports = function (sequelize, DataTypes) {
 | 
				
			|||||||
            },
 | 
					            },
 | 
				
			||||||
            order: '"createdAt" DESC'
 | 
					            order: '"createdAt" DESC'
 | 
				
			||||||
          }).then(function (count) {
 | 
					          }).then(function (count) {
 | 
				
			||||||
                        if (count <= 0) return callback(null, null);
 | 
					            if (count <= 0) return callback(null, null)
 | 
				
			||||||
            sendDmpWorker({
 | 
					            sendDmpWorker({
 | 
				
			||||||
              msg: 'get revision',
 | 
					              msg: 'get revision',
 | 
				
			||||||
              revisions: revisions,
 | 
					              revisions: revisions,
 | 
				
			||||||
              count: count
 | 
					              count: count
 | 
				
			||||||
                        }, callback);
 | 
					            }, callback)
 | 
				
			||||||
          }).catch(function (err) {
 | 
					          }).catch(function (err) {
 | 
				
			||||||
                        return callback(err, null);
 | 
					            return callback(err, null)
 | 
				
			||||||
                    });
 | 
					          })
 | 
				
			||||||
        }).catch(function (err) {
 | 
					        }).catch(function (err) {
 | 
				
			||||||
                    return callback(err, null);
 | 
					          return callback(err, null)
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      checkAllNotesRevision: function (callback) {
 | 
					      checkAllNotesRevision: function (callback) {
 | 
				
			||||||
        Revision.saveAllNotesRevision(function (err, notes) {
 | 
					        Revision.saveAllNotesRevision(function (err, notes) {
 | 
				
			||||||
                    if (err) return callback(err, null);
 | 
					          if (err) return callback(err, null)
 | 
				
			||||||
          if (!notes || notes.length <= 0) {
 | 
					          if (!notes || notes.length <= 0) {
 | 
				
			||||||
                        return callback(null, notes);
 | 
					            return callback(null, notes)
 | 
				
			||||||
          } else {
 | 
					          } else {
 | 
				
			||||||
                        Revision.checkAllNotesRevision(callback);
 | 
					            Revision.checkAllNotesRevision(callback)
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      saveAllNotesRevision: function (callback) {
 | 
					      saveAllNotesRevision: function (callback) {
 | 
				
			||||||
        sequelize.models.Note.findAll({
 | 
					        sequelize.models.Note.findAll({
 | 
				
			||||||
@ -195,35 +193,37 @@ module.exports = function (sequelize, DataTypes) {
 | 
				
			|||||||
            ]
 | 
					            ]
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }).then(function (notes) {
 | 
					        }).then(function (notes) {
 | 
				
			||||||
                    if (notes.length <= 0) return callback(null, notes);
 | 
					          if (notes.length <= 0) return callback(null, notes)
 | 
				
			||||||
                    var savedNotes = [];
 | 
					          var savedNotes = []
 | 
				
			||||||
          async.each(notes, function (note, _callback) {
 | 
					          async.each(notes, function (note, _callback) {
 | 
				
			||||||
            // revision saving policy: note not been modified for 5 mins or not save for 10 mins
 | 
					            // revision saving policy: note not been modified for 5 mins or not save for 10 mins
 | 
				
			||||||
            if (note.lastchangeAt && note.savedAt) {
 | 
					            if (note.lastchangeAt && note.savedAt) {
 | 
				
			||||||
                            var lastchangeAt = moment(note.lastchangeAt);
 | 
					              var lastchangeAt = moment(note.lastchangeAt)
 | 
				
			||||||
                            var savedAt = moment(note.savedAt);
 | 
					              var savedAt = moment(note.savedAt)
 | 
				
			||||||
              if (moment().isAfter(lastchangeAt.add(5, 'minutes'))) {
 | 
					              if (moment().isAfter(lastchangeAt.add(5, 'minutes'))) {
 | 
				
			||||||
                                savedNotes.push(note);
 | 
					                savedNotes.push(note)
 | 
				
			||||||
                                Revision.saveNoteRevision(note, _callback);
 | 
					                Revision.saveNoteRevision(note, _callback)
 | 
				
			||||||
              } else if (lastchangeAt.isAfter(savedAt.add(10, 'minutes'))) {
 | 
					              } else if (lastchangeAt.isAfter(savedAt.add(10, 'minutes'))) {
 | 
				
			||||||
                                savedNotes.push(note);
 | 
					                savedNotes.push(note)
 | 
				
			||||||
                                Revision.saveNoteRevision(note, _callback);
 | 
					                Revision.saveNoteRevision(note, _callback)
 | 
				
			||||||
              } else {
 | 
					              } else {
 | 
				
			||||||
                                return _callback(null, null);
 | 
					                return _callback(null, null)
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                            savedNotes.push(note);
 | 
					              savedNotes.push(note)
 | 
				
			||||||
                            Revision.saveNoteRevision(note, _callback);
 | 
					              Revision.saveNoteRevision(note, _callback)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
          }, function (err) {
 | 
					          }, function (err) {
 | 
				
			||||||
                        if (err) return callback(err, null);
 | 
					            if (err) {
 | 
				
			||||||
 | 
					              return callback(err, null)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            // return null when no notes need saving at this moment but have delayed tasks to be done
 | 
					            // return null when no notes need saving at this moment but have delayed tasks to be done
 | 
				
			||||||
                        var result = ((savedNotes.length == 0) && (notes.length > savedNotes.length)) ? null : savedNotes;
 | 
					            var result = ((savedNotes.length === 0) && (notes.length > savedNotes.length)) ? null : savedNotes
 | 
				
			||||||
                        return callback(null, result);
 | 
					            return callback(null, result)
 | 
				
			||||||
                    });
 | 
					          })
 | 
				
			||||||
        }).catch(function (err) {
 | 
					        }).catch(function (err) {
 | 
				
			||||||
                    return callback(err, null);
 | 
					          return callback(err, null)
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      saveNoteRevision: function (note, callback) {
 | 
					      saveNoteRevision: function (note, callback) {
 | 
				
			||||||
        Revision.findAll({
 | 
					        Revision.findAll({
 | 
				
			||||||
@ -240,30 +240,30 @@ module.exports = function (sequelize, DataTypes) {
 | 
				
			|||||||
              length: note.content.length,
 | 
					              length: note.content.length,
 | 
				
			||||||
              authorship: note.authorship
 | 
					              authorship: note.authorship
 | 
				
			||||||
            }).then(function (revision) {
 | 
					            }).then(function (revision) {
 | 
				
			||||||
                            Revision.finishSaveNoteRevision(note, revision, callback);
 | 
					              Revision.finishSaveNoteRevision(note, revision, callback)
 | 
				
			||||||
            }).catch(function (err) {
 | 
					            }).catch(function (err) {
 | 
				
			||||||
                            return callback(err, null);
 | 
					              return callback(err, null)
 | 
				
			||||||
                        });
 | 
					            })
 | 
				
			||||||
          } else {
 | 
					          } else {
 | 
				
			||||||
                        var latestRevision = revisions[0];
 | 
					            var latestRevision = revisions[0]
 | 
				
			||||||
                        var lastContent = latestRevision.content || latestRevision.lastContent;
 | 
					            var lastContent = latestRevision.content || latestRevision.lastContent
 | 
				
			||||||
                        var content = note.content;
 | 
					            var content = note.content
 | 
				
			||||||
            sendDmpWorker({
 | 
					            sendDmpWorker({
 | 
				
			||||||
              msg: 'create patch',
 | 
					              msg: 'create patch',
 | 
				
			||||||
              lastDoc: lastContent,
 | 
					              lastDoc: lastContent,
 | 
				
			||||||
                            currDoc: content,
 | 
					              currDoc: content
 | 
				
			||||||
            }, function (err, patch) {
 | 
					            }, function (err, patch) {
 | 
				
			||||||
                            if (err) logger.error('save note revision error', err);
 | 
					              if (err) logger.error('save note revision error', err)
 | 
				
			||||||
              if (!patch) {
 | 
					              if (!patch) {
 | 
				
			||||||
                // if patch is empty (means no difference) then just update the latest revision updated time
 | 
					                // if patch is empty (means no difference) then just update the latest revision updated time
 | 
				
			||||||
                                latestRevision.changed('updatedAt', true); 
 | 
					                latestRevision.changed('updatedAt', true)
 | 
				
			||||||
                latestRevision.update({
 | 
					                latestRevision.update({
 | 
				
			||||||
                  updatedAt: Date.now()
 | 
					                  updatedAt: Date.now()
 | 
				
			||||||
                }).then(function (revision) {
 | 
					                }).then(function (revision) {
 | 
				
			||||||
                                    Revision.finishSaveNoteRevision(note, revision, callback);
 | 
					                  Revision.finishSaveNoteRevision(note, revision, callback)
 | 
				
			||||||
                }).catch(function (err) {
 | 
					                }).catch(function (err) {
 | 
				
			||||||
                                    return callback(err, null);
 | 
					                  return callback(err, null)
 | 
				
			||||||
                                });
 | 
					                })
 | 
				
			||||||
              } else {
 | 
					              } else {
 | 
				
			||||||
                Revision.create({
 | 
					                Revision.create({
 | 
				
			||||||
                  noteId: note.id,
 | 
					                  noteId: note.id,
 | 
				
			||||||
@ -276,31 +276,31 @@ module.exports = function (sequelize, DataTypes) {
 | 
				
			|||||||
                  latestRevision.update({
 | 
					                  latestRevision.update({
 | 
				
			||||||
                    content: null
 | 
					                    content: null
 | 
				
			||||||
                  }).then(function () {
 | 
					                  }).then(function () {
 | 
				
			||||||
                                        Revision.finishSaveNoteRevision(note, revision, callback);
 | 
					                    Revision.finishSaveNoteRevision(note, revision, callback)
 | 
				
			||||||
                  }).catch(function (err) {
 | 
					                  }).catch(function (err) {
 | 
				
			||||||
                                        return callback(err, null);
 | 
					                    return callback(err, null)
 | 
				
			||||||
                                    });
 | 
					                  })
 | 
				
			||||||
                }).catch(function (err) {
 | 
					                }).catch(function (err) {
 | 
				
			||||||
                                    return callback(err, null);
 | 
					                  return callback(err, null)
 | 
				
			||||||
                                });
 | 
					                })
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
                        });
 | 
					            })
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }).catch(function (err) {
 | 
					        }).catch(function (err) {
 | 
				
			||||||
                    return callback(err, null);
 | 
					          return callback(err, null)
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      finishSaveNoteRevision: function (note, revision, callback) {
 | 
					      finishSaveNoteRevision: function (note, revision, callback) {
 | 
				
			||||||
        note.update({
 | 
					        note.update({
 | 
				
			||||||
          savedAt: revision.updatedAt
 | 
					          savedAt: revision.updatedAt
 | 
				
			||||||
        }).then(function () {
 | 
					        }).then(function () {
 | 
				
			||||||
                    return callback(null, revision);
 | 
					          return callback(null, revision)
 | 
				
			||||||
        }).catch(function (err) {
 | 
					        }).catch(function (err) {
 | 
				
			||||||
                    return callback(err, null);
 | 
					          return callback(err, null)
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return Revision;
 | 
					  return Revision
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,10 +1,8 @@
 | 
				
			|||||||
"use strict";
 | 
					// external modules
 | 
				
			||||||
 | 
					var shortId = require('shortid')
 | 
				
			||||||
//external modules
 | 
					 | 
				
			||||||
var shortId = require('shortid');
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = function (sequelize, DataTypes) {
 | 
					module.exports = function (sequelize, DataTypes) {
 | 
				
			||||||
    var Temp = sequelize.define("Temp", {
 | 
					  var Temp = sequelize.define('Temp', {
 | 
				
			||||||
    id: {
 | 
					    id: {
 | 
				
			||||||
      type: DataTypes.STRING,
 | 
					      type: DataTypes.STRING,
 | 
				
			||||||
      primaryKey: true,
 | 
					      primaryKey: true,
 | 
				
			||||||
@ -13,7 +11,7 @@ module.exports = function (sequelize, DataTypes) {
 | 
				
			|||||||
    data: {
 | 
					    data: {
 | 
				
			||||||
      type: DataTypes.TEXT
 | 
					      type: DataTypes.TEXT
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return Temp;
 | 
					  return Temp
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,16 +1,14 @@
 | 
				
			|||||||
"use strict";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// external modules
 | 
					// external modules
 | 
				
			||||||
var md5 = require("blueimp-md5");
 | 
					var md5 = require('blueimp-md5')
 | 
				
			||||||
var Sequelize = require("sequelize");
 | 
					var Sequelize = require('sequelize')
 | 
				
			||||||
var scrypt = require('scrypt');
 | 
					var scrypt = require('scrypt')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// core
 | 
					// core
 | 
				
			||||||
var logger = require("../logger.js");
 | 
					var logger = require('../logger.js')
 | 
				
			||||||
var letterAvatars = require('../letter-avatars.js');
 | 
					var letterAvatars = require('../letter-avatars.js')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = function (sequelize, DataTypes) {
 | 
					module.exports = function (sequelize, DataTypes) {
 | 
				
			||||||
    var User = sequelize.define("User", {
 | 
					  var User = sequelize.define('User', {
 | 
				
			||||||
    id: {
 | 
					    id: {
 | 
				
			||||||
      type: DataTypes.UUID,
 | 
					      type: DataTypes.UUID,
 | 
				
			||||||
      primaryKey: true,
 | 
					      primaryKey: true,
 | 
				
			||||||
@ -40,41 +38,41 @@ module.exports = function (sequelize, DataTypes) {
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
    password: {
 | 
					    password: {
 | 
				
			||||||
      type: Sequelize.TEXT,
 | 
					      type: Sequelize.TEXT,
 | 
				
			||||||
            set: function(value) {
 | 
					      set: function (value) {
 | 
				
			||||||
                var hash = scrypt.kdfSync(value, scrypt.paramsSync(0.1)).toString("hex");
 | 
					        var hash = scrypt.kdfSync(value, scrypt.paramsSync(0.1)).toString('hex')
 | 
				
			||||||
                this.setDataValue('password', hash);
 | 
					        this.setDataValue('password', hash)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }, {
 | 
					  }, {
 | 
				
			||||||
    instanceMethods: {
 | 
					    instanceMethods: {
 | 
				
			||||||
            verifyPassword: function(attempt) {
 | 
					      verifyPassword: function (attempt) {
 | 
				
			||||||
                if (scrypt.verifyKdfSync(new Buffer(this.password, "hex"), attempt)) {
 | 
					        if (scrypt.verifyKdfSync(new Buffer(this.password, 'hex'), attempt)) {
 | 
				
			||||||
                    return this;
 | 
					          return this
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
                    return false;
 | 
					          return false
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    classMethods: {
 | 
					    classMethods: {
 | 
				
			||||||
      associate: function (models) {
 | 
					      associate: function (models) {
 | 
				
			||||||
        User.hasMany(models.Note, {
 | 
					        User.hasMany(models.Note, {
 | 
				
			||||||
                    foreignKey: "ownerId",
 | 
					          foreignKey: 'ownerId',
 | 
				
			||||||
          constraints: false
 | 
					          constraints: false
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
        User.hasMany(models.Note, {
 | 
					        User.hasMany(models.Note, {
 | 
				
			||||||
                    foreignKey: "lastchangeuserId",
 | 
					          foreignKey: 'lastchangeuserId',
 | 
				
			||||||
          constraints: false
 | 
					          constraints: false
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      getProfile: function (user) {
 | 
					      getProfile: function (user) {
 | 
				
			||||||
                return user.profile ? User.parseProfile(user.profile) : (user.email ? User.parseProfileByEmail(user.email) : null);
 | 
					        return user.profile ? User.parseProfile(user.profile) : (user.email ? User.parseProfileByEmail(user.email) : null)
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      parseProfile: function (profile) {
 | 
					      parseProfile: function (profile) {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
                    var profile = JSON.parse(profile);
 | 
					          profile = JSON.parse(profile)
 | 
				
			||||||
        } catch (err) {
 | 
					        } catch (err) {
 | 
				
			||||||
                    logger.error(err);
 | 
					          logger.error(err)
 | 
				
			||||||
                    profile = null;
 | 
					          profile = null
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (profile) {
 | 
					        if (profile) {
 | 
				
			||||||
          profile = {
 | 
					          profile = {
 | 
				
			||||||
@ -83,67 +81,67 @@ module.exports = function (sequelize, DataTypes) {
 | 
				
			|||||||
            biggerphoto: User.parsePhotoByProfile(profile, true)
 | 
					            biggerphoto: User.parsePhotoByProfile(profile, true)
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
                return profile;
 | 
					        return profile
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      parsePhotoByProfile: function (profile, bigger) {
 | 
					      parsePhotoByProfile: function (profile, bigger) {
 | 
				
			||||||
                var photo = null;
 | 
					        var photo = null
 | 
				
			||||||
        switch (profile.provider) {
 | 
					        switch (profile.provider) {
 | 
				
			||||||
                    case "facebook":
 | 
					          case 'facebook':
 | 
				
			||||||
                        photo = 'https://graph.facebook.com/' + profile.id + '/picture';
 | 
					            photo = 'https://graph.facebook.com/' + profile.id + '/picture'
 | 
				
			||||||
                        if (bigger) photo += '?width=400';
 | 
					            if (bigger) photo += '?width=400'
 | 
				
			||||||
                        else photo += '?width=96';
 | 
					            else photo += '?width=96'
 | 
				
			||||||
                        break;
 | 
					            break
 | 
				
			||||||
                    case "twitter":
 | 
					          case 'twitter':
 | 
				
			||||||
                        photo = 'https://twitter.com/' + profile.username + '/profile_image';
 | 
					            photo = 'https://twitter.com/' + profile.username + '/profile_image'
 | 
				
			||||||
                        if (bigger) photo += '?size=original';
 | 
					            if (bigger) photo += '?size=original'
 | 
				
			||||||
                        else photo += '?size=bigger';
 | 
					            else photo += '?size=bigger'
 | 
				
			||||||
                        break;
 | 
					            break
 | 
				
			||||||
                    case "github":
 | 
					          case 'github':
 | 
				
			||||||
                        photo = 'https://avatars.githubusercontent.com/u/' + profile.id;
 | 
					            photo = 'https://avatars.githubusercontent.com/u/' + profile.id
 | 
				
			||||||
                        if (bigger) photo += '?s=400';
 | 
					            if (bigger) photo += '?s=400'
 | 
				
			||||||
                        else photo += '?s=96';
 | 
					            else photo += '?s=96'
 | 
				
			||||||
                        break;
 | 
					            break
 | 
				
			||||||
                    case "gitlab":
 | 
					          case 'gitlab':
 | 
				
			||||||
                        photo = profile.avatarUrl;
 | 
					            photo = profile.avatarUrl
 | 
				
			||||||
                        if (bigger) photo = photo.replace(/(\?s=)\d*$/i, '$1400');
 | 
					            if (bigger) photo = photo.replace(/(\?s=)\d*$/i, '$1400')
 | 
				
			||||||
                        else photo = photo.replace(/(\?s=)\d*$/i, '$196');
 | 
					            else photo = photo.replace(/(\?s=)\d*$/i, '$196')
 | 
				
			||||||
                        break;
 | 
					            break
 | 
				
			||||||
                    case "dropbox":
 | 
					          case 'dropbox':
 | 
				
			||||||
                        //no image api provided, use gravatar
 | 
					            // no image api provided, use gravatar
 | 
				
			||||||
                        photo = 'https://www.gravatar.com/avatar/' + md5(profile.emails[0].value);
 | 
					            photo = 'https://www.gravatar.com/avatar/' + md5(profile.emails[0].value)
 | 
				
			||||||
                        if (bigger) photo += '?s=400';
 | 
					            if (bigger) photo += '?s=400'
 | 
				
			||||||
                        else photo += '?s=96';
 | 
					            else photo += '?s=96'
 | 
				
			||||||
                        break;
 | 
					            break
 | 
				
			||||||
                    case "google":
 | 
					          case 'google':
 | 
				
			||||||
                        photo = profile.photos[0].value;
 | 
					            photo = profile.photos[0].value
 | 
				
			||||||
                        if (bigger) photo = photo.replace(/(\?sz=)\d*$/i, '$1400');
 | 
					            if (bigger) photo = photo.replace(/(\?sz=)\d*$/i, '$1400')
 | 
				
			||||||
                        else photo = photo.replace(/(\?sz=)\d*$/i, '$196');
 | 
					            else photo = photo.replace(/(\?sz=)\d*$/i, '$196')
 | 
				
			||||||
                        break;
 | 
					            break
 | 
				
			||||||
                    case "ldap":
 | 
					          case 'ldap':
 | 
				
			||||||
                        //no image api provided,
 | 
					            // no image api provided,
 | 
				
			||||||
                        //use gravatar if email exists,
 | 
					            // use gravatar if email exists,
 | 
				
			||||||
                        //otherwise generate a letter avatar
 | 
					            // otherwise generate a letter avatar
 | 
				
			||||||
            if (profile.emails[0]) {
 | 
					            if (profile.emails[0]) {
 | 
				
			||||||
                            photo = 'https://www.gravatar.com/avatar/' + md5(profile.emails[0]);
 | 
					              photo = 'https://www.gravatar.com/avatar/' + md5(profile.emails[0])
 | 
				
			||||||
                            if (bigger) photo += '?s=400';
 | 
					              if (bigger) photo += '?s=400'
 | 
				
			||||||
                            else photo += '?s=96';
 | 
					              else photo += '?s=96'
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                            photo = letterAvatars(profile.username);
 | 
					              photo = letterAvatars(profile.username)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
                        break;
 | 
					            break
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
                return photo;
 | 
					        return photo
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      parseProfileByEmail: function (email) {
 | 
					      parseProfileByEmail: function (email) {
 | 
				
			||||||
                var photoUrl = 'https://www.gravatar.com/avatar/' + md5(email);
 | 
					        var photoUrl = 'https://www.gravatar.com/avatar/' + md5(email)
 | 
				
			||||||
        return {
 | 
					        return {
 | 
				
			||||||
                    name: email.substring(0, email.lastIndexOf("@")),
 | 
					          name: email.substring(0, email.lastIndexOf('@')),
 | 
				
			||||||
                    photo: photoUrl += '?s=96',
 | 
					          photo: photoUrl + '?s=96',
 | 
				
			||||||
                    biggerphoto: photoUrl += '?s=400'
 | 
					          biggerphoto: photoUrl + '?s=400'
 | 
				
			||||||
                };
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    });
 | 
					    }
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return User;
 | 
					  return User
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										981
									
								
								lib/realtime.js
									
									
									
									
									
								
							
							
						
						
									
										981
									
								
								lib/realtime.js
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										586
									
								
								lib/response.js
									
									
									
									
									
								
							
							
						
						
									
										586
									
								
								lib/response.js
									
									
									
									
									
								
							@ -1,36 +1,34 @@
 | 
				
			|||||||
//response
 | 
					// response
 | 
				
			||||||
//external modules
 | 
					// external modules
 | 
				
			||||||
var fs = require('fs');
 | 
					var fs = require('fs')
 | 
				
			||||||
var path = require('path');
 | 
					var markdownpdf = require('markdown-pdf')
 | 
				
			||||||
var markdownpdf = require("markdown-pdf");
 | 
					var LZString = require('lz-string')
 | 
				
			||||||
var LZString = require('lz-string');
 | 
					var shortId = require('shortid')
 | 
				
			||||||
var S = require('string');
 | 
					var querystring = require('querystring')
 | 
				
			||||||
var shortId = require('shortid');
 | 
					var request = require('request')
 | 
				
			||||||
var querystring = require('querystring');
 | 
					var moment = require('moment')
 | 
				
			||||||
var request = require('request');
 | 
					 | 
				
			||||||
var moment = require('moment');
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
//core
 | 
					// core
 | 
				
			||||||
var config = require("./config.js");
 | 
					var config = require('./config.js')
 | 
				
			||||||
var logger = require("./logger.js");
 | 
					var logger = require('./logger.js')
 | 
				
			||||||
var models = require("./models");
 | 
					var models = require('./models')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//public
 | 
					// public
 | 
				
			||||||
var response = {
 | 
					var response = {
 | 
				
			||||||
  errorForbidden: function (res) {
 | 
					  errorForbidden: function (res) {
 | 
				
			||||||
        responseError(res, "403", "Forbidden", "oh no.");
 | 
					    responseError(res, '403', 'Forbidden', 'oh no.')
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  errorNotFound: function (res) {
 | 
					  errorNotFound: function (res) {
 | 
				
			||||||
        responseError(res, "404", "Not Found", "oops.");
 | 
					    responseError(res, '404', 'Not Found', 'oops.')
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  errorBadRequest: function (res) {
 | 
					  errorBadRequest: function (res) {
 | 
				
			||||||
        responseError(res, "400", "Bad Request", "something not right.");
 | 
					    responseError(res, '400', 'Bad Request', 'something not right.')
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  errorInternalError: function (res) {
 | 
					  errorInternalError: function (res) {
 | 
				
			||||||
        responseError(res, "500", "Internal Error", "wtf.");
 | 
					    responseError(res, '500', 'Internal Error', 'wtf.')
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  errorServiceUnavailable: function (res) {
 | 
					  errorServiceUnavailable: function (res) {
 | 
				
			||||||
        res.status(503).send("I'm busy right now, try again later.");
 | 
					    res.status(503).send("I'm busy right now, try again later.")
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  newNote: newNote,
 | 
					  newNote: newNote,
 | 
				
			||||||
  showNote: showNote,
 | 
					  showNote: showNote,
 | 
				
			||||||
@ -42,9 +40,9 @@ var response = {
 | 
				
			|||||||
  publishSlideActions: publishSlideActions,
 | 
					  publishSlideActions: publishSlideActions,
 | 
				
			||||||
  githubActions: githubActions,
 | 
					  githubActions: githubActions,
 | 
				
			||||||
  gitlabActions: gitlabActions
 | 
					  gitlabActions: gitlabActions
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function responseError(res, code, detail, msg) {
 | 
					function responseError (res, code, detail, msg) {
 | 
				
			||||||
  res.status(code).render(config.errorpath, {
 | 
					  res.status(code).render(config.errorpath, {
 | 
				
			||||||
    url: config.serverurl,
 | 
					    url: config.serverurl,
 | 
				
			||||||
    title: code + ' ' + detail + ' ' + msg,
 | 
					    title: code + ' ' + detail + ' ' + msg,
 | 
				
			||||||
@ -52,10 +50,10 @@ function responseError(res, code, detail, msg) {
 | 
				
			|||||||
    detail: detail,
 | 
					    detail: detail,
 | 
				
			||||||
    msg: msg,
 | 
					    msg: msg,
 | 
				
			||||||
    useCDN: config.usecdn
 | 
					    useCDN: config.usecdn
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function showIndex(req, res, next) {
 | 
					function showIndex (req, res, next) {
 | 
				
			||||||
  res.render(config.indexpath, {
 | 
					  res.render(config.indexpath, {
 | 
				
			||||||
    url: config.serverurl,
 | 
					    url: config.serverurl,
 | 
				
			||||||
    useCDN: config.usecdn,
 | 
					    useCDN: config.usecdn,
 | 
				
			||||||
@ -72,19 +70,19 @@ function showIndex(req, res, next) {
 | 
				
			|||||||
    signin: req.isAuthenticated(),
 | 
					    signin: req.isAuthenticated(),
 | 
				
			||||||
    infoMessage: req.flash('info'),
 | 
					    infoMessage: req.flash('info'),
 | 
				
			||||||
    errorMessage: req.flash('error')
 | 
					    errorMessage: req.flash('error')
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function responseHackMD(res, note) {
 | 
					function responseHackMD (res, note) {
 | 
				
			||||||
    var body = note.content;
 | 
					  var body = note.content
 | 
				
			||||||
    var extracted = models.Note.extractMeta(body);
 | 
					  var extracted = models.Note.extractMeta(body)
 | 
				
			||||||
    var meta = models.Note.parseMeta(extracted.meta);
 | 
					  var meta = models.Note.parseMeta(extracted.meta)
 | 
				
			||||||
    var title = models.Note.decodeTitle(note.title);
 | 
					  var title = models.Note.decodeTitle(note.title)
 | 
				
			||||||
    title = models.Note.generateWebTitle(meta.title || title);
 | 
					  title = models.Note.generateWebTitle(meta.title || title)
 | 
				
			||||||
  res.set({
 | 
					  res.set({
 | 
				
			||||||
    'Cache-Control': 'private', // only cache by client
 | 
					    'Cache-Control': 'private', // only cache by client
 | 
				
			||||||
    'X-Robots-Tag': 'noindex, nofollow' // prevent crawling
 | 
					    'X-Robots-Tag': 'noindex, nofollow' // prevent crawling
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
  res.render(config.hackmdpath, {
 | 
					  res.render(config.hackmdpath, {
 | 
				
			||||||
    url: config.serverurl,
 | 
					    url: config.serverurl,
 | 
				
			||||||
    title: title,
 | 
					    title: title,
 | 
				
			||||||
@ -99,47 +97,44 @@ function responseHackMD(res, note) {
 | 
				
			|||||||
    ldap: config.ldap,
 | 
					    ldap: config.ldap,
 | 
				
			||||||
    email: config.email,
 | 
					    email: config.email,
 | 
				
			||||||
    allowemailregister: config.allowemailregister
 | 
					    allowemailregister: config.allowemailregister
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function newNote(req, res, next) {
 | 
					function newNote (req, res, next) {
 | 
				
			||||||
    var owner = null;
 | 
					  var owner = null
 | 
				
			||||||
  if (req.isAuthenticated()) {
 | 
					  if (req.isAuthenticated()) {
 | 
				
			||||||
        owner = req.user.id;
 | 
					    owner = req.user.id
 | 
				
			||||||
  } else if (!config.allowanonymous) {
 | 
					  } else if (!config.allowanonymous) {
 | 
				
			||||||
        return response.errorForbidden(res);
 | 
					    return response.errorForbidden(res)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  models.Note.create({
 | 
					  models.Note.create({
 | 
				
			||||||
    ownerId: owner,
 | 
					    ownerId: owner,
 | 
				
			||||||
    alias: req.alias ? req.alias : null
 | 
					    alias: req.alias ? req.alias : null
 | 
				
			||||||
  }).then(function (note) {
 | 
					  }).then(function (note) {
 | 
				
			||||||
        return res.redirect(config.serverurl + "/" + LZString.compressToBase64(note.id));
 | 
					    return res.redirect(config.serverurl + '/' + LZString.compressToBase64(note.id))
 | 
				
			||||||
  }).catch(function (err) {
 | 
					  }).catch(function (err) {
 | 
				
			||||||
        logger.error(err);
 | 
					    logger.error(err)
 | 
				
			||||||
        return response.errorInternalError(res);
 | 
					    return response.errorInternalError(res)
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function checkViewPermission(req, note) {
 | 
					function checkViewPermission (req, note) {
 | 
				
			||||||
    if (note.permission == 'private') {
 | 
					  if (note.permission === 'private') {
 | 
				
			||||||
        if (!req.isAuthenticated() || note.ownerId != req.user.id)
 | 
					    if (!req.isAuthenticated() || note.ownerId !== req.user.id) { return false } else { return true }
 | 
				
			||||||
            return false;
 | 
					  } else if (note.permission === 'limited' || note.permission === 'protected') {
 | 
				
			||||||
        else
 | 
					    if (!req.isAuthenticated()) { return false } else { return true }
 | 
				
			||||||
            return true;
 | 
					 | 
				
			||||||
    } else if (note.permission == 'limited' || note.permission == 'protected') {
 | 
					 | 
				
			||||||
        if(!req.isAuthenticated())
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
        else
 | 
					 | 
				
			||||||
            return true;
 | 
					 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
        return true;
 | 
					    return true
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function findNote(req, res, callback, include) {
 | 
					function findNote (req, res, callback, include) {
 | 
				
			||||||
    var noteId = req.params.noteId;
 | 
					  var noteId = req.params.noteId
 | 
				
			||||||
    var id = req.params.noteId || req.params.shortid;
 | 
					  var id = req.params.noteId || req.params.shortid
 | 
				
			||||||
  models.Note.parseNoteId(id, function (err, _id) {
 | 
					  models.Note.parseNoteId(id, function (err, _id) {
 | 
				
			||||||
 | 
					    if (err) {
 | 
				
			||||||
 | 
					      logger.log(err)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    models.Note.findOne({
 | 
					    models.Note.findOne({
 | 
				
			||||||
      where: {
 | 
					      where: {
 | 
				
			||||||
        id: _id
 | 
					        id: _id
 | 
				
			||||||
@ -148,61 +143,61 @@ function findNote(req, res, callback, include) {
 | 
				
			|||||||
    }).then(function (note) {
 | 
					    }).then(function (note) {
 | 
				
			||||||
      if (!note) {
 | 
					      if (!note) {
 | 
				
			||||||
        if (config.allowfreeurl && noteId) {
 | 
					        if (config.allowfreeurl && noteId) {
 | 
				
			||||||
                    req.alias = noteId;
 | 
					          req.alias = noteId
 | 
				
			||||||
                    return newNote(req, res);
 | 
					          return newNote(req, res)
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
                    return response.errorNotFound(res);
 | 
					          return response.errorNotFound(res)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      if (!checkViewPermission(req, note)) {
 | 
					      if (!checkViewPermission(req, note)) {
 | 
				
			||||||
                return response.errorForbidden(res);
 | 
					        return response.errorForbidden(res)
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
                return callback(note);
 | 
					        return callback(note)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }).catch(function (err) {
 | 
					    }).catch(function (err) {
 | 
				
			||||||
            logger.error(err);
 | 
					      logger.error(err)
 | 
				
			||||||
            return response.errorInternalError(res);
 | 
					      return response.errorInternalError(res)
 | 
				
			||||||
        });
 | 
					    })
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function showNote(req, res, next) {
 | 
					function showNote (req, res, next) {
 | 
				
			||||||
  findNote(req, res, function (note) {
 | 
					  findNote(req, res, function (note) {
 | 
				
			||||||
    // force to use note id
 | 
					    // force to use note id
 | 
				
			||||||
        var noteId = req.params.noteId;
 | 
					    var noteId = req.params.noteId
 | 
				
			||||||
        var id = LZString.compressToBase64(note.id);
 | 
					    var id = LZString.compressToBase64(note.id)
 | 
				
			||||||
        if ((note.alias && noteId != note.alias) || (!note.alias && noteId != id))
 | 
					    if ((note.alias && noteId !== note.alias) || (!note.alias && noteId !== id)) { return res.redirect(config.serverurl + '/' + (note.alias || id)) }
 | 
				
			||||||
            return res.redirect(config.serverurl + "/" + (note.alias || id));
 | 
					    return responseHackMD(res, note)
 | 
				
			||||||
        return responseHackMD(res, note);
 | 
					  })
 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function showPublishNote(req, res, next) {
 | 
					function showPublishNote (req, res, next) {
 | 
				
			||||||
  var include = [{
 | 
					  var include = [{
 | 
				
			||||||
    model: models.User,
 | 
					    model: models.User,
 | 
				
			||||||
        as: "owner"
 | 
					    as: 'owner'
 | 
				
			||||||
  }, {
 | 
					  }, {
 | 
				
			||||||
    model: models.User,
 | 
					    model: models.User,
 | 
				
			||||||
        as: "lastchangeuser"
 | 
					    as: 'lastchangeuser'
 | 
				
			||||||
    }];
 | 
					  }]
 | 
				
			||||||
  findNote(req, res, function (note) {
 | 
					  findNote(req, res, function (note) {
 | 
				
			||||||
    // force to use short id
 | 
					    // force to use short id
 | 
				
			||||||
        var shortid = req.params.shortid;
 | 
					    var shortid = req.params.shortid
 | 
				
			||||||
        if ((note.alias && shortid != note.alias) || (!note.alias && shortid != note.shortid))
 | 
					    if ((note.alias && shortid !== note.alias) || (!note.alias && shortid !== note.shortid)) {
 | 
				
			||||||
            return res.redirect(config.serverurl + "/s/" + (note.alias || note.shortid));
 | 
					      return res.redirect(config.serverurl + '/s/' + (note.alias || note.shortid))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    note.increment('viewcount').then(function (note) {
 | 
					    note.increment('viewcount').then(function (note) {
 | 
				
			||||||
      if (!note) {
 | 
					      if (!note) {
 | 
				
			||||||
                return response.errorNotFound(res);
 | 
					        return response.errorNotFound(res)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
            var body = note.content;
 | 
					      var body = note.content
 | 
				
			||||||
            var extracted = models.Note.extractMeta(body);
 | 
					      var extracted = models.Note.extractMeta(body)
 | 
				
			||||||
            markdown = extracted.markdown;
 | 
					      var markdown = extracted.markdown
 | 
				
			||||||
            meta = models.Note.parseMeta(extracted.meta);
 | 
					      var meta = models.Note.parseMeta(extracted.meta)
 | 
				
			||||||
            var createtime = note.createdAt;
 | 
					      var createtime = note.createdAt
 | 
				
			||||||
            var updatetime = note.lastchangeAt;
 | 
					      var updatetime = note.lastchangeAt
 | 
				
			||||||
            var title = models.Note.decodeTitle(note.title);
 | 
					      var title = models.Note.decodeTitle(note.title)
 | 
				
			||||||
            title = models.Note.generateWebTitle(meta.title || title);
 | 
					      title = models.Note.generateWebTitle(meta.title || title)
 | 
				
			||||||
            var origin = config.serverurl;
 | 
					      var origin = config.serverurl
 | 
				
			||||||
      var data = {
 | 
					      var data = {
 | 
				
			||||||
        title: title,
 | 
					        title: title,
 | 
				
			||||||
        description: meta.description || (markdown ? models.Note.generateDescription(markdown) : null),
 | 
					        description: meta.description || (markdown ? models.Note.generateDescription(markdown) : null),
 | 
				
			||||||
@ -216,238 +211,237 @@ function showPublishNote(req, res, next) {
 | 
				
			|||||||
        ownerprofile: note.owner ? models.User.getProfile(note.owner) : null,
 | 
					        ownerprofile: note.owner ? models.User.getProfile(note.owner) : null,
 | 
				
			||||||
        lastchangeuser: note.lastchangeuser ? note.lastchangeuser.id : null,
 | 
					        lastchangeuser: note.lastchangeuser ? note.lastchangeuser.id : null,
 | 
				
			||||||
        lastchangeuserprofile: note.lastchangeuser ? models.User.getProfile(note.lastchangeuser) : null,
 | 
					        lastchangeuserprofile: note.lastchangeuser ? models.User.getProfile(note.lastchangeuser) : null,
 | 
				
			||||||
                robots: meta.robots || false, //default allow robots
 | 
					        robots: meta.robots || false, // default allow robots
 | 
				
			||||||
        GA: meta.GA,
 | 
					        GA: meta.GA,
 | 
				
			||||||
        disqus: meta.disqus
 | 
					        disqus: meta.disqus
 | 
				
			||||||
            };
 | 
					      }
 | 
				
			||||||
            return renderPublish(data, res);
 | 
					      return renderPublish(data, res)
 | 
				
			||||||
    }).catch(function (err) {
 | 
					    }).catch(function (err) {
 | 
				
			||||||
            logger.error(err);
 | 
					      logger.error(err)
 | 
				
			||||||
            return response.errorInternalError(res);
 | 
					      return response.errorInternalError(res)
 | 
				
			||||||
        });
 | 
					    })
 | 
				
			||||||
    }, include);
 | 
					  }, include)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function renderPublish(data, res) {
 | 
					function renderPublish (data, res) {
 | 
				
			||||||
  res.set({
 | 
					  res.set({
 | 
				
			||||||
    'Cache-Control': 'private' // only cache by client
 | 
					    'Cache-Control': 'private' // only cache by client
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
    res.render(config.prettypath, data);
 | 
					  res.render(config.prettypath, data)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function actionPublish(req, res, note) {
 | 
					function actionPublish (req, res, note) {
 | 
				
			||||||
    res.redirect(config.serverurl + "/s/" + (note.alias || note.shortid));
 | 
					  res.redirect(config.serverurl + '/s/' + (note.alias || note.shortid))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function actionSlide(req, res, note) {
 | 
					function actionSlide (req, res, note) {
 | 
				
			||||||
    res.redirect(config.serverurl + "/p/" + (note.alias || note.shortid));
 | 
					  res.redirect(config.serverurl + '/p/' + (note.alias || note.shortid))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function actionDownload(req, res, note) {
 | 
					function actionDownload (req, res, note) {
 | 
				
			||||||
    var body = note.content;
 | 
					  var body = note.content
 | 
				
			||||||
    var title = models.Note.decodeTitle(note.title);
 | 
					  var title = models.Note.decodeTitle(note.title)
 | 
				
			||||||
    var filename = title;
 | 
					  var filename = title
 | 
				
			||||||
    filename = encodeURIComponent(filename);
 | 
					  filename = encodeURIComponent(filename)
 | 
				
			||||||
  res.set({
 | 
					  res.set({
 | 
				
			||||||
        'Access-Control-Allow-Origin': '*', //allow CORS as API
 | 
					    'Access-Control-Allow-Origin': '*', // allow CORS as API
 | 
				
			||||||
    'Access-Control-Allow-Headers': 'Range',
 | 
					    'Access-Control-Allow-Headers': 'Range',
 | 
				
			||||||
    'Access-Control-Expose-Headers': 'Cache-Control, Content-Encoding, Content-Range',
 | 
					    'Access-Control-Expose-Headers': 'Cache-Control, Content-Encoding, Content-Range',
 | 
				
			||||||
    'Content-Type': 'text/markdown; charset=UTF-8',
 | 
					    'Content-Type': 'text/markdown; charset=UTF-8',
 | 
				
			||||||
    'Cache-Control': 'private',
 | 
					    'Cache-Control': 'private',
 | 
				
			||||||
    'Content-disposition': 'attachment; filename=' + filename + '.md',
 | 
					    'Content-disposition': 'attachment; filename=' + filename + '.md',
 | 
				
			||||||
    'X-Robots-Tag': 'noindex, nofollow' // prevent crawling
 | 
					    'X-Robots-Tag': 'noindex, nofollow' // prevent crawling
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
    res.send(body);
 | 
					  res.send(body)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function actionInfo(req, res, note) {
 | 
					function actionInfo (req, res, note) {
 | 
				
			||||||
    var body = note.content;
 | 
					  var body = note.content
 | 
				
			||||||
    var extracted = models.Note.extractMeta(body);
 | 
					  var extracted = models.Note.extractMeta(body)
 | 
				
			||||||
    var markdown = extracted.markdown;
 | 
					  var markdown = extracted.markdown
 | 
				
			||||||
    var meta = models.Note.parseMeta(extracted.meta);
 | 
					  var meta = models.Note.parseMeta(extracted.meta)
 | 
				
			||||||
    var createtime = note.createdAt;
 | 
					  var createtime = note.createdAt
 | 
				
			||||||
    var updatetime = note.lastchangeAt;
 | 
					  var updatetime = note.lastchangeAt
 | 
				
			||||||
    var title = models.Note.decodeTitle(note.title);
 | 
					  var title = models.Note.decodeTitle(note.title)
 | 
				
			||||||
  var data = {
 | 
					  var data = {
 | 
				
			||||||
    title: meta.title || title,
 | 
					    title: meta.title || title,
 | 
				
			||||||
    description: meta.description || (markdown ? models.Note.generateDescription(markdown) : null),
 | 
					    description: meta.description || (markdown ? models.Note.generateDescription(markdown) : null),
 | 
				
			||||||
    viewcount: note.viewcount,
 | 
					    viewcount: note.viewcount,
 | 
				
			||||||
    createtime: createtime,
 | 
					    createtime: createtime,
 | 
				
			||||||
    updatetime: updatetime
 | 
					    updatetime: updatetime
 | 
				
			||||||
    };
 | 
					  }
 | 
				
			||||||
  res.set({
 | 
					  res.set({
 | 
				
			||||||
        'Access-Control-Allow-Origin': '*', //allow CORS as API
 | 
					    'Access-Control-Allow-Origin': '*', // allow CORS as API
 | 
				
			||||||
    'Access-Control-Allow-Headers': 'Range',
 | 
					    'Access-Control-Allow-Headers': 'Range',
 | 
				
			||||||
    'Access-Control-Expose-Headers': 'Cache-Control, Content-Encoding, Content-Range',
 | 
					    'Access-Control-Expose-Headers': 'Cache-Control, Content-Encoding, Content-Range',
 | 
				
			||||||
    'Cache-Control': 'private', // only cache by client
 | 
					    'Cache-Control': 'private', // only cache by client
 | 
				
			||||||
    'X-Robots-Tag': 'noindex, nofollow' // prevent crawling
 | 
					    'X-Robots-Tag': 'noindex, nofollow' // prevent crawling
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
    res.send(data);
 | 
					  res.send(data)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function actionPDF(req, res, note) {
 | 
					function actionPDF (req, res, note) {
 | 
				
			||||||
    var body = note.content;
 | 
					  var body = note.content
 | 
				
			||||||
    var extracted = models.Note.extractMeta(body);
 | 
					  var extracted = models.Note.extractMeta(body)
 | 
				
			||||||
    var title = models.Note.decodeTitle(note.title);
 | 
					  var title = models.Note.decodeTitle(note.title)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (!fs.existsSync(config.tmppath)) {
 | 
					  if (!fs.existsSync(config.tmppath)) {
 | 
				
			||||||
        fs.mkdirSync(config.tmppath);
 | 
					    fs.mkdirSync(config.tmppath)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
    var path = config.tmppath + '/' + Date.now() + '.pdf';
 | 
					  var path = config.tmppath + '/' + Date.now() + '.pdf'
 | 
				
			||||||
  markdownpdf().from.string(extracted.markdown).to(path, function () {
 | 
					  markdownpdf().from.string(extracted.markdown).to(path, function () {
 | 
				
			||||||
        var stream = fs.createReadStream(path);
 | 
					    var stream = fs.createReadStream(path)
 | 
				
			||||||
        var filename = title;
 | 
					    var filename = title
 | 
				
			||||||
    // Be careful of special characters
 | 
					    // Be careful of special characters
 | 
				
			||||||
        filename = encodeURIComponent(filename);
 | 
					    filename = encodeURIComponent(filename)
 | 
				
			||||||
    // Ideally this should strip them
 | 
					    // Ideally this should strip them
 | 
				
			||||||
        res.setHeader('Content-disposition', 'attachment; filename="' + filename + '.pdf"');
 | 
					    res.setHeader('Content-disposition', 'attachment; filename="' + filename + '.pdf"')
 | 
				
			||||||
        res.setHeader('Cache-Control', 'private');
 | 
					    res.setHeader('Cache-Control', 'private')
 | 
				
			||||||
        res.setHeader('Content-Type', 'application/pdf; charset=UTF-8');
 | 
					    res.setHeader('Content-Type', 'application/pdf; charset=UTF-8')
 | 
				
			||||||
        res.setHeader('X-Robots-Tag', 'noindex, nofollow'); // prevent crawling
 | 
					    res.setHeader('X-Robots-Tag', 'noindex, nofollow') // prevent crawling
 | 
				
			||||||
        stream.pipe(res);
 | 
					    stream.pipe(res)
 | 
				
			||||||
        fs.unlink(path);
 | 
					    fs.unlink(path)
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function actionGist(req, res, note) {
 | 
					function actionGist (req, res, note) {
 | 
				
			||||||
  var data = {
 | 
					  var data = {
 | 
				
			||||||
    client_id: config.github.clientID,
 | 
					    client_id: config.github.clientID,
 | 
				
			||||||
    redirect_uri: config.serverurl + '/auth/github/callback/' + LZString.compressToBase64(note.id) + '/gist',
 | 
					    redirect_uri: config.serverurl + '/auth/github/callback/' + LZString.compressToBase64(note.id) + '/gist',
 | 
				
			||||||
        scope: "gist",
 | 
					    scope: 'gist',
 | 
				
			||||||
    state: shortId.generate()
 | 
					    state: shortId.generate()
 | 
				
			||||||
    };
 | 
					  }
 | 
				
			||||||
    var query = querystring.stringify(data);
 | 
					  var query = querystring.stringify(data)
 | 
				
			||||||
    res.redirect("https://github.com/login/oauth/authorize?" + query);
 | 
					  res.redirect('https://github.com/login/oauth/authorize?' + query)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function actionRevision(req, res, note) {
 | 
					function actionRevision (req, res, note) {
 | 
				
			||||||
    var actionId = req.params.actionId;
 | 
					  var actionId = req.params.actionId
 | 
				
			||||||
  if (actionId) {
 | 
					  if (actionId) {
 | 
				
			||||||
        var time = moment(parseInt(actionId));
 | 
					    var time = moment(parseInt(actionId))
 | 
				
			||||||
    if (time.isValid()) {
 | 
					    if (time.isValid()) {
 | 
				
			||||||
      models.Revision.getPatchedNoteRevisionByTime(note, time, function (err, content) {
 | 
					      models.Revision.getPatchedNoteRevisionByTime(note, time, function (err, content) {
 | 
				
			||||||
        if (err) {
 | 
					        if (err) {
 | 
				
			||||||
                    logger.error(err);
 | 
					          logger.error(err)
 | 
				
			||||||
                    return response.errorInternalError(res);
 | 
					          return response.errorInternalError(res)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (!content) {
 | 
					        if (!content) {
 | 
				
			||||||
                    return response.errorNotFound(res);
 | 
					          return response.errorNotFound(res)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        res.set({
 | 
					        res.set({
 | 
				
			||||||
                    'Access-Control-Allow-Origin': '*', //allow CORS as API
 | 
					          'Access-Control-Allow-Origin': '*', // allow CORS as API
 | 
				
			||||||
          'Access-Control-Allow-Headers': 'Range',
 | 
					          'Access-Control-Allow-Headers': 'Range',
 | 
				
			||||||
          'Access-Control-Expose-Headers': 'Cache-Control, Content-Encoding, Content-Range',
 | 
					          'Access-Control-Expose-Headers': 'Cache-Control, Content-Encoding, Content-Range',
 | 
				
			||||||
          'Cache-Control': 'private', // only cache by client
 | 
					          'Cache-Control': 'private', // only cache by client
 | 
				
			||||||
          'X-Robots-Tag': 'noindex, nofollow' // prevent crawling
 | 
					          'X-Robots-Tag': 'noindex, nofollow' // prevent crawling
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
                res.send(content);
 | 
					        res.send(content)
 | 
				
			||||||
            });
 | 
					      })
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
            return response.errorNotFound(res);
 | 
					      return response.errorNotFound(res)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    models.Revision.getNoteRevisions(note, function (err, data) {
 | 
					    models.Revision.getNoteRevisions(note, function (err, data) {
 | 
				
			||||||
      if (err) {
 | 
					      if (err) {
 | 
				
			||||||
                logger.error(err);
 | 
					        logger.error(err)
 | 
				
			||||||
                return response.errorInternalError(res);
 | 
					        return response.errorInternalError(res)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      var out = {
 | 
					      var out = {
 | 
				
			||||||
        revision: data
 | 
					        revision: data
 | 
				
			||||||
            };
 | 
					      }
 | 
				
			||||||
      res.set({
 | 
					      res.set({
 | 
				
			||||||
                'Access-Control-Allow-Origin': '*', //allow CORS as API
 | 
					        'Access-Control-Allow-Origin': '*', // allow CORS as API
 | 
				
			||||||
        'Access-Control-Allow-Headers': 'Range',
 | 
					        'Access-Control-Allow-Headers': 'Range',
 | 
				
			||||||
        'Access-Control-Expose-Headers': 'Cache-Control, Content-Encoding, Content-Range',
 | 
					        'Access-Control-Expose-Headers': 'Cache-Control, Content-Encoding, Content-Range',
 | 
				
			||||||
        'Cache-Control': 'private', // only cache by client
 | 
					        'Cache-Control': 'private', // only cache by client
 | 
				
			||||||
        'X-Robots-Tag': 'noindex, nofollow' // prevent crawling
 | 
					        'X-Robots-Tag': 'noindex, nofollow' // prevent crawling
 | 
				
			||||||
            });
 | 
					      })
 | 
				
			||||||
            res.send(out);
 | 
					      res.send(out)
 | 
				
			||||||
        });
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function noteActions(req, res, next) {
 | 
					function noteActions (req, res, next) {
 | 
				
			||||||
    var noteId = req.params.noteId;
 | 
					  var noteId = req.params.noteId
 | 
				
			||||||
  findNote(req, res, function (note) {
 | 
					  findNote(req, res, function (note) {
 | 
				
			||||||
        var action = req.params.action;
 | 
					    var action = req.params.action
 | 
				
			||||||
    switch (action) {
 | 
					    switch (action) {
 | 
				
			||||||
        case "publish":
 | 
					      case 'publish':
 | 
				
			||||||
        case "pretty": //pretty deprecated
 | 
					      case 'pretty': // pretty deprecated
 | 
				
			||||||
            actionPublish(req, res, note);
 | 
					        actionPublish(req, res, note)
 | 
				
			||||||
            break;
 | 
					        break
 | 
				
			||||||
        case "slide":
 | 
					      case 'slide':
 | 
				
			||||||
            actionSlide(req, res, note);
 | 
					        actionSlide(req, res, note)
 | 
				
			||||||
            break;
 | 
					        break
 | 
				
			||||||
        case "download":
 | 
					      case 'download':
 | 
				
			||||||
            actionDownload(req, res, note);
 | 
					        actionDownload(req, res, note)
 | 
				
			||||||
            break;
 | 
					        break
 | 
				
			||||||
        case "info":
 | 
					      case 'info':
 | 
				
			||||||
            actionInfo(req, res, note);
 | 
					        actionInfo(req, res, note)
 | 
				
			||||||
            break;
 | 
					        break
 | 
				
			||||||
        case "pdf":
 | 
					      case 'pdf':
 | 
				
			||||||
            actionPDF(req, res, note);
 | 
					        actionPDF(req, res, note)
 | 
				
			||||||
            break;
 | 
					        break
 | 
				
			||||||
        case "gist":
 | 
					      case 'gist':
 | 
				
			||||||
            actionGist(req, res, note);
 | 
					        actionGist(req, res, note)
 | 
				
			||||||
            break;
 | 
					        break
 | 
				
			||||||
        case "revision":
 | 
					      case 'revision':
 | 
				
			||||||
            actionRevision(req, res, note);
 | 
					        actionRevision(req, res, note)
 | 
				
			||||||
            break;
 | 
					        break
 | 
				
			||||||
      default:
 | 
					      default:
 | 
				
			||||||
            return res.redirect(config.serverurl + '/' + noteId);
 | 
					        return res.redirect(config.serverurl + '/' + noteId)
 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function publishNoteActions(req, res, next) {
 | 
					function publishNoteActions (req, res, next) {
 | 
				
			||||||
  findNote(req, res, function (note) {
 | 
					  findNote(req, res, function (note) {
 | 
				
			||||||
        var action = req.params.action;
 | 
					    var action = req.params.action
 | 
				
			||||||
    switch (action) {
 | 
					    switch (action) {
 | 
				
			||||||
        case "edit":
 | 
					      case 'edit':
 | 
				
			||||||
            res.redirect(config.serverurl + '/' + (note.alias ? note.alias : LZString.compressToBase64(note.id)));
 | 
					        res.redirect(config.serverurl + '/' + (note.alias ? note.alias : LZString.compressToBase64(note.id)))
 | 
				
			||||||
            break;
 | 
					        break
 | 
				
			||||||
      default:
 | 
					      default:
 | 
				
			||||||
            res.redirect(config.serverurl + '/s/' + note.shortid);
 | 
					        res.redirect(config.serverurl + '/s/' + note.shortid)
 | 
				
			||||||
            break;
 | 
					        break
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function publishSlideActions(req, res, next) {
 | 
					function publishSlideActions (req, res, next) {
 | 
				
			||||||
  findNote(req, res, function (note) {
 | 
					  findNote(req, res, function (note) {
 | 
				
			||||||
        var action = req.params.action;
 | 
					    var action = req.params.action
 | 
				
			||||||
    switch (action) {
 | 
					    switch (action) {
 | 
				
			||||||
        case "edit":
 | 
					      case 'edit':
 | 
				
			||||||
            res.redirect(config.serverurl + '/' + (note.alias ? note.alias : LZString.compressToBase64(note.id)));
 | 
					        res.redirect(config.serverurl + '/' + (note.alias ? note.alias : LZString.compressToBase64(note.id)))
 | 
				
			||||||
            break;
 | 
					        break
 | 
				
			||||||
      default:
 | 
					      default:
 | 
				
			||||||
            res.redirect(config.serverurl + '/p/' + note.shortid);
 | 
					        res.redirect(config.serverurl + '/p/' + note.shortid)
 | 
				
			||||||
            break;
 | 
					        break
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function githubActions(req, res, next) {
 | 
					function githubActions (req, res, next) {
 | 
				
			||||||
    var noteId = req.params.noteId;
 | 
					  var noteId = req.params.noteId
 | 
				
			||||||
  findNote(req, res, function (note) {
 | 
					  findNote(req, res, function (note) {
 | 
				
			||||||
        var action = req.params.action;
 | 
					    var action = req.params.action
 | 
				
			||||||
    switch (action) {
 | 
					    switch (action) {
 | 
				
			||||||
        case "gist":
 | 
					      case 'gist':
 | 
				
			||||||
            githubActionGist(req, res, note);
 | 
					        githubActionGist(req, res, note)
 | 
				
			||||||
            break;
 | 
					        break
 | 
				
			||||||
      default:
 | 
					      default:
 | 
				
			||||||
            res.redirect(config.serverurl + '/' + noteId);
 | 
					        res.redirect(config.serverurl + '/' + noteId)
 | 
				
			||||||
            break;
 | 
					        break
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function githubActionGist(req, res, note) {
 | 
					function githubActionGist (req, res, note) {
 | 
				
			||||||
    var code = req.query.code;
 | 
					  var code = req.query.code
 | 
				
			||||||
    var state = req.query.state;
 | 
					  var state = req.query.state
 | 
				
			||||||
  if (!code || !state) {
 | 
					  if (!code || !state) {
 | 
				
			||||||
        return response.errorForbidden(res);
 | 
					    return response.errorForbidden(res)
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    var data = {
 | 
					    var data = {
 | 
				
			||||||
      client_id: config.github.clientID,
 | 
					      client_id: config.github.clientID,
 | 
				
			||||||
@ -455,124 +449,122 @@ function githubActionGist(req, res, note) {
 | 
				
			|||||||
      code: code,
 | 
					      code: code,
 | 
				
			||||||
      state: state
 | 
					      state: state
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
        var auth_url = 'https://github.com/login/oauth/access_token';
 | 
					    var authUrl = 'https://github.com/login/oauth/access_token'
 | 
				
			||||||
    request({
 | 
					    request({
 | 
				
			||||||
                url: auth_url,
 | 
					      url: authUrl,
 | 
				
			||||||
                method: "POST",
 | 
					      method: 'POST',
 | 
				
			||||||
      json: data
 | 
					      json: data
 | 
				
			||||||
    }, function (error, httpResponse, body) {
 | 
					    }, function (error, httpResponse, body) {
 | 
				
			||||||
            if (!error && httpResponse.statusCode == 200) {
 | 
					      if (!error && httpResponse.statusCode === 200) {
 | 
				
			||||||
                var access_token = body.access_token;
 | 
					        var accessToken = body.access_token
 | 
				
			||||||
                if (access_token) {
 | 
					        if (accessToken) {
 | 
				
			||||||
                    var content = note.content;
 | 
					          var content = note.content
 | 
				
			||||||
                    var title = models.Note.decodeTitle(note.title);
 | 
					          var title = models.Note.decodeTitle(note.title)
 | 
				
			||||||
                    var filename = title.replace('/', ' ') + '.md';
 | 
					          var filename = title.replace('/', ' ') + '.md'
 | 
				
			||||||
          var gist = {
 | 
					          var gist = {
 | 
				
			||||||
                        "files": {}
 | 
					            'files': {}
 | 
				
			||||||
                    };
 | 
					          }
 | 
				
			||||||
          gist.files[filename] = {
 | 
					          gist.files[filename] = {
 | 
				
			||||||
                        "content": content
 | 
					            'content': content
 | 
				
			||||||
                    };
 | 
					          }
 | 
				
			||||||
                    var gist_url = "https://api.github.com/gists";
 | 
					          var gistUrl = 'https://api.github.com/gists'
 | 
				
			||||||
          request({
 | 
					          request({
 | 
				
			||||||
                        url: gist_url,
 | 
					            url: gistUrl,
 | 
				
			||||||
            headers: {
 | 
					            headers: {
 | 
				
			||||||
              'User-Agent': 'HackMD',
 | 
					              'User-Agent': 'HackMD',
 | 
				
			||||||
                            'Authorization': 'token ' + access_token
 | 
					              'Authorization': 'token ' + accessToken
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
                        method: "POST",
 | 
					            method: 'POST',
 | 
				
			||||||
            json: gist
 | 
					            json: gist
 | 
				
			||||||
          }, function (error, httpResponse, body) {
 | 
					          }, function (error, httpResponse, body) {
 | 
				
			||||||
                        if (!error && httpResponse.statusCode == 201) {
 | 
					            if (!error && httpResponse.statusCode === 201) {
 | 
				
			||||||
                            res.setHeader('referer', '');
 | 
					              res.setHeader('referer', '')
 | 
				
			||||||
                            res.redirect(body.html_url);
 | 
					              res.redirect(body.html_url)
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                            return response.errorForbidden(res);
 | 
					              return response.errorForbidden(res)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
                    });
 | 
					          })
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
                    return response.errorForbidden(res);
 | 
					          return response.errorForbidden(res)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
                return response.errorForbidden(res);
 | 
					        return response.errorForbidden(res)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function gitlabActions(req, res, next) {
 | 
					function gitlabActions (req, res, next) {
 | 
				
			||||||
    var noteId = req.params.noteId;
 | 
					  var noteId = req.params.noteId
 | 
				
			||||||
  findNote(req, res, function (note) {
 | 
					  findNote(req, res, function (note) {
 | 
				
			||||||
        var action = req.params.action;
 | 
					    var action = req.params.action
 | 
				
			||||||
    switch (action) {
 | 
					    switch (action) {
 | 
				
			||||||
        case "projects":
 | 
					      case 'projects':
 | 
				
			||||||
            gitlabActionProjects(req, res, note);
 | 
					        gitlabActionProjects(req, res, note)
 | 
				
			||||||
            break;
 | 
					        break
 | 
				
			||||||
      default:
 | 
					      default:
 | 
				
			||||||
            res.redirect(config.serverurl + '/' + noteId);
 | 
					        res.redirect(config.serverurl + '/' + noteId)
 | 
				
			||||||
            break;
 | 
					        break
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function gitlabActionProjects(req, res, note) {
 | 
					function gitlabActionProjects (req, res, note) {
 | 
				
			||||||
  if (req.isAuthenticated()) {
 | 
					  if (req.isAuthenticated()) {
 | 
				
			||||||
    models.User.findOne({
 | 
					    models.User.findOne({
 | 
				
			||||||
      where: {
 | 
					      where: {
 | 
				
			||||||
        id: req.user.id
 | 
					        id: req.user.id
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }).then(function (user) {
 | 
					    }).then(function (user) {
 | 
				
			||||||
            if (!user)
 | 
					      if (!user) { return response.errorNotFound(res) }
 | 
				
			||||||
                return response.errorNotFound(res);
 | 
					      var ret = { baseURL: config.gitlab.baseURL }
 | 
				
			||||||
            var ret = { baseURL: config.gitlab.baseURL };
 | 
					      ret.accesstoken = user.accessToken
 | 
				
			||||||
            ret.accesstoken = user.accessToken;
 | 
					      ret.profileid = user.profileid
 | 
				
			||||||
            ret.profileid = user.profileid;
 | 
					 | 
				
			||||||
      request(
 | 
					      request(
 | 
				
			||||||
                config.gitlab.baseURL + '/api/v3/projects?access_token=' + user.accessToken,
 | 
					                config.gitlab.baseURL + '/api/v3/projects?access_token=' + user.accessToken,
 | 
				
			||||||
                function(error, httpResponse, body) {
 | 
					                function (error, httpResponse, body) {
 | 
				
			||||||
                    if (!error && httpResponse.statusCode == 200) {
 | 
					                  if (!error && httpResponse.statusCode === 200) {
 | 
				
			||||||
                        ret.projects = JSON.parse(body);
 | 
					                    ret.projects = JSON.parse(body)
 | 
				
			||||||
                        return res.send(ret);
 | 
					                    return res.send(ret)
 | 
				
			||||||
                  } else {
 | 
					                  } else {
 | 
				
			||||||
                        return res.send(ret);
 | 
					                    return res.send(ret)
 | 
				
			||||||
                  }
 | 
					                  }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            );
 | 
					            )
 | 
				
			||||||
    }).catch(function (err) {
 | 
					    }).catch(function (err) {
 | 
				
			||||||
            logger.error('gitlab action projects failed: ' + err);
 | 
					      logger.error('gitlab action projects failed: ' + err)
 | 
				
			||||||
            return response.errorInternalError(res);
 | 
					      return response.errorInternalError(res)
 | 
				
			||||||
        });
 | 
					    })
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
        return response.errorForbidden(res);
 | 
					    return response.errorForbidden(res)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function showPublishSlide(req, res, next) {
 | 
					function showPublishSlide (req, res, next) {
 | 
				
			||||||
  var include = [{
 | 
					  var include = [{
 | 
				
			||||||
    model: models.User,
 | 
					    model: models.User,
 | 
				
			||||||
        as: "owner"
 | 
					    as: 'owner'
 | 
				
			||||||
  }, {
 | 
					  }, {
 | 
				
			||||||
    model: models.User,
 | 
					    model: models.User,
 | 
				
			||||||
        as: "lastchangeuser"
 | 
					    as: 'lastchangeuser'
 | 
				
			||||||
    }];
 | 
					  }]
 | 
				
			||||||
  findNote(req, res, function (note) {
 | 
					  findNote(req, res, function (note) {
 | 
				
			||||||
    // force to use short id
 | 
					    // force to use short id
 | 
				
			||||||
        var shortid = req.params.shortid;
 | 
					    var shortid = req.params.shortid
 | 
				
			||||||
        if ((note.alias && shortid != note.alias) || (!note.alias && shortid != note.shortid))
 | 
					    if ((note.alias && shortid !== note.alias) || (!note.alias && shortid !== note.shortid)) { return res.redirect(config.serverurl + '/p/' + (note.alias || note.shortid)) }
 | 
				
			||||||
            return res.redirect(config.serverurl + "/p/" + (note.alias || note.shortid));
 | 
					 | 
				
			||||||
    note.increment('viewcount').then(function (note) {
 | 
					    note.increment('viewcount').then(function (note) {
 | 
				
			||||||
      if (!note) {
 | 
					      if (!note) {
 | 
				
			||||||
                return response.errorNotFound(res);
 | 
					        return response.errorNotFound(res)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
            var body = note.content;
 | 
					      var body = note.content
 | 
				
			||||||
            var extracted = models.Note.extractMeta(body);
 | 
					      var extracted = models.Note.extractMeta(body)
 | 
				
			||||||
            markdown = extracted.markdown;
 | 
					      var markdown = extracted.markdown
 | 
				
			||||||
            meta = models.Note.parseMeta(extracted.meta);
 | 
					      var meta = models.Note.parseMeta(extracted.meta)
 | 
				
			||||||
            var createtime = note.createdAt;
 | 
					      var createtime = note.createdAt
 | 
				
			||||||
            var updatetime = note.lastchangeAt;
 | 
					      var updatetime = note.lastchangeAt
 | 
				
			||||||
            var title = models.Note.decodeTitle(note.title);
 | 
					      var title = models.Note.decodeTitle(note.title)
 | 
				
			||||||
            title = models.Note.generateWebTitle(meta.title || title);
 | 
					      title = models.Note.generateWebTitle(meta.title || title)
 | 
				
			||||||
            var origin = config.serverurl;
 | 
					      var origin = config.serverurl
 | 
				
			||||||
      var data = {
 | 
					      var data = {
 | 
				
			||||||
        title: title,
 | 
					        title: title,
 | 
				
			||||||
        description: meta.description || (markdown ? models.Note.generateDescription(markdown) : null),
 | 
					        description: meta.description || (markdown ? models.Note.generateDescription(markdown) : null),
 | 
				
			||||||
@ -587,23 +579,23 @@ function showPublishSlide(req, res, next) {
 | 
				
			|||||||
        ownerprofile: note.owner ? models.User.getProfile(note.owner) : null,
 | 
					        ownerprofile: note.owner ? models.User.getProfile(note.owner) : null,
 | 
				
			||||||
        lastchangeuser: note.lastchangeuser ? note.lastchangeuser.id : null,
 | 
					        lastchangeuser: note.lastchangeuser ? note.lastchangeuser.id : null,
 | 
				
			||||||
        lastchangeuserprofile: note.lastchangeuser ? models.User.getProfile(note.lastchangeuser) : null,
 | 
					        lastchangeuserprofile: note.lastchangeuser ? models.User.getProfile(note.lastchangeuser) : null,
 | 
				
			||||||
                robots: meta.robots || false, //default allow robots
 | 
					        robots: meta.robots || false, // default allow robots
 | 
				
			||||||
        GA: meta.GA,
 | 
					        GA: meta.GA,
 | 
				
			||||||
        disqus: meta.disqus
 | 
					        disqus: meta.disqus
 | 
				
			||||||
            };
 | 
					      }
 | 
				
			||||||
            return renderPublishSlide(data, res);
 | 
					      return renderPublishSlide(data, res)
 | 
				
			||||||
    }).catch(function (err) {
 | 
					    }).catch(function (err) {
 | 
				
			||||||
            logger.error(err);
 | 
					      logger.error(err)
 | 
				
			||||||
            return response.errorInternalError(res);
 | 
					      return response.errorInternalError(res)
 | 
				
			||||||
        });
 | 
					    })
 | 
				
			||||||
    }, include);
 | 
					  }, include)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function renderPublishSlide(data, res) {
 | 
					function renderPublishSlide (data, res) {
 | 
				
			||||||
  res.set({
 | 
					  res.set({
 | 
				
			||||||
    'Cache-Control': 'private' // only cache by client
 | 
					    'Cache-Control': 'private' // only cache by client
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
    res.render(config.slidepath, data);
 | 
					  res.render(config.slidepath, data)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = response;
 | 
					module.exports = response
 | 
				
			||||||
 | 
				
			|||||||
@ -1,140 +1,137 @@
 | 
				
			|||||||
// external modules
 | 
					// external modules
 | 
				
			||||||
var DiffMatchPatch = require('diff-match-patch');
 | 
					var DiffMatchPatch = require('diff-match-patch')
 | 
				
			||||||
var dmp = new DiffMatchPatch();
 | 
					var dmp = new DiffMatchPatch()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// core
 | 
					// core
 | 
				
			||||||
var config = require("../config.js");
 | 
					var config = require('../config.js')
 | 
				
			||||||
var logger = require("../logger.js");
 | 
					var logger = require('../logger.js')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
process.on('message', function(data) {
 | 
					process.on('message', function (data) {
 | 
				
			||||||
  if (!data || !data.msg || !data.cacheKey) {
 | 
					  if (!data || !data.msg || !data.cacheKey) {
 | 
				
			||||||
        return logger.error('dmp worker error: not enough data');
 | 
					    return logger.error('dmp worker error: not enough data')
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  switch (data.msg) {
 | 
					  switch (data.msg) {
 | 
				
			||||||
    case 'create patch':
 | 
					    case 'create patch':
 | 
				
			||||||
      if (!data.hasOwnProperty('lastDoc') || !data.hasOwnProperty('currDoc')) {
 | 
					      if (!data.hasOwnProperty('lastDoc') || !data.hasOwnProperty('currDoc')) {
 | 
				
			||||||
                return logger.error('dmp worker error: not enough data on create patch');
 | 
					        return logger.error('dmp worker error: not enough data on create patch')
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      try {
 | 
					      try {
 | 
				
			||||||
                var patch = createPatch(data.lastDoc, data.currDoc);
 | 
					        var patch = createPatch(data.lastDoc, data.currDoc)
 | 
				
			||||||
        process.send({
 | 
					        process.send({
 | 
				
			||||||
          msg: 'check',
 | 
					          msg: 'check',
 | 
				
			||||||
          result: patch,
 | 
					          result: patch,
 | 
				
			||||||
          cacheKey: data.cacheKey
 | 
					          cacheKey: data.cacheKey
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
      } catch (err) {
 | 
					      } catch (err) {
 | 
				
			||||||
                logger.error('dmp worker error', err);
 | 
					        logger.error('dmp worker error', err)
 | 
				
			||||||
        process.send({
 | 
					        process.send({
 | 
				
			||||||
          msg: 'error',
 | 
					          msg: 'error',
 | 
				
			||||||
          error: err,
 | 
					          error: err,
 | 
				
			||||||
          cacheKey: data.cacheKey
 | 
					          cacheKey: data.cacheKey
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
            break;
 | 
					      break
 | 
				
			||||||
    case 'get revision':
 | 
					    case 'get revision':
 | 
				
			||||||
      if (!data.hasOwnProperty('revisions') || !data.hasOwnProperty('count')) {
 | 
					      if (!data.hasOwnProperty('revisions') || !data.hasOwnProperty('count')) {
 | 
				
			||||||
                return logger.error('dmp worker error: not enough data on get revision');
 | 
					        return logger.error('dmp worker error: not enough data on get revision')
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      try {
 | 
					      try {
 | 
				
			||||||
                var result = getRevision(data.revisions, data.count);
 | 
					        var result = getRevision(data.revisions, data.count)
 | 
				
			||||||
        process.send({
 | 
					        process.send({
 | 
				
			||||||
          msg: 'check',
 | 
					          msg: 'check',
 | 
				
			||||||
          result: result,
 | 
					          result: result,
 | 
				
			||||||
          cacheKey: data.cacheKey
 | 
					          cacheKey: data.cacheKey
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
      } catch (err) {
 | 
					      } catch (err) {
 | 
				
			||||||
                logger.error('dmp worker error', err);
 | 
					        logger.error('dmp worker error', err)
 | 
				
			||||||
        process.send({
 | 
					        process.send({
 | 
				
			||||||
          msg: 'error',
 | 
					          msg: 'error',
 | 
				
			||||||
          error: err,
 | 
					          error: err,
 | 
				
			||||||
          cacheKey: data.cacheKey
 | 
					          cacheKey: data.cacheKey
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
            break;
 | 
					      break
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function createPatch(lastDoc, currDoc) {
 | 
					function createPatch (lastDoc, currDoc) {
 | 
				
			||||||
    var ms_start = (new Date()).getTime();
 | 
					  var msStart = (new Date()).getTime()
 | 
				
			||||||
    var diff = dmp.diff_main(lastDoc, currDoc);
 | 
					  var diff = dmp.diff_main(lastDoc, currDoc)
 | 
				
			||||||
    var patch = dmp.patch_make(lastDoc, diff);
 | 
					  var patch = dmp.patch_make(lastDoc, diff)
 | 
				
			||||||
    patch = dmp.patch_toText(patch);
 | 
					  patch = dmp.patch_toText(patch)
 | 
				
			||||||
    var ms_end = (new Date()).getTime();
 | 
					  var msEnd = (new Date()).getTime()
 | 
				
			||||||
  if (config.debug) {
 | 
					  if (config.debug) {
 | 
				
			||||||
        logger.info(patch);
 | 
					    logger.info(patch)
 | 
				
			||||||
        logger.info((ms_end - ms_start) + 'ms');
 | 
					    logger.info((msEnd - msStart) + 'ms')
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
    return patch;
 | 
					  return patch
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function getRevision(revisions, count) {
 | 
					function getRevision (revisions, count) {
 | 
				
			||||||
    var ms_start = (new Date()).getTime();
 | 
					  var msStart = (new Date()).getTime()
 | 
				
			||||||
    var startContent = null;
 | 
					  var startContent = null
 | 
				
			||||||
    var lastPatch = [];
 | 
					  var lastPatch = []
 | 
				
			||||||
    var applyPatches = [];
 | 
					  var applyPatches = []
 | 
				
			||||||
    var authorship = [];
 | 
					  var authorship = []
 | 
				
			||||||
  if (count <= Math.round(revisions.length / 2)) {
 | 
					  if (count <= Math.round(revisions.length / 2)) {
 | 
				
			||||||
    // start from top to target
 | 
					    // start from top to target
 | 
				
			||||||
        for (var i = 0; i < count; i++) {
 | 
					    for (let i = 0; i < count; i++) {
 | 
				
			||||||
            var revision = revisions[i];
 | 
					      let revision = revisions[i]
 | 
				
			||||||
            if (i == 0) {
 | 
					      if (i === 0) {
 | 
				
			||||||
                startContent = revision.content || revision.lastContent;
 | 
					        startContent = revision.content || revision.lastContent
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
            if (i != count - 1) {
 | 
					      if (i !== count - 1) {
 | 
				
			||||||
                var patch = dmp.patch_fromText(revision.patch);
 | 
					        let patch = dmp.patch_fromText(revision.patch)
 | 
				
			||||||
                applyPatches = applyPatches.concat(patch);
 | 
					        applyPatches = applyPatches.concat(patch)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
            lastPatch = revision.patch;
 | 
					      lastPatch = revision.patch
 | 
				
			||||||
            authorship = revision.authorship;
 | 
					      authorship = revision.authorship
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    // swap DIFF_INSERT and DIFF_DELETE to achieve unpatching
 | 
					    // swap DIFF_INSERT and DIFF_DELETE to achieve unpatching
 | 
				
			||||||
        for (var i = 0, l = applyPatches.length; i < l; i++) {
 | 
					    for (let i = 0, l = applyPatches.length; i < l; i++) {
 | 
				
			||||||
            for (var j = 0, m = applyPatches[i].diffs.length; j < m; j++) {
 | 
					      for (let j = 0, m = applyPatches[i].diffs.length; j < m; j++) {
 | 
				
			||||||
                var diff = applyPatches[i].diffs[j];
 | 
					        var diff = applyPatches[i].diffs[j]
 | 
				
			||||||
                if (diff[0] == DiffMatchPatch.DIFF_INSERT)
 | 
					        if (diff[0] === DiffMatchPatch.DIFF_INSERT) { diff[0] = DiffMatchPatch.DIFF_DELETE } else if (diff[0] === DiffMatchPatch.DIFF_DELETE) { diff[0] = DiffMatchPatch.DIFF_INSERT }
 | 
				
			||||||
                    diff[0] = DiffMatchPatch.DIFF_DELETE;
 | 
					 | 
				
			||||||
                else if (diff[0] == DiffMatchPatch.DIFF_DELETE)
 | 
					 | 
				
			||||||
                    diff[0] = DiffMatchPatch.DIFF_INSERT;
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    // start from bottom to target
 | 
					    // start from bottom to target
 | 
				
			||||||
        var l = revisions.length - 1;
 | 
					    var l = revisions.length - 1
 | 
				
			||||||
    for (var i = l; i >= count - 1; i--) {
 | 
					    for (var i = l; i >= count - 1; i--) {
 | 
				
			||||||
            var revision = revisions[i];
 | 
					      let revision = revisions[i]
 | 
				
			||||||
            if (i == l) {
 | 
					      if (i === l) {
 | 
				
			||||||
                startContent = revision.lastContent;
 | 
					        startContent = revision.lastContent
 | 
				
			||||||
                authorship = revision.authorship;
 | 
					        authorship = revision.authorship
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      if (revision.patch) {
 | 
					      if (revision.patch) {
 | 
				
			||||||
                var patch = dmp.patch_fromText(revision.patch);
 | 
					        let patch = dmp.patch_fromText(revision.patch)
 | 
				
			||||||
                applyPatches = applyPatches.concat(patch);
 | 
					        applyPatches = applyPatches.concat(patch)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
            lastPatch = revision.patch;
 | 
					      lastPatch = revision.patch
 | 
				
			||||||
            authorship = revision.authorship;
 | 
					      authorship = revision.authorship
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  try {
 | 
					  try {
 | 
				
			||||||
        var finalContent = dmp.patch_apply(applyPatches, startContent)[0];
 | 
					    var finalContent = dmp.patch_apply(applyPatches, startContent)[0]
 | 
				
			||||||
  } catch (err) {
 | 
					  } catch (err) {
 | 
				
			||||||
        throw new Error(err);
 | 
					    throw new Error(err)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  var data = {
 | 
					  var data = {
 | 
				
			||||||
    content: finalContent,
 | 
					    content: finalContent,
 | 
				
			||||||
    patch: dmp.patch_fromText(lastPatch),
 | 
					    patch: dmp.patch_fromText(lastPatch),
 | 
				
			||||||
    authorship: authorship
 | 
					    authorship: authorship
 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
    var ms_end = (new Date()).getTime();
 | 
					 | 
				
			||||||
    if (config.debug) {
 | 
					 | 
				
			||||||
        logger.info((ms_end - ms_start) + 'ms');
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
    return data;
 | 
					  var msEnd = (new Date()).getTime()
 | 
				
			||||||
 | 
					  if (config.debug) {
 | 
				
			||||||
 | 
					    logger.info((msEnd - msStart) + 'ms')
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return data
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// log uncaught exception
 | 
					// log uncaught exception
 | 
				
			||||||
process.on('uncaughtException', function (err) {
 | 
					process.on('uncaughtException', function (err) {
 | 
				
			||||||
    logger.error('An uncaught exception has occured.');
 | 
					  logger.error('An uncaught exception has occured.')
 | 
				
			||||||
    logger.error(err);
 | 
					  logger.error(err)
 | 
				
			||||||
    logger.error('Process will exit now.');
 | 
					  logger.error('Process will exit now.')
 | 
				
			||||||
    process.exit(1);
 | 
					  process.exit(1)
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -5,7 +5,7 @@
 | 
				
			|||||||
  "main": "app.js",
 | 
					  "main": "app.js",
 | 
				
			||||||
  "license": "MIT",
 | 
					  "license": "MIT",
 | 
				
			||||||
  "scripts": {
 | 
					  "scripts": {
 | 
				
			||||||
    "test": "npm run-script lint",
 | 
					    "test": "node ./node_modules/standard/bin/cmd.js && npm run-script lint",
 | 
				
			||||||
    "lint": "eslint .",
 | 
					    "lint": "eslint .",
 | 
				
			||||||
    "dev": "webpack --config webpack.config.js --progress --colors --watch",
 | 
					    "dev": "webpack --config webpack.config.js --progress --colors --watch",
 | 
				
			||||||
    "build": "webpack --config webpack.production.js --progress --colors",
 | 
					    "build": "webpack --config webpack.production.js --progress --colors",
 | 
				
			||||||
@ -165,8 +165,15 @@
 | 
				
			|||||||
    "optimize-css-assets-webpack-plugin": "^1.3.0",
 | 
					    "optimize-css-assets-webpack-plugin": "^1.3.0",
 | 
				
			||||||
    "script-loader": "^0.7.0",
 | 
					    "script-loader": "^0.7.0",
 | 
				
			||||||
    "style-loader": "^0.13.1",
 | 
					    "style-loader": "^0.13.1",
 | 
				
			||||||
 | 
					    "standard": "^9.0.1",
 | 
				
			||||||
    "url-loader": "^0.5.7",
 | 
					    "url-loader": "^0.5.7",
 | 
				
			||||||
    "webpack": "^1.14.0",
 | 
					    "webpack": "^1.14.0",
 | 
				
			||||||
    "webpack-parallel-uglify-plugin": "^0.2.0"
 | 
					    "webpack-parallel-uglify-plugin": "^0.2.0"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "standard": {
 | 
				
			||||||
 | 
					    "ignore": [
 | 
				
			||||||
 | 
					      "lib/ot",
 | 
				
			||||||
 | 
					      "public/vendor"
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user