2017-04-12 13:16:33 -04:00
|
|
|
'use strict'
|
2017-03-08 05:45:51 -05:00
|
|
|
// app
|
|
|
|
// external modules
|
2021-02-15 03:42:51 -05:00
|
|
|
const express = require('express')
|
|
|
|
|
|
|
|
const ejs = require('ejs')
|
|
|
|
const passport = require('passport')
|
|
|
|
const methodOverride = require('method-override')
|
|
|
|
const cookieParser = require('cookie-parser')
|
|
|
|
const compression = require('compression')
|
|
|
|
const session = require('express-session')
|
|
|
|
const SequelizeStore = require('connect-session-sequelize')(session.Store)
|
|
|
|
const fs = require('fs')
|
|
|
|
const path = require('path')
|
|
|
|
|
|
|
|
const morgan = require('morgan')
|
|
|
|
const passportSocketIo = require('passport.socketio')
|
|
|
|
const helmet = require('helmet')
|
|
|
|
const i18n = require('i18n')
|
|
|
|
const flash = require('connect-flash')
|
2021-04-18 06:31:28 -04:00
|
|
|
const apiMetrics = require('prometheus-api-metrics')
|
2017-03-08 05:45:51 -05:00
|
|
|
|
|
|
|
// core
|
2021-02-15 03:42:51 -05:00
|
|
|
const config = require('./lib/config')
|
|
|
|
const logger = require('./lib/logger')
|
|
|
|
const errors = require('./lib/errors')
|
|
|
|
const models = require('./lib/models')
|
|
|
|
const csp = require('./lib/csp')
|
2021-04-18 07:20:43 -04:00
|
|
|
const metrics = require('./lib/prometheus')
|
2021-07-20 17:56:54 -04:00
|
|
|
const { useUnless } = require('./lib/utils')
|
2017-03-08 05:45:51 -05:00
|
|
|
|
2021-04-25 18:18:08 -04:00
|
|
|
const supportedLocalesList = Object.keys(require('./locales/_supported.json'))
|
|
|
|
|
2017-03-08 05:45:51 -05:00
|
|
|
// server setup
|
2021-02-15 03:42:51 -05:00
|
|
|
const app = express()
|
|
|
|
let server = null
|
2018-03-07 09:17:35 -05:00
|
|
|
if (config.useSSL) {
|
2021-02-15 03:42:51 -05:00
|
|
|
const ca = (function () {
|
|
|
|
let i, len
|
|
|
|
const results = []
|
2018-03-07 09:17:35 -05:00
|
|
|
for (i = 0, len = config.sslCAPath.length; i < len; i++) {
|
|
|
|
results.push(fs.readFileSync(config.sslCAPath[i], 'utf8'))
|
2017-03-08 05:45:51 -05:00
|
|
|
}
|
|
|
|
return results
|
|
|
|
})()
|
2021-02-15 03:42:51 -05:00
|
|
|
const options = {
|
2018-03-07 09:17:35 -05:00
|
|
|
key: fs.readFileSync(config.sslKeyPath, 'utf8'),
|
|
|
|
cert: fs.readFileSync(config.sslCertPath, 'utf8'),
|
2017-03-08 05:45:51 -05:00
|
|
|
ca: ca,
|
2018-03-07 09:17:35 -05:00
|
|
|
dhparam: fs.readFileSync(config.dhParamPath, 'utf8'),
|
2017-03-08 05:45:51 -05:00
|
|
|
requestCert: false,
|
|
|
|
rejectUnauthorized: false
|
|
|
|
}
|
|
|
|
server = require('https').createServer(options, app)
|
2015-05-15 00:58:13 -04:00
|
|
|
} else {
|
2017-03-08 05:45:51 -05:00
|
|
|
server = require('http').createServer(app)
|
2015-05-15 00:58:13 -04:00
|
|
|
}
|
2015-07-01 12:10:20 -04:00
|
|
|
|
2020-06-10 06:21:11 -04:00
|
|
|
// if we manage to provide HTTPS domains, but don't provide TLS ourselves
|
|
|
|
// obviously a proxy is involded. In order to make sure express is aware of
|
|
|
|
// this, we provide the option to trust proxies here.
|
|
|
|
if (!config.useSSL && config.protocolUseSSL) {
|
|
|
|
app.set('trust proxy', 1)
|
|
|
|
}
|
|
|
|
|
2021-12-28 09:26:46 -05:00
|
|
|
// check if the request is from container healthcheck
|
|
|
|
function isContainerHealthCheck (req, _) {
|
|
|
|
return req.headers['user-agent'] === 'hedgedoc-container-healthcheck/1.0' && req.ip === '127.0.0.1'
|
|
|
|
}
|
|
|
|
|
2017-03-08 05:45:51 -05:00
|
|
|
// logger
|
2015-06-01 06:04:25 -04:00
|
|
|
app.use(morgan('combined', {
|
2021-12-28 09:26:46 -05:00
|
|
|
skip: isContainerHealthCheck,
|
2021-02-15 03:42:51 -05:00
|
|
|
stream: logger.stream
|
2017-03-08 05:45:51 -05:00
|
|
|
}))
|
2015-05-04 03:53:29 -04:00
|
|
|
|
2021-04-18 06:31:28 -04:00
|
|
|
// Register prometheus metrics endpoint
|
|
|
|
app.use(apiMetrics())
|
2021-04-18 07:20:43 -04:00
|
|
|
metrics.setupCustomPrometheusMetrics()
|
2021-04-18 06:31:28 -04:00
|
|
|
|
2017-03-08 05:45:51 -05:00
|
|
|
// socket io
|
2021-02-15 03:42:51 -05:00
|
|
|
const io = require('socket.io')(server, { cookie: false })
|
2015-07-01 12:10:20 -04:00
|
|
|
|
2017-03-08 05:45:51 -05:00
|
|
|
// others
|
2021-02-15 03:42:51 -05:00
|
|
|
const realtime = require('./lib/realtime.js')
|
2015-05-04 03:53:29 -04:00
|
|
|
|
2017-03-08 05:45:51 -05:00
|
|
|
// assign socket io to realtime
|
|
|
|
realtime.io = io
|
2015-09-23 23:36:41 -04:00
|
|
|
|
2017-03-08 05:45:51 -05:00
|
|
|
// methodOverride
|
|
|
|
app.use(methodOverride('_method'))
|
2015-05-04 03:53:29 -04:00
|
|
|
|
2017-03-08 05:45:51 -05:00
|
|
|
// session store
|
2021-02-15 03:42:51 -05:00
|
|
|
const sessionStore = new SequelizeStore({
|
2017-03-08 05:45:51 -05:00
|
|
|
db: models.sequelize
|
|
|
|
})
|
2015-06-01 06:04:25 -04:00
|
|
|
|
2017-03-08 05:45:51 -05:00
|
|
|
// compression
|
|
|
|
app.use(compression())
|
2015-05-04 03:53:29 -04:00
|
|
|
|
2016-03-14 22:41:49 -04:00
|
|
|
// use hsts to tell https users stick to this
|
2017-10-12 19:09:04 -04:00
|
|
|
if (config.hsts.enable) {
|
|
|
|
app.use(helmet.hsts({
|
2018-11-19 16:01:43 -05:00
|
|
|
maxAge: config.hsts.maxAgeSeconds,
|
2020-11-11 14:50:52 -05:00
|
|
|
includeSubDomains: config.hsts.includeSubdomains,
|
2017-10-12 19:09:04 -04:00
|
|
|
preload: config.hsts.preload
|
|
|
|
}))
|
2018-03-07 09:17:35 -05:00
|
|
|
} else if (config.useSSL) {
|
2017-10-12 19:09:04 -04:00
|
|
|
logger.info('Consider enabling HSTS for extra security:')
|
|
|
|
logger.info('https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security')
|
|
|
|
}
|
2016-03-14 22:41:49 -04:00
|
|
|
|
2018-02-11 19:29:58 -05:00
|
|
|
// Add referrer policy to improve privacy
|
|
|
|
app.use(
|
|
|
|
helmet.referrerPolicy({
|
|
|
|
policy: 'same-origin'
|
|
|
|
})
|
|
|
|
)
|
|
|
|
|
2017-10-21 19:22:48 -04:00
|
|
|
// Generate a random nonce per request, for CSP with inline scripts
|
|
|
|
app.use(csp.addNonceToLocals)
|
2017-10-18 11:48:53 -04:00
|
|
|
|
2017-10-18 11:10:23 -04:00
|
|
|
// use Content-Security-Policy to limit XSS, dangerous plugins, etc.
|
|
|
|
// https://helmetjs.github.io/docs/csp/
|
|
|
|
if (config.csp.enable) {
|
|
|
|
app.use(helmet.contentSecurityPolicy({
|
2017-10-21 19:22:48 -04:00
|
|
|
directives: csp.computeDirectives()
|
2017-10-18 11:10:23 -04:00
|
|
|
}))
|
|
|
|
} else {
|
2017-10-18 13:37:55 -04:00
|
|
|
logger.info('Content-Security-Policy is disabled. This may be a security risk.')
|
2017-10-18 11:10:23 -04:00
|
|
|
}
|
|
|
|
|
2016-08-18 23:49:24 -04:00
|
|
|
i18n.configure({
|
2021-04-25 18:18:08 -04:00
|
|
|
locales: supportedLocalesList,
|
2017-03-08 05:45:51 -05:00
|
|
|
cookie: 'locale',
|
2019-04-04 06:31:08 -04:00
|
|
|
indent: ' ', // this is the style poeditor.com exports it, this creates less churn
|
2018-06-04 19:29:27 -04:00
|
|
|
directory: path.join(__dirname, '/locales'),
|
|
|
|
updateFiles: config.updateI18nFiles
|
2017-03-08 05:45:51 -05:00
|
|
|
})
|
2016-08-18 23:49:24 -04:00
|
|
|
|
2017-03-08 05:45:51 -05:00
|
|
|
app.use(cookieParser())
|
2016-08-18 23:49:24 -04:00
|
|
|
|
2017-03-08 05:45:51 -05:00
|
|
|
app.use(i18n.init)
|
2016-08-18 23:49:24 -04:00
|
|
|
|
2016-04-20 06:14:28 -04:00
|
|
|
// routes without sessions
|
|
|
|
// static files
|
2019-09-18 16:18:22 -04:00
|
|
|
app.use('/', express.static(path.join(__dirname, '/public'), { maxAge: config.staticCacheTime, index: false, redirect: false }))
|
|
|
|
app.use('/docs', express.static(path.resolve(__dirname, config.docsPath), { maxAge: config.staticCacheTime, redirect: false }))
|
|
|
|
app.use('/uploads', express.static(path.resolve(__dirname, config.uploadsPath), { maxAge: config.staticCacheTime, redirect: false }))
|
2018-09-26 11:18:33 -04:00
|
|
|
app.use('/default.md', express.static(path.resolve(__dirname, config.defaultNotePath), { maxAge: config.staticCacheTime }))
|
2016-04-20 06:14:28 -04:00
|
|
|
|
2017-03-08 05:45:51 -05:00
|
|
|
// session
|
2021-07-20 17:56:54 -04:00
|
|
|
app.use(useUnless(['/status', '/metrics'], session({
|
2018-03-07 09:17:35 -05:00
|
|
|
name: config.sessionName,
|
|
|
|
secret: config.sessionSecret,
|
2017-03-08 05:45:51 -05:00
|
|
|
resave: false, // don't save session if unmodified
|
|
|
|
saveUninitialized: true, // always create session to ensure the origin
|
|
|
|
rolling: true, // reset maxAge on every response
|
|
|
|
cookie: {
|
2020-06-08 09:27:31 -04:00
|
|
|
maxAge: config.sessionLife,
|
2020-08-26 20:04:49 -04:00
|
|
|
sameSite: config.cookiePolicy, // be careful: setting a SameSite value of none without https breaks the editor
|
2020-06-08 09:11:17 -04:00
|
|
|
secure: config.useSSL || config.protocolUseSSL || false
|
2017-03-08 05:45:51 -05:00
|
|
|
},
|
|
|
|
store: sessionStore
|
2021-07-20 17:56:54 -04:00
|
|
|
})))
|
2015-05-04 03:53:29 -04:00
|
|
|
|
2016-03-14 22:42:07 -04:00
|
|
|
// session resumption
|
2021-02-15 03:42:51 -05:00
|
|
|
const tlsSessionStore = {}
|
2016-03-14 22:42:07 -04:00
|
|
|
server.on('newSession', function (id, data, cb) {
|
2017-03-08 05:45:51 -05:00
|
|
|
tlsSessionStore[id.toString('hex')] = data
|
|
|
|
cb()
|
|
|
|
})
|
2016-03-14 22:42:07 -04:00
|
|
|
server.on('resumeSession', function (id, cb) {
|
2017-03-08 05:45:51 -05:00
|
|
|
cb(null, tlsSessionStore[id.toString('hex')] || null)
|
|
|
|
})
|
2016-03-14 22:42:07 -04:00
|
|
|
|
2017-03-08 05:45:51 -05:00
|
|
|
// middleware which blocks requests when we're too busy
|
2017-04-11 18:05:43 -04:00
|
|
|
app.use(require('./lib/web/middleware/tooBusy'))
|
2015-05-04 03:53:29 -04:00
|
|
|
|
2017-03-08 05:45:51 -05:00
|
|
|
app.use(flash())
|
2016-12-01 12:58:14 -05:00
|
|
|
|
2017-03-08 05:45:51 -05:00
|
|
|
// passport
|
|
|
|
app.use(passport.initialize())
|
2021-12-03 04:30:57 -05:00
|
|
|
app.use(useUnless(['/status', '/metrics'], passport.session()))
|
2015-05-04 03:53:29 -04:00
|
|
|
|
2016-12-03 01:37:24 -05:00
|
|
|
// check uri is valid before going further
|
2017-04-13 09:28:00 -04:00
|
|
|
app.use(require('./lib/web/middleware/checkURIValid'))
|
2016-12-11 21:50:43 -05:00
|
|
|
// redirect url without trailing slashes
|
2017-05-07 12:52:13 -04:00
|
|
|
app.use(require('./lib/web/middleware/redirectWithoutTrailingSlashes'))
|
2020-07-02 11:22:52 -04:00
|
|
|
app.use(require('./lib/web/middleware/hedgeDocVersion'))
|
2016-04-20 06:19:11 -04:00
|
|
|
|
2016-04-20 06:14:28 -04:00
|
|
|
// routes need sessions
|
2017-03-08 05:45:51 -05:00
|
|
|
// template files
|
2018-09-10 16:35:38 -04:00
|
|
|
app.set('views', config.viewPath)
|
2017-03-08 05:45:51 -05:00
|
|
|
// set render engine
|
|
|
|
app.engine('ejs', ejs.renderFile)
|
|
|
|
// set view engine
|
|
|
|
app.set('view engine', 'ejs')
|
2018-09-13 15:26:39 -04:00
|
|
|
// set generally available variables for all views
|
|
|
|
app.locals.serverURL = config.serverURL
|
2018-10-05 13:33:40 -04:00
|
|
|
app.locals.sourceURL = config.sourceURL
|
2018-09-13 15:26:39 -04:00
|
|
|
app.locals.allowAnonymous = config.allowAnonymous
|
|
|
|
app.locals.allowAnonymousEdits = config.allowAnonymousEdits
|
|
|
|
app.locals.authProviders = {
|
|
|
|
facebook: config.isFacebookEnable,
|
|
|
|
twitter: config.isTwitterEnable,
|
|
|
|
github: config.isGitHubEnable,
|
|
|
|
gitlab: config.isGitLabEnable,
|
|
|
|
mattermost: config.isMattermostEnable,
|
|
|
|
dropbox: config.isDropboxEnable,
|
|
|
|
google: config.isGoogleEnable,
|
|
|
|
ldap: config.isLDAPEnable,
|
|
|
|
ldapProviderName: config.ldap.providerName,
|
|
|
|
saml: config.isSAMLEnable,
|
|
|
|
oauth2: config.isOAuth2Enable,
|
|
|
|
oauth2ProviderName: config.oauth2.providerName,
|
|
|
|
openID: config.isOpenIDEnable,
|
|
|
|
email: config.isEmailEnable,
|
|
|
|
allowEmailRegister: config.allowEmailRegister
|
|
|
|
}
|
2018-11-07 07:12:50 -05:00
|
|
|
|
|
|
|
// Export/Import menu items
|
|
|
|
app.locals.enableDropBoxSave = config.isDropboxEnable
|
|
|
|
app.locals.enableGitHubGist = config.isGitHubEnable
|
|
|
|
app.locals.enableGitlabSnippets = config.isGitlabSnippetsEnable
|
2016-07-31 12:06:07 -04:00
|
|
|
|
2017-04-11 17:38:54 -04:00
|
|
|
app.use(require('./lib/web/baseRouter'))
|
2017-04-11 17:39:41 -04:00
|
|
|
app.use(require('./lib/web/statusRouter'))
|
2017-04-11 17:41:14 -04:00
|
|
|
app.use(require('./lib/web/auth'))
|
2017-04-11 17:48:55 -04:00
|
|
|
app.use(require('./lib/web/historyRouter'))
|
2017-04-11 17:55:36 -04:00
|
|
|
app.use(require('./lib/web/userRouter'))
|
2017-04-11 18:01:45 -04:00
|
|
|
app.use(require('./lib/web/imageRouter'))
|
2019-10-27 08:51:53 -04:00
|
|
|
app.use(require('./lib/web/note/router'))
|
2017-04-11 17:56:20 -04:00
|
|
|
|
|
|
|
// response not found if no any route matxches
|
2016-04-20 06:19:29 -04:00
|
|
|
app.get('*', function (req, res) {
|
2019-10-27 08:51:53 -04:00
|
|
|
errors.errorNotFound(res)
|
2017-03-08 05:45:51 -05:00
|
|
|
})
|
2015-05-04 03:53:29 -04:00
|
|
|
|
2017-03-08 05:45:51 -05:00
|
|
|
// socket.io secure
|
|
|
|
io.use(realtime.secure)
|
|
|
|
// socket.io auth
|
2015-06-01 06:04:25 -04:00
|
|
|
io.use(passportSocketIo.authorize({
|
2017-03-08 05:45:51 -05:00
|
|
|
cookieParser: cookieParser,
|
2018-03-07 09:17:35 -05:00
|
|
|
key: config.sessionName,
|
|
|
|
secret: config.sessionSecret,
|
2017-03-08 05:45:51 -05:00
|
|
|
store: sessionStore,
|
|
|
|
success: realtime.onAuthorizeSuccess,
|
|
|
|
fail: realtime.onAuthorizeFail
|
|
|
|
}))
|
|
|
|
// socket.io heartbeat
|
2018-03-07 09:17:35 -05:00
|
|
|
io.set('heartbeat interval', config.heartbeatInterval)
|
|
|
|
io.set('heartbeat timeout', config.heartbeatTimeout)
|
2017-03-08 05:45:51 -05:00
|
|
|
// socket.io connection
|
|
|
|
io.sockets.on('connection', realtime.connection)
|
|
|
|
|
|
|
|
// listen
|
|
|
|
function startListen () {
|
2021-02-15 03:42:51 -05:00
|
|
|
let address
|
|
|
|
const listenCallback = function () {
|
|
|
|
const schema = config.useSSL ? 'HTTPS' : 'HTTP'
|
2018-07-22 20:27:51 -04:00
|
|
|
logger.info('%s Server listening at %s', schema, address)
|
2017-04-12 13:57:55 -04:00
|
|
|
realtime.maintenance = false
|
2018-07-22 20:27:51 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// use unix domain socket if 'path' is specified
|
|
|
|
if (config.path) {
|
|
|
|
address = config.path
|
|
|
|
server.listen(config.path, listenCallback)
|
|
|
|
} else {
|
|
|
|
address = config.host + ':' + config.port
|
|
|
|
server.listen(config.port, config.host, listenCallback)
|
|
|
|
}
|
2015-07-11 00:44:16 -04:00
|
|
|
}
|
2016-04-20 06:03:55 -04:00
|
|
|
|
2021-05-04 12:25:30 -04:00
|
|
|
const maxDBTries = 30
|
|
|
|
let currentDBTry = 1
|
|
|
|
function syncAndListen () {
|
|
|
|
// sync db then start listen
|
|
|
|
models.sequelize.authenticate().then(function () {
|
|
|
|
models.runMigrations().then(() => {
|
|
|
|
sessionStore.sync()
|
|
|
|
// check if realtime is ready
|
|
|
|
if (realtime.isReady()) {
|
|
|
|
models.Revision.checkAllNotesRevision(function (err, notes) {
|
|
|
|
if (err) throw new Error(err)
|
|
|
|
if (!notes || notes.length <= 0) return startListen()
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
logger.error('server still not ready after db synced')
|
|
|
|
process.exit(1)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}).catch(() => {
|
|
|
|
if (currentDBTry < maxDBTries) {
|
|
|
|
logger.warn(`Database cannot be reached. Try ${currentDBTry} of ${maxDBTries}.`)
|
|
|
|
currentDBTry++
|
|
|
|
setTimeout(function () {
|
|
|
|
syncAndListen()
|
|
|
|
}, 1000)
|
2021-02-27 15:33:05 -05:00
|
|
|
} else {
|
2021-05-04 12:25:30 -04:00
|
|
|
logger.error('Cannot reach database! Exiting.')
|
|
|
|
process.exit(1)
|
2021-02-27 15:33:05 -05:00
|
|
|
}
|
|
|
|
})
|
2021-05-04 12:25:30 -04:00
|
|
|
}
|
|
|
|
syncAndListen()
|
2016-04-20 06:03:55 -04:00
|
|
|
|
|
|
|
// log uncaught exception
|
2015-07-11 00:44:16 -04:00
|
|
|
process.on('uncaughtException', function (err) {
|
2017-03-08 05:45:51 -05:00
|
|
|
logger.error('An uncaught exception has occured.')
|
|
|
|
logger.error(err)
|
|
|
|
logger.error('Process will exit now.')
|
|
|
|
process.exit(1)
|
|
|
|
})
|
2016-06-01 02:18:54 -04:00
|
|
|
|
2021-08-14 17:59:37 -04:00
|
|
|
let alreadyHandlingTermSignals = false
|
2017-01-19 20:13:09 -05:00
|
|
|
// install exit handler
|
2017-03-08 05:45:51 -05:00
|
|
|
function handleTermSignals () {
|
2021-08-14 17:59:37 -04:00
|
|
|
if (alreadyHandlingTermSignals) {
|
|
|
|
logger.info('Forcefully exiting.')
|
|
|
|
process.exit(1)
|
|
|
|
}
|
2020-07-02 11:22:52 -04:00
|
|
|
logger.info('HedgeDoc has been killed by signal, try to exit gracefully...')
|
2021-08-14 17:59:37 -04:00
|
|
|
alreadyHandlingTermSignals = true
|
2017-04-12 14:00:27 -04:00
|
|
|
realtime.maintenance = true
|
2017-03-08 05:45:51 -05:00
|
|
|
// disconnect all socket.io clients
|
|
|
|
Object.keys(io.sockets.sockets).forEach(function (key) {
|
2021-02-15 03:42:51 -05:00
|
|
|
const socket = io.sockets.sockets[key]
|
2017-03-08 05:45:51 -05:00
|
|
|
// notify client server going into maintenance status
|
|
|
|
socket.emit('maintenance')
|
|
|
|
setTimeout(function () {
|
|
|
|
socket.disconnect(true)
|
|
|
|
}, 0)
|
|
|
|
})
|
2019-04-16 12:19:11 -04:00
|
|
|
if (config.path) {
|
2021-03-29 16:23:09 -04:00
|
|
|
fs.unlink(config.path, err => {
|
|
|
|
if (err) {
|
|
|
|
logger.error(`Could not cleanup socket: ${err.message}`)
|
|
|
|
} else {
|
|
|
|
logger.info('Successfully cleaned up socket')
|
|
|
|
}
|
|
|
|
})
|
2019-04-16 12:19:11 -04:00
|
|
|
}
|
2021-08-14 16:19:07 -04:00
|
|
|
const maxCleanTries = 30
|
|
|
|
let currentCleanTry = 1
|
2021-02-15 03:42:51 -05:00
|
|
|
const checkCleanTimer = setInterval(function () {
|
2017-03-08 05:45:51 -05:00
|
|
|
if (realtime.isReady()) {
|
|
|
|
models.Revision.checkAllNotesRevision(function (err, notes) {
|
2021-08-14 16:19:07 -04:00
|
|
|
if (err) {
|
|
|
|
logger.error('Error while saving note revisions: ' + err)
|
|
|
|
if (currentCleanTry <= maxCleanTries) {
|
|
|
|
logger.warn(`Trying again. Try ${currentCleanTry} of ${maxCleanTries}`)
|
|
|
|
currentCleanTry++
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
logger.error(`Could not save note revisions after ${maxCleanTries} tries! Exiting.`)
|
|
|
|
process.exit(1)
|
|
|
|
}
|
2017-03-08 05:45:51 -05:00
|
|
|
if (!notes || notes.length <= 0) {
|
|
|
|
clearInterval(checkCleanTimer)
|
2021-08-14 16:19:07 -04:00
|
|
|
process.exit(0)
|
2016-06-01 02:18:54 -04:00
|
|
|
}
|
2017-03-08 05:45:51 -05:00
|
|
|
})
|
|
|
|
}
|
2021-08-14 16:19:07 -04:00
|
|
|
}, 200)
|
2017-01-19 20:13:09 -05:00
|
|
|
}
|
2017-03-08 05:45:51 -05:00
|
|
|
process.on('SIGINT', handleTermSignals)
|
|
|
|
process.on('SIGTERM', handleTermSignals)
|
2017-03-22 03:26:35 -04:00
|
|
|
process.on('SIGQUIT', handleTermSignals)
|