mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-05-19 09:45:37 -04:00
sort app.ts
moved handleTermSignals to utils.ts Signed-off-by: Philip Molares <philip.molares@udo.edu> Signed-off-by: David Mehren <dmehren1@gmail.com>
This commit is contained in:
parent
9d17f6a7f4
commit
94c3857d49
2 changed files with 127 additions and 118 deletions
208
lib/app.ts
208
lib/app.ts
|
@ -25,14 +25,22 @@ import { errors } from './errors'
|
||||||
import { logger } from './logger'
|
import { logger } from './logger'
|
||||||
import { Revision, sequelize } from './models'
|
import { Revision, sequelize } from './models'
|
||||||
import { realtime } from './realtime'
|
import { realtime } from './realtime'
|
||||||
|
import { handleTermSignals } from './utils'
|
||||||
import { AuthRouter, BaseRouter, HistoryRouter, ImageRouter, NoteRouter, StatusRouter, UserRouter } from './web/'
|
import { AuthRouter, BaseRouter, HistoryRouter, ImageRouter, NoteRouter, StatusRouter, UserRouter } from './web/'
|
||||||
import { tooBusy, checkURI, redirectWithoutTrailingSlashes, codiMDVersion } from './web/middleware'
|
import { tooBusy, checkURI, redirectWithoutTrailingSlashes, codiMDVersion } from './web/middleware'
|
||||||
|
|
||||||
const SequelizeStore = connect_session_sequelize(session.Store)
|
|
||||||
const rootPath = path.join(__dirname, '..')
|
const rootPath = path.join(__dirname, '..')
|
||||||
|
|
||||||
|
// session store
|
||||||
|
const SequelizeStore = connect_session_sequelize(session.Store)
|
||||||
|
|
||||||
|
const sessionStore = new SequelizeStore({
|
||||||
|
db: sequelize
|
||||||
|
})
|
||||||
|
|
||||||
// server setup
|
// server setup
|
||||||
const app = express()
|
const app = express()
|
||||||
let server: any = null
|
let server: http.Server
|
||||||
if (config.useSSL) {
|
if (config.useSSL) {
|
||||||
const ca: string[] = []
|
const ca: string[] = []
|
||||||
for (const path of config.sslCAPath) {
|
for (const path of config.sslCAPath) {
|
||||||
|
@ -54,15 +62,6 @@ if (config.useSSL) {
|
||||||
server = http.createServer(app)
|
server = http.createServer(app)
|
||||||
}
|
}
|
||||||
|
|
||||||
// logger
|
|
||||||
app.use(morgan('combined', {
|
|
||||||
stream: {
|
|
||||||
write: function (message): void {
|
|
||||||
logger.info(message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
// socket io
|
// socket io
|
||||||
const io = SocketIO(server)
|
const io = SocketIO(server)
|
||||||
io.engine.ws = new WebSocket.Server({
|
io.engine.ws = new WebSocket.Server({
|
||||||
|
@ -72,16 +71,28 @@ io.engine.ws = new WebSocket.Server({
|
||||||
// assign socket io to realtime
|
// assign socket io to realtime
|
||||||
realtime.io = io
|
realtime.io = io
|
||||||
|
|
||||||
// methodOverride
|
// socket.io secure
|
||||||
app.use(methodOverride('_method'))
|
io.use(realtime.secure)
|
||||||
|
// socket.io auth
|
||||||
|
io.use(passportSocketIo.authorize({
|
||||||
|
cookieParser: cookieParser,
|
||||||
|
key: config.sessionName,
|
||||||
|
secret: config.sessionSecret,
|
||||||
|
store: sessionStore,
|
||||||
|
success: realtime.onAuthorizeSuccess,
|
||||||
|
fail: realtime.onAuthorizeFail
|
||||||
|
}))
|
||||||
|
// socket.io connection
|
||||||
|
io.sockets.on('connection', realtime.connection)
|
||||||
|
|
||||||
// session store
|
// logger
|
||||||
const sessionStore = new SequelizeStore({
|
app.use(morgan('combined', {
|
||||||
db: sequelize
|
stream: {
|
||||||
})
|
write: function (message): void {
|
||||||
|
logger.info(message)
|
||||||
// compression
|
}
|
||||||
app.use(compression())
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
// use hsts to tell https users stick to this
|
// use hsts to tell https users stick to this
|
||||||
if (config.hsts.enable) {
|
if (config.hsts.enable) {
|
||||||
|
@ -95,13 +106,6 @@ if (config.hsts.enable) {
|
||||||
logger.info('https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security')
|
logger.info('https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add referrer policy to improve privacy
|
|
||||||
app.use(
|
|
||||||
helmet.referrerPolicy({
|
|
||||||
policy: 'same-origin'
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
// Generate a random nonce per request, for CSP with inline scripts
|
// Generate a random nonce per request, for CSP with inline scripts
|
||||||
app.use(addNonceToLocals)
|
app.use(addNonceToLocals)
|
||||||
|
|
||||||
|
@ -115,6 +119,21 @@ if (config.csp.enable) {
|
||||||
logger.info('Content-Security-Policy is disabled. This may be a security risk.')
|
logger.info('Content-Security-Policy is disabled. This may be a security risk.')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add referrer policy to improve privacy
|
||||||
|
app.use(
|
||||||
|
helmet.referrerPolicy({
|
||||||
|
policy: 'same-origin'
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
// methodOverride
|
||||||
|
app.use(methodOverride('_method'))
|
||||||
|
|
||||||
|
// compression
|
||||||
|
app.use(compression())
|
||||||
|
|
||||||
|
app.use(cookieParser())
|
||||||
|
|
||||||
i18n.configure({
|
i18n.configure({
|
||||||
locales: ['en', 'zh-CN', 'zh-TW', 'fr', 'de', 'ja', 'es', 'ca', 'el', 'pt', 'it', 'tr', 'ru', 'nl', 'hr', 'pl', 'uk', 'hi', 'sv', 'eo', 'da', 'ko', 'id', 'sr', 'vi', 'ar', 'cs', 'sk'],
|
locales: ['en', 'zh-CN', 'zh-TW', 'fr', 'de', 'ja', 'es', 'ca', 'el', 'pt', 'it', 'tr', 'ru', 'nl', 'hr', 'pl', 'uk', 'hi', 'sv', 'eo', 'da', 'ko', 'id', 'sr', 'vi', 'ar', 'cs', 'sk'],
|
||||||
cookie: 'locale',
|
cookie: 'locale',
|
||||||
|
@ -123,16 +142,35 @@ i18n.configure({
|
||||||
updateFiles: config.updateI18nFiles
|
updateFiles: config.updateI18nFiles
|
||||||
})
|
})
|
||||||
|
|
||||||
app.use(cookieParser())
|
|
||||||
|
|
||||||
app.use(i18n.init)
|
app.use(i18n.init)
|
||||||
|
|
||||||
// routes without sessions
|
// set generally available variables for all views
|
||||||
// static files
|
app.locals.useCDN = config.useCDN
|
||||||
app.use('/', express.static(path.resolve(rootPath, config.publicPath), { maxAge: config.staticCacheTime, index: false, redirect: false }))
|
app.locals.serverURL = config.serverURL
|
||||||
app.use('/docs', express.static(path.resolve(rootPath, config.docsPath), { maxAge: config.staticCacheTime, redirect: false }))
|
app.locals.sourceURL = config.sourceURL
|
||||||
app.use('/uploads', express.static(path.resolve(rootPath, config.uploadsPath), { maxAge: config.staticCacheTime, redirect: false }))
|
app.locals.allowAnonymous = config.allowAnonymous
|
||||||
app.use('/default.md', express.static(path.resolve(rootPath, config.defaultNotePath), { maxAge: config.staticCacheTime }))
|
app.locals.allowAnonymousEdits = config.allowAnonymousEdits
|
||||||
|
app.locals.authProviders = {
|
||||||
|
facebook: config.isFacebookEnable,
|
||||||
|
twitter: config.isTwitterEnable,
|
||||||
|
github: config.isGitHubEnable,
|
||||||
|
gitlab: config.isGitLabEnable,
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export/Import menu items
|
||||||
|
app.locals.enableDropBoxSave = config.isDropboxEnable
|
||||||
|
app.locals.enableGitHubGist = config.isGitHubEnable
|
||||||
|
app.locals.enableGitlabSnippets = config.isGitlabSnippetsEnable
|
||||||
|
|
||||||
// session
|
// session
|
||||||
app.use(session({
|
app.use(session({
|
||||||
|
@ -172,6 +210,13 @@ app.use(checkURI)
|
||||||
app.use(redirectWithoutTrailingSlashes)
|
app.use(redirectWithoutTrailingSlashes)
|
||||||
app.use(codiMDVersion)
|
app.use(codiMDVersion)
|
||||||
|
|
||||||
|
// routes without sessions
|
||||||
|
// static files
|
||||||
|
app.use('/', express.static(path.resolve(rootPath, config.publicPath), { maxAge: config.staticCacheTime, index: false, redirect: false }))
|
||||||
|
app.use('/docs', express.static(path.resolve(rootPath, config.docsPath), { maxAge: config.staticCacheTime, redirect: false }))
|
||||||
|
app.use('/uploads', express.static(path.resolve(rootPath, config.uploadsPath), { maxAge: config.staticCacheTime, redirect: false }))
|
||||||
|
app.use('/default.md', express.static(path.resolve(rootPath, config.defaultNotePath), { maxAge: config.staticCacheTime }))
|
||||||
|
|
||||||
// routes need sessions
|
// routes need sessions
|
||||||
// template files
|
// template files
|
||||||
app.set('views', config.viewPath)
|
app.set('views', config.viewPath)
|
||||||
|
@ -179,33 +224,6 @@ app.set('views', config.viewPath)
|
||||||
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')
|
||||||
// set generally available variables for all views
|
|
||||||
app.locals.useCDN = config.useCDN
|
|
||||||
app.locals.serverURL = config.serverURL
|
|
||||||
app.locals.sourceURL = config.sourceURL
|
|
||||||
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,
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// Export/Import menu items
|
|
||||||
app.locals.enableDropBoxSave = config.isDropboxEnable
|
|
||||||
app.locals.enableGitHubGist = config.isGitHubEnable
|
|
||||||
app.locals.enableGitlabSnippets = config.isGitlabSnippetsEnable
|
|
||||||
|
|
||||||
app.use(BaseRouter)
|
app.use(BaseRouter)
|
||||||
app.use(StatusRouter)
|
app.use(StatusRouter)
|
||||||
|
@ -220,19 +238,13 @@ app.get('*', function (req, res) {
|
||||||
errors.errorNotFound(res)
|
errors.errorNotFound(res)
|
||||||
})
|
})
|
||||||
|
|
||||||
// socket.io secure
|
// log uncaught exception
|
||||||
io.use(realtime.secure)
|
process.on('uncaughtException', function (err) {
|
||||||
// socket.io auth
|
logger.error('An uncaught exception has occured.')
|
||||||
io.use(passportSocketIo.authorize({
|
logger.error(err)
|
||||||
cookieParser: cookieParser,
|
logger.error('Process will exit now.')
|
||||||
key: config.sessionName,
|
process.exit(1)
|
||||||
secret: config.sessionSecret,
|
})
|
||||||
store: sessionStore,
|
|
||||||
success: realtime.onAuthorizeSuccess,
|
|
||||||
fail: realtime.onAuthorizeFail
|
|
||||||
}))
|
|
||||||
// socket.io connection
|
|
||||||
io.sockets.on('connection', realtime.connection)
|
|
||||||
|
|
||||||
// listen
|
// listen
|
||||||
function startListen (): void {
|
function startListen (): void {
|
||||||
|
@ -270,46 +282,6 @@ sequelize.authenticate().then(function () {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// log uncaught exception
|
process.on('SIGINT', () => handleTermSignals(io))
|
||||||
process.on('uncaughtException', function (err) {
|
process.on('SIGTERM', () => handleTermSignals(io))
|
||||||
logger.error('An uncaught exception has occured.')
|
process.on('SIGQUIT', () => handleTermSignals(io))
|
||||||
logger.error(err)
|
|
||||||
logger.error('Process will exit now.')
|
|
||||||
process.exit(1)
|
|
||||||
})
|
|
||||||
|
|
||||||
// install exit handler
|
|
||||||
function handleTermSignals (): void {
|
|
||||||
logger.info('CodiMD has been killed by signal, try to exit gracefully...')
|
|
||||||
realtime.maintenance = true
|
|
||||||
// disconnect all socket.io clients
|
|
||||||
Object.keys(io.sockets.sockets).forEach(function (key) {
|
|
||||||
const socket = io.sockets.sockets[key]
|
|
||||||
// notify client server going into maintenance status
|
|
||||||
socket.emit('maintenance')
|
|
||||||
setTimeout(function () {
|
|
||||||
socket.disconnect(true)
|
|
||||||
}, 0)
|
|
||||||
})
|
|
||||||
if (config.path) {
|
|
||||||
// ToDo: add a proper error handler
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
||||||
fs.unlink(config.path, (_) => {})
|
|
||||||
}
|
|
||||||
const checkCleanTimer = setInterval(function () {
|
|
||||||
if (realtime.isReady()) {
|
|
||||||
Revision.checkAllNotesRevision(function (err, notes) {
|
|
||||||
if (err) {
|
|
||||||
return logger.error(err)
|
|
||||||
}
|
|
||||||
if (!notes || notes.length <= 0) {
|
|
||||||
clearInterval(checkCleanTimer)
|
|
||||||
return process.exit(0)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}, 100)
|
|
||||||
}
|
|
||||||
process.on('SIGINT', handleTermSignals)
|
|
||||||
process.on('SIGTERM', handleTermSignals)
|
|
||||||
process.on('SIGQUIT', handleTermSignals)
|
|
||||||
|
|
37
lib/utils.ts
37
lib/utils.ts
|
@ -1,4 +1,9 @@
|
||||||
import { Sequelize } from 'sequelize-typescript'
|
import { Sequelize } from 'sequelize-typescript'
|
||||||
|
import { logger } from './logger'
|
||||||
|
import { realtime } from './realtime'
|
||||||
|
import { config } from './config'
|
||||||
|
import fs from 'fs'
|
||||||
|
import { Revision } from './models'
|
||||||
|
|
||||||
export function isSQLite (sequelize: Sequelize): boolean {
|
export function isSQLite (sequelize: Sequelize): boolean {
|
||||||
return sequelize.options.dialect === 'sqlite'
|
return sequelize.options.dialect === 'sqlite'
|
||||||
|
@ -49,3 +54,35 @@ export function processData (data, _default, process?) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function handleTermSignals (io): void {
|
||||||
|
logger.info('CodiMD has been killed by signal, try to exit gracefully...')
|
||||||
|
realtime.maintenance = true
|
||||||
|
// disconnect all socket.io clients
|
||||||
|
Object.keys(io.sockets.sockets).forEach(function (key) {
|
||||||
|
const socket = io.sockets.sockets[key]
|
||||||
|
// notify client server going into maintenance status
|
||||||
|
socket.emit('maintenance')
|
||||||
|
setTimeout(function () {
|
||||||
|
socket.disconnect(true)
|
||||||
|
}, 0)
|
||||||
|
})
|
||||||
|
if (config.path) {
|
||||||
|
// ToDo: add a proper error handler
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||||
|
fs.unlink(config.path, (_) => {})
|
||||||
|
}
|
||||||
|
const checkCleanTimer = setInterval(function () {
|
||||||
|
if (realtime.isReady()) {
|
||||||
|
Revision.checkAllNotesRevision(function (err, notes) {
|
||||||
|
if (err) {
|
||||||
|
return logger.error(err)
|
||||||
|
}
|
||||||
|
if (!notes || notes.length <= 0) {
|
||||||
|
clearInterval(checkCleanTimer)
|
||||||
|
return process.exit(0)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue