feat: let's encrypt

This commit is contained in:
NGPixel 2020-01-11 22:33:19 -05:00
parent 73da73a595
commit c6933a2d20
13 changed files with 459 additions and 160 deletions

View File

@ -29,7 +29,6 @@
v-btn.px-5(value='rradial') v-btn.px-5(value='rradial')
v-icon(left, :color='graphMode === `rradial` ? `primary` : `grey darken-3`') mdi-blur-radial v-icon(left, :color='graphMode === `rradial` ? `primary` : `grey darken-3`') mdi-blur-radial
span.text-none Relational Radial span.text-none Relational Radial
v-chip.ml-3(x-small) Beta
.admin-pages-visualize-svg.pa-10(ref='svgContainer') .admin-pages-visualize-svg.pa-10(ref='svgContainer')
v-alert(v-if='pages.length < 1', outlined, type='warning', style='max-width: 650px; margin: 0 auto;') Looks like there's no data yet to graph! v-alert(v-if='pages.length < 1', outlined, type='warning', style='max-width: 650px; margin: 0 auto;') Looks like there's no data yet to graph!
</template> </template>
@ -61,9 +60,14 @@ export default {
} }
}, },
methods: { methods: {
goToPage (d) {
if (_.get(d, 'data.id', 0) > 0) {
this.$router.push(`${d.data.id}`)
}
},
bilink (root) { bilink (root) {
const map = new Map(root.leaves().map(d => [d.data.path, d])) const map = new Map(root.descendants().map(d => [d.data.path, d]))
for (const d of root.leaves()) { for (const d of root.descendants()) {
d.incoming = [] d.incoming = []
d.outgoing = [] d.outgoing = []
d.data.links.forEach(i => { d.data.links.forEach(i => {
@ -73,7 +77,7 @@ export default {
} }
}) })
} }
for (const d of root.leaves()) { for (const d of root.descendants()) {
for (const o of d.outgoing) { for (const o of d.outgoing) {
if (o[1]) { if (o[1]) {
o[1].incoming.push(o) o[1].incoming.push(o)
@ -112,8 +116,11 @@ export default {
children: result children: result
} }
}, },
/**
* Relational Radial
*/
drawRelations () { drawRelations () {
const data = this.hierarchy(this.pages) const data = this.hierarchy(this.pages, true)
const line = d3.lineRadial() const line = d3.lineRadial()
.curve(d3.curveBundle.beta(0.85)) .curve(d3.curveBundle.beta(0.85))
@ -124,16 +131,26 @@ export default {
.size([2 * Math.PI, this.radius - 100]) .size([2 * Math.PI, this.radius - 100])
const root = tree(this.bilink(d3.hierarchy(data) const root = tree(this.bilink(d3.hierarchy(data)
.sort((a, b) => d3.ascending(a.height, b.height) || d3.ascending(a.data.title, b.data.title)))) .sort((a, b) => d3.ascending(a.height, b.height) || d3.ascending(a.data.path, b.data.path))))
const svg = d3.create('svg') const svg = d3.create('svg')
.attr('viewBox', [-this.width / 2, -this.width / 2, this.width, this.width]) .attr('viewBox', [-this.width / 2, -this.width / 2, this.width, this.width])
const link = svg.append('g')
.attr('stroke', '#CCC')
.attr('fill', 'none')
.selectAll('path')
.data(root.descendants().flatMap(leaf => leaf.outgoing))
.join('path')
.style('mix-blend-mode', 'multiply')
.attr('d', ([i, o]) => line(i.path(o)))
.each(function(d) { d.path = this })
svg.append('g') svg.append('g')
.attr('font-family', 'sans-serif') .attr('font-family', 'sans-serif')
.attr('font-size', 10) .attr('font-size', 10)
.selectAll('g') .selectAll('g')
.data(root.leaves()) .data(root.descendants())
.join('g') .join('g')
.attr('transform', d => `rotate(${d.x * 180 / Math.PI - 90}) translate(${d.y},0)`) .attr('transform', d => `rotate(${d.x * 180 / Math.PI - 90}) translate(${d.y},0)`)
.append('text') .append('text')
@ -142,23 +159,17 @@ export default {
.attr('text-anchor', d => d.x < Math.PI ? 'start' : 'end') .attr('text-anchor', d => d.x < Math.PI ? 'start' : 'end')
.attr('transform', d => d.x >= Math.PI ? 'rotate(180)' : null) .attr('transform', d => d.x >= Math.PI ? 'rotate(180)' : null)
.attr('fill', this.$vuetify.theme.dark ? 'white' : '') .attr('fill', this.$vuetify.theme.dark ? 'white' : '')
.attr('cursor', 'pointer')
.text(d => d.data.title) .text(d => d.data.title)
.each(function(d) { d.text = this }) .each(function(d) { d.text = this })
.on('mouseover', overed) .on('mouseover', overed)
.on('mouseout', outed) .on('mouseout', outed)
.on('click', d => this.goToPage(d))
.call(text => text.append('title').text(d => `${d.data.path} .call(text => text.append('title').text(d => `${d.data.path}
${d.outgoing.length} outgoing ${d.outgoing.length} outgoing
${d.incoming.length} incoming`)) ${d.incoming.length} incoming`))
.clone(true).lower()
const link = svg.append('g') .attr('stroke', this.$vuetify.theme.dark ? '#222' : 'white')
.attr('stroke', '#CCC')
.attr('fill', 'none')
.selectAll('path')
.data(root.leaves().flatMap(leaf => leaf.outgoing))
.join('path')
.style('mix-blend-mode', 'multiply')
.attr('d', ([i, o]) => line(i.path(o)))
.each(function(d) { d.path = this })
function overed(d) { function overed(d) {
link.style('mix-blend-mode', null) link.style('mix-blend-mode', null)
@ -180,6 +191,9 @@ export default {
this.$refs.svgContainer.appendChild(svg.node()) this.$refs.svgContainer.appendChild(svg.node())
}, },
/**
* Hierarchical Tree
*/
drawTree () { drawTree () {
const data = this.hierarchy(this.pages, true) const data = this.hierarchy(this.pages, true)
@ -232,12 +246,17 @@ export default {
.attr('x', d => d.children ? -6 : 6) .attr('x', d => d.children ? -6 : 6)
.attr('text-anchor', d => d.children ? 'end' : 'start') .attr('text-anchor', d => d.children ? 'end' : 'start')
.attr('fill', this.$vuetify.theme.dark ? 'white' : '') .attr('fill', this.$vuetify.theme.dark ? 'white' : '')
.attr('cursor', 'pointer')
.text(d => d.data.title) .text(d => d.data.title)
.on('click', d => this.goToPage(d))
.clone(true).lower() .clone(true).lower()
.attr('stroke', this.$vuetify.theme.dark ? '#222' : 'white') .attr('stroke', this.$vuetify.theme.dark ? '#222' : 'white')
this.$refs.svgContainer.appendChild(svg.node()) this.$refs.svgContainer.appendChild(svg.node())
}, },
/**
* Hierarchical Radial
*/
drawRadialTree () { drawRadialTree () {
const data = this.hierarchy(this.pages) const data = this.hierarchy(this.pages)
@ -286,7 +305,9 @@ export default {
.attr('transform', d => d.x >= Math.PI ? 'rotate(180)' : null) .attr('transform', d => d.x >= Math.PI ? 'rotate(180)' : null)
/* eslint-enable no-mixed-operators */ /* eslint-enable no-mixed-operators */
.attr('fill', this.$vuetify.theme.dark ? 'white' : '') .attr('fill', this.$vuetify.theme.dark ? 'white' : '')
.attr('cursor', 'pointer')
.text(d => d.data.title) .text(d => d.data.title)
.on('click', d => this.goToPage(d))
.clone(true).lower() .clone(true).lower()
.attr('stroke', this.$vuetify.theme.dark ? '#222' : 'white') .attr('stroke', this.$vuetify.theme.dark ? '#222' : 'white')

View File

@ -59,7 +59,12 @@ db:
ssl: ssl:
enabled: false enabled: false
port: 3443
# Provider to use, possible values: custom, letsencrypt
provider: custom
# ++++++ For custom only ++++++
# Certificate format, either 'pem' or 'pfx': # Certificate format, either 'pem' or 'pfx':
format: pem format: pem
# Using PEM format: # Using PEM format:
@ -73,9 +78,9 @@ ssl:
# to 1024 bits (default: null): # to 1024 bits (default: null):
dhparam: null dhparam: null
# Listen on this HTTP port and redirect all requests to HTTPS. # ++++++ For letsencrypt only ++++++
# Set to false to disable (default: 80): domain: wiki.yourdomain.com
redirectNonSSLPort: 80 maintainerEmail: admin@example.com
# --------------------------------------------------------------------- # ---------------------------------------------------------------------
# Database Pool Options # Database Pool Options

View File

@ -29,6 +29,7 @@ LABEL maintainer="requarks.io"
RUN apk add bash curl git openssh gnupg sqlite --no-cache && \ RUN apk add bash curl git openssh gnupg sqlite --no-cache && \
mkdir -p /wiki && \ mkdir -p /wiki && \
mkdir -p /logs && \ mkdir -p /logs && \
mkdir -p /wiki/data/content && \
chown -R node:node /wiki /logs chown -R node:node /wiki /logs
WORKDIR /wiki WORKDIR /wiki
@ -44,8 +45,11 @@ COPY --chown=node:node ./LICENSE ./LICENSE
USER node USER node
EXPOSE 3000 VOLUME ["/wiki/data/content"]
# HEALTHCHECK --interval=30s --timeout=30s --start-period=30s --retries=3 CMD curl -f http://localhost/healthz EXPOSE 3000
EXPOSE 3443
# HEALTHCHECK --interval=30s --timeout=30s --start-period=30s --retries=3 CMD curl -f http://localhost:3000/healthz
CMD ["node", "server"] CMD ["node", "server"]

View File

@ -9,5 +9,10 @@ db:
db: $(DB_NAME) db: $(DB_NAME)
storage: $(DB_FILEPATH) storage: $(DB_FILEPATH)
ssl: $(DB_SSL) ssl: $(DB_SSL)
trustProxy: $(TRUST_PROXY) ssl:
enabled: $(SSL_ACTIVE)
port: 3443
provider: letsencrypt
domain: $(LETSENCRYPT_DOMAIN)
maintainerEmail: $(LETSENCRYPT_EMAIL)
logLevel: info logLevel: info

View File

@ -27,6 +27,7 @@ services:
restart: unless-stopped restart: unless-stopped
ports: ports:
- "80:3000" - "80:3000"
- "443:3443"
volumes: volumes:
db-data: db-data:

View File

@ -67,8 +67,8 @@ const init = {
console.warn(chalk.yellow('--- Closing DB connections...')) console.warn(chalk.yellow('--- Closing DB connections...'))
await global.WIKI.models.knex.destroy() await global.WIKI.models.knex.destroy()
console.warn(chalk.yellow('--- Closing Server connections...')) console.warn(chalk.yellow('--- Closing Server connections...'))
if (global.WIKI.server) { if (global.WIKI.servers) {
await new Promise((resolve, reject) => global.WIKI.server.destroy(resolve)) await global.WIKI.servers.stopServers()
} }
console.warn(chalk.yellow('--- Purging node modules cache...')) console.warn(chalk.yellow('--- Purging node modules cache...'))

View File

@ -39,6 +39,10 @@
"@azure/storage-blob": "12.0.1", "@azure/storage-blob": "12.0.1",
"@bugsnag/js": "6.5.0", "@bugsnag/js": "6.5.0",
"@exlinc/keycloak-passport": "1.0.2", "@exlinc/keycloak-passport": "1.0.2",
"@root/csr": "0.8.1",
"@root/keypairs": "0.9.0",
"@root/pem": "1.0.4",
"acme": "3.0.3",
"algoliasearch": "3.35.1", "algoliasearch": "3.35.1",
"apollo-fetch": "0.7.0", "apollo-fetch": "0.7.0",
"apollo-server": "2.9.15", "apollo-server": "2.9.15",
@ -143,6 +147,7 @@
"pg-query-stream": "2.1.2", "pg-query-stream": "2.1.2",
"pg-tsquery": "8.1.0", "pg-tsquery": "8.1.0",
"pug": "2.0.4", "pug": "2.0.4",
"punycode": "2.1.1",
"qr-image": "3.2.0", "qr-image": "3.2.0",
"raven": "2.6.4", "raven": "2.6.4",
"remove-markdown": "0.3.0", "remove-markdown": "0.3.0",

View File

@ -0,0 +1,25 @@
const express = require('express')
const router = express.Router()
const _ = require('lodash')
/* global WIKI */
/**
* Let's Encrypt Challenge
*/
router.get('/.well-known/acme-challenge/:token', (req, res, next) => {
res.type('text/plain')
if (_.get(WIKI.config, 'letsencrypt.challenge', false)) {
if (WIKI.config.letsencrypt.challenge.token === req.params.token) {
res.send(WIKI.config.letsencrypt.challenge.keyAuthorization)
WIKI.logger.info(`(LETSENCRYPT) Received valid challenge request. [ ACCEPTED ]`)
} else {
res.status(406).send('Invalid Challenge Token!')
WIKI.logger.warn(`(LETSENCRYPT) Received invalid challenge request. [ REJECTED ]`)
}
} else {
res.status(418).end()
}
})
module.exports = router

View File

@ -34,6 +34,7 @@ module.exports = {
await this.initTelemetry() await this.initTelemetry()
WIKI.cache = require('./cache').init() WIKI.cache = require('./cache').init()
WIKI.scheduler = require('./scheduler').init() WIKI.scheduler = require('./scheduler').init()
WIKI.servers = require('./servers')
WIKI.sideloader = require('./sideloader').init() WIKI.sideloader = require('./sideloader').init()
WIKI.events = new EventEmitter() WIKI.events = new EventEmitter()
} catch (err) { } catch (err) {

125
server/core/letsencrypt.js Normal file
View File

@ -0,0 +1,125 @@
const ACME = require('acme')
const Keypairs = require('@root/keypairs')
const _ = require('lodash')
const moment = require('moment')
const CSR = require('@root/csr')
const PEM = require('@root/pem')
// eslint-disable-next-line node/no-deprecated-api
const punycode = require('punycode')
/* global WIKI */
module.exports = {
apiDirectory: WIKI.dev ? 'https://acme-staging-v02.api.letsencrypt.org/directory' : 'https://acme-v02.api.letsencrypt.org/directory',
acme: null,
async init () {
if (!_.get(WIKI.config, 'letsencrypt.payload', false)) {
await this.requestCertificate()
} else if (WIKI.config.letsencrypt.domain !== WIKI.config.ssl.domain) {
WIKI.logger.info(`(LETSENCRYPT) Domain has changed. Requesting new certificates...`)
await this.requestCertificate()
} else if (moment(WIKI.config.letsencrypt.payload.expires).isSameOrBefore(moment().add(5, 'days'))) {
WIKI.logger.info(`(LETSENCRYPT) Certificate is about to or has expired, requesting a new one...`)
await this.requestCertificate()
} else {
WIKI.logger.info(`(LETSENCRYPT) Using existing certificate for ${WIKI.config.ssl.domain}, expires on ${WIKI.config.letsencrypt.payload.expires}: [ OK ]`)
}
WIKI.config.ssl.format = 'pem'
WIKI.config.ssl.inline = true
WIKI.config.ssl.key = WIKI.config.letsencrypt.serverKey
WIKI.config.ssl.cert = WIKI.config.letsencrypt.payload.cert + '\n' + WIKI.config.letsencrypt.payload.chain
WIKI.config.ssl.passphrase = null
WIKI.config.ssl.dhparam = null
},
async requestCertificate () {
try {
WIKI.logger.info(`(LETSENCRYPT) Initializing Let's Encrypt client...`)
this.acme = ACME.create({
maintainerEmail: WIKI.config.ssl.maintainerEmail,
packageAgent: `wikijs/${WIKI.version}`,
notify: (ev, msg) => {
if (_.includes(['warning', 'error'], ev)) {
WIKI.logger.warn(`${ev}: ${msg}`)
} else {
WIKI.logger.debug(`${ev}: ${JSON.stringify(msg)}`)
}
}
})
await this.acme.init(this.apiDirectory)
// -> Create ACME Subscriber account
if (!_.get(WIKI.config, 'letsencrypt.account', false)) {
WIKI.logger.info(`(LETSENCRYPT) Setting up account for the first time...`)
const accountKeypair = await Keypairs.generate({ kty: 'EC', format: 'jwk' })
const account = await this.acme.accounts.create({
subscriberEmail: WIKI.config.ssl.maintainerEmail,
agreeToTerms: true,
accountKey: accountKeypair.private
})
WIKI.config.letsencrypt = {
accountKeypair: accountKeypair,
account: account,
domain: WIKI.config.ssl.domain
}
await WIKI.configSvc.saveToDb(['letsencrypt'])
WIKI.logger.info(`(LETSENCRYPT) Account was setup successfully [ OK ]`)
}
// -> Create Server Keypair
if (!WIKI.config.letsencrypt.serverKey) {
WIKI.logger.info(`(LETSENCRYPT) Generating server keypairs...`)
const serverKeypair = await Keypairs.generate({ kty: 'RSA', format: 'jwk' })
WIKI.config.letsencrypt.serverKey = await Keypairs.export({ jwk: serverKeypair.private })
WIKI.logger.info(`(LETSENCRYPT) Server keypairs generated successfully [ OK ]`)
}
// -> Create CSR
WIKI.logger.info(`(LETSENCRYPT) Generating certificate signing request (CSR)...`)
const domains = [ punycode.toASCII(WIKI.config.ssl.domain) ]
const serverKey = await Keypairs.import({ pem: WIKI.config.letsencrypt.serverKey })
const csrDer = await CSR.csr({ jwk: serverKey, domains, encoding: 'der' })
const csr = PEM.packBlock({ type: 'CERTIFICATE REQUEST', bytes: csrDer })
WIKI.logger.info(`(LETSENCRYPT) CSR generated successfully [ OK ]`)
// -> Verify Domain + Get Certificate
WIKI.logger.info(`(LETSENCRYPT) Requesting certificate from Let's Encrypt...`)
const certResp = await this.acme.certificates.create({
account: WIKI.config.letsencrypt.account,
accountKey: WIKI.config.letsencrypt.accountKeypair.private,
csr,
domains,
challenges: {
'http-01': {
init () {},
set (data) {
WIKI.logger.info(`(LETSENCRYPT) Setting HTTP challenge for ${data.challenge.hostname}: [ READY ]`)
WIKI.config.letsencrypt.challenge = data.challenge
WIKI.logger.info(`(LETSENCRYPT) Waiting for challenge to complete...`)
return null // <- this is needed, cannot be undefined
},
get (data) {
return WIKI.config.letsencrypt.challenge
},
async remove (data) {
WIKI.logger.info(`(LETSENCRYPT) Removing HTTP challenge: [ OK ]`)
WIKI.config.letsencrypt.challenge = null
return null // <- this is needed, cannot be undefined
}
}
}
})
WIKI.logger.info(`(LETSENCRYPT) New certifiate received successfully: [ COMPLETED ]`)
WIKI.config.letsencrypt.payload = certResp
WIKI.config.letsencrypt.domain = WIKI.config.ssl.domain
await WIKI.configSvc.saveToDb(['letsencrypt'])
} catch (err) {
WIKI.logger.warn(`(LETSENCRYPT) ${err}`)
throw err
}
}
}

159
server/core/servers.js Normal file
View File

@ -0,0 +1,159 @@
const fs = require('fs-extra')
const http = require('http')
const https = require('https')
const { ApolloServer } = require('apollo-server-express')
const Promise = require('bluebird')
const _ = require('lodash')
/* global WIKI */
module.exports = {
servers: {
graph: null,
http: null,
https: null
},
connections: new Map(),
le: null,
/**
* Start HTTP Server
*/
async startHTTP () {
WIKI.logger.info(`HTTP Server on port: [ ${WIKI.config.port} ]`)
this.servers.http = http.createServer(WIKI.app)
this.servers.graph.installSubscriptionHandlers(this.servers.http)
this.servers.http.listen(WIKI.config.port, WIKI.config.bindIP)
this.servers.http.on('error', (error) => {
if (error.syscall !== 'listen') {
throw error
}
switch (error.code) {
case 'EACCES':
WIKI.logger.error('Listening on port ' + WIKI.config.port + ' requires elevated privileges!')
return process.exit(1)
case 'EADDRINUSE':
WIKI.logger.error('Port ' + WIKI.config.port + ' is already in use!')
return process.exit(1)
default:
throw error
}
})
this.servers.http.on('listening', () => {
WIKI.logger.info('HTTP Server: [ RUNNING ]')
})
this.servers.http.on('connection', conn => {
let connKey = `${conn.remoteAddress}:${conn.remotePort}`
this.connections.set(connKey, conn)
conn.on('close', () => {
this.connections.delete(connKey)
})
})
},
/**
* Start HTTPS Server
*/
async startHTTPS () {
if (WIKI.config.ssl.provider === 'letsencrypt') {
this.le = require('./letsencrypt')
await this.le.init()
}
WIKI.logger.info(`HTTPS Server on port: [ ${WIKI.config.ssl.port} ]`)
const tlsOpts = {}
try {
if (WIKI.config.ssl.format === 'pem') {
tlsOpts.key = WIKI.config.ssl.inline ? WIKI.config.ssl.key : fs.readFileSync(WIKI.config.ssl.key)
tlsOpts.cert = WIKI.config.ssl.inline ? WIKI.config.ssl.cert : fs.readFileSync(WIKI.config.ssl.cert)
} else {
tlsOpts.pfx = WIKI.config.ssl.inline ? WIKI.config.ssl.pfx : fs.readFileSync(WIKI.config.ssl.pfx)
}
if (!_.isEmpty(WIKI.config.ssl.passphrase)) {
tlsOpts.passphrase = WIKI.config.ssl.passphrase
}
if (!_.isEmpty(WIKI.config.ssl.dhparam)) {
tlsOpts.dhparam = WIKI.config.ssl.dhparam
}
} catch (err) {
WIKI.logger.error('Failed to setup HTTPS server parameters:')
WIKI.logger.error(err)
return process.exit(1)
}
this.servers.https = https.createServer(tlsOpts, WIKI.app)
this.servers.graph.installSubscriptionHandlers(this.servers.https)
this.servers.https.listen(WIKI.config.ssl.port, WIKI.config.bindIP)
this.servers.https.on('error', (error) => {
if (error.syscall !== 'listen') {
throw error
}
switch (error.code) {
case 'EACCES':
WIKI.logger.error('Listening on port ' + WIKI.config.ssl.port + ' requires elevated privileges!')
return process.exit(1)
case 'EADDRINUSE':
WIKI.logger.error('Port ' + WIKI.config.ssl.port + ' is already in use!')
return process.exit(1)
default:
throw error
}
})
this.servers.https.on('listening', () => {
WIKI.logger.info('HTTPS Server: [ RUNNING ]')
})
this.servers.https.on('connection', conn => {
let connKey = `${conn.remoteAddress}:${conn.remotePort}`
this.connections.set(connKey, conn)
conn.on('close', () => {
this.connections.delete(connKey)
})
})
},
/**
* Start GraphQL Server
*/
async startGraphQL () {
const graphqlSchema = require('../graph')
this.servers.graph = new ApolloServer({
...graphqlSchema,
context: ({ req, res }) => ({ req, res }),
subscriptions: {
onConnect: (connectionParams, webSocket) => {
},
path: '/graphql-subscriptions'
}
})
this.servers.graph.applyMiddleware({ app: WIKI.app })
},
/**
* Close all active connections
*/
closeConnections () {
for (const conn of this.connections) {
conn.destroy()
}
this.connections.clear()
},
/**
* Stop all servers
*/
async stopServers () {
this.closeConnections()
if (this.servers.http) {
await Promise.fromCallback(cb => { this.servers.http.close(cb) })
this.servers.http = null
}
if (this.servers.https) {
await Promise.fromCallback(cb => { this.servers.https.close(cb) })
this.servers.https = null
}
this.servers.graph = null
}
}

View File

@ -7,12 +7,8 @@ const express = require('express')
const session = require('express-session') const session = require('express-session')
const KnexSessionStore = require('connect-session-knex')(session) const KnexSessionStore = require('connect-session-knex')(session)
const favicon = require('serve-favicon') const favicon = require('serve-favicon')
const fs = require('fs-extra')
const http = require('http')
const https = require('https')
const path = require('path') const path = require('path')
const _ = require('lodash') const _ = require('lodash')
const { ApolloServer } = require('apollo-server-express')
/* global WIKI */ /* global WIKI */
@ -62,6 +58,12 @@ module.exports = async () => {
maxAge: '7d' maxAge: '7d'
})) }))
// ----------------------------------------
// Let's Encrypt Challenge
// ----------------------------------------
app.use('/', ctrl.letsencrypt)
// ---------------------------------------- // ----------------------------------------
// Passport Authentication // Passport Authentication
// ---------------------------------------- // ----------------------------------------
@ -104,6 +106,7 @@ module.exports = async () => {
// View accessible data // View accessible data
// ---------------------------------------- // ----------------------------------------
app.locals.siteConfig = {}
app.locals.analyticsCode = {} app.locals.analyticsCode = {}
app.locals.basedir = WIKI.ROOTPATH app.locals.basedir = WIKI.ROOTPATH
app.locals.config = WIKI.config app.locals.config = WIKI.config
@ -124,23 +127,6 @@ module.exports = async () => {
app.use(global.WP_DEV.hotMiddleware) app.use(global.WP_DEV.hotMiddleware)
} }
// ----------------------------------------
// Apollo Server (GraphQL)
// ----------------------------------------
const graphqlSchema = require('./graph')
const apolloServer = new ApolloServer({
...graphqlSchema,
context: ({ req, res }) => ({ req, res }),
subscriptions: {
onConnect: (connectionParams, webSocket) => {
},
path: '/graphql-subscriptions'
}
})
apolloServer.applyMiddleware({ app })
// ---------------------------------------- // ----------------------------------------
// Routing // Routing
// ---------------------------------------- // ----------------------------------------
@ -184,118 +170,14 @@ module.exports = async () => {
}) })
// ---------------------------------------- // ----------------------------------------
// HTTP/S server // Start HTTP Server(s)
// ---------------------------------------- // ----------------------------------------
let srvConnections = {} await WIKI.servers.startGraphQL()
await WIKI.servers.startHTTP()
app.set('port', WIKI.config.port) if (WIKI.config.ssl.enabled === true || WIKI.config.ssl.enabled === 'true' || WIKI.config.ssl.enabled === 1 || WIKI.config.ssl.enabled === '1') {
if (WIKI.config.ssl.enabled) { await WIKI.servers.startHTTPS()
WIKI.logger.info(`HTTPS Server on port: [ ${WIKI.config.port} ]`)
const tlsOpts = {}
try {
if (WIKI.config.ssl.format === 'pem') {
tlsOpts.key = fs.readFileSync(WIKI.config.ssl.key)
tlsOpts.cert = fs.readFileSync(WIKI.config.ssl.cert)
} else {
tlsOpts.pfx = fs.readFileSync(WIKI.config.ssl.pfx)
}
if (!_.isEmpty(WIKI.config.ssl.passphrase)) {
tlsOpts.passphrase = WIKI.config.ssl.passphrase
}
if (!_.isEmpty(WIKI.config.ssl.dhparam)) {
tlsOpts.dhparam = WIKI.config.ssl.dhparam
}
} catch (err) {
WIKI.logger.error('Failed to setup HTTPS server parameters:')
WIKI.logger.error(err)
return process.exit(1)
}
WIKI.server = https.createServer(tlsOpts, app)
// HTTP Redirect Server
if (WIKI.config.ssl.redirectNonSSLPort) {
WIKI.serverAlt = http.createServer((req, res) => {
res.writeHead(301, { 'Location': 'https://' + req.headers['host'] + req.url })
res.end()
})
}
} else {
WIKI.logger.info(`HTTP Server on port: [ ${WIKI.config.port} ]`)
WIKI.server = http.createServer(app)
}
apolloServer.installSubscriptionHandlers(WIKI.server)
WIKI.server.listen(WIKI.config.port, WIKI.config.bindIP)
WIKI.server.on('error', (error) => {
if (error.syscall !== 'listen') {
throw error
}
// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
WIKI.logger.error('Listening on port ' + WIKI.config.port + ' requires elevated privileges!')
return process.exit(1)
case 'EADDRINUSE':
WIKI.logger.error('Port ' + WIKI.config.port + ' is already in use!')
return process.exit(1)
default:
throw error
}
})
WIKI.server.on('connection', conn => {
let key = `${conn.remoteAddress}:${conn.remotePort}`
srvConnections[key] = conn
conn.on('close', function() {
delete srvConnections[key]
})
})
WIKI.server.on('listening', () => {
if (WIKI.config.ssl.enabled) {
WIKI.logger.info('HTTPS Server: [ RUNNING ]')
// Start HTTP Redirect Server
if (WIKI.config.ssl.redirectNonSSLPort) {
WIKI.serverAlt.listen(WIKI.config.ssl.redirectNonSSLPort, WIKI.config.bindIP)
WIKI.serverAlt.on('error', (error) => {
if (error.syscall !== 'listen') {
throw error
}
switch (error.code) {
case 'EACCES':
WIKI.logger.error('(HTTP Redirect) Listening on port ' + WIKI.config.port + ' requires elevated privileges!')
return process.exit(1)
case 'EADDRINUSE':
WIKI.logger.error('(HTTP Redirect) Port ' + WIKI.config.port + ' is already in use!')
return process.exit(1)
default:
throw error
}
})
WIKI.serverAlt.on('listening', () => {
WIKI.logger.info('HTTP Server: [ RUNNING in redirect mode ]')
})
}
} else {
WIKI.logger.info('HTTP Server: [ RUNNING ]')
}
})
WIKI.server.destroy = (cb) => {
WIKI.server.close(cb)
for (let key in srvConnections) {
srvConnections[key].destroy()
}
if (WIKI.config.ssl.enabled && WIKI.config.ssl.redirectNonSSLPort) {
WIKI.serverAlt.close(cb)
}
} }
return true return true

View File

@ -1553,6 +1553,65 @@
resolved "https://registry.yarnpkg.com/@requarks/ckeditor5/-/ckeditor5-12.4.0-wiki.14.tgz#eca91568cc5f1471fff684df18c638dfab9f0aef" resolved "https://registry.yarnpkg.com/@requarks/ckeditor5/-/ckeditor5-12.4.0-wiki.14.tgz#eca91568cc5f1471fff684df18c638dfab9f0aef"
integrity sha512-3DD2GmagyTYeSHsm437FR0OxlzNFYnc3PEflt7p0/mTM68nRHUHv/pO9wvbUXaRk667WKsRVqckbbMQbdRK43g== integrity sha512-3DD2GmagyTYeSHsm437FR0OxlzNFYnc3PEflt7p0/mTM68nRHUHv/pO9wvbUXaRk667WKsRVqckbbMQbdRK43g==
"@root/acme@^3.0.2":
version "3.0.9"
resolved "https://registry.yarnpkg.com/@root/acme/-/acme-3.0.9.tgz#437f4b46015e70587fb50119807889c944370388"
integrity sha512-/FgJF6RUrkqNpLmxqjktHaWMsLOwma6D+e4EBoxKtTjTAI+dBqW8Z8cH38feUsiIBR5LimPeYmBo/oqU3oMkKQ==
dependencies:
"@root/encoding" "^1.0.1"
"@root/keypairs" "^0.9.0"
"@root/pem" "^1.0.4"
"@root/request" "^1.3.11"
"@root/x509" "^0.7.2"
"@root/asn1@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@root/asn1/-/asn1-1.0.0.tgz#8748cf7b4497324de91a154b606ca1ddfe97c5d7"
integrity sha512-0lfZNuOULKJDJmdIkP8V9RnbV3XaK6PAHD3swnFy4tZwtlMDzLKoM/dfNad7ut8Hu3r91wy9uK0WA/9zym5mig==
dependencies:
"@root/encoding" "^1.0.1"
"@root/csr@0.8.1":
version "0.8.1"
resolved "https://registry.yarnpkg.com/@root/csr/-/csr-0.8.1.tgz#97a1b821331a4ed5895eee33bedb6ad49cdef74e"
integrity sha512-hKl0VuE549TK6SnS2Yn9nRvKbFZXn/oAg+dZJU/tlKl/f/0yRXeuUzf8akg3JjtJq+9E592zDqeXZ7yyrg8fSQ==
dependencies:
"@root/asn1" "^1.0.0"
"@root/pem" "^1.0.4"
"@root/x509" "^0.7.2"
"@root/encoding@^1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@root/encoding/-/encoding-1.0.1.tgz#60cf3ccb9e2239226b30e8394c7a4a258a161c3e"
integrity sha512-OaEub02ufoU038gy6bsNHQOjIn8nUjGiLcaRmJ40IUykneJkIW5fxDqKxQx48cszuNflYldsJLPPXCrGfHs8yQ==
"@root/keypairs@0.9.0", "@root/keypairs@^0.9.0":
version "0.9.0"
resolved "https://registry.yarnpkg.com/@root/keypairs/-/keypairs-0.9.0.tgz#0340a8a9cd865a9adc3ad5dd1f98221a7cb310df"
integrity sha512-NXE2L9Gv7r3iC4kB/gTPZE1vO9Ox/p14zDzAJ5cGpTpytbWOlWF7QoHSJbtVX4H7mRG/Hp7HR3jWdWdb2xaaXg==
dependencies:
"@root/encoding" "^1.0.1"
"@root/pem" "^1.0.4"
"@root/x509" "^0.7.2"
"@root/pem@1.0.4", "@root/pem@^1.0.4":
version "1.0.4"
resolved "https://registry.yarnpkg.com/@root/pem/-/pem-1.0.4.tgz#9ecc3419c0a05c92b8ecbfe0a41f474970f8c72f"
integrity sha512-rEUDiUsHtild8GfIjFE9wXtcVxeS+ehCJQBwbQQ3IVfORKHK93CFnRtkr69R75lZFjcmKYVc+AXDB+AeRFOULA==
"@root/request@^1.3.11":
version "1.4.2"
resolved "https://registry.yarnpkg.com/@root/request/-/request-1.4.2.tgz#a51e93c64eb8b9b0df06f34677e63d8239c4311c"
integrity sha512-J8FM4+SJuc7WRC+Jz17m+VT2lgI7HtatHhxN1F2ck5aIKUAxJEaR4u/gLBsgT60mVHevKCjKN0O8115UtJjwLw==
"@root/x509@^0.7.2":
version "0.7.2"
resolved "https://registry.yarnpkg.com/@root/x509/-/x509-0.7.2.tgz#422ca7e46c09f5ddf3af86306383778eb004ace1"
integrity sha512-ENq3LGYORK5NiMFHEVeNMt+fTXaC7DTS6sQXoqV+dFdfT0vmiL5cDLjaXQhaklJQq0NiwicZegzJRl1ZOTp3WQ==
dependencies:
"@root/asn1" "^1.0.0"
"@root/encoding" "^1.0.1"
"@types/accepts@*", "@types/accepts@^1.3.5": "@types/accepts@*", "@types/accepts@^1.3.5":
version "1.3.5" version "1.3.5"
resolved "https://registry.yarnpkg.com/@types/accepts/-/accepts-1.3.5.tgz#c34bec115cfc746e04fe5a059df4ce7e7b391575" resolved "https://registry.yarnpkg.com/@types/accepts/-/accepts-1.3.5.tgz#c34bec115cfc746e04fe5a059df4ce7e7b391575"
@ -2232,6 +2291,13 @@ accepts@^1.3.5, accepts@~1.3.5, accepts@~1.3.7:
mime-types "~2.1.24" mime-types "~2.1.24"
negotiator "0.6.2" negotiator "0.6.2"
acme@3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/acme/-/acme-3.0.3.tgz#e3dd9086027118d2cda7bf72572446c02e0696ea"
integrity sha512-NMEugs9cWvryNRidu9GeyV3EbqDlb5XFKQjaJ6E99RFpzrHM/iYYLImdrdpnkbS4vFTCrB2nMA7MQB0vAk801w==
dependencies:
"@root/acme" "^3.0.2"
acorn-globals@^3.0.0: acorn-globals@^3.0.0:
version "3.1.0" version "3.1.0"
resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-3.1.0.tgz#fd8270f71fbb4996b004fa880ee5d46573a731bf" resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-3.1.0.tgz#fd8270f71fbb4996b004fa880ee5d46573a731bf"
@ -12071,16 +12137,16 @@ punycode@1.3.2:
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=
punycode@2.1.1, punycode@^2.1.0, punycode@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
punycode@^1.2.4, punycode@^1.4.1: punycode@^1.2.4, punycode@^1.4.1:
version "1.4.1" version "1.4.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= integrity sha1-wNWmOycYgArY4esPpSachN1BhF4=
punycode@^2.1.0, punycode@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
q@^1.1.2, q@^1.5.0: q@^1.1.2, q@^1.5.0:
version "1.5.1" version "1.5.1"
resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"