Skip to content
This repository has been archived by the owner on Feb 24, 2021. It is now read-only.

Reverse proxy dynamic subpath #540

Merged
merged 40 commits into from Jun 8, 2020
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
75afb77
Removed unused template
May 29, 2020
ae5564a
Use EJS for index page in dev.
May 29, 2020
bfb0827
Fix webpack; move default config
May 29, 2020
6f65b6a
Dynamically render index
May 29, 2020
88bc89c
Built production works
May 30, 2020
c5219fb
Mostly working
May 30, 2020
c06011c
Add some documentation
May 30, 2020
ffc7ae8
Fix up documentation
May 30, 2020
6fc834c
Link to subpath docs from main README
May 30, 2020
efaf968
Add unit tests
May 30, 2020
58f7d91
Merge pull request #1 from fabio-torchetti/feature/select-base
fabio-torchetti May 30, 2020
c738c5f
Add missing (?) ejs package
May 30, 2020
6f54569
TIL: Socket Namespaces
Jun 1, 2020
1a8c26b
Merge branch 'master' into feature/select-base
robertsLando Jun 3, 2020
86cc323
Update docs/subpath.md
fabio-torchetti Jun 3, 2020
881aace
Update src/apis/ConfigApis.js
fabio-torchetti Jun 3, 2020
9ae6c8a
Update docs/subpath.md
fabio-torchetti Jun 3, 2020
cbcc7f3
Add reverse proxy setup to Readme TOC
Jun 3, 2020
b2cf1a3
Remove trailing white line
Jun 3, 2020
b6fb1e5
Remove trailing semicolon from webpack config
Jun 3, 2020
cb56e4c
Move require to the requirements block
Jun 3, 2020
6cf94e0
Fix MD language for NGinx
Jun 3, 2020
529ab95
Remove `favicons` comment
Jun 3, 2020
1fc9669
Merge branch 'master' into feature/select-base
fabio-torchetti Jun 3, 2020
95e1fa2
Add 'proxyquire' as a dev dependency
Jun 3, 2020
d75b8a5
Ensure base path has always a trailing slash
Jun 3, 2020
300a8cd
Fix development assets layout
Jun 3, 2020
066c228
Add full practical example of reverse proxy
Jun 3, 2020
6b1a593
Proxy to nodejs service for development of UI
Jun 3, 2020
5d08024
Merge branch 'master' into feature/select-base
robertsLando Jun 4, 2020
7761703
Fix missing mapping
Jun 4, 2020
be648c5
Merge branch 'master' into feature/select-base
Jun 5, 2020
347e793
Merge remote-tracking branch 'upstream/master' into feature/select-base
Jun 5, 2020
a957e17
Linter fixes -- need to start auto running it!
Jun 5, 2020
35efeca
Update README.md
fabio-torchetti Jun 6, 2020
1381250
Update build/webpack.dev.conf.js
fabio-torchetti Jun 6, 2020
bd00ec9
Update src/App.vue
fabio-torchetti Jun 6, 2020
791d7aa
Update test/lib/renderIndex.test.js
fabio-torchetti Jun 6, 2020
c303f8e
Make proxy URL configurable; UT fixes
Jun 6, 2020
3490d7e
Merge branch 'master' into feature/select-base
fabio-torchetti Jun 8, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Expand Up @@ -42,6 +42,7 @@ After a [discussion](https://github.com/OpenZWave/Zwave2Mqtt/issues/201) with Op
- [DOCKER :tada: way](#docker-tada-way)
- [Kubernetes way](#kubernetes-way)
- [NodeJS or PKG version](#nodejs-or-pkg-version)
- [Reverse Proxy Setup](#reverse-proxy-setup)
- [:nerd_face: Development](#nerdface-development)
- [:wrench: Usage](#wrench-usage)
- [Zwave](#zwave)
Expand Down Expand Up @@ -168,6 +169,11 @@ kubectl apply -k https://raw.githubusercontent.com/openzwave/zwave2mqtt/master/k

4. Open the browser <http://localhost:8091>

### Reverse Proxy Setup
fabio-torchetti marked this conversation as resolved.
Show resolved Hide resolved

If you need to setup ZWave To MQTT behind a reverse proxy that needs a *subpath* to
work, take a look at [this](docs/subpath.md).
fabio-torchetti marked this conversation as resolved.
Show resolved Hide resolved

## :nerd_face: Development

Developers who wants to debug the application have to open 2 terminals.
Expand Down
8 changes: 5 additions & 3 deletions app.js
Expand Up @@ -14,7 +14,7 @@ var store = reqlib('config/store.js')
var debug = reqlib('/lib/debug')('App')
var history = require('connect-history-api-fallback')
var utils = reqlib('/lib/utils.js')

const renderIndex = reqlib('/lib/renderIndex')
var gw; //the gateway instance
let io;

Expand All @@ -30,6 +30,8 @@ app.use(bodyParser.json({ limit: "50mb" }));
app.use(bodyParser.urlencoded({ limit: "50mb", extended: true, parameterLimit: 50000 }));
app.use(cookieParser());

app.get('/', renderIndex);

app.use('/', express.static(utils.joinPath(false, 'dist')));

app.use(cors());
Expand Down Expand Up @@ -60,7 +62,7 @@ app.startSocket = function (server) {
io.on('connection', function (socket) {

debug("New connection", socket.id);

socket.on('INITED', function () {
if(gw.zwave)
socket.emit('INIT', { nodes: gw.zwave.nodes, info: gw.zwave.ozwConfig, error: gw.zwave.error, cntStatus: gw.zwave.cntStatus })
Expand Down Expand Up @@ -145,7 +147,7 @@ app.get('/health/:client', async function (req, res) {
status = gw && gw[client] ? gw[client].getStatus().status : false
}

res.status(status ? 200 : 500).send(status ? 'Ok' : 'Error')
res.status(status ? 200 : 500).send(status ? 'Ok' : 'Error')
})


Expand Down
3 changes: 2 additions & 1 deletion build/webpack.base.conf.js
Expand Up @@ -76,7 +76,8 @@ module.exports = {
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
name: utils.assetsPath('fonts/[name].[hash:7].[ext]'),
publicPath: '../..'
}
},
{
Expand Down
13 changes: 7 additions & 6 deletions build/webpack.dev.conf.js
Expand Up @@ -2,6 +2,7 @@
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
const appConfig = require('../config/webConfig')
const merge = require('webpack-merge')
const path = require('path')
const baseWebpackConfig = require('./webpack.base.conf')
Expand All @@ -24,11 +25,7 @@ const devWebpackConfig = merge(baseWebpackConfig, {
// these devServer options should be customized in /config/index.js
devServer: {
clientLogLevel: 'warning',
historyApiFallback: {
rewrites: [
{ from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
],
},
historyApiFallback: true,
hot: true,
contentBase: false, // since we use CopyWebpackPlugin.
compress: true,
Expand All @@ -55,8 +52,12 @@ const devWebpackConfig = merge(baseWebpackConfig, {
new webpack.NoEmitOnErrorsPlugin(),
// https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
title: 'ZWave To MQTT',
fabio-torchetti marked this conversation as resolved.
Show resolved Hide resolved
filename: 'index.html',
template: 'index.html',
template: 'views/index.ejs',
templateParameters: {
config: appConfig
},
inject: true
}),
// copy custom static assets
Expand Down
16 changes: 0 additions & 16 deletions build/webpack.prod.conf.js
Expand Up @@ -6,7 +6,6 @@ const config = require('../config')
const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.conf')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
const TerserPlugin = require('terser-webpack-plugin')
Expand Down Expand Up @@ -52,21 +51,6 @@ const webpackConfig = merge(baseWebpackConfig, {
? { safe: true, map: { inline: false } }
: { safe: true }
}),
// generate dist index.html with correct asset hash for caching.
// you can customize output by editing /index.html
// see https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: config.build.index,
template: 'index.html',
inject: true,
minify: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
// more options:
// https://github.com/kangax/html-minifier#options-quick-reference
},
}),
// keep module.id stable when vendor modules does not change
new webpack.HashedModuleIdsPlugin(),
// enable scope hoisting
Expand Down
2 changes: 2 additions & 0 deletions config/app.js
@@ -1,5 +1,7 @@
// config/app.js
module.exports = {
'title': 'ZWave To MQTT',
'storeDir': 'store',
'base': '/',
'port': 8091
};
9 changes: 8 additions & 1 deletion config/index.js
Expand Up @@ -10,7 +10,14 @@ module.exports = {
// Paths
assetsSubDirectory: 'static',
assetsPublicPath: '/',
proxyTable: {},
proxyTable: {
'/socket.io': {
target: 'ws://localhost:8091',
fabio-torchetti marked this conversation as resolved.
Show resolved Hide resolved
ws: true
},
'/health': 'http://localhost:8091',
'/api': 'http://localhost:8091'
},

// Various Dev Server settings
host: 'localhost', // can be overwritten by process.env.HOST
Expand Down
13 changes: 13 additions & 0 deletions config/webConfig.js
@@ -0,0 +1,13 @@
const appConfig = require('./app');

appConfig.base = appConfig.base && appConfig.base.replace(/\/?$/, '/')

const defaultConfig = {
base: '/',
title: 'ZWave To MQTT'
};

module.exports = {
...defaultConfig,
...appConfig
};
61 changes: 61 additions & 0 deletions docs/subpath.md
@@ -0,0 +1,61 @@
# ZWave To MQTT Behind a Reverse Proxy

There are two ways to enable ZWave To MQTT to sit behing a proxy that uses
subpaths to serve the pages and services.

You can use a header to signal where the external path is or you can configure
the base path. In both cases these are dynamic configurations, so you can deploy
without having to build again the frontend.

## Using an HTTP header

You can pass the external path by setting the `X-External-Path` header, for example
suppose you had the following `nginx` configuration:

```nginx
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}

server {
listen 9000 default_server;
listen [::]:9000 default_server;

location /hassio/ingress/ {
proxy_pass http://localhost:8091/;
proxy_set_header X-External-Path /hassio/ingress;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}
```

This will tell the application to serve the application and relevant elements under
`/some/deep/map`.

In case you are using the [ingress of Home Assistant](https://www.home-assistant.io/blog/2019/04/15/hassio-ingress/) you will want to
pick up the `X-Ingress-Path;` and map it, something along
these lines:

```nginx
proxy_set_header X-External-Path $http_x_ingress_path;
```

## Using the configuration

You can simply change the `config/app.js` and set `base` to whatever is
the subpath you will be serving this from.

As an example, if your proxy is placing the app behind `/zwave/` your configuration
would look like:

```javascript
module.exports = {
'title': 'ZWave to MQTT',
'storeDir': 'store',
'base': '/zwave/',
'port': 8091
};
```
27 changes: 0 additions & 27 deletions index.html

This file was deleted.

35 changes: 35 additions & 0 deletions lib/renderIndex.js
@@ -0,0 +1,35 @@
const fs = require('fs')
const path = require('path')
const reqlib = require('app-root-path').require

const webConfig = reqlib('/config/webConfig')

function findFiles (folder, ext) {
const folderPath = path.join(__dirname, '..', 'dist', folder)
const folderFiles = fs.readdirSync(folderPath)
return folderFiles.filter(function (file) {
return path.extname(file).toLowerCase() === `.${ext.toLowerCase()}`
}).map(function (file) {
return path.join(folder, file)
})
}

let cssFiles
let jsFiles

function basePath (config, headers) {
return (headers['x-external-path'] || config.base).replace(/\/?$/, '/')
}

module.exports = function (req, res) {
cssFiles = cssFiles || findFiles(path.join('static', 'css'), 'css')
jsFiles = jsFiles || findFiles(path.join('static', 'js'), 'js')
res.render('index.ejs', {
config: {
...webConfig,
base: basePath(webConfig, req.headers)
},
cssFiles,
jsFiles
})
}