Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: https support #535

Merged
merged 6 commits into from
Feb 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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