Skip to content

Commit

Permalink
feat: https support (#535)
Browse files Browse the repository at this point in the history
* feat: https support

* docs: document https env var

* fix: dev server over https

* fix: lint issues
  • Loading branch information
robertsLando authored Feb 10, 2021
1 parent 7764ee2 commit 346d638
Show file tree
Hide file tree
Showing 7 changed files with 144 additions and 57 deletions.
89 changes: 81 additions & 8 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const path = require('path')
const { storeDir } = reqlib('config/app.js')
const renderIndex = reqlib('/lib/renderIndex')
const archiver = require('archiver')
const { createCertificate } = require('pem').promisified
const rateLimit = require('express-rate-limit')

const storeLimiter = rateLimit({
Expand All @@ -45,7 +46,54 @@ let restarting = false

// ### UTILS

function start (server) {
/**
* Start http/https server and all the manager
*
* @param {string} host
* @param {number} port
*/
async function startServer (host, port) {
let server

if (process.env.HTTPS) {
const { cert, key } = await loadCertKey()
server = require('https').createServer(
{
key,
cert
},
app
)
} else {
server = require('http').createServer(app)
}

server.listen(port, host, function () {
const addr = server.address()
const bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port
logger.info(`Listening on ${bind}`)
})

server.on('error', function (error) {
if (error.syscall !== 'listen') {
throw error
}

const bind = typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port

// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
logger.error(bind + ' requires elevated privileges')
process.exit(1)
case 'EADDRINUSE':
logger.error(bind + ' is already in use')
process.exit(1)
default:
throw error
}
})

setupSocket(server)
setupInterceptor()
startGateway()
Expand All @@ -71,6 +119,37 @@ function getSafePath (req) {
return reqPath
}

async function loadCertKey () {
const certFile = utils.joinPath(storeDir, 'cert.pem')
const keyFile = utils.joinPath(storeDir, 'key.pem')

let key
let cert

try {
cert = await fs.readFile(certFile)
key = await fs.readFile(keyFile)
} catch (error) {}

if (!cert || !key) {
logger.info('Generating certificates...')

const result = await createCertificate({
days: 99999,
selfSigned: true
})

key = result.serviceKey
cert = result.certificate

await fs.writeFile(keyFile, result.serviceKey)
await fs.writeFile(certFile, result.certificate)
logger.info('New cert and key created')
}

return { cert, key }
}

function setupLogging (settings) {
loggers.setupAll(settings ? settings.gateway : null)
}
Expand Down Expand Up @@ -151,12 +230,6 @@ app.use(cors())
* @param {HttpServer} server
*/
function setupSocket (server) {
server.on('listening', function () {
const addr = server.address()
const bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port
logger.info(`Listening on ${bind}`)
})

socketManager.bindServer(server)

socketManager.on(inboundEvents.init, function (socket) {
Expand Down Expand Up @@ -485,4 +558,4 @@ process.on('SIGINT', function () {
})
})

module.exports = { app, start }
module.exports = { app, startServer }
44 changes: 2 additions & 42 deletions bin/www
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ console.log(`
// if jsonstore fails exit the application
jsonStore.init(store)
.then(() => {
const { app, start } = reqlib('app.js')
const http = require('http')
const { app, startServer } = reqlib('app.js')

/**
* Normalize a port into a number, string, or false.
Expand All @@ -47,55 +46,16 @@ jsonStore.init(store)
return false
}

/**
* Event listener for HTTP server "error" event.
*/

function onError (error) {
if (error.syscall !== 'listen') {
throw error
}

const bind = typeof port === 'string'
? 'Pipe ' + port
: 'Port ' + port

// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges')
process.exit(1)
case 'EADDRINUSE':
console.error(bind + ' is already in use')
process.exit(1)
default:
throw error
}
}

/**
* Get port from environment and store in Express.
*/

const port = normalizePort(process.env.PORT || conf.port)
app.set('port', port)

/**
* Create HTTP server.
*/

const server = http.createServer(app)

/**
* Listen on provided port, on preferred network interfaces.
*/

const host = process.env.HOST || conf.host || '0.0.0.0'

server.listen(port, host)
server.on('error', onError)

start(server)
return startServer(host, port)
})
.catch(err => {
console.error(err)
Expand Down
1 change: 1 addition & 0 deletions build/webpack.dev.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const devWebpackConfig = merge(baseWebpackConfig, {
clientLogLevel: 'warning',
historyApiFallback: true,
hot: true,
https: config.dev.https,
contentBase: false, // since we use CopyWebpackPlugin.
compress: true,
disableHostCheck: true,
Expand Down
8 changes: 6 additions & 2 deletions config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,23 @@ const proxyWSURL = process.env.SERVER_WS_URL
? process.env.SERVER_WS_URL
: `${proxyWebSocketScheme}://${proxyHostname}:${proxyPort}`

// this props are used on build/ files as general settings for webpack
module.exports = {
dev: {
// Paths
assetsSubDirectory: 'static',
assetsPublicPath: '/',
proxyTable: {
'/socket.io': {
target: proxyWSURL,
ws: true
target: proxyURL,
ws: true,
secure: false,
changeOrigin: true
},
'/health': proxyURL,
'/api': proxyURL
},
https: !!process.env.SERVER_SSL,

// Various Dev Server settings
host: 'localhost', // can be overwritten by process.env.HOST
Expand Down
8 changes: 8 additions & 0 deletions docs/guide/env-vars.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@
This is the list of the actually supported env vars:

- `NETWORK_KEY`: Zwave Network key
- `HTTPS`: Enable https
- `PORT`: The port to listen to for incoming requests. Default is `8091`
- `HOST`: The host address to bind to. Default is `0.0.0.0`
- `STORE_DIR`: The absolute path to the directory where all files will be stored. Default is `<path to your z2m dir>/store`

This env vars can be used when running webpack dev server with HMR (most users will not need them):

- `SERVER_PORT`: The port the server is running. Default is `8091`
- `SERVER_SSL`: Set to 'true' if server is using HTTPS / WSS scheme
- `SERVER_HOST`: The host address the server is binded to. Default is `0.0.0.0`
- `SERVER_URL`: Complete URL to server. If not set a combination of `SERVER_SSL`, `SERVER_HOST` and `SERVER_PORT` will be used.
50 changes: 45 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@
"mqtt-nedb-store": "^0.1.1",
"native-url": "^0.3.4",
"nedb": "^1.8.0",
"pem": "^1.14.4",
"prismjs": "^1.23.0",
"serialport": "^9.0.6",
"serve-favicon": "^2.5.0",
Expand Down

0 comments on commit 346d638

Please sign in to comment.