Merge pull request #421 from hackmdio/BackendRefactor
Backend code refactoring - part 1
This commit is contained in:
commit
ea9b1557f9
45 changed files with 1590 additions and 1556 deletions
|
@ -1 +0,0 @@
|
||||||
*.min.js
|
|
268
.eslintrc
268
.eslintrc
|
@ -1,268 +0,0 @@
|
||||||
{
|
|
||||||
"env": {
|
|
||||||
"browser": true,
|
|
||||||
"es6": true,
|
|
||||||
"node": true
|
|
||||||
},
|
|
||||||
"extends": "eslint:recommended",
|
|
||||||
"parserOptions": {
|
|
||||||
"sourceType": "module"
|
|
||||||
},
|
|
||||||
"rules": {
|
|
||||||
"accessor-pairs": "error",
|
|
||||||
"array-bracket-spacing": [
|
|
||||||
"error",
|
|
||||||
"never"
|
|
||||||
],
|
|
||||||
"array-callback-return": "error",
|
|
||||||
"arrow-body-style": "error",
|
|
||||||
"arrow-spacing": "error",
|
|
||||||
"block-scoped-var": "off",
|
|
||||||
"block-spacing": "error",
|
|
||||||
"brace-style": [
|
|
||||||
"error",
|
|
||||||
"1tbs"
|
|
||||||
],
|
|
||||||
"callback-return": "off",
|
|
||||||
"camelcase": "error",
|
|
||||||
"class-methods-use-this": "error",
|
|
||||||
"comma-dangle": "error",
|
|
||||||
"comma-spacing": [
|
|
||||||
"error", {
|
|
||||||
"after": true,
|
|
||||||
"before": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"comma-style": [
|
|
||||||
"error",
|
|
||||||
"last"
|
|
||||||
],
|
|
||||||
"complexity": "error",
|
|
||||||
"computed-property-spacing": [
|
|
||||||
"error",
|
|
||||||
"never"
|
|
||||||
],
|
|
||||||
"consistent-return": "off",
|
|
||||||
"consistent-this": "error",
|
|
||||||
"curly": "off",
|
|
||||||
"default-case": "error",
|
|
||||||
"dot-location": [
|
|
||||||
"error",
|
|
||||||
"property"
|
|
||||||
],
|
|
||||||
"dot-notation": [
|
|
||||||
"error", {
|
|
||||||
"allowKeywords": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"eol-last": "error",
|
|
||||||
"eqeqeq": "off",
|
|
||||||
"func-call-spacing": "error",
|
|
||||||
"func-names": [
|
|
||||||
"error",
|
|
||||||
"never"
|
|
||||||
],
|
|
||||||
"func-style": [
|
|
||||||
"error",
|
|
||||||
"declaration"
|
|
||||||
],
|
|
||||||
"generator-star-spacing": "error",
|
|
||||||
"global-require": "off",
|
|
||||||
"guard-for-in": "error",
|
|
||||||
"handle-callback-err": "error",
|
|
||||||
"id-blacklist": "error",
|
|
||||||
"id-length": "off",
|
|
||||||
"id-match": "error",
|
|
||||||
"indent": 2,
|
|
||||||
"init-declarations": "off",
|
|
||||||
"jsx-quotes": "error",
|
|
||||||
"key-spacing": "error",
|
|
||||||
"keyword-spacing": [
|
|
||||||
"error", {
|
|
||||||
"after": true,
|
|
||||||
"before": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"line-comment-position": "off",
|
|
||||||
"linebreak-style": [
|
|
||||||
"error",
|
|
||||||
"unix"
|
|
||||||
],
|
|
||||||
"lines-around-comment": "error",
|
|
||||||
"lines-around-directive": "error",
|
|
||||||
"max-depth": "error",
|
|
||||||
"max-len": "off",
|
|
||||||
"max-lines": "off",
|
|
||||||
"max-nested-callbacks": "error",
|
|
||||||
"max-params": "error",
|
|
||||||
"max-statements": "error",
|
|
||||||
"max-statements-per-line": "error",
|
|
||||||
"multiline-ternary": [
|
|
||||||
"error",
|
|
||||||
"never"
|
|
||||||
],
|
|
||||||
"new-cap": "error",
|
|
||||||
"new-parens": "error",
|
|
||||||
"newline-after-var": "off",
|
|
||||||
"newline-before-return": "off",
|
|
||||||
"newline-per-chained-call": "off",
|
|
||||||
"no-alert": "error",
|
|
||||||
"no-array-constructor": "error",
|
|
||||||
"no-bitwise": "error",
|
|
||||||
"no-caller": "error",
|
|
||||||
"no-catch-shadow": "off",
|
|
||||||
"no-confusing-arrow": "error",
|
|
||||||
"no-continue": "error",
|
|
||||||
"no-div-regex": "error",
|
|
||||||
"no-duplicate-imports": "error",
|
|
||||||
"no-else-return": "error",
|
|
||||||
"no-empty-function": "error",
|
|
||||||
"no-eq-null": "error",
|
|
||||||
"no-eval": "error",
|
|
||||||
"no-extend-native": "error",
|
|
||||||
"no-extra-bind": "error",
|
|
||||||
"no-extra-label": "error",
|
|
||||||
"no-extra-parens": "warn",
|
|
||||||
"no-floating-decimal": "error",
|
|
||||||
"no-global-assign": "error",
|
|
||||||
"no-implicit-coercion": "error",
|
|
||||||
"no-implicit-globals": "error",
|
|
||||||
"no-implied-eval": "error",
|
|
||||||
"no-inline-comments": "off",
|
|
||||||
"no-inner-declarations": [
|
|
||||||
"error",
|
|
||||||
"functions"
|
|
||||||
],
|
|
||||||
"no-invalid-this": "error",
|
|
||||||
"no-iterator": "error",
|
|
||||||
"no-label-var": "error",
|
|
||||||
"no-labels": "error",
|
|
||||||
"no-lone-blocks": "error",
|
|
||||||
"no-lonely-if": "error",
|
|
||||||
"no-loop-func": "error",
|
|
||||||
"no-magic-numbers": "off",
|
|
||||||
"no-mixed-operators": "error",
|
|
||||||
"no-mixed-requires": "error",
|
|
||||||
"no-multi-spaces": "error",
|
|
||||||
"no-multi-str": "error",
|
|
||||||
"no-multiple-empty-lines": "error",
|
|
||||||
"no-negated-condition": "off",
|
|
||||||
"no-nested-ternary": "error",
|
|
||||||
"no-new": "error",
|
|
||||||
"no-new-func": "error",
|
|
||||||
"no-new-object": "error",
|
|
||||||
"no-new-require": "error",
|
|
||||||
"no-new-wrappers": "error",
|
|
||||||
"no-octal-escape": "error",
|
|
||||||
"no-param-reassign": [
|
|
||||||
"error", {
|
|
||||||
"props": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"no-path-concat": "off",
|
|
||||||
"no-plusplus": [
|
|
||||||
"error", {
|
|
||||||
"allowForLoopAfterthoughts": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"no-process-env": "error",
|
|
||||||
"no-process-exit": "off",
|
|
||||||
"no-proto": "error",
|
|
||||||
"no-prototype-builtins": "error",
|
|
||||||
"no-restricted-globals": "error",
|
|
||||||
"no-restricted-imports": "error",
|
|
||||||
"no-restricted-modules": "error",
|
|
||||||
"no-restricted-properties": "error",
|
|
||||||
"no-restricted-syntax": "error",
|
|
||||||
"no-return-assign": "error",
|
|
||||||
"no-script-url": "error",
|
|
||||||
"no-self-compare": "error",
|
|
||||||
"no-sequences": "error",
|
|
||||||
"no-shadow": "off",
|
|
||||||
"no-shadow-restricted-names": "error",
|
|
||||||
"no-spaced-func": "error",
|
|
||||||
"no-sync": "off",
|
|
||||||
"no-tabs": "error",
|
|
||||||
"no-template-curly-in-string": "error",
|
|
||||||
"no-ternary": "off",
|
|
||||||
"no-throw-literal": "error",
|
|
||||||
"no-trailing-spaces": "error",
|
|
||||||
"no-undef-init": "error",
|
|
||||||
"no-undefined": "error",
|
|
||||||
"no-underscore-dangle": "error",
|
|
||||||
"no-unmodified-loop-condition": "error",
|
|
||||||
"no-unneeded-ternary": "error",
|
|
||||||
"no-unsafe-negation": "error",
|
|
||||||
"no-unused-expressions": "error",
|
|
||||||
"no-use-before-define": "warn",
|
|
||||||
"no-useless-call": "error",
|
|
||||||
"no-useless-computed-key": "error",
|
|
||||||
"no-useless-concat": "error",
|
|
||||||
"no-useless-constructor": "error",
|
|
||||||
"no-useless-escape": "error",
|
|
||||||
"no-useless-rename": "error",
|
|
||||||
"no-var": "off",
|
|
||||||
"no-void": "error",
|
|
||||||
"no-warning-comments": "error",
|
|
||||||
"no-whitespace-before-property": "error",
|
|
||||||
"no-with": "error",
|
|
||||||
"object-curly-newline": "off",
|
|
||||||
"object-curly-spacing": [
|
|
||||||
"error",
|
|
||||||
"always"
|
|
||||||
],
|
|
||||||
"object-property-newline": "error",
|
|
||||||
"object-shorthand": "off",
|
|
||||||
"one-var": "off",
|
|
||||||
"one-var-declaration-per-line": [
|
|
||||||
"error",
|
|
||||||
"initializations"
|
|
||||||
],
|
|
||||||
"operator-assignment": "error",
|
|
||||||
"operator-linebreak": "error",
|
|
||||||
"padded-blocks": "off",
|
|
||||||
"prefer-arrow-callback": "off",
|
|
||||||
"prefer-const": "error",
|
|
||||||
"prefer-numeric-literals": "error",
|
|
||||||
"prefer-reflect": "error",
|
|
||||||
"prefer-rest-params": "error",
|
|
||||||
"prefer-spread": "error",
|
|
||||||
"prefer-template": "off",
|
|
||||||
"quote-props": "off",
|
|
||||||
"quotes": "off",
|
|
||||||
"radix": "error",
|
|
||||||
"require-jsdoc": "off",
|
|
||||||
"rest-spread-spacing": "error",
|
|
||||||
"semi": "off",
|
|
||||||
"semi-spacing": [
|
|
||||||
"error", {
|
|
||||||
"after": true,
|
|
||||||
"before": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"sort-imports": "error",
|
|
||||||
"sort-keys": "off",
|
|
||||||
"sort-vars": "error",
|
|
||||||
"space-before-blocks": "error",
|
|
||||||
"space-before-function-paren": "off",
|
|
||||||
"space-in-parens": [
|
|
||||||
"error",
|
|
||||||
"never"
|
|
||||||
],
|
|
||||||
"space-infix-ops": "error",
|
|
||||||
"space-unary-ops": "error",
|
|
||||||
"spaced-comment": "off",
|
|
||||||
"strict": "error",
|
|
||||||
"symbol-description": "error",
|
|
||||||
"template-curly-spacing": "error",
|
|
||||||
"unicode-bom": [
|
|
||||||
"error",
|
|
||||||
"never"
|
|
||||||
],
|
|
||||||
"valid-jsdoc": "error",
|
|
||||||
"vars-on-top": "off",
|
|
||||||
"wrap-regex": "error",
|
|
||||||
"yield-star-spacing": "error",
|
|
||||||
"yoda": "off"
|
|
||||||
}
|
|
||||||
}
|
|
483
app.js
483
app.js
|
@ -1,52 +1,42 @@
|
||||||
|
'use strict'
|
||||||
// app
|
// app
|
||||||
// external modules
|
// external modules
|
||||||
var express = require('express')
|
var express = require('express')
|
||||||
var toobusy = require('toobusy-js')
|
|
||||||
var ejs = require('ejs')
|
var ejs = require('ejs')
|
||||||
var passport = require('passport')
|
var passport = require('passport')
|
||||||
var methodOverride = require('method-override')
|
var methodOverride = require('method-override')
|
||||||
var cookieParser = require('cookie-parser')
|
var cookieParser = require('cookie-parser')
|
||||||
var bodyParser = require('body-parser')
|
|
||||||
var compression = require('compression')
|
var compression = require('compression')
|
||||||
var session = require('express-session')
|
var session = require('express-session')
|
||||||
var SequelizeStore = require('connect-session-sequelize')(session.Store)
|
var SequelizeStore = require('connect-session-sequelize')(session.Store)
|
||||||
var fs = require('fs')
|
var fs = require('fs')
|
||||||
var url = require('url')
|
|
||||||
var path = require('path')
|
var path = require('path')
|
||||||
var imgur = require('imgur')
|
|
||||||
var formidable = require('formidable')
|
|
||||||
var morgan = require('morgan')
|
var morgan = require('morgan')
|
||||||
var passportSocketIo = require('passport.socketio')
|
var passportSocketIo = require('passport.socketio')
|
||||||
var helmet = require('helmet')
|
var helmet = require('helmet')
|
||||||
var i18n = require('i18n')
|
var i18n = require('i18n')
|
||||||
var flash = require('connect-flash')
|
var flash = require('connect-flash')
|
||||||
var validator = require('validator')
|
|
||||||
|
|
||||||
// utils
|
|
||||||
var getImageMimeType = require('./lib/utils.js').getImageMimeType
|
|
||||||
|
|
||||||
// core
|
// core
|
||||||
var config = require('./lib/config.js')
|
var config = require('./lib/config')
|
||||||
var logger = require('./lib/logger.js')
|
var logger = require('./lib/logger')
|
||||||
var auth = require('./lib/auth.js')
|
var response = require('./lib/response')
|
||||||
var response = require('./lib/response.js')
|
|
||||||
var models = require('./lib/models')
|
var models = require('./lib/models')
|
||||||
|
|
||||||
// generate front-end constants by template
|
// generate front-end constants by template
|
||||||
var configJson = config.raw
|
|
||||||
var constpath = path.join(__dirname, './public/js/lib/common/constant.ejs')
|
var constpath = path.join(__dirname, './public/js/lib/common/constant.ejs')
|
||||||
var googleApiKey = (fs.existsSync('/run/secrets/google_apiKey') && config.handleDockerSecret('google_apiKey')) || process.env.HMD_GOOGLE_API_KEY || (configJson.google && configJson.google.apiKey) || ''
|
|
||||||
var googleClientID = (fs.existsSync('/run/secrets/google_clientID') && config.handleDockerSecret('google_clientID')) || process.env.HMD_GOOGLE_CLIENT_ID || (configJson.google && configJson.google.clientID) || ''
|
|
||||||
var dropboxAppKey = (fs.existsSync('/run/secrets/dropbox_appKey') && config.handleDockerSecret('dropbox_appKey')) || process.env.HMD_DROPBOX_APP_KEY || (configJson.dropbox && configJson.dropbox.appKey) || ''
|
|
||||||
var data = {
|
var data = {
|
||||||
domain: config.domain,
|
domain: config.domain,
|
||||||
urlpath: config.urlpath,
|
urlpath: config.urlpath,
|
||||||
debug: config.debug,
|
debug: config.debug,
|
||||||
version: config.version,
|
version: config.version,
|
||||||
GOOGLE_API_KEY: googleApiKey,
|
GOOGLE_API_KEY: config.google.clientSecret,
|
||||||
GOOGLE_CLIENT_ID: googleClientID,
|
GOOGLE_CLIENT_ID: config.google.clientID,
|
||||||
DROPBOX_APP_KEY: dropboxAppKey
|
DROPBOX_APP_KEY: config.dropbox.clientSecret
|
||||||
}
|
}
|
||||||
|
|
||||||
ejs.renderFile(constpath, data, {}, function (err, str) {
|
ejs.renderFile(constpath, data, {}, function (err, str) {
|
||||||
if (err) throw new Error(err)
|
if (err) throw new Error(err)
|
||||||
fs.writeFileSync(path.join(__dirname, './public/build/constant.js'), str)
|
fs.writeFileSync(path.join(__dirname, './public/build/constant.js'), str)
|
||||||
|
@ -79,7 +69,7 @@ if (config.usessl) {
|
||||||
|
|
||||||
// logger
|
// logger
|
||||||
app.use(morgan('combined', {
|
app.use(morgan('combined', {
|
||||||
'stream': logger.stream
|
'stream': logger
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// socket io
|
// socket io
|
||||||
|
@ -98,12 +88,6 @@ realtime.io = io
|
||||||
// methodOverride
|
// methodOverride
|
||||||
app.use(methodOverride('_method'))
|
app.use(methodOverride('_method'))
|
||||||
|
|
||||||
// create application/x-www-form-urlencoded parser
|
|
||||||
var urlencodedParser = bodyParser.urlencoded({
|
|
||||||
extended: false,
|
|
||||||
limit: 1024 * 1024 * 10 // 10 mb
|
|
||||||
})
|
|
||||||
|
|
||||||
// session store
|
// session store
|
||||||
var sessionStore = new SequelizeStore({
|
var sessionStore = new SequelizeStore({
|
||||||
db: models.sequelize
|
db: models.sequelize
|
||||||
|
@ -157,63 +141,18 @@ server.on('resumeSession', function (id, cb) {
|
||||||
})
|
})
|
||||||
|
|
||||||
// middleware which blocks requests when we're too busy
|
// middleware which blocks requests when we're too busy
|
||||||
app.use(function (req, res, next) {
|
app.use(require('./lib/web/middleware/tooBusy'))
|
||||||
if (toobusy()) {
|
|
||||||
response.errorServiceUnavailable(res)
|
|
||||||
} else {
|
|
||||||
next()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
app.use(flash())
|
app.use(flash())
|
||||||
|
|
||||||
// passport
|
// passport
|
||||||
app.use(passport.initialize())
|
app.use(passport.initialize())
|
||||||
app.use(passport.session())
|
app.use(passport.session())
|
||||||
auth.registerAuthMethod()
|
|
||||||
|
|
||||||
// serialize and deserialize
|
|
||||||
passport.serializeUser(function (user, done) {
|
|
||||||
logger.info('serializeUser: ' + user.id)
|
|
||||||
return done(null, user.id)
|
|
||||||
})
|
|
||||||
passport.deserializeUser(function (id, done) {
|
|
||||||
models.User.findOne({
|
|
||||||
where: {
|
|
||||||
id: id
|
|
||||||
}
|
|
||||||
}).then(function (user) {
|
|
||||||
logger.info('deserializeUser: ' + user.id)
|
|
||||||
return done(null, user)
|
|
||||||
}).catch(function (err) {
|
|
||||||
logger.error(err)
|
|
||||||
return done(err, null)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
// check uri is valid before going further
|
// check uri is valid before going further
|
||||||
app.use(function (req, res, next) {
|
app.use(require('./lib/web/middleware/checkURIValid'))
|
||||||
try {
|
|
||||||
decodeURIComponent(req.path)
|
|
||||||
} catch (err) {
|
|
||||||
logger.error(err)
|
|
||||||
return response.errorBadRequest(res)
|
|
||||||
}
|
|
||||||
next()
|
|
||||||
})
|
|
||||||
|
|
||||||
// redirect url without trailing slashes
|
// redirect url without trailing slashes
|
||||||
app.use(function (req, res, next) {
|
app.use(require('./lib/web/middleware/redirectWithoutTrailingSlashes'))
|
||||||
if (req.method === 'GET' && req.path.substr(-1) === '/' && req.path.length > 1) {
|
|
||||||
var query = req.url.slice(req.path.length)
|
|
||||||
var urlpath = req.path.slice(0, -1)
|
|
||||||
var serverurl = config.serverurl
|
|
||||||
if (config.urlpath) serverurl = serverurl.slice(0, -(config.urlpath.length + 1))
|
|
||||||
res.redirect(301, serverurl + urlpath + query)
|
|
||||||
} else {
|
|
||||||
next()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// routes need sessions
|
// routes need sessions
|
||||||
// template files
|
// template files
|
||||||
|
@ -222,390 +161,16 @@ app.set('views', path.join(__dirname, '/public/views'))
|
||||||
app.engine('ejs', ejs.renderFile)
|
app.engine('ejs', ejs.renderFile)
|
||||||
// set view engine
|
// set view engine
|
||||||
app.set('view engine', 'ejs')
|
app.set('view engine', 'ejs')
|
||||||
// get index
|
|
||||||
app.get('/', response.showIndex)
|
|
||||||
// get 403 forbidden
|
|
||||||
app.get('/403', function (req, res) {
|
|
||||||
response.errorForbidden(res)
|
|
||||||
})
|
|
||||||
// get 404 not found
|
|
||||||
app.get('/404', function (req, res) {
|
|
||||||
response.errorNotFound(res)
|
|
||||||
})
|
|
||||||
// get 500 internal error
|
|
||||||
app.get('/500', function (req, res) {
|
|
||||||
response.errorInternalError(res)
|
|
||||||
})
|
|
||||||
// get status
|
|
||||||
app.get('/status', function (req, res, next) {
|
|
||||||
realtime.getStatus(function (data) {
|
|
||||||
res.set({
|
|
||||||
'Cache-Control': 'private', // only cache by client
|
|
||||||
'X-Robots-Tag': 'noindex, nofollow', // prevent crawling
|
|
||||||
'HackMD-Version': config.version
|
|
||||||
})
|
|
||||||
res.send(data)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
// get status
|
|
||||||
app.get('/temp', function (req, res) {
|
|
||||||
var host = req.get('host')
|
|
||||||
if (config.alloworigin.indexOf(host) === -1) {
|
|
||||||
response.errorForbidden(res)
|
|
||||||
} else {
|
|
||||||
var tempid = req.query.tempid
|
|
||||||
if (!tempid) {
|
|
||||||
response.errorForbidden(res)
|
|
||||||
} else {
|
|
||||||
models.Temp.findOne({
|
|
||||||
where: {
|
|
||||||
id: tempid
|
|
||||||
}
|
|
||||||
}).then(function (temp) {
|
|
||||||
if (!temp) {
|
|
||||||
response.errorNotFound(res)
|
|
||||||
} else {
|
|
||||||
res.header('Access-Control-Allow-Origin', '*')
|
|
||||||
res.send({
|
|
||||||
temp: temp.data
|
|
||||||
})
|
|
||||||
temp.destroy().catch(function (err) {
|
|
||||||
if (err) {
|
|
||||||
logger.error('remove temp failed: ' + err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}).catch(function (err) {
|
|
||||||
logger.error(err)
|
|
||||||
return response.errorInternalError(res)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
// post status
|
|
||||||
app.post('/temp', urlencodedParser, function (req, res) {
|
|
||||||
var host = req.get('host')
|
|
||||||
if (config.alloworigin.indexOf(host) === -1) {
|
|
||||||
response.errorForbidden(res)
|
|
||||||
} else {
|
|
||||||
var data = req.body.data
|
|
||||||
if (!data) {
|
|
||||||
response.errorForbidden(res)
|
|
||||||
} else {
|
|
||||||
if (config.debug) {
|
|
||||||
logger.info('SERVER received temp from [' + host + ']: ' + req.body.data)
|
|
||||||
}
|
|
||||||
models.Temp.create({
|
|
||||||
data: data
|
|
||||||
}).then(function (temp) {
|
|
||||||
if (temp) {
|
|
||||||
res.header('Access-Control-Allow-Origin', '*')
|
|
||||||
res.send({
|
|
||||||
status: 'ok',
|
|
||||||
id: temp.id
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
response.errorInternalError(res)
|
|
||||||
}
|
|
||||||
}).catch(function (err) {
|
|
||||||
logger.error(err)
|
|
||||||
return response.errorInternalError(res)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
function setReturnToFromReferer (req) {
|
app.use(require('./lib/web/baseRouter'))
|
||||||
var referer = req.get('referer')
|
app.use(require('./lib/web/statusRouter'))
|
||||||
if (!req.session) req.session = {}
|
app.use(require('./lib/web/auth'))
|
||||||
req.session.returnTo = referer
|
app.use(require('./lib/web/historyRouter'))
|
||||||
}
|
app.use(require('./lib/web/userRouter'))
|
||||||
|
app.use(require('./lib/web/imageRouter'))
|
||||||
|
app.use(require('./lib/web/noteRouter'))
|
||||||
|
|
||||||
// facebook auth
|
// response not found if no any route matxches
|
||||||
if (config.facebook) {
|
|
||||||
app.get('/auth/facebook', function (req, res, next) {
|
|
||||||
setReturnToFromReferer(req)
|
|
||||||
passport.authenticate('facebook')(req, res, next)
|
|
||||||
})
|
|
||||||
// facebook auth callback
|
|
||||||
app.get('/auth/facebook/callback',
|
|
||||||
passport.authenticate('facebook', {
|
|
||||||
successReturnToOrRedirect: config.serverurl + '/',
|
|
||||||
failureRedirect: config.serverurl + '/'
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
// twitter auth
|
|
||||||
if (config.twitter) {
|
|
||||||
app.get('/auth/twitter', function (req, res, next) {
|
|
||||||
setReturnToFromReferer(req)
|
|
||||||
passport.authenticate('twitter')(req, res, next)
|
|
||||||
})
|
|
||||||
// twitter auth callback
|
|
||||||
app.get('/auth/twitter/callback',
|
|
||||||
passport.authenticate('twitter', {
|
|
||||||
successReturnToOrRedirect: config.serverurl + '/',
|
|
||||||
failureRedirect: config.serverurl + '/'
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
// github auth
|
|
||||||
if (config.github) {
|
|
||||||
app.get('/auth/github', function (req, res, next) {
|
|
||||||
setReturnToFromReferer(req)
|
|
||||||
passport.authenticate('github')(req, res, next)
|
|
||||||
})
|
|
||||||
// github auth callback
|
|
||||||
app.get('/auth/github/callback',
|
|
||||||
passport.authenticate('github', {
|
|
||||||
successReturnToOrRedirect: config.serverurl + '/',
|
|
||||||
failureRedirect: config.serverurl + '/'
|
|
||||||
}))
|
|
||||||
if (!config.gitlab.scope || config.gitlab.scope === 'api') {
|
|
||||||
// gitlab callback actions
|
|
||||||
app.get('/auth/gitlab/callback/:noteId/:action', response.gitlabActions)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// gitlab auth
|
|
||||||
if (config.gitlab) {
|
|
||||||
app.get('/auth/gitlab', function (req, res, next) {
|
|
||||||
setReturnToFromReferer(req)
|
|
||||||
passport.authenticate('gitlab')(req, res, next)
|
|
||||||
})
|
|
||||||
// gitlab auth callback
|
|
||||||
app.get('/auth/gitlab/callback',
|
|
||||||
passport.authenticate('gitlab', {
|
|
||||||
successReturnToOrRedirect: config.serverurl + '/',
|
|
||||||
failureRedirect: config.serverurl + '/'
|
|
||||||
}))
|
|
||||||
// gitlab callback actions
|
|
||||||
app.get('/auth/gitlab/callback/:noteId/:action', response.gitlabActions)
|
|
||||||
}
|
|
||||||
// dropbox auth
|
|
||||||
if (config.dropbox) {
|
|
||||||
app.get('/auth/dropbox', function (req, res, next) {
|
|
||||||
setReturnToFromReferer(req)
|
|
||||||
passport.authenticate('dropbox-oauth2')(req, res, next)
|
|
||||||
})
|
|
||||||
// dropbox auth callback
|
|
||||||
app.get('/auth/dropbox/callback',
|
|
||||||
passport.authenticate('dropbox-oauth2', {
|
|
||||||
successReturnToOrRedirect: config.serverurl + '/',
|
|
||||||
failureRedirect: config.serverurl + '/'
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
// google auth
|
|
||||||
if (config.google) {
|
|
||||||
app.get('/auth/google', function (req, res, next) {
|
|
||||||
setReturnToFromReferer(req)
|
|
||||||
passport.authenticate('google', { scope: ['profile'] })(req, res, next)
|
|
||||||
})
|
|
||||||
// google auth callback
|
|
||||||
app.get('/auth/google/callback',
|
|
||||||
passport.authenticate('google', {
|
|
||||||
successReturnToOrRedirect: config.serverurl + '/',
|
|
||||||
failureRedirect: config.serverurl + '/'
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
// ldap auth
|
|
||||||
if (config.ldap) {
|
|
||||||
app.post('/auth/ldap', urlencodedParser, function (req, res, next) {
|
|
||||||
if (!req.body.username || !req.body.password) return response.errorBadRequest(res)
|
|
||||||
setReturnToFromReferer(req)
|
|
||||||
passport.authenticate('ldapauth', {
|
|
||||||
successReturnToOrRedirect: config.serverurl + '/',
|
|
||||||
failureRedirect: config.serverurl + '/',
|
|
||||||
failureFlash: true
|
|
||||||
})(req, res, next)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
// email auth
|
|
||||||
if (config.email) {
|
|
||||||
if (config.allowemailregister) {
|
|
||||||
app.post('/register', urlencodedParser, function (req, res, next) {
|
|
||||||
if (!req.body.email || !req.body.password) return response.errorBadRequest(res)
|
|
||||||
if (!validator.isEmail(req.body.email)) return response.errorBadRequest(res)
|
|
||||||
models.User.findOrCreate({
|
|
||||||
where: {
|
|
||||||
email: req.body.email
|
|
||||||
},
|
|
||||||
defaults: {
|
|
||||||
password: req.body.password
|
|
||||||
}
|
|
||||||
}).spread(function (user, created) {
|
|
||||||
if (user) {
|
|
||||||
if (created) {
|
|
||||||
if (config.debug) {
|
|
||||||
logger.info('user registered: ' + user.id)
|
|
||||||
}
|
|
||||||
req.flash('info', "You've successfully registered, please signin.")
|
|
||||||
} else {
|
|
||||||
if (config.debug) {
|
|
||||||
logger.info('user found: ' + user.id)
|
|
||||||
}
|
|
||||||
req.flash('error', 'This email has been used, please try another one.')
|
|
||||||
}
|
|
||||||
return res.redirect(config.serverurl + '/')
|
|
||||||
}
|
|
||||||
req.flash('error', 'Failed to register your account, please try again.')
|
|
||||||
return res.redirect(config.serverurl + '/')
|
|
||||||
}).catch(function (err) {
|
|
||||||
logger.error('auth callback failed: ' + err)
|
|
||||||
return response.errorInternalError(res)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
app.post('/login', urlencodedParser, function (req, res, next) {
|
|
||||||
if (!req.body.email || !req.body.password) return response.errorBadRequest(res)
|
|
||||||
if (!validator.isEmail(req.body.email)) return response.errorBadRequest(res)
|
|
||||||
setReturnToFromReferer(req)
|
|
||||||
passport.authenticate('local', {
|
|
||||||
successReturnToOrRedirect: config.serverurl + '/',
|
|
||||||
failureRedirect: config.serverurl + '/',
|
|
||||||
failureFlash: 'Invalid email or password.'
|
|
||||||
})(req, res, next)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
// logout
|
|
||||||
app.get('/logout', function (req, res) {
|
|
||||||
if (config.debug && req.isAuthenticated()) { logger.info('user logout: ' + req.user.id) }
|
|
||||||
req.logout()
|
|
||||||
res.redirect(config.serverurl + '/')
|
|
||||||
})
|
|
||||||
var history = require('./lib/history.js')
|
|
||||||
// get history
|
|
||||||
app.get('/history', history.historyGet)
|
|
||||||
// post history
|
|
||||||
app.post('/history', urlencodedParser, history.historyPost)
|
|
||||||
// post history by note id
|
|
||||||
app.post('/history/:noteId', urlencodedParser, history.historyPost)
|
|
||||||
// delete history
|
|
||||||
app.delete('/history', history.historyDelete)
|
|
||||||
// delete history by note id
|
|
||||||
app.delete('/history/:noteId', history.historyDelete)
|
|
||||||
// get me info
|
|
||||||
app.get('/me', function (req, res) {
|
|
||||||
if (req.isAuthenticated()) {
|
|
||||||
models.User.findOne({
|
|
||||||
where: {
|
|
||||||
id: req.user.id
|
|
||||||
}
|
|
||||||
}).then(function (user) {
|
|
||||||
if (!user) { return response.errorNotFound(res) }
|
|
||||||
var profile = models.User.getProfile(user)
|
|
||||||
res.send({
|
|
||||||
status: 'ok',
|
|
||||||
id: req.user.id,
|
|
||||||
name: profile.name,
|
|
||||||
photo: profile.photo
|
|
||||||
})
|
|
||||||
}).catch(function (err) {
|
|
||||||
logger.error('read me failed: ' + err)
|
|
||||||
return response.errorInternalError(res)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
res.send({
|
|
||||||
status: 'forbidden'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// upload image
|
|
||||||
app.post('/uploadimage', function (req, res) {
|
|
||||||
var form = new formidable.IncomingForm()
|
|
||||||
|
|
||||||
form.keepExtensions = true
|
|
||||||
|
|
||||||
if (config.imageUploadType === 'filesystem') {
|
|
||||||
form.uploadDir = 'public/uploads'
|
|
||||||
}
|
|
||||||
|
|
||||||
form.parse(req, function (err, fields, files) {
|
|
||||||
if (err || !files.image || !files.image.path) {
|
|
||||||
response.errorForbidden(res)
|
|
||||||
} else {
|
|
||||||
if (config.debug) { logger.info('SERVER received uploadimage: ' + JSON.stringify(files.image)) }
|
|
||||||
|
|
||||||
try {
|
|
||||||
switch (config.imageUploadType) {
|
|
||||||
case 'filesystem':
|
|
||||||
res.send({
|
|
||||||
link: url.resolve(config.serverurl + '/', files.image.path.match(/^public\/(.+$)/)[1])
|
|
||||||
})
|
|
||||||
|
|
||||||
break
|
|
||||||
|
|
||||||
case 's3':
|
|
||||||
var AWS = require('aws-sdk')
|
|
||||||
var awsConfig = new AWS.Config(config.s3)
|
|
||||||
var s3 = new AWS.S3(awsConfig)
|
|
||||||
|
|
||||||
fs.readFile(files.image.path, function (err, buffer) {
|
|
||||||
if (err) {
|
|
||||||
logger.error(err)
|
|
||||||
res.status(500).end('upload image error')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var params = {
|
|
||||||
Bucket: config.s3bucket,
|
|
||||||
Key: path.join('uploads', path.basename(files.image.path)),
|
|
||||||
Body: buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
var mimeType = getImageMimeType(files.image.path)
|
|
||||||
if (mimeType) { params.ContentType = mimeType }
|
|
||||||
|
|
||||||
s3.putObject(params, function (err, data) {
|
|
||||||
if (err) {
|
|
||||||
logger.error(err)
|
|
||||||
res.status(500).end('upload image error')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
res.send({
|
|
||||||
link: `https://s3-${config.s3.region}.amazonaws.com/${config.s3bucket}/${params.Key}`
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
break
|
|
||||||
case 'imgur':
|
|
||||||
default:
|
|
||||||
imgur.setClientId(config.imgur.clientID)
|
|
||||||
imgur.uploadFile(files.image.path)
|
|
||||||
.then(function (json) {
|
|
||||||
if (config.debug) { logger.info('SERVER uploadimage success: ' + JSON.stringify(json)) }
|
|
||||||
res.send({
|
|
||||||
link: json.data.link.replace(/^http:\/\//i, 'https://')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.catch(function (err) {
|
|
||||||
logger.error(err)
|
|
||||||
return res.status(500).end('upload image error')
|
|
||||||
})
|
|
||||||
break
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
logger.error(err)
|
|
||||||
return res.status(500).end('upload image error')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
// get new note
|
|
||||||
app.get('/new', response.newNote)
|
|
||||||
// get publish note
|
|
||||||
app.get('/s/:shortid', response.showPublishNote)
|
|
||||||
// publish note actions
|
|
||||||
app.get('/s/:shortid/:action', response.publishNoteActions)
|
|
||||||
// get publish slide
|
|
||||||
app.get('/p/:shortid', response.showPublishSlide)
|
|
||||||
// publish slide actions
|
|
||||||
app.get('/p/:shortid/:action', response.publishSlideActions)
|
|
||||||
// get note by id
|
|
||||||
app.get('/:noteId', response.showNote)
|
|
||||||
// note actions
|
|
||||||
app.get('/:noteId/:action', response.noteActions)
|
|
||||||
// note actions with action id
|
|
||||||
app.get('/:noteId/:action/:actionId', response.noteActions)
|
|
||||||
// response not found if no any route matches
|
|
||||||
app.get('*', function (req, res) {
|
app.get('*', function (req, res) {
|
||||||
response.errorNotFound(res)
|
response.errorNotFound(res)
|
||||||
})
|
})
|
||||||
|
@ -632,7 +197,7 @@ function startListen () {
|
||||||
server.listen(config.port, function () {
|
server.listen(config.port, function () {
|
||||||
var schema = config.usessl ? 'HTTPS' : 'HTTP'
|
var schema = config.usessl ? 'HTTPS' : 'HTTP'
|
||||||
logger.info('%s Server listening at port %d', schema, config.port)
|
logger.info('%s Server listening at port %d', schema, config.port)
|
||||||
config.maintenance = false
|
realtime.maintenance = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -660,7 +225,7 @@ process.on('uncaughtException', function (err) {
|
||||||
// install exit handler
|
// install exit handler
|
||||||
function handleTermSignals () {
|
function handleTermSignals () {
|
||||||
logger.info('hackmd has been killed by signal, try to exit gracefully...')
|
logger.info('hackmd has been killed by signal, try to exit gracefully...')
|
||||||
config.maintenance = true
|
realtime.maintenance = true
|
||||||
// disconnect all socket.io clients
|
// disconnect all socket.io clients
|
||||||
Object.keys(io.sockets.sockets).forEach(function (key) {
|
Object.keys(io.sockets.sockets).forEach(function (key) {
|
||||||
var socket = io.sockets.sockets[key]
|
var socket = io.sockets.sockets[key]
|
||||||
|
|
194
lib/auth.js
194
lib/auth.js
|
@ -1,194 +0,0 @@
|
||||||
'use strict'
|
|
||||||
// auth
|
|
||||||
// external modules
|
|
||||||
var passport = require('passport')
|
|
||||||
var FacebookStrategy = require('passport-facebook').Strategy
|
|
||||||
var TwitterStrategy = require('passport-twitter').Strategy
|
|
||||||
var GithubStrategy = require('passport-github').Strategy
|
|
||||||
var GitlabStrategy = require('passport-gitlab2').Strategy
|
|
||||||
var DropboxStrategy = require('passport-dropbox-oauth2').Strategy
|
|
||||||
var GoogleStrategy = require('passport-google-oauth20').Strategy
|
|
||||||
var LdapStrategy = require('passport-ldapauth')
|
|
||||||
var LocalStrategy = require('passport-local').Strategy
|
|
||||||
var validator = require('validator')
|
|
||||||
|
|
||||||
// core
|
|
||||||
var config = require('./config.js')
|
|
||||||
var logger = require('./logger.js')
|
|
||||||
var models = require('./models')
|
|
||||||
|
|
||||||
function callback (accessToken, refreshToken, profile, done) {
|
|
||||||
// logger.info(profile.displayName || profile.username);
|
|
||||||
var stringifiedProfile = JSON.stringify(profile)
|
|
||||||
models.User.findOrCreate({
|
|
||||||
where: {
|
|
||||||
profileid: profile.id.toString()
|
|
||||||
},
|
|
||||||
defaults: {
|
|
||||||
profile: stringifiedProfile,
|
|
||||||
accessToken: accessToken,
|
|
||||||
refreshToken: refreshToken
|
|
||||||
}
|
|
||||||
}).spread(function (user, created) {
|
|
||||||
if (user) {
|
|
||||||
var needSave = false
|
|
||||||
if (user.profile !== stringifiedProfile) {
|
|
||||||
user.profile = stringifiedProfile
|
|
||||||
needSave = true
|
|
||||||
}
|
|
||||||
if (user.accessToken !== accessToken) {
|
|
||||||
user.accessToken = accessToken
|
|
||||||
needSave = true
|
|
||||||
}
|
|
||||||
if (user.refreshToken !== refreshToken) {
|
|
||||||
user.refreshToken = refreshToken
|
|
||||||
needSave = true
|
|
||||||
}
|
|
||||||
if (needSave) {
|
|
||||||
user.save().then(function () {
|
|
||||||
if (config.debug) { logger.info('user login: ' + user.id) }
|
|
||||||
return done(null, user)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
if (config.debug) { logger.info('user login: ' + user.id) }
|
|
||||||
return done(null, user)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).catch(function (err) {
|
|
||||||
logger.error('auth callback failed: ' + err)
|
|
||||||
return done(err, null)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function registerAuthMethod () {
|
|
||||||
// facebook
|
|
||||||
if (config.facebook) {
|
|
||||||
passport.use(new FacebookStrategy({
|
|
||||||
clientID: config.facebook.clientID,
|
|
||||||
clientSecret: config.facebook.clientSecret,
|
|
||||||
callbackURL: config.serverurl + '/auth/facebook/callback'
|
|
||||||
}, callback))
|
|
||||||
}
|
|
||||||
// twitter
|
|
||||||
if (config.twitter) {
|
|
||||||
passport.use(new TwitterStrategy({
|
|
||||||
consumerKey: config.twitter.consumerKey,
|
|
||||||
consumerSecret: config.twitter.consumerSecret,
|
|
||||||
callbackURL: config.serverurl + '/auth/twitter/callback'
|
|
||||||
}, callback))
|
|
||||||
}
|
|
||||||
// github
|
|
||||||
if (config.github) {
|
|
||||||
passport.use(new GithubStrategy({
|
|
||||||
clientID: config.github.clientID,
|
|
||||||
clientSecret: config.github.clientSecret,
|
|
||||||
callbackURL: config.serverurl + '/auth/github/callback'
|
|
||||||
}, callback))
|
|
||||||
}
|
|
||||||
// gitlab
|
|
||||||
if (config.gitlab) {
|
|
||||||
passport.use(new GitlabStrategy({
|
|
||||||
baseURL: config.gitlab.baseURL,
|
|
||||||
clientID: config.gitlab.clientID,
|
|
||||||
clientSecret: config.gitlab.clientSecret,
|
|
||||||
scope: config.gitlab.scope,
|
|
||||||
callbackURL: config.serverurl + '/auth/gitlab/callback'
|
|
||||||
}, callback))
|
|
||||||
}
|
|
||||||
// dropbox
|
|
||||||
if (config.dropbox) {
|
|
||||||
passport.use(new DropboxStrategy({
|
|
||||||
apiVersion: '2',
|
|
||||||
clientID: config.dropbox.clientID,
|
|
||||||
clientSecret: config.dropbox.clientSecret,
|
|
||||||
callbackURL: config.serverurl + '/auth/dropbox/callback'
|
|
||||||
}, callback))
|
|
||||||
}
|
|
||||||
// google
|
|
||||||
if (config.google) {
|
|
||||||
passport.use(new GoogleStrategy({
|
|
||||||
clientID: config.google.clientID,
|
|
||||||
clientSecret: config.google.clientSecret,
|
|
||||||
callbackURL: config.serverurl + '/auth/google/callback'
|
|
||||||
}, callback))
|
|
||||||
}
|
|
||||||
// ldap
|
|
||||||
if (config.ldap) {
|
|
||||||
passport.use(new LdapStrategy({
|
|
||||||
server: {
|
|
||||||
url: config.ldap.url || null,
|
|
||||||
bindDn: config.ldap.bindDn || null,
|
|
||||||
bindCredentials: config.ldap.bindCredentials || null,
|
|
||||||
searchBase: config.ldap.searchBase || null,
|
|
||||||
searchFilter: config.ldap.searchFilter || null,
|
|
||||||
searchAttributes: config.ldap.searchAttributes || null,
|
|
||||||
tlsOptions: config.ldap.tlsOptions || null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
function (user, done) {
|
|
||||||
var profile = {
|
|
||||||
id: 'LDAP-' + user.uidNumber,
|
|
||||||
username: user.uid,
|
|
||||||
displayName: user.displayName,
|
|
||||||
emails: user.mail ? [user.mail] : [],
|
|
||||||
avatarUrl: null,
|
|
||||||
profileUrl: null,
|
|
||||||
provider: 'ldap'
|
|
||||||
}
|
|
||||||
var stringifiedProfile = JSON.stringify(profile)
|
|
||||||
models.User.findOrCreate({
|
|
||||||
where: {
|
|
||||||
profileid: profile.id.toString()
|
|
||||||
},
|
|
||||||
defaults: {
|
|
||||||
profile: stringifiedProfile
|
|
||||||
}
|
|
||||||
}).spread(function (user, created) {
|
|
||||||
if (user) {
|
|
||||||
var needSave = false
|
|
||||||
if (user.profile !== stringifiedProfile) {
|
|
||||||
user.profile = stringifiedProfile
|
|
||||||
needSave = true
|
|
||||||
}
|
|
||||||
if (needSave) {
|
|
||||||
user.save().then(function () {
|
|
||||||
if (config.debug) { logger.info('user login: ' + user.id) }
|
|
||||||
return done(null, user)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
if (config.debug) { logger.info('user login: ' + user.id) }
|
|
||||||
return done(null, user)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).catch(function (err) {
|
|
||||||
logger.error('ldap auth failed: ' + err)
|
|
||||||
return done(err, null)
|
|
||||||
})
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
// email
|
|
||||||
if (config.email) {
|
|
||||||
passport.use(new LocalStrategy({
|
|
||||||
usernameField: 'email'
|
|
||||||
},
|
|
||||||
function (email, password, done) {
|
|
||||||
if (!validator.isEmail(email)) return done(null, false)
|
|
||||||
models.User.findOne({
|
|
||||||
where: {
|
|
||||||
email: email
|
|
||||||
}
|
|
||||||
}).then(function (user) {
|
|
||||||
if (!user) return done(null, false)
|
|
||||||
if (!user.verifyPassword(password)) return done(null, false)
|
|
||||||
return done(null, user)
|
|
||||||
}).catch(function (err) {
|
|
||||||
logger.error(err)
|
|
||||||
return done(err)
|
|
||||||
})
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
registerAuthMethod: registerAuthMethod
|
|
||||||
}
|
|
223
lib/config.js
223
lib/config.js
|
@ -1,223 +0,0 @@
|
||||||
'use strict'
|
|
||||||
// external modules
|
|
||||||
var fs = require('fs')
|
|
||||||
var path = require('path')
|
|
||||||
|
|
||||||
// configs
|
|
||||||
var env = process.env.NODE_ENV || 'development'
|
|
||||||
var config = require(path.join(__dirname, '..', 'config.json'))[env]
|
|
||||||
var debug = process.env.DEBUG ? (process.env.DEBUG === 'true') : ((typeof config.debug === 'boolean') ? config.debug : (env === 'development'))
|
|
||||||
|
|
||||||
// Create function that reads docker secrets but fails fast in case of a non docker environment
|
|
||||||
var handleDockerSecret = fs.existsSync('/run/secrets/') ? function (secret) {
|
|
||||||
return fs.existsSync('/run/secrets/' + secret) ? fs.readFileSync('/run/secrets/' + secret) : null
|
|
||||||
} : function () {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
// url
|
|
||||||
var domain = process.env.DOMAIN || process.env.HMD_DOMAIN || config.domain || ''
|
|
||||||
var urlpath = process.env.URL_PATH || process.env.HMD_URL_PATH || config.urlpath || ''
|
|
||||||
var port = process.env.PORT || process.env.HMD_PORT || config.port || 3000
|
|
||||||
var alloworigin = process.env.HMD_ALLOW_ORIGIN ? process.env.HMD_ALLOW_ORIGIN.split(',') : (config.alloworigin || ['localhost'])
|
|
||||||
|
|
||||||
var usessl = !!config.usessl
|
|
||||||
var protocolusessl = (usessl === true && typeof process.env.HMD_PROTOCOL_USESSL === 'undefined' && typeof config.protocolusessl === 'undefined')
|
|
||||||
? true : (process.env.HMD_PROTOCOL_USESSL ? (process.env.HMD_PROTOCOL_USESSL === 'true') : !!config.protocolusessl)
|
|
||||||
var urladdport = process.env.HMD_URL_ADDPORT ? (process.env.HMD_URL_ADDPORT === 'true') : !!config.urladdport
|
|
||||||
|
|
||||||
var usecdn = process.env.HMD_USECDN ? (process.env.HMD_USECDN === 'true') : ((typeof config.usecdn === 'boolean') ? config.usecdn : true)
|
|
||||||
|
|
||||||
var allowanonymous = process.env.HMD_ALLOW_ANONYMOUS ? (process.env.HMD_ALLOW_ANONYMOUS === 'true') : ((typeof config.allowanonymous === 'boolean') ? config.allowanonymous : true)
|
|
||||||
|
|
||||||
var allowfreeurl = process.env.HMD_ALLOW_FREEURL ? (process.env.HMD_ALLOW_FREEURL === 'true') : !!config.allowfreeurl
|
|
||||||
|
|
||||||
var permissions = ['editable', 'limited', 'locked', 'protected', 'private']
|
|
||||||
if (allowanonymous) {
|
|
||||||
permissions.unshift('freely')
|
|
||||||
}
|
|
||||||
|
|
||||||
var defaultpermission = process.env.HMD_DEFAULT_PERMISSION || config.defaultpermission
|
|
||||||
defaultpermission = permissions.indexOf(defaultpermission) !== -1 ? defaultpermission : 'editable'
|
|
||||||
|
|
||||||
// db
|
|
||||||
var dburl = process.env.HMD_DB_URL || process.env.DATABASE_URL || config.dburl
|
|
||||||
var db = config.db || {}
|
|
||||||
|
|
||||||
// ssl path
|
|
||||||
var sslkeypath = (fs.existsSync('/run/secrets/key.pem') ? '/run/secrets/key.pem' : null) || config.sslkeypath || ''
|
|
||||||
var sslcertpath = (fs.existsSync('/run/secrets/cert.pem') ? '/run/secrets/cert.pem' : null) || config.sslcertpath || ''
|
|
||||||
var sslcapath = (fs.existsSync('/run/secrets/ca.pem') ? '/run/secrets/ca.pem' : null) || config.sslcapath || ''
|
|
||||||
var dhparampath = (fs.existsSync('/run/secrets/dhparam.pem') ? '/run/secrets/dhparam.pem' : null) || config.dhparampath || ''
|
|
||||||
|
|
||||||
// other path
|
|
||||||
var tmppath = config.tmppath || './tmp'
|
|
||||||
var defaultnotepath = config.defaultnotepath || './public/default.md'
|
|
||||||
var docspath = config.docspath || './public/docs'
|
|
||||||
var indexpath = config.indexpath || './public/views/index.ejs'
|
|
||||||
var hackmdpath = config.hackmdpath || './public/views/hackmd.ejs'
|
|
||||||
var errorpath = config.errorpath || './public/views/error.ejs'
|
|
||||||
var prettypath = config.prettypath || './public/views/pretty.ejs'
|
|
||||||
var slidepath = config.slidepath || './public/views/slide.ejs'
|
|
||||||
|
|
||||||
// session
|
|
||||||
var sessionname = config.sessionname || 'connect.sid'
|
|
||||||
var sessionsecret = handleDockerSecret('sessionsecret') || config.sessionsecret || 'secret'
|
|
||||||
var sessionlife = config.sessionlife || 14 * 24 * 60 * 60 * 1000 // 14 days
|
|
||||||
|
|
||||||
// static files
|
|
||||||
var staticcachetime = config.staticcachetime || 1 * 24 * 60 * 60 * 1000 // 1 day
|
|
||||||
|
|
||||||
// socket.io
|
|
||||||
var heartbeatinterval = config.heartbeatinterval || 5000
|
|
||||||
var heartbeattimeout = config.heartbeattimeout || 10000
|
|
||||||
|
|
||||||
// document
|
|
||||||
var documentmaxlength = config.documentmaxlength || 100000
|
|
||||||
|
|
||||||
// image upload setting, available options are imgur/s3/filesystem
|
|
||||||
var imageUploadType = process.env.HMD_IMAGE_UPLOAD_TYPE || config.imageUploadType || 'imgur'
|
|
||||||
|
|
||||||
config.s3 = config.s3 || {}
|
|
||||||
var s3 = {
|
|
||||||
accessKeyId: handleDockerSecret('s3_acccessKeyId') || process.env.HMD_S3_ACCESS_KEY_ID || config.s3.accessKeyId,
|
|
||||||
secretAccessKey: handleDockerSecret('s3_secretAccessKey') || process.env.HMD_S3_SECRET_ACCESS_KEY || config.s3.secretAccessKey,
|
|
||||||
region: process.env.HMD_S3_REGION || config.s3.region
|
|
||||||
}
|
|
||||||
var s3bucket = process.env.HMD_S3_BUCKET || config.s3.bucket
|
|
||||||
|
|
||||||
// auth
|
|
||||||
var facebook = ((process.env.HMD_FACEBOOK_CLIENTID && process.env.HMD_FACEBOOK_CLIENTSECRET) || (fs.existsSync('/run/secrets/facebook_clientID') && fs.existsSync('/run/secrets/facebook_clientSecret'))) ? {
|
|
||||||
clientID: handleDockerSecret('facebook_clientID') || process.env.HMD_FACEBOOK_CLIENTID,
|
|
||||||
clientSecret: handleDockerSecret('facebook_clientSecret') || process.env.HMD_FACEBOOK_CLIENTSECRET
|
|
||||||
} : config.facebook || false
|
|
||||||
var twitter = ((process.env.HMD_TWITTER_CONSUMERKEY && process.env.HMD_TWITTER_CONSUMERSECRET) || (fs.existsSync('/run/secrets/twitter_consumerKey') && fs.existsSync('/run/secrets/twitter_consumerSecret'))) ? {
|
|
||||||
consumerKey: handleDockerSecret('twitter_consumerKey') || process.env.HMD_TWITTER_CONSUMERKEY,
|
|
||||||
consumerSecret: handleDockerSecret('twitter_consumerSecret') || process.env.HMD_TWITTER_CONSUMERSECRET
|
|
||||||
} : config.twitter || false
|
|
||||||
var github = ((process.env.HMD_GITHUB_CLIENTID && process.env.HMD_GITHUB_CLIENTSECRET) || (fs.existsSync('/run/secrets/github_clientID') && fs.existsSync('/run/secrets/github_clientSecret'))) ? {
|
|
||||||
clientID: handleDockerSecret('github_clientID') || process.env.HMD_GITHUB_CLIENTID,
|
|
||||||
clientSecret: handleDockerSecret('github_clientSecret') || process.env.HMD_GITHUB_CLIENTSECRET
|
|
||||||
} : config.github || false
|
|
||||||
var gitlab = ((process.env.HMD_GITLAB_CLIENTID && process.env.HMD_GITLAB_CLIENTSECRET) || (fs.existsSync('/run/secrets/gitlab_clientID') && fs.existsSync('/run/secrets/gitlab_clientSecret'))) ? {
|
|
||||||
baseURL: process.env.HMD_GITLAB_BASEURL,
|
|
||||||
clientID: handleDockerSecret('gitlab_clientID') || process.env.HMD_GITLAB_CLIENTID,
|
|
||||||
clientSecret: handleDockerSecret('gitlab_clientSecret') || process.env.HMD_GITLAB_CLIENTSECRET,
|
|
||||||
scope: process.env.HMD_GITLAB_SCOPE
|
|
||||||
} : (config.gitlab && config.gitlab.clientID && config.gitlab.clientSecret && config.gitlab) || false
|
|
||||||
var dropbox = ((process.env.HMD_DROPBOX_CLIENTID && process.env.HMD_DROPBOX_CLIENTSECRET) || (fs.existsSync('/run/secrets/dropbox_clientID') && fs.existsSync('/run/secrets/dropbox_clientSecret'))) ? {
|
|
||||||
clientID: handleDockerSecret('dropbox_clientID') || process.env.HMD_DROPBOX_CLIENTID,
|
|
||||||
clientSecret: handleDockerSecret('dropbox_clientSecret') || process.env.HMD_DROPBOX_CLIENTSECRET
|
|
||||||
} : (config.dropbox && config.dropbox.clientID && config.dropbox.clientSecret && config.dropbox) || false
|
|
||||||
var google = ((process.env.HMD_GOOGLE_CLIENTID && process.env.HMD_GOOGLE_CLIENTSECRET) ||
|
|
||||||
(fs.existsSync('/run/secrets/google_clientID') && fs.existsSync('/run/secrets/google_clientSecret'))) ? {
|
|
||||||
clientID: handleDockerSecret('google_clientID') || process.env.HMD_GOOGLE_CLIENTID,
|
|
||||||
clientSecret: handleDockerSecret('google_clientSecret') || process.env.HMD_GOOGLE_CLIENTSECRET
|
|
||||||
} : (config.google && config.google.clientID && config.google.clientSecret && config.google) || false
|
|
||||||
var ldap = config.ldap || ((
|
|
||||||
process.env.HMD_LDAP_URL ||
|
|
||||||
process.env.HMD_LDAP_BINDDN ||
|
|
||||||
process.env.HMD_LDAP_BINDCREDENTIALS ||
|
|
||||||
process.env.HMD_LDAP_TOKENSECRET ||
|
|
||||||
process.env.HMD_LDAP_SEARCHBASE ||
|
|
||||||
process.env.HMD_LDAP_SEARCHFILTER ||
|
|
||||||
process.env.HMD_LDAP_SEARCHATTRIBUTES ||
|
|
||||||
process.env.HMD_LDAP_TLS_CA ||
|
|
||||||
process.env.HMD_LDAP_PROVIDERNAME
|
|
||||||
) ? {} : false)
|
|
||||||
if (process.env.HMD_LDAP_URL) { ldap.url = process.env.HMD_LDAP_URL }
|
|
||||||
if (process.env.HMD_LDAP_BINDDN) { ldap.bindDn = process.env.HMD_LDAP_BINDDN }
|
|
||||||
if (process.env.HMD_LDAP_BINDCREDENTIALS) { ldap.bindCredentials = process.env.HMD_LDAP_BINDCREDENTIALS }
|
|
||||||
if (process.env.HMD_LDAP_TOKENSECRET) { ldap.tokenSecret = process.env.HMD_LDAP_TOKENSECRET }
|
|
||||||
if (process.env.HMD_LDAP_SEARCHBASE) { ldap.searchBase = process.env.HMD_LDAP_SEARCHBASE }
|
|
||||||
if (process.env.HMD_LDAP_SEARCHFILTER) { ldap.searchFilter = process.env.HMD_LDAP_SEARCHFILTER }
|
|
||||||
if (process.env.HMD_LDAP_SEARCHATTRIBUTES) { ldap.searchAttributes = process.env.HMD_LDAP_SEARCHATTRIBUTES }
|
|
||||||
if (process.env.HMD_LDAP_TLS_CA) {
|
|
||||||
var ca = {
|
|
||||||
ca: process.env.HMD_LDAP_TLS_CA.split(',')
|
|
||||||
}
|
|
||||||
ldap.tlsOptions = ldap.tlsOptions ? Object.assign(ldap.tlsOptions, ca) : ca
|
|
||||||
if (Array.isArray(ldap.tlsOptions.ca) && ldap.tlsOptions.ca.length > 0) {
|
|
||||||
var i, len, results
|
|
||||||
results = []
|
|
||||||
for (i = 0, len = ldap.tlsOptions.ca.length; i < len; i++) {
|
|
||||||
results.push(fs.readFileSync(ldap.tlsOptions.ca[i], 'utf8'))
|
|
||||||
}
|
|
||||||
ldap.tlsOptions.ca = results
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (process.env.HMD_LDAP_PROVIDERNAME) {
|
|
||||||
ldap.providerName = process.env.HMD_LDAP_PROVIDERNAME
|
|
||||||
}
|
|
||||||
var imgur = handleDockerSecret('imgur_clientid') || process.env.HMD_IMGUR_CLIENTID || config.imgur || false
|
|
||||||
var email = process.env.HMD_EMAIL ? (process.env.HMD_EMAIL === 'true') : !!config.email
|
|
||||||
var allowemailregister = process.env.HMD_ALLOW_EMAIL_REGISTER ? (process.env.HMD_ALLOW_EMAIL_REGISTER === 'true') : ((typeof config.allowemailregister === 'boolean') ? config.allowemailregister : true)
|
|
||||||
|
|
||||||
function getserverurl () {
|
|
||||||
var url = ''
|
|
||||||
if (domain) {
|
|
||||||
var protocol = protocolusessl ? 'https://' : 'http://'
|
|
||||||
url = protocol + domain
|
|
||||||
if (urladdport && ((usessl && port !== 443) || (!usessl && port !== 80))) { url += ':' + port }
|
|
||||||
}
|
|
||||||
if (urlpath) { url += '/' + urlpath }
|
|
||||||
return url
|
|
||||||
}
|
|
||||||
|
|
||||||
var version = '0.5.1'
|
|
||||||
var minimumCompatibleVersion = '0.5.0'
|
|
||||||
var maintenance = true
|
|
||||||
var cwd = path.join(__dirname, '..')
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
raw: config,
|
|
||||||
handleDockerSecret: handleDockerSecret,
|
|
||||||
version: version,
|
|
||||||
minimumCompatibleVersion: minimumCompatibleVersion,
|
|
||||||
maintenance: maintenance,
|
|
||||||
domain: domain,
|
|
||||||
urlpath: urlpath,
|
|
||||||
debug: debug,
|
|
||||||
port: port,
|
|
||||||
alloworigin: alloworigin,
|
|
||||||
usessl: usessl,
|
|
||||||
serverurl: getserverurl(),
|
|
||||||
usecdn: usecdn,
|
|
||||||
allowanonymous: allowanonymous,
|
|
||||||
allowfreeurl: allowfreeurl,
|
|
||||||
defaultpermission: defaultpermission,
|
|
||||||
dburl: dburl,
|
|
||||||
db: db,
|
|
||||||
sslkeypath: path.join(cwd, sslkeypath),
|
|
||||||
sslcertpath: path.join(cwd, sslcertpath),
|
|
||||||
sslcapath: path.join(cwd, sslcapath),
|
|
||||||
dhparampath: path.join(cwd, dhparampath),
|
|
||||||
tmppath: path.join(cwd, tmppath),
|
|
||||||
defaultnotepath: path.join(cwd, defaultnotepath),
|
|
||||||
docspath: path.join(cwd, docspath),
|
|
||||||
indexpath: path.join(cwd, indexpath),
|
|
||||||
hackmdpath: path.join(cwd, hackmdpath),
|
|
||||||
errorpath: path.join(cwd, errorpath),
|
|
||||||
prettypath: path.join(cwd, prettypath),
|
|
||||||
slidepath: path.join(cwd, slidepath),
|
|
||||||
sessionname: sessionname,
|
|
||||||
sessionsecret: sessionsecret,
|
|
||||||
sessionlife: sessionlife,
|
|
||||||
staticcachetime: staticcachetime,
|
|
||||||
heartbeatinterval: heartbeatinterval,
|
|
||||||
heartbeattimeout: heartbeattimeout,
|
|
||||||
documentmaxlength: documentmaxlength,
|
|
||||||
facebook: facebook,
|
|
||||||
twitter: twitter,
|
|
||||||
github: github,
|
|
||||||
gitlab: gitlab,
|
|
||||||
dropbox: dropbox,
|
|
||||||
google: google,
|
|
||||||
ldap: ldap,
|
|
||||||
imgur: imgur,
|
|
||||||
email: email,
|
|
||||||
allowemailregister: allowemailregister,
|
|
||||||
imageUploadType: imageUploadType,
|
|
||||||
s3: s3,
|
|
||||||
s3bucket: s3bucket
|
|
||||||
}
|
|
92
lib/config/default.js
Normal file
92
lib/config/default.js
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
domain: '',
|
||||||
|
urlpath: '',
|
||||||
|
port: 3000,
|
||||||
|
urladdport: false,
|
||||||
|
alloworigin: ['localhost'],
|
||||||
|
usessl: false,
|
||||||
|
protocolusessl: false,
|
||||||
|
usecdn: true,
|
||||||
|
allowanonymous: true,
|
||||||
|
allowfreeurl: false,
|
||||||
|
defaultpermission: 'editable',
|
||||||
|
dburl: '',
|
||||||
|
db: {},
|
||||||
|
// ssl path
|
||||||
|
sslkeypath: '',
|
||||||
|
sslcertpath: '',
|
||||||
|
sslcapath: '',
|
||||||
|
dhparampath: '',
|
||||||
|
// other path
|
||||||
|
tmppath: './tmp',
|
||||||
|
defaultnotepath: './public/default.md',
|
||||||
|
docspath: './public/docs',
|
||||||
|
indexpath: './public/views/index.ejs',
|
||||||
|
hackmdpath: './public/views/hackmd.ejs',
|
||||||
|
errorpath: './public/views/error.ejs',
|
||||||
|
prettypath: './public/views/pretty.ejs',
|
||||||
|
slidepath: './public/views/slide.ejs',
|
||||||
|
// session
|
||||||
|
sessionname: 'connect.sid',
|
||||||
|
sessionsecret: 'secret',
|
||||||
|
sessionlife: 14 * 24 * 60 * 60 * 1000, // 14 days
|
||||||
|
staticcachetime: 1 * 24 * 60 * 60 * 1000, // 1 day
|
||||||
|
// socket.io
|
||||||
|
heartbeatinterval: 5000,
|
||||||
|
heartbeattimeout: 10000,
|
||||||
|
// document
|
||||||
|
documentmaxlength: 100000,
|
||||||
|
// image upload setting, available options are imgur/s3/filesystem
|
||||||
|
imageUploadType: 'filesystem',
|
||||||
|
imgur: {
|
||||||
|
clientID: undefined
|
||||||
|
},
|
||||||
|
s3: {
|
||||||
|
accessKeyId: undefined,
|
||||||
|
secretAccessKey: undefined,
|
||||||
|
region: undefined
|
||||||
|
},
|
||||||
|
s3bucket: undefined,
|
||||||
|
// authentication
|
||||||
|
facebook: {
|
||||||
|
clientID: undefined,
|
||||||
|
clientSecret: undefined
|
||||||
|
},
|
||||||
|
twitter: {
|
||||||
|
consumerKey: undefined,
|
||||||
|
consumerSecret: undefined
|
||||||
|
},
|
||||||
|
github: {
|
||||||
|
clientID: undefined,
|
||||||
|
clientSecret: undefined
|
||||||
|
},
|
||||||
|
gitlab: {
|
||||||
|
baseURL: undefined,
|
||||||
|
clientID: undefined,
|
||||||
|
clientSecret: undefined,
|
||||||
|
scope: undefined
|
||||||
|
},
|
||||||
|
dropbox: {
|
||||||
|
clientID: undefined,
|
||||||
|
clientSecret: undefined
|
||||||
|
},
|
||||||
|
google: {
|
||||||
|
clientID: undefined,
|
||||||
|
clientSecret: undefined
|
||||||
|
},
|
||||||
|
ldap: {
|
||||||
|
providerName: undefined,
|
||||||
|
url: undefined,
|
||||||
|
bindDn: undefined,
|
||||||
|
bindCredentials: undefined,
|
||||||
|
tokenSecret: undefined,
|
||||||
|
searchBase: undefined,
|
||||||
|
searchFilter: undefined,
|
||||||
|
searchAttributes: undefined,
|
||||||
|
tlsca: undefined
|
||||||
|
},
|
||||||
|
email: true,
|
||||||
|
allowemailregister: true
|
||||||
|
}
|
17
lib/config/defaultSSL.js
Normal file
17
lib/config/defaultSSL.js
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
const fs = require('fs')
|
||||||
|
|
||||||
|
function getFile (path) {
|
||||||
|
if (fs.existsSync(path)) {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
sslkeypath: getFile('/run/secrets/key.pem'),
|
||||||
|
sslcertpath: getFile('/run/secrets/cert.pem'),
|
||||||
|
sslcapath: getFile('/run/secrets/ca.pem'),
|
||||||
|
dhparampath: getFile('/run/secrets/dhparam.pem')
|
||||||
|
}
|
51
lib/config/dockerSecret.js
Normal file
51
lib/config/dockerSecret.js
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
const fs = require('fs')
|
||||||
|
const path = require('path')
|
||||||
|
|
||||||
|
const basePath = path.resolve('/var/run/secrets/')
|
||||||
|
|
||||||
|
function getSecret (secret) {
|
||||||
|
const filePath = path.join(basePath, secret)
|
||||||
|
if (fs.existsSync(filePath)) return fs.readFileSync(filePath)
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fs.existsSync(basePath)) {
|
||||||
|
module.exports = {
|
||||||
|
sessionsecret: getSecret('sessionsecret'),
|
||||||
|
sslkeypath: getSecret('sslkeypath'),
|
||||||
|
sslcertpath: getSecret('sslcertpath'),
|
||||||
|
sslcapath: getSecret('sslcapath'),
|
||||||
|
dhparampath: getSecret('dhparampath'),
|
||||||
|
s3: {
|
||||||
|
accessKeyId: getSecret('s3_acccessKeyId'),
|
||||||
|
secretAccessKey: getSecret('s3_secretAccessKey')
|
||||||
|
},
|
||||||
|
facebook: {
|
||||||
|
clientID: getSecret('facebook_clientID'),
|
||||||
|
clientSecret: getSecret('facebook_clientSecret')
|
||||||
|
},
|
||||||
|
twitter: {
|
||||||
|
consumerKey: getSecret('twitter_consumerKey'),
|
||||||
|
consumerSecret: getSecret('twitter_consumerSecret')
|
||||||
|
},
|
||||||
|
github: {
|
||||||
|
clientID: getSecret('github_clientID'),
|
||||||
|
clientSecret: getSecret('github_clientSecret')
|
||||||
|
},
|
||||||
|
gitlab: {
|
||||||
|
clientID: getSecret('gitlab_clientID'),
|
||||||
|
clientSecret: getSecret('gitlab_clientSecret')
|
||||||
|
},
|
||||||
|
dropbox: {
|
||||||
|
clientID: getSecret('dropbox_clientID'),
|
||||||
|
clientSecret: getSecret('dropbox_clientSecret')
|
||||||
|
},
|
||||||
|
google: {
|
||||||
|
clientID: getSecret('google_clientID'),
|
||||||
|
clientSecret: getSecret('google_clientSecret')
|
||||||
|
},
|
||||||
|
imgur: getSecret('imgur_clientid')
|
||||||
|
}
|
||||||
|
}
|
16
lib/config/enum.js
Normal file
16
lib/config/enum.js
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
exports.Environment = {
|
||||||
|
development: 'development',
|
||||||
|
production: 'production',
|
||||||
|
test: 'test'
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.Permission = {
|
||||||
|
freely: 'freely',
|
||||||
|
editable: 'editable',
|
||||||
|
limited: 'limited',
|
||||||
|
locked: 'locked',
|
||||||
|
protected: 'protected',
|
||||||
|
private: 'private'
|
||||||
|
}
|
67
lib/config/environment.js
Normal file
67
lib/config/environment.js
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
const {toBooleanConfig} = require('./utils')
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
domain: process.env.HMD_DOMAIN,
|
||||||
|
urlpath: process.env.HMD_URL_PATH,
|
||||||
|
port: process.env.HMD_PORT,
|
||||||
|
urladdport: process.env.HMD_URL_ADDPORT,
|
||||||
|
usessl: toBooleanConfig(process.env.HMD_USESSL),
|
||||||
|
protocolusessl: toBooleanConfig(process.env.HMD_PROTOCOL_USESSL),
|
||||||
|
alloworigin: process.env.HMD_ALLOW_ORIGIN ? process.env.HMD_ALLOW_ORIGIN.split(',') : undefined,
|
||||||
|
usecdn: toBooleanConfig(process.env.HMD_USECDN),
|
||||||
|
allowanonymous: toBooleanConfig(process.env.HMD_ALLOW_ANONYMOUS),
|
||||||
|
allowfreeurl: toBooleanConfig(process.env.HMD_ALLOW_FREEURL),
|
||||||
|
defaultpermission: process.env.HMD_DEFAULT_PERMISSION,
|
||||||
|
dburl: process.env.HMD_DB_URL,
|
||||||
|
imageUploadType: process.env.HMD_IMAGE_UPLOAD_TYPE,
|
||||||
|
imgur: {
|
||||||
|
clientID: process.env.HMD_IMGUR_CLIENTID
|
||||||
|
},
|
||||||
|
s3: {
|
||||||
|
accessKeyId: process.env.HMD_S3_ACCESS_KEY_ID,
|
||||||
|
secretAccessKey: process.env.HMD_S3_SECRET_ACCESS_KEY,
|
||||||
|
region: process.env.HMD_S3_REGION
|
||||||
|
},
|
||||||
|
s3bucket: process.env.HMD_S3_BUCKET,
|
||||||
|
facebook: {
|
||||||
|
clientID: process.env.HMD_FACEBOOK_CLIENTID,
|
||||||
|
clientSecret: process.env.HMD_FACEBOOK_CLIENTSECRET
|
||||||
|
},
|
||||||
|
twitter: {
|
||||||
|
consumerKey: process.env.HMD_TWITTER_CONSUMERKEY,
|
||||||
|
consumerSecret: process.env.HMD_TWITTER_CONSUMERSECRET
|
||||||
|
},
|
||||||
|
github: {
|
||||||
|
clientID: process.env.HMD_GITHUB_CLIENTID,
|
||||||
|
clientSecret: process.env.HMD_GITHUB_CLIENTSECRET
|
||||||
|
},
|
||||||
|
gitlab: {
|
||||||
|
baseURL: process.env.HMD_GITLAB_BASEURL,
|
||||||
|
clientID: process.env.HMD_GITLAB_CLIENTID,
|
||||||
|
clientSecret: process.env.HMD_GITLAB_CLIENTSECRET,
|
||||||
|
scope: process.env.HMD_GITLAB_SCOPE
|
||||||
|
},
|
||||||
|
dropbox: {
|
||||||
|
clientID: process.env.HMD_DROPBOX_CLIENTID,
|
||||||
|
clientSecret: process.env.HMD_DROPBOX_CLIENTSECRET
|
||||||
|
},
|
||||||
|
google: {
|
||||||
|
clientID: process.env.HMD_GOOGLE_CLIENTID,
|
||||||
|
clientSecret: process.env.HMD_GOOGLE_CLIENTSECRET
|
||||||
|
},
|
||||||
|
ldap: {
|
||||||
|
providerName: process.env.HMD_LDAP_PROVIDERNAME,
|
||||||
|
url: process.env.HMD_LDAP_URL,
|
||||||
|
bindDn: process.env.HMD_LDAP_BINDDN,
|
||||||
|
bindCredentials: process.env.HMD_LDAP_BINDCREDENTIALS,
|
||||||
|
tokenSecret: process.env.HMD_LDAP_TOKENSECRET,
|
||||||
|
searchBase: process.env.HMD_LDAP_SEARCHBASE,
|
||||||
|
searchFilter: process.env.HMD_LDAP_SEARCHFILTER,
|
||||||
|
searchAttributes: process.env.HMD_LDAP_SEARCHATTRIBUTES,
|
||||||
|
tlsca: process.env.HMD_LDAP_TLS_CA
|
||||||
|
},
|
||||||
|
email: toBooleanConfig(process.env.HMD_EMAIL),
|
||||||
|
allowemailregister: toBooleanConfig(process.env.HMD_ALLOW_EMAIL_REGISTER)
|
||||||
|
}
|
112
lib/config/index.js
Normal file
112
lib/config/index.js
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
const fs = require('fs')
|
||||||
|
const path = require('path')
|
||||||
|
const {merge} = require('lodash')
|
||||||
|
const deepFreeze = require('deep-freeze')
|
||||||
|
const {Environment, Permission} = require('./enum')
|
||||||
|
|
||||||
|
const appRootPath = path.join(__dirname, '../../')
|
||||||
|
const env = process.env.NODE_ENV || Environment.development
|
||||||
|
const debugConfig = {
|
||||||
|
debug: (env === Environment.development)
|
||||||
|
}
|
||||||
|
|
||||||
|
const packageConfig = {
|
||||||
|
version: '0.5.1',
|
||||||
|
minimumCompatibleVersion: '0.5.0'
|
||||||
|
}
|
||||||
|
|
||||||
|
const configFilePath = path.join(__dirname, '../../config.json')
|
||||||
|
const fileConfig = fs.existsSync(configFilePath) ? require(configFilePath)[env] : undefined
|
||||||
|
|
||||||
|
let config = require('./default')
|
||||||
|
merge(config, require('./defaultSSL'))
|
||||||
|
merge(config, debugConfig)
|
||||||
|
merge(config, packageConfig)
|
||||||
|
merge(config, fileConfig)
|
||||||
|
merge(config, require('./oldEnvironment'))
|
||||||
|
merge(config, require('./environment'))
|
||||||
|
merge(config, require('./dockerSecret'))
|
||||||
|
|
||||||
|
// load LDAP CA
|
||||||
|
if (config.ldap.tlsca) {
|
||||||
|
let ca = config.ldap.tlsca.split(',')
|
||||||
|
let caContent = []
|
||||||
|
for (let i of ca) {
|
||||||
|
if (fs.existsSync(ca[i])) {
|
||||||
|
caContent.push(fs.readFileSync(ca[i], 'utf8'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let tlsOptions = {
|
||||||
|
ca: caContent
|
||||||
|
}
|
||||||
|
config.ldap.tlsOptions = config.ldap.tlsOptions ? Object.assign(config.ldap.tlsOptions, tlsOptions) : tlsOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// Permission
|
||||||
|
config.permission = Permission
|
||||||
|
if (!config.allowanonymous) {
|
||||||
|
delete config.permission.freely
|
||||||
|
}
|
||||||
|
if (!(config.defaultpermission in config.permission)) {
|
||||||
|
config.defaultpermission = config.permission.editable
|
||||||
|
}
|
||||||
|
|
||||||
|
// cache result, cannot change config in runtime!!!
|
||||||
|
config.isStandardHTTPsPort = (function isStandardHTTPsPort () {
|
||||||
|
return config.usessl && config.port === 443
|
||||||
|
})()
|
||||||
|
config.isStandardHTTPPort = (function isStandardHTTPPort () {
|
||||||
|
return !config.usessl && config.port === 80
|
||||||
|
})()
|
||||||
|
|
||||||
|
// cache serverURL
|
||||||
|
config.serverurl = (function getserverurl () {
|
||||||
|
var url = ''
|
||||||
|
if (config.domain) {
|
||||||
|
var protocol = config.protocolusessl ? 'https://' : 'http://'
|
||||||
|
url = protocol + config.domain
|
||||||
|
if (config.urladdport) {
|
||||||
|
if (!config.isStandardHTTPPort || !config.isStandardHTTPsPort) {
|
||||||
|
url += ':' + config.port
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (config.urlpath) {
|
||||||
|
url += '/' + config.urlpath
|
||||||
|
}
|
||||||
|
return url
|
||||||
|
})()
|
||||||
|
|
||||||
|
config.Environment = Environment
|
||||||
|
|
||||||
|
// auth method
|
||||||
|
config.isFacebookEnable = config.facebook.clientID && config.facebook.clientSecret
|
||||||
|
config.isGoogleEnable = config.google.clientID && config.google.clientSecret
|
||||||
|
config.isDropboxEnable = config.dropbox.clientID && config.dropbox.clientSecret
|
||||||
|
config.isTwitterEnable = config.twitter.consumerKey && config.twitter.consumerSecret
|
||||||
|
config.isEmailEnable = config.email
|
||||||
|
config.isGitHubEnable = config.github.clientID && config.github.clientSecret
|
||||||
|
config.isGitLabEnable = config.gitlab.clientID && config.gitlab.clientSecret
|
||||||
|
config.isLDAPEnable = config.ldap.url
|
||||||
|
|
||||||
|
// generate correct path
|
||||||
|
config.sslcapath = path.join(appRootPath, config.sslcapath)
|
||||||
|
config.sslcertpath = path.join(appRootPath, config.sslcertpath)
|
||||||
|
config.sslkeypath = path.join(appRootPath, config.sslkeypath)
|
||||||
|
config.dhparampath = path.join(appRootPath, config.dhparampath)
|
||||||
|
|
||||||
|
config.tmppath = path.join(appRootPath, config.tmppath)
|
||||||
|
config.defaultnotepath = path.join(appRootPath, config.defaultnotepath)
|
||||||
|
config.docspath = path.join(appRootPath, config.docspath)
|
||||||
|
config.indexpath = path.join(appRootPath, config.indexpath)
|
||||||
|
config.hackmdpath = path.join(appRootPath, config.hackmdpath)
|
||||||
|
config.errorpath = path.join(appRootPath, config.errorpath)
|
||||||
|
config.prettypath = path.join(appRootPath, config.prettypath)
|
||||||
|
config.slidepath = path.join(appRootPath, config.slidepath)
|
||||||
|
|
||||||
|
// maek config readonly
|
||||||
|
config = deepFreeze(config)
|
||||||
|
|
||||||
|
module.exports = config
|
10
lib/config/oldEnvironment.js
Normal file
10
lib/config/oldEnvironment.js
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
const {toBooleanConfig} = require('./utils')
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
debug: toBooleanConfig(process.env.DEBUG),
|
||||||
|
dburl: process.env.DATABASE_URL,
|
||||||
|
urlpath: process.env.URL_PATH,
|
||||||
|
port: process.env.PORT
|
||||||
|
}
|
8
lib/config/utils.js
Normal file
8
lib/config/utils.js
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
exports.toBooleanConfig = function toBooleanConfig (configValue) {
|
||||||
|
if (configValue && typeof configValue === 'string') {
|
||||||
|
return (configValue === 'true')
|
||||||
|
}
|
||||||
|
return configValue
|
||||||
|
}
|
|
@ -3,9 +3,9 @@
|
||||||
// external modules
|
// external modules
|
||||||
|
|
||||||
// core
|
// core
|
||||||
var config = require('./config.js')
|
var config = require('./config')
|
||||||
var logger = require('./logger.js')
|
var logger = require('./logger')
|
||||||
var response = require('./response.js')
|
var response = require('./response')
|
||||||
var models = require('./models')
|
var models = require('./models')
|
||||||
|
|
||||||
// public
|
// public
|
||||||
|
|
|
@ -1,23 +1,23 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
var winston = require('winston')
|
const winston = require('winston')
|
||||||
winston.emitErrs = true
|
|
||||||
|
|
||||||
var logger = new winston.Logger({
|
class Logger extends winston.Logger {
|
||||||
|
// Implement stream.writable.write interface
|
||||||
|
write (chunk) {
|
||||||
|
this.info(chunk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = new Logger({
|
||||||
transports: [
|
transports: [
|
||||||
new winston.transports.Console({
|
new winston.transports.Console({
|
||||||
level: 'debug',
|
level: 'debug',
|
||||||
handleExceptions: true,
|
handleExceptions: true,
|
||||||
json: false,
|
json: false,
|
||||||
colorize: true,
|
colorize: false,
|
||||||
timestamp: true
|
timestamp: true
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
|
emitErrs: true,
|
||||||
exitOnError: false
|
exitOnError: false
|
||||||
})
|
})
|
||||||
|
|
||||||
module.exports = logger
|
|
||||||
module.exports.stream = {
|
|
||||||
write: function (message, encoding) {
|
|
||||||
logger.info(message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -3,13 +3,14 @@
|
||||||
var fs = require('fs')
|
var fs = require('fs')
|
||||||
var path = require('path')
|
var path = require('path')
|
||||||
var Sequelize = require('sequelize')
|
var Sequelize = require('sequelize')
|
||||||
|
const {cloneDeep} = require('lodash')
|
||||||
|
|
||||||
// core
|
// core
|
||||||
var config = require('../config.js')
|
var config = require('../config')
|
||||||
var logger = require('../logger.js')
|
var logger = require('../logger')
|
||||||
|
|
||||||
var dbconfig = config.db
|
var dbconfig = cloneDeep(config.db)
|
||||||
dbconfig.logging = config.debug ? logger.info : false
|
dbconfig.logger = config.debug ? logger.info : false
|
||||||
|
|
||||||
var sequelize = null
|
var sequelize = null
|
||||||
|
|
||||||
|
|
|
@ -15,11 +15,11 @@ var dmp = new DiffMatchPatch()
|
||||||
var S = require('string')
|
var S = require('string')
|
||||||
|
|
||||||
// core
|
// core
|
||||||
var config = require('../config.js')
|
var config = require('../config')
|
||||||
var logger = require('../logger.js')
|
var logger = require('../logger')
|
||||||
|
|
||||||
// ot
|
// ot
|
||||||
var ot = require('../ot/index.js')
|
var ot = require('../ot')
|
||||||
|
|
||||||
// permission types
|
// permission types
|
||||||
var permissionTypes = ['freely', 'editable', 'limited', 'locked', 'protected', 'private']
|
var permissionTypes = ['freely', 'editable', 'limited', 'locked', 'protected', 'private']
|
||||||
|
|
|
@ -7,8 +7,8 @@ var childProcess = require('child_process')
|
||||||
var shortId = require('shortid')
|
var shortId = require('shortid')
|
||||||
|
|
||||||
// core
|
// core
|
||||||
var config = require('../config.js')
|
var config = require('../config')
|
||||||
var logger = require('../logger.js')
|
var logger = require('../logger')
|
||||||
|
|
||||||
var dmpWorker = createDmpWorker()
|
var dmpWorker = createDmpWorker()
|
||||||
var dmpCallbackCache = {}
|
var dmpCallbackCache = {}
|
||||||
|
|
|
@ -5,8 +5,8 @@ var Sequelize = require('sequelize')
|
||||||
var scrypt = require('scrypt')
|
var scrypt = require('scrypt')
|
||||||
|
|
||||||
// core
|
// core
|
||||||
var logger = require('../logger.js')
|
var logger = require('../logger')
|
||||||
var letterAvatars = require('../letter-avatars.js')
|
var letterAvatars = require('../letter-avatars')
|
||||||
|
|
||||||
module.exports = function (sequelize, DataTypes) {
|
module.exports = function (sequelize, DataTypes) {
|
||||||
var User = sequelize.define('User', {
|
var User = sequelize.define('User', {
|
||||||
|
|
|
@ -12,13 +12,13 @@ var chance = new Chance()
|
||||||
var moment = require('moment')
|
var moment = require('moment')
|
||||||
|
|
||||||
// core
|
// core
|
||||||
var config = require('./config.js')
|
var config = require('./config')
|
||||||
var logger = require('./logger.js')
|
var logger = require('./logger')
|
||||||
var history = require('./history.js')
|
var history = require('./history')
|
||||||
var models = require('./models')
|
var models = require('./models')
|
||||||
|
|
||||||
// ot
|
// ot
|
||||||
var ot = require('./ot/index.js')
|
var ot = require('./ot')
|
||||||
|
|
||||||
// public
|
// public
|
||||||
var realtime = {
|
var realtime = {
|
||||||
|
@ -28,7 +28,8 @@ var realtime = {
|
||||||
secure: secure,
|
secure: secure,
|
||||||
connection: connection,
|
connection: connection,
|
||||||
getStatus: getStatus,
|
getStatus: getStatus,
|
||||||
isReady: isReady
|
isReady: isReady,
|
||||||
|
maintenance: true
|
||||||
}
|
}
|
||||||
|
|
||||||
function onAuthorizeSuccess (data, accept) {
|
function onAuthorizeSuccess (data, accept) {
|
||||||
|
@ -699,7 +700,7 @@ function updateHistory (userId, note, time) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function connection (socket) {
|
function connection (socket) {
|
||||||
if (config.maintenance) return
|
if (realtime.maintenance) return
|
||||||
parseNoteIdFromSocket(socket, function (err, noteId) {
|
parseNoteIdFromSocket(socket, function (err, noteId) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return failConnection(500, err, socket)
|
return failConnection(500, err, socket)
|
||||||
|
|
|
@ -10,8 +10,8 @@ var request = require('request')
|
||||||
var moment = require('moment')
|
var moment = require('moment')
|
||||||
|
|
||||||
// core
|
// core
|
||||||
var config = require('./config.js')
|
var config = require('./config')
|
||||||
var logger = require('./logger.js')
|
var logger = require('./logger')
|
||||||
var models = require('./models')
|
var models = require('./models')
|
||||||
|
|
||||||
// public
|
// public
|
||||||
|
@ -59,14 +59,14 @@ function showIndex (req, res, next) {
|
||||||
url: config.serverurl,
|
url: config.serverurl,
|
||||||
useCDN: config.usecdn,
|
useCDN: config.usecdn,
|
||||||
allowAnonymous: config.allowanonymous,
|
allowAnonymous: config.allowanonymous,
|
||||||
facebook: config.facebook,
|
facebook: config.isFacebookEnable,
|
||||||
twitter: config.twitter,
|
twitter: config.isTwitterEnable,
|
||||||
github: config.github,
|
github: config.isGitHubEnable,
|
||||||
gitlab: config.gitlab,
|
gitlab: config.isGitLabEnable,
|
||||||
dropbox: config.dropbox,
|
dropbox: config.isDropboxEnable,
|
||||||
google: config.google,
|
google: config.isGoogleEnable,
|
||||||
ldap: config.ldap,
|
ldap: config.isLDAPEnable,
|
||||||
email: config.email,
|
email: config.isEmailEnable,
|
||||||
allowemailregister: config.allowemailregister,
|
allowemailregister: config.allowemailregister,
|
||||||
signin: req.isAuthenticated(),
|
signin: req.isAuthenticated(),
|
||||||
infoMessage: req.flash('info'),
|
infoMessage: req.flash('info'),
|
||||||
|
@ -89,14 +89,14 @@ function responseHackMD (res, note) {
|
||||||
title: title,
|
title: title,
|
||||||
useCDN: config.usecdn,
|
useCDN: config.usecdn,
|
||||||
allowAnonymous: config.allowanonymous,
|
allowAnonymous: config.allowanonymous,
|
||||||
facebook: config.facebook,
|
facebook: config.isFacebookEnable,
|
||||||
twitter: config.twitter,
|
twitter: config.isTwitterEnable,
|
||||||
github: config.github,
|
github: config.isGitHubEnable,
|
||||||
gitlab: config.gitlab,
|
gitlab: config.isGitLabEnable,
|
||||||
dropbox: config.dropbox,
|
dropbox: config.isDropboxEnable,
|
||||||
google: config.google,
|
google: config.isGoogleEnable,
|
||||||
ldap: config.ldap,
|
ldap: config.isLDAPEnable,
|
||||||
email: config.email,
|
email: config.isEmailEnable,
|
||||||
allowemailregister: config.allowemailregister
|
allowemailregister: config.allowemailregister
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
29
lib/web/auth/dropbox/index.js
Normal file
29
lib/web/auth/dropbox/index.js
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
const Router = require('express').Router
|
||||||
|
const passport = require('passport')
|
||||||
|
const DropboxStrategy = require('passport-dropbox-oauth2').Strategy
|
||||||
|
const config = require('../../../config')
|
||||||
|
const {setReturnToFromReferer, passportGeneralCallback} = require('../utils')
|
||||||
|
|
||||||
|
let dropboxAuth = module.exports = Router()
|
||||||
|
|
||||||
|
passport.use(new DropboxStrategy({
|
||||||
|
apiVersion: '2',
|
||||||
|
clientID: config.dropbox.clientID,
|
||||||
|
clientSecret: config.dropbox.clientSecret,
|
||||||
|
callbackURL: config.serverurl + '/auth/dropbox/callback'
|
||||||
|
}, passportGeneralCallback))
|
||||||
|
|
||||||
|
dropboxAuth.get('/auth/dropbox', function (req, res, next) {
|
||||||
|
setReturnToFromReferer(req)
|
||||||
|
passport.authenticate('dropbox-oauth2')(req, res, next)
|
||||||
|
})
|
||||||
|
|
||||||
|
// dropbox auth callback
|
||||||
|
dropboxAuth.get('/auth/dropbox/callback',
|
||||||
|
passport.authenticate('dropbox-oauth2', {
|
||||||
|
successReturnToOrRedirect: config.serverurl + '/',
|
||||||
|
failureRedirect: config.serverurl + '/'
|
||||||
|
})
|
||||||
|
)
|
74
lib/web/auth/email/index.js
Normal file
74
lib/web/auth/email/index.js
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
const Router = require('express').Router
|
||||||
|
const passport = require('passport')
|
||||||
|
const validator = require('validator')
|
||||||
|
const LocalStrategy = require('passport-local').Strategy
|
||||||
|
const config = require('../../../config')
|
||||||
|
const models = require('../../../models')
|
||||||
|
const logger = require('../../../logger')
|
||||||
|
const {setReturnToFromReferer} = require('../utils')
|
||||||
|
const {urlencodedParser} = require('../../utils')
|
||||||
|
const response = require('../../../response')
|
||||||
|
|
||||||
|
let emailAuth = module.exports = Router()
|
||||||
|
|
||||||
|
passport.use(new LocalStrategy({
|
||||||
|
usernameField: 'email'
|
||||||
|
}, function (email, password, done) {
|
||||||
|
if (!validator.isEmail(email)) return done(null, false)
|
||||||
|
models.User.findOne({
|
||||||
|
where: {
|
||||||
|
email: email
|
||||||
|
}
|
||||||
|
}).then(function (user) {
|
||||||
|
if (!user) return done(null, false)
|
||||||
|
if (!user.verifyPassword(password)) return done(null, false)
|
||||||
|
return done(null, user)
|
||||||
|
}).catch(function (err) {
|
||||||
|
logger.error(err)
|
||||||
|
return done(err)
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
|
||||||
|
if (config.allowemailregister) {
|
||||||
|
emailAuth.post('/register', urlencodedParser, function (req, res, next) {
|
||||||
|
if (!req.body.email || !req.body.password) return response.errorBadRequest(res)
|
||||||
|
if (!validator.isEmail(req.body.email)) return response.errorBadRequest(res)
|
||||||
|
models.User.findOrCreate({
|
||||||
|
where: {
|
||||||
|
email: req.body.email
|
||||||
|
},
|
||||||
|
defaults: {
|
||||||
|
password: req.body.password
|
||||||
|
}
|
||||||
|
}).spread(function (user, created) {
|
||||||
|
if (user) {
|
||||||
|
if (created) {
|
||||||
|
logger.debug('user registered: ' + user.id)
|
||||||
|
req.flash('info', "You've successfully registered, please signin.")
|
||||||
|
} else {
|
||||||
|
logger.debug('user found: ' + user.id)
|
||||||
|
req.flash('error', 'This email has been used, please try another one.')
|
||||||
|
}
|
||||||
|
return res.redirect(config.serverurl + '/')
|
||||||
|
}
|
||||||
|
req.flash('error', 'Failed to register your account, please try again.')
|
||||||
|
return res.redirect(config.serverurl + '/')
|
||||||
|
}).catch(function (err) {
|
||||||
|
logger.error('auth callback failed: ' + err)
|
||||||
|
return response.errorInternalError(res)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
emailAuth.post('/login', urlencodedParser, function (req, res, next) {
|
||||||
|
if (!req.body.email || !req.body.password) return response.errorBadRequest(res)
|
||||||
|
if (!validator.isEmail(req.body.email)) return response.errorBadRequest(res)
|
||||||
|
setReturnToFromReferer(req)
|
||||||
|
passport.authenticate('local', {
|
||||||
|
successReturnToOrRedirect: config.serverurl + '/',
|
||||||
|
failureRedirect: config.serverurl + '/',
|
||||||
|
failureFlash: 'Invalid email or password.'
|
||||||
|
})(req, res, next)
|
||||||
|
})
|
29
lib/web/auth/facebook/index.js
Normal file
29
lib/web/auth/facebook/index.js
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
const Router = require('express').Router
|
||||||
|
const passport = require('passport')
|
||||||
|
const FacebookStrategy = require('passport-facebook').Strategy
|
||||||
|
|
||||||
|
const config = require('../../../config')
|
||||||
|
const {setReturnToFromReferer, passportGeneralCallback} = require('../utils')
|
||||||
|
|
||||||
|
let facebookAuth = module.exports = Router()
|
||||||
|
|
||||||
|
passport.use(new FacebookStrategy({
|
||||||
|
clientID: config.facebook.clientID,
|
||||||
|
clientSecret: config.facebook.clientSecret,
|
||||||
|
callbackURL: config.serverurl + '/auth/facebook/callback'
|
||||||
|
}, passportGeneralCallback))
|
||||||
|
|
||||||
|
facebookAuth.get('/auth/facebook', function (req, res, next) {
|
||||||
|
setReturnToFromReferer(req)
|
||||||
|
passport.authenticate('facebook')(req, res, next)
|
||||||
|
})
|
||||||
|
|
||||||
|
// facebook auth callback
|
||||||
|
facebookAuth.get('/auth/facebook/callback',
|
||||||
|
passport.authenticate('facebook', {
|
||||||
|
successReturnToOrRedirect: config.serverurl + '/',
|
||||||
|
failureRedirect: config.serverurl + '/'
|
||||||
|
})
|
||||||
|
)
|
28
lib/web/auth/github/index.js
Normal file
28
lib/web/auth/github/index.js
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
const Router = require('express').Router
|
||||||
|
const passport = require('passport')
|
||||||
|
const GithubStrategy = require('passport-github').Strategy
|
||||||
|
const config = require('../../../config')
|
||||||
|
const {setReturnToFromReferer, passportGeneralCallback} = require('../utils')
|
||||||
|
|
||||||
|
let githubAuth = module.exports = Router()
|
||||||
|
|
||||||
|
passport.use(new GithubStrategy({
|
||||||
|
clientID: config.github.clientID,
|
||||||
|
clientSecret: config.github.clientSecret,
|
||||||
|
callbackURL: config.serverurl + '/auth/github/callback'
|
||||||
|
}, passportGeneralCallback))
|
||||||
|
|
||||||
|
githubAuth.get('/auth/github', function (req, res, next) {
|
||||||
|
setReturnToFromReferer(req)
|
||||||
|
passport.authenticate('github')(req, res, next)
|
||||||
|
})
|
||||||
|
|
||||||
|
// github auth callback
|
||||||
|
githubAuth.get('/auth/github/callback',
|
||||||
|
passport.authenticate('github', {
|
||||||
|
successReturnToOrRedirect: config.serverurl + '/',
|
||||||
|
failureRedirect: config.serverurl + '/'
|
||||||
|
})
|
||||||
|
)
|
36
lib/web/auth/gitlab/index.js
Normal file
36
lib/web/auth/gitlab/index.js
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
const Router = require('express').Router
|
||||||
|
const passport = require('passport')
|
||||||
|
const GitlabStrategy = require('passport-gitlab2').Strategy
|
||||||
|
const config = require('../../../config')
|
||||||
|
const response = require('../../../response')
|
||||||
|
const {setReturnToFromReferer, passportGeneralCallback} = require('../utils')
|
||||||
|
|
||||||
|
let gitlabAuth = module.exports = Router()
|
||||||
|
|
||||||
|
passport.use(new GitlabStrategy({
|
||||||
|
baseURL: config.gitlab.baseURL,
|
||||||
|
clientID: config.gitlab.clientID,
|
||||||
|
clientSecret: config.gitlab.clientSecret,
|
||||||
|
scope: config.gitlab.scope,
|
||||||
|
callbackURL: config.serverurl + '/auth/gitlab/callback'
|
||||||
|
}, passportGeneralCallback))
|
||||||
|
|
||||||
|
gitlabAuth.get('/auth/gitlab', function (req, res, next) {
|
||||||
|
setReturnToFromReferer(req)
|
||||||
|
passport.authenticate('gitlab')(req, res, next)
|
||||||
|
})
|
||||||
|
|
||||||
|
// gitlab auth callback
|
||||||
|
gitlabAuth.get('/auth/gitlab/callback',
|
||||||
|
passport.authenticate('gitlab', {
|
||||||
|
successReturnToOrRedirect: config.serverurl + '/',
|
||||||
|
failureRedirect: config.serverurl + '/'
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!config.gitlab.scope || config.gitlab.scope === 'api') {
|
||||||
|
// gitlab callback actions
|
||||||
|
gitlabAuth.get('/auth/gitlab/callback/:noteId/:action', response.gitlabActions)
|
||||||
|
}
|
27
lib/web/auth/google/index.js
Normal file
27
lib/web/auth/google/index.js
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
const Router = require('express').Router
|
||||||
|
const passport = require('passport')
|
||||||
|
var GoogleStrategy = require('passport-google-oauth20').Strategy
|
||||||
|
const config = require('../../../config')
|
||||||
|
const {setReturnToFromReferer, passportGeneralCallback} = require('../utils')
|
||||||
|
|
||||||
|
let facebookAuth = module.exports = Router()
|
||||||
|
|
||||||
|
passport.use(new GoogleStrategy({
|
||||||
|
clientID: config.google.clientID,
|
||||||
|
clientSecret: config.google.clientSecret,
|
||||||
|
callbackURL: config.serverurl + '/auth/google/callback'
|
||||||
|
}, passportGeneralCallback))
|
||||||
|
|
||||||
|
facebookAuth.get('/auth/google', function (req, res, next) {
|
||||||
|
setReturnToFromReferer(req)
|
||||||
|
passport.authenticate('google', { scope: ['profile'] })(req, res, next)
|
||||||
|
})
|
||||||
|
// google auth callback
|
||||||
|
facebookAuth.get('/auth/google/callback',
|
||||||
|
passport.authenticate('google', {
|
||||||
|
successReturnToOrRedirect: config.serverurl + '/',
|
||||||
|
failureRedirect: config.serverurl + '/'
|
||||||
|
})
|
||||||
|
)
|
48
lib/web/auth/index.js
Normal file
48
lib/web/auth/index.js
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
const Router = require('express').Router
|
||||||
|
const passport = require('passport')
|
||||||
|
|
||||||
|
const config = require('../../config')
|
||||||
|
const logger = require('../../logger')
|
||||||
|
const models = require('../../models')
|
||||||
|
|
||||||
|
const authRouter = module.exports = Router()
|
||||||
|
|
||||||
|
// serialize and deserialize
|
||||||
|
passport.serializeUser(function (user, done) {
|
||||||
|
logger.info('serializeUser: ' + user.id)
|
||||||
|
return done(null, user.id)
|
||||||
|
})
|
||||||
|
|
||||||
|
passport.deserializeUser(function (id, done) {
|
||||||
|
models.User.findOne({
|
||||||
|
where: {
|
||||||
|
id: id
|
||||||
|
}
|
||||||
|
}).then(function (user) {
|
||||||
|
logger.info('deserializeUser: ' + user.id)
|
||||||
|
return done(null, user)
|
||||||
|
}).catch(function (err) {
|
||||||
|
logger.error(err)
|
||||||
|
return done(err, null)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
if (config.isFacebookEnable) authRouter.use(require('./facebook'))
|
||||||
|
if (config.isTwitterEnable) authRouter.use(require('./twitter'))
|
||||||
|
if (config.isGitHubEnable) authRouter.use(require('./github'))
|
||||||
|
if (config.isGitLabEnable) authRouter.use(require('./gitlab'))
|
||||||
|
if (config.isDropboxEnable) authRouter.use(require('./dropbox'))
|
||||||
|
if (config.isGoogleEnable) authRouter.use(require('./google'))
|
||||||
|
if (config.isLDAPEnable) authRouter.use(require('./ldap'))
|
||||||
|
if (config.isEmailEnable) authRouter.use(require('./email'))
|
||||||
|
|
||||||
|
// logout
|
||||||
|
authRouter.get('/logout', function (req, res) {
|
||||||
|
if (config.debug && req.isAuthenticated()) {
|
||||||
|
logger.debug('user logout: ' + req.user.id)
|
||||||
|
}
|
||||||
|
req.logout()
|
||||||
|
res.redirect(config.serverurl + '/')
|
||||||
|
})
|
74
lib/web/auth/ldap/index.js
Normal file
74
lib/web/auth/ldap/index.js
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
const Router = require('express').Router
|
||||||
|
const passport = require('passport')
|
||||||
|
const LDAPStrategy = require('passport-ldapauth')
|
||||||
|
const config = require('../../../config')
|
||||||
|
const models = require('../../../models')
|
||||||
|
const logger = require('../../../logger')
|
||||||
|
const {setReturnToFromReferer} = require('../utils')
|
||||||
|
const {urlencodedParser} = require('../../utils')
|
||||||
|
const response = require('../../../response')
|
||||||
|
|
||||||
|
let ldapAuth = module.exports = Router()
|
||||||
|
|
||||||
|
passport.use(new LDAPStrategy({
|
||||||
|
server: {
|
||||||
|
url: config.ldap.url || null,
|
||||||
|
bindDn: config.ldap.bindDn || null,
|
||||||
|
bindCredentials: config.ldap.bindCredentials || null,
|
||||||
|
searchBase: config.ldap.searchBase || null,
|
||||||
|
searchFilter: config.ldap.searchFilter || null,
|
||||||
|
searchAttributes: config.ldap.searchAttributes || null,
|
||||||
|
tlsOptions: config.ldap.tlsOptions || null
|
||||||
|
}
|
||||||
|
}, function (user, done) {
|
||||||
|
var profile = {
|
||||||
|
id: 'LDAP-' + user.uidNumber,
|
||||||
|
username: user.uid,
|
||||||
|
displayName: user.displayName,
|
||||||
|
emails: user.mail ? [user.mail] : [],
|
||||||
|
avatarUrl: null,
|
||||||
|
profileUrl: null,
|
||||||
|
provider: 'ldap'
|
||||||
|
}
|
||||||
|
var stringifiedProfile = JSON.stringify(profile)
|
||||||
|
models.User.findOrCreate({
|
||||||
|
where: {
|
||||||
|
profileid: profile.id.toString()
|
||||||
|
},
|
||||||
|
defaults: {
|
||||||
|
profile: stringifiedProfile
|
||||||
|
}
|
||||||
|
}).spread(function (user, created) {
|
||||||
|
if (user) {
|
||||||
|
var needSave = false
|
||||||
|
if (user.profile !== stringifiedProfile) {
|
||||||
|
user.profile = stringifiedProfile
|
||||||
|
needSave = true
|
||||||
|
}
|
||||||
|
if (needSave) {
|
||||||
|
user.save().then(function () {
|
||||||
|
if (config.debug) { logger.debug('user login: ' + user.id) }
|
||||||
|
return done(null, user)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
if (config.debug) { logger.debug('user login: ' + user.id) }
|
||||||
|
return done(null, user)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).catch(function (err) {
|
||||||
|
logger.error('ldap auth failed: ' + err)
|
||||||
|
return done(err, null)
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
|
||||||
|
ldapAuth.post('/auth/ldap', urlencodedParser, function (req, res, next) {
|
||||||
|
if (!req.body.username || !req.body.password) return response.errorBadRequest(res)
|
||||||
|
setReturnToFromReferer(req)
|
||||||
|
passport.authenticate('ldapauth', {
|
||||||
|
successReturnToOrRedirect: config.serverurl + '/',
|
||||||
|
failureRedirect: config.serverurl + '/',
|
||||||
|
failureFlash: true
|
||||||
|
})(req, res, next)
|
||||||
|
})
|
29
lib/web/auth/twitter/index.js
Normal file
29
lib/web/auth/twitter/index.js
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
const Router = require('express').Router
|
||||||
|
const passport = require('passport')
|
||||||
|
const TwitterStrategy = require('passport-twitter').Strategy
|
||||||
|
|
||||||
|
const config = require('../../../config')
|
||||||
|
const {setReturnToFromReferer, passportGeneralCallback} = require('../utils')
|
||||||
|
|
||||||
|
let twitterAuth = module.exports = Router()
|
||||||
|
|
||||||
|
passport.use(new TwitterStrategy({
|
||||||
|
consumerKey: config.twitter.consumerKey,
|
||||||
|
consumerSecret: config.twitter.consumerSecret,
|
||||||
|
callbackURL: config.serverurl + '/auth/twitter/callback'
|
||||||
|
}, passportGeneralCallback))
|
||||||
|
|
||||||
|
twitterAuth.get('/auth/twitter', function (req, res, next) {
|
||||||
|
setReturnToFromReferer(req)
|
||||||
|
passport.authenticate('twitter')(req, res, next)
|
||||||
|
})
|
||||||
|
|
||||||
|
// twitter auth callback
|
||||||
|
twitterAuth.get('/auth/twitter/callback',
|
||||||
|
passport.authenticate('twitter', {
|
||||||
|
successReturnToOrRedirect: config.serverurl + '/',
|
||||||
|
failureRedirect: config.serverurl + '/'
|
||||||
|
})
|
||||||
|
)
|
53
lib/web/auth/utils.js
Normal file
53
lib/web/auth/utils.js
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
const models = require('../../models')
|
||||||
|
const config = require('../../config')
|
||||||
|
const logger = require('../../logger')
|
||||||
|
|
||||||
|
exports.setReturnToFromReferer = function setReturnToFromReferer (req) {
|
||||||
|
var referer = req.get('referer')
|
||||||
|
if (!req.session) req.session = {}
|
||||||
|
req.session.returnTo = referer
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.passportGeneralCallback = function callback (accessToken, refreshToken, profile, done) {
|
||||||
|
var stringifiedProfile = JSON.stringify(profile)
|
||||||
|
models.User.findOrCreate({
|
||||||
|
where: {
|
||||||
|
profileid: profile.id.toString()
|
||||||
|
},
|
||||||
|
defaults: {
|
||||||
|
profile: stringifiedProfile,
|
||||||
|
accessToken: accessToken,
|
||||||
|
refreshToken: refreshToken
|
||||||
|
}
|
||||||
|
}).spread(function (user, created) {
|
||||||
|
if (user) {
|
||||||
|
var needSave = false
|
||||||
|
if (user.profile !== stringifiedProfile) {
|
||||||
|
user.profile = stringifiedProfile
|
||||||
|
needSave = true
|
||||||
|
}
|
||||||
|
if (user.accessToken !== accessToken) {
|
||||||
|
user.accessToken = accessToken
|
||||||
|
needSave = true
|
||||||
|
}
|
||||||
|
if (user.refreshToken !== refreshToken) {
|
||||||
|
user.refreshToken = refreshToken
|
||||||
|
needSave = true
|
||||||
|
}
|
||||||
|
if (needSave) {
|
||||||
|
user.save().then(function () {
|
||||||
|
if (config.debug) { logger.info('user login: ' + user.id) }
|
||||||
|
return done(null, user)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
if (config.debug) { logger.info('user login: ' + user.id) }
|
||||||
|
return done(null, user)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).catch(function (err) {
|
||||||
|
logger.error('auth callback failed: ' + err)
|
||||||
|
return done(err, null)
|
||||||
|
})
|
||||||
|
}
|
22
lib/web/baseRouter.js
Normal file
22
lib/web/baseRouter.js
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
const Router = require('express').Router
|
||||||
|
|
||||||
|
const response = require('../response')
|
||||||
|
|
||||||
|
const baseRouter = module.exports = Router()
|
||||||
|
|
||||||
|
// get index
|
||||||
|
baseRouter.get('/', response.showIndex)
|
||||||
|
// get 403 forbidden
|
||||||
|
baseRouter.get('/403', function (req, res) {
|
||||||
|
response.errorForbidden(res)
|
||||||
|
})
|
||||||
|
// get 404 not found
|
||||||
|
baseRouter.get('/404', function (req, res) {
|
||||||
|
response.errorNotFound(res)
|
||||||
|
})
|
||||||
|
// get 500 internal error
|
||||||
|
baseRouter.get('/500', function (req, res) {
|
||||||
|
response.errorInternalError(res)
|
||||||
|
})
|
18
lib/web/historyRouter.js
Normal file
18
lib/web/historyRouter.js
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
const Router = require('express').Router
|
||||||
|
|
||||||
|
const {urlencodedParser} = require('./utils')
|
||||||
|
const history = require('../history')
|
||||||
|
const historyRouter = module.exports = Router()
|
||||||
|
|
||||||
|
// get history
|
||||||
|
historyRouter.get('/history', history.historyGet)
|
||||||
|
// post history
|
||||||
|
historyRouter.post('/history', urlencodedParser, history.historyPost)
|
||||||
|
// post history by note id
|
||||||
|
historyRouter.post('/history/:noteId', urlencodedParser, history.historyPost)
|
||||||
|
// delete history
|
||||||
|
historyRouter.delete('/history', history.historyDelete)
|
||||||
|
// delete history by note id
|
||||||
|
historyRouter.delete('/history/:noteId', history.historyDelete)
|
95
lib/web/imageRouter.js
Normal file
95
lib/web/imageRouter.js
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
'use strict'
|
||||||
|
var fs = require('fs')
|
||||||
|
var url = require('url')
|
||||||
|
var path = require('path')
|
||||||
|
|
||||||
|
const Router = require('express').Router
|
||||||
|
const formidable = require('formidable')
|
||||||
|
var imgur = require('imgur')
|
||||||
|
|
||||||
|
const config = require('../config')
|
||||||
|
const logger = require('../logger')
|
||||||
|
const response = require('../response')
|
||||||
|
|
||||||
|
const imageRouter = module.exports = Router()
|
||||||
|
|
||||||
|
// upload image
|
||||||
|
imageRouter.post('/uploadimage', function (req, res) {
|
||||||
|
var form = new formidable.IncomingForm()
|
||||||
|
|
||||||
|
form.keepExtensions = true
|
||||||
|
|
||||||
|
if (config.imageUploadType === 'filesystem') {
|
||||||
|
form.uploadDir = 'public/uploads'
|
||||||
|
}
|
||||||
|
|
||||||
|
form.parse(req, function (err, fields, files) {
|
||||||
|
if (err || !files.image || !files.image.path) {
|
||||||
|
response.errorForbidden(res)
|
||||||
|
} else {
|
||||||
|
if (config.debug) { logger.info('SERVER received uploadimage: ' + JSON.stringify(files.image)) }
|
||||||
|
|
||||||
|
try {
|
||||||
|
switch (config.imageUploadType) {
|
||||||
|
case 'filesystem':
|
||||||
|
res.send({
|
||||||
|
link: url.resolve(config.serverurl + '/', files.image.path.match(/^public\/(.+$)/)[1])
|
||||||
|
})
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
case 's3':
|
||||||
|
var AWS = require('aws-sdk')
|
||||||
|
var awsConfig = new AWS.Config(config.s3)
|
||||||
|
var s3 = new AWS.S3(awsConfig)
|
||||||
|
const {getImageMimeType} = require('../utils')
|
||||||
|
fs.readFile(files.image.path, function (err, buffer) {
|
||||||
|
if (err) {
|
||||||
|
logger.error(err)
|
||||||
|
res.status(500).end('upload image error')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var params = {
|
||||||
|
Bucket: config.s3bucket,
|
||||||
|
Key: path.join('uploads', path.basename(files.image.path)),
|
||||||
|
Body: buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
var mimeType = getImageMimeType(files.image.path)
|
||||||
|
if (mimeType) { params.ContentType = mimeType }
|
||||||
|
|
||||||
|
s3.putObject(params, function (err, data) {
|
||||||
|
if (err) {
|
||||||
|
logger.error(err)
|
||||||
|
res.status(500).end('upload image error')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res.send({
|
||||||
|
link: `https://s3-${config.s3.region}.amazonaws.com/${config.s3bucket}/${params.Key}`
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case 'imgur':
|
||||||
|
default:
|
||||||
|
imgur.setClientId(config.imgur.clientID)
|
||||||
|
imgur.uploadFile(files.image.path)
|
||||||
|
.then(function (json) {
|
||||||
|
if (config.debug) { logger.info('SERVER uploadimage success: ' + JSON.stringify(json)) }
|
||||||
|
res.send({
|
||||||
|
link: json.data.link.replace(/^http:\/\//i, 'https://')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch(function (err) {
|
||||||
|
logger.error(err)
|
||||||
|
return res.status(500).end('upload image error')
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
logger.error(err)
|
||||||
|
return res.status(500).end('upload image error')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
14
lib/web/middleware/checkURIValid.js
Normal file
14
lib/web/middleware/checkURIValid.js
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
const logger = require('../../logger')
|
||||||
|
const response = require('../../response')
|
||||||
|
|
||||||
|
module.exports = function (req, res, next) {
|
||||||
|
try {
|
||||||
|
decodeURIComponent(req.path)
|
||||||
|
} catch (err) {
|
||||||
|
logger.error(err)
|
||||||
|
return response.errorBadRequest(res)
|
||||||
|
}
|
||||||
|
next()
|
||||||
|
}
|
17
lib/web/middleware/redirectWithoutTrailingSlashes.js
Normal file
17
lib/web/middleware/redirectWithoutTrailingSlashes.js
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
const config = require('../../config')
|
||||||
|
|
||||||
|
module.exports = function (req, res, next) {
|
||||||
|
if (req.method === 'GET' && req.path.substr(-1) === '/' && req.path.length > 1) {
|
||||||
|
const queryString = req.url.slice(req.path.length)
|
||||||
|
const urlPath = req.path.slice(0, -1)
|
||||||
|
let serverURL = config.serverurl
|
||||||
|
if (config.urlpath) {
|
||||||
|
serverURL = serverURL.slice(0, -(config.urlpath.length + 1))
|
||||||
|
}
|
||||||
|
res.redirect(301, serverURL + urlPath + queryString)
|
||||||
|
} else {
|
||||||
|
next()
|
||||||
|
}
|
||||||
|
}
|
13
lib/web/middleware/tooBusy.js
Normal file
13
lib/web/middleware/tooBusy.js
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
const toobusy = require('toobusy-js')
|
||||||
|
|
||||||
|
const response = require('../../response')
|
||||||
|
|
||||||
|
module.exports = function (req, res, next) {
|
||||||
|
if (toobusy()) {
|
||||||
|
response.errorServiceUnavailable(res)
|
||||||
|
} else {
|
||||||
|
next()
|
||||||
|
}
|
||||||
|
}
|
24
lib/web/noteRouter.js
Normal file
24
lib/web/noteRouter.js
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
const Router = require('express').Router
|
||||||
|
|
||||||
|
const response = require('../response')
|
||||||
|
|
||||||
|
const noteRouter = module.exports = Router()
|
||||||
|
|
||||||
|
// get new note
|
||||||
|
noteRouter.get('/new', response.newNote)
|
||||||
|
// get publish note
|
||||||
|
noteRouter.get('/s/:shortid', response.showPublishNote)
|
||||||
|
// publish note actions
|
||||||
|
noteRouter.get('/s/:shortid/:action', response.publishNoteActions)
|
||||||
|
// get publish slide
|
||||||
|
noteRouter.get('/p/:shortid', response.showPublishSlide)
|
||||||
|
// publish slide actions
|
||||||
|
noteRouter.get('/p/:shortid/:action', response.publishSlideActions)
|
||||||
|
// get note by id
|
||||||
|
noteRouter.get('/:noteId', response.showNote)
|
||||||
|
// note actions
|
||||||
|
noteRouter.get('/:noteId/:action', response.noteActions)
|
||||||
|
// note actions with action id
|
||||||
|
noteRouter.get('/:noteId/:action/:actionId', response.noteActions)
|
92
lib/web/statusRouter.js
Normal file
92
lib/web/statusRouter.js
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
const Router = require('express').Router
|
||||||
|
|
||||||
|
const response = require('../response')
|
||||||
|
const realtime = require('../realtime')
|
||||||
|
const config = require('../config')
|
||||||
|
const models = require('../models')
|
||||||
|
const logger = require('../logger')
|
||||||
|
|
||||||
|
const {urlencodedParser} = require('./utils')
|
||||||
|
|
||||||
|
const statusRouter = module.exports = Router()
|
||||||
|
|
||||||
|
// get status
|
||||||
|
statusRouter.get('/status', function (req, res, next) {
|
||||||
|
realtime.getStatus(function (data) {
|
||||||
|
res.set({
|
||||||
|
'Cache-Control': 'private', // only cache by client
|
||||||
|
'X-Robots-Tag': 'noindex, nofollow', // prevent crawling
|
||||||
|
'HackMD-Version': config.version
|
||||||
|
})
|
||||||
|
res.send(data)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
// get status
|
||||||
|
statusRouter.get('/temp', function (req, res) {
|
||||||
|
var host = req.get('host')
|
||||||
|
if (config.alloworigin.indexOf(host) === -1) {
|
||||||
|
response.errorForbidden(res)
|
||||||
|
} else {
|
||||||
|
var tempid = req.query.tempid
|
||||||
|
if (!tempid) {
|
||||||
|
response.errorForbidden(res)
|
||||||
|
} else {
|
||||||
|
models.Temp.findOne({
|
||||||
|
where: {
|
||||||
|
id: tempid
|
||||||
|
}
|
||||||
|
}).then(function (temp) {
|
||||||
|
if (!temp) {
|
||||||
|
response.errorNotFound(res)
|
||||||
|
} else {
|
||||||
|
res.header('Access-Control-Allow-Origin', '*')
|
||||||
|
res.send({
|
||||||
|
temp: temp.data
|
||||||
|
})
|
||||||
|
temp.destroy().catch(function (err) {
|
||||||
|
if (err) {
|
||||||
|
logger.error('remove temp failed: ' + err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}).catch(function (err) {
|
||||||
|
logger.error(err)
|
||||||
|
return response.errorInternalError(res)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// post status
|
||||||
|
statusRouter.post('/temp', urlencodedParser, function (req, res) {
|
||||||
|
var host = req.get('host')
|
||||||
|
if (config.alloworigin.indexOf(host) === -1) {
|
||||||
|
response.errorForbidden(res)
|
||||||
|
} else {
|
||||||
|
var data = req.body.data
|
||||||
|
if (!data) {
|
||||||
|
response.errorForbidden(res)
|
||||||
|
} else {
|
||||||
|
if (config.debug) {
|
||||||
|
logger.info('SERVER received temp from [' + host + ']: ' + req.body.data)
|
||||||
|
}
|
||||||
|
models.Temp.create({
|
||||||
|
data: data
|
||||||
|
}).then(function (temp) {
|
||||||
|
if (temp) {
|
||||||
|
res.header('Access-Control-Allow-Origin', '*')
|
||||||
|
res.send({
|
||||||
|
status: 'ok',
|
||||||
|
id: temp.id
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
response.errorInternalError(res)
|
||||||
|
}
|
||||||
|
}).catch(function (err) {
|
||||||
|
logger.error(err)
|
||||||
|
return response.errorInternalError(res)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
36
lib/web/userRouter.js
Normal file
36
lib/web/userRouter.js
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
const Router = require('express').Router
|
||||||
|
|
||||||
|
const response = require('../response')
|
||||||
|
const models = require('../models')
|
||||||
|
const logger = require('../logger')
|
||||||
|
|
||||||
|
const UserRouter = module.exports = Router()
|
||||||
|
|
||||||
|
// get me info
|
||||||
|
UserRouter.get('/me', function (req, res) {
|
||||||
|
if (req.isAuthenticated()) {
|
||||||
|
models.User.findOne({
|
||||||
|
where: {
|
||||||
|
id: req.user.id
|
||||||
|
}
|
||||||
|
}).then(function (user) {
|
||||||
|
if (!user) { return response.errorNotFound(res) }
|
||||||
|
var profile = models.User.getProfile(user)
|
||||||
|
res.send({
|
||||||
|
status: 'ok',
|
||||||
|
id: req.user.id,
|
||||||
|
name: profile.name,
|
||||||
|
photo: profile.photo
|
||||||
|
})
|
||||||
|
}).catch(function (err) {
|
||||||
|
logger.error('read me failed: ' + err)
|
||||||
|
return response.errorInternalError(res)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
res.send({
|
||||||
|
status: 'forbidden'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
9
lib/web/utils.js
Normal file
9
lib/web/utils.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
const bodyParser = require('body-parser')
|
||||||
|
|
||||||
|
// create application/x-www-form-urlencoded parser
|
||||||
|
exports.urlencodedParser = bodyParser.urlencoded({
|
||||||
|
extended: false,
|
||||||
|
limit: 1024 * 1024 * 10 // 10 mb
|
||||||
|
})
|
|
@ -4,8 +4,8 @@ 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')
|
||||||
var logger = require('../logger.js')
|
var logger = require('../logger')
|
||||||
|
|
||||||
process.on('message', function (data) {
|
process.on('message', function (data) {
|
||||||
if (!data || !data.msg || !data.cacheKey) {
|
if (!data || !data.msg || !data.cacheKey) {
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
"connect-session-sequelize": "^4.1.0",
|
"connect-session-sequelize": "^4.1.0",
|
||||||
"cookie": "0.3.1",
|
"cookie": "0.3.1",
|
||||||
"cookie-parser": "1.4.3",
|
"cookie-parser": "1.4.3",
|
||||||
|
"deep-freeze": "^0.0.1",
|
||||||
"diff-match-patch": "git+https://github.com/hackmdio/diff-match-patch.git",
|
"diff-match-patch": "git+https://github.com/hackmdio/diff-match-patch.git",
|
||||||
"ejs": "^2.5.5",
|
"ejs": "^2.5.5",
|
||||||
"emojify.js": "~1.1.0",
|
"emojify.js": "~1.1.0",
|
||||||
|
|
|
@ -750,7 +750,7 @@ export function generateToc (id) {
|
||||||
'targetId': id,
|
'targetId': id,
|
||||||
'process': getHeaderContent
|
'process': getHeaderContent
|
||||||
})
|
})
|
||||||
/* eslint-enable no-unsed-vars */
|
/* eslint-enable no-unused-vars */
|
||||||
if (target.text() === 'undefined') { target.html('') }
|
if (target.text() === 'undefined') { target.html('') }
|
||||||
const tocMenu = $('<div class="toc-menu"></div')
|
const tocMenu = $('<div class="toc-menu"></div')
|
||||||
const toggle = $('<a class="expand-toggle" href="#">Expand all</a>')
|
const toggle = $('<a class="expand-toggle" href="#">Expand all</a>')
|
||||||
|
@ -888,7 +888,7 @@ export function renderTOC (view) {
|
||||||
const target = $(`#${id}`)
|
const target = $(`#${id}`)
|
||||||
target.html('')
|
target.html('')
|
||||||
/* eslint-disable no-unused-vars */
|
/* eslint-disable no-unused-vars */
|
||||||
var toc = new window.Toc('doc', {
|
let TOC = new window.Toc('doc', {
|
||||||
'level': 3,
|
'level': 3,
|
||||||
'top': -1,
|
'top': -1,
|
||||||
'class': 'toc',
|
'class': 'toc',
|
||||||
|
|
Loading…
Reference in a new issue