Compare commits
40 Commits
Author | SHA1 | Date | |
---|---|---|---|
8b8f5fac69 | |||
424ccce43c | |||
ad41cc985d | |||
981d5a199f | |||
48f2bb4cd8 | |||
aa270925e9 | |||
3836f7c40a | |||
9fcd32c2ca | |||
2657bcf30c | |||
86ad7d6238 | |||
c97e6ada5b | |||
cd40ca7f0a | |||
e2ac3b4880 | |||
7f8b185e48 | |||
e923db7e94 | |||
e53d9fa3eb | |||
411734f392 | |||
a457a40359 | |||
caa183c8de | |||
0ea5014edb | |||
046cb0b76e | |||
9fd480cf77 | |||
0f94e68dca | |||
c15edf318d | |||
a73cbc7116 | |||
f9876326c9 | |||
7d5ca84501 | |||
0335370cfb | |||
9b852f01e3 | |||
20fd185652 | |||
ad41986bd5 | |||
c826ed8c1f | |||
eaebc48f66 | |||
eb391959aa | |||
dba4340548 | |||
0b8a49469f | |||
b16a68052f | |||
83686c4535 | |||
efa1424cad | |||
4fe26ec4c0 |
@ -29,4 +29,11 @@ ADD knexfile.js /app/knexfile.js
|
||||
VOLUME [ "/data", "/etc/letsencrypt" ]
|
||||
CMD [ "/init" ]
|
||||
|
||||
# Ports
|
||||
EXPOSE 80
|
||||
EXPOSE 81
|
||||
EXPOSE 443
|
||||
EXPOSE 9876
|
||||
|
||||
HEALTHCHECK --interval=15s --timeout=3s CMD curl -f http://localhost:9876/health || exit 1
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
FROM jc21/nginx-proxy-manager-base:armhf
|
||||
FROM jc21/nginx-proxy-manager-base:latest-armhf
|
||||
|
||||
MAINTAINER Jamie Curnow <jc@jc21.com>
|
||||
LABEL maintainer="Jamie Curnow <jc@jc21.com>"
|
||||
@ -29,4 +29,10 @@ ADD knexfile.js /app/knexfile.js
|
||||
VOLUME [ "/data", "/etc/letsencrypt" ]
|
||||
CMD [ "/init" ]
|
||||
|
||||
# Ports
|
||||
EXPOSE 80
|
||||
EXPOSE 81
|
||||
EXPOSE 443
|
||||
EXPOSE 9876
|
||||
|
||||
HEALTHCHECK --interval=15s --timeout=3s CMD curl -f http://localhost:9876/health || exit 1
|
||||
|
86
Jenkinsfile
vendored
86
Jenkinsfile
vendored
@ -6,7 +6,7 @@ pipeline {
|
||||
agent any
|
||||
environment {
|
||||
IMAGE_NAME = "nginx-proxy-manager"
|
||||
BASE_IMAGE_NAME = "jc21/nginx-proxy-manager-base:v2"
|
||||
BASE_IMAGE_NAME = "jc21/nginx-proxy-manager-base:latest"
|
||||
TEMP_IMAGE_NAME = "nginx-proxy-manager-build_${BUILD_NUMBER}"
|
||||
TEMP_IMAGE_NAME_ARM = "nginx-proxy-manager-arm-build_${BUILD_NUMBER}"
|
||||
TAG_VERSION = getPackageVersion()
|
||||
@ -18,7 +18,41 @@ pipeline {
|
||||
sh 'docker pull $DOCKER_CI_TOOLS'
|
||||
}
|
||||
}
|
||||
stage('Build') {
|
||||
stage('Build Develop') {
|
||||
when {
|
||||
branch 'develop'
|
||||
}
|
||||
steps {
|
||||
ansiColor('xterm') {
|
||||
// Codebase
|
||||
sh 'docker run --rm -v $(pwd):/app -w /app $BASE_IMAGE_NAME yarn install'
|
||||
sh 'docker run --rm -v $(pwd):/app -w /app $BASE_IMAGE_NAME npm run-script build'
|
||||
sh 'rm -rf node_modules'
|
||||
sh 'docker run --rm -v $(pwd):/app -w /app $BASE_IMAGE_NAME yarn install --prod'
|
||||
sh 'docker run --rm -v $(pwd):/data $DOCKER_CI_TOOLS node-prune'
|
||||
|
||||
// Docker Build
|
||||
sh 'docker build --pull --no-cache --squash --compress -t $TEMP_IMAGE_NAME .'
|
||||
|
||||
// Dockerhub
|
||||
sh 'docker tag $TEMP_IMAGE_NAME docker.io/jc21/$IMAGE_NAME:develop'
|
||||
withCredentials([usernamePassword(credentialsId: 'jc21-dockerhub', passwordVariable: 'dpass', usernameVariable: 'duser')]) {
|
||||
sh "docker login -u '${duser}' -p '$dpass'"
|
||||
sh 'docker push docker.io/jc21/$IMAGE_NAME:develop'
|
||||
}
|
||||
|
||||
// Private Registry
|
||||
sh 'docker tag $TEMP_IMAGE_NAME $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:develop'
|
||||
withCredentials([usernamePassword(credentialsId: 'jc21-private-registry', passwordVariable: 'dpass', usernameVariable: 'duser')]) {
|
||||
sh "docker login -u '${duser}' -p '$dpass' $DOCKER_PRIVATE_REGISTRY"
|
||||
sh 'docker push $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:develop'
|
||||
}
|
||||
|
||||
sh 'docker rmi $TEMP_IMAGE_NAME'
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Build Master') {
|
||||
parallel {
|
||||
stage('x86_64') {
|
||||
when {
|
||||
@ -27,23 +61,15 @@ pipeline {
|
||||
steps {
|
||||
ansiColor('xterm') {
|
||||
// Codebase
|
||||
sh 'docker run --rm -v $(pwd):/app -w /app $BASE_IMAGE_NAME yarn --registry=$NPM_REGISTRY install'
|
||||
sh 'docker run --rm -v $(pwd):/app -w /app $BASE_IMAGE_NAME yarn install'
|
||||
sh 'docker run --rm -v $(pwd):/app -w /app $BASE_IMAGE_NAME npm run-script build'
|
||||
sh 'rm -rf node_modules'
|
||||
sh 'docker run --rm -v $(pwd):/app -w /app $BASE_IMAGE_NAME yarn --registry=$NPM_REGISTRY install --prod'
|
||||
sh 'docker run --rm -v $(pwd):/app -w /app $BASE_IMAGE_NAME yarn install --prod'
|
||||
sh 'docker run --rm -v $(pwd):/data $DOCKER_CI_TOOLS node-prune'
|
||||
|
||||
// Docker Build
|
||||
sh 'docker build --pull --no-cache --squash --compress -t $TEMP_IMAGE_NAME .'
|
||||
|
||||
// Private Registry
|
||||
sh 'docker tag $TEMP_IMAGE_NAME $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:$TAG_VERSION'
|
||||
sh 'docker push $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:$TAG_VERSION'
|
||||
sh 'docker tag $TEMP_IMAGE_NAME $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:$MAJOR_VERSION'
|
||||
sh 'docker push $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:$MAJOR_VERSION'
|
||||
sh 'docker tag $TEMP_IMAGE_NAME $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:latest'
|
||||
sh 'docker push $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:latest'
|
||||
|
||||
// Dockerhub
|
||||
sh 'docker tag $TEMP_IMAGE_NAME docker.io/jc21/$IMAGE_NAME:$TAG_VERSION'
|
||||
sh 'docker tag $TEMP_IMAGE_NAME docker.io/jc21/$IMAGE_NAME:$MAJOR_VERSION'
|
||||
@ -56,6 +82,18 @@ pipeline {
|
||||
sh 'docker push docker.io/jc21/$IMAGE_NAME:latest'
|
||||
}
|
||||
|
||||
// Private Registry
|
||||
sh 'docker tag $TEMP_IMAGE_NAME $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:$TAG_VERSION'
|
||||
sh 'docker tag $TEMP_IMAGE_NAME $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:$MAJOR_VERSION'
|
||||
sh 'docker tag $TEMP_IMAGE_NAME $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:latest'
|
||||
|
||||
withCredentials([usernamePassword(credentialsId: 'jc21-private-registry', passwordVariable: 'dpass', usernameVariable: 'duser')]) {
|
||||
sh "docker login -u '${duser}' -p '$dpass' $DOCKER_PRIVATE_REGISTRY"
|
||||
sh 'docker push $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:$TAG_VERSION'
|
||||
sh 'docker push $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:$MAJOR_VERSION'
|
||||
sh 'docker push $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:latest'
|
||||
}
|
||||
|
||||
sh 'docker rmi $TEMP_IMAGE_NAME'
|
||||
}
|
||||
}
|
||||
@ -70,22 +108,14 @@ pipeline {
|
||||
steps {
|
||||
ansiColor('xterm') {
|
||||
// Codebase
|
||||
sh 'docker run --rm -v $(pwd):/app -w /app $BASE_IMAGE_NAME-armhf yarn --registry=$NPM_REGISTRY install'
|
||||
sh 'docker run --rm -v $(pwd):/app -w /app $BASE_IMAGE_NAME-armhf yarn install'
|
||||
sh 'docker run --rm -v $(pwd):/app -w /app $BASE_IMAGE_NAME-armhf npm run-script build'
|
||||
sh 'rm -rf node_modules'
|
||||
sh 'docker run --rm -v $(pwd):/app -w /app $BASE_IMAGE_NAME-armhf yarn --registry=$NPM_REGISTRY install --prod'
|
||||
sh 'docker run --rm -v $(pwd):/app -w /app $BASE_IMAGE_NAME-armhf yarn install --prod'
|
||||
|
||||
// Docker Build
|
||||
sh 'docker build --pull --no-cache --squash --compress -t $TEMP_IMAGE_NAME_ARM -f Dockerfile.armhf .'
|
||||
|
||||
// Private Registry
|
||||
sh 'docker tag $TEMP_IMAGE_NAME_ARM $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:$TAG_VERSION-armhf'
|
||||
sh 'docker push $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:$TAG_VERSION-armhf'
|
||||
sh 'docker tag $TEMP_IMAGE_NAME_ARM $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:$MAJOR_VERSION-armhf'
|
||||
sh 'docker push $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:$MAJOR_VERSION-armhf'
|
||||
sh 'docker tag $TEMP_IMAGE_NAME_ARM $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:latest-armhf'
|
||||
sh 'docker push $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:latest-armhf'
|
||||
|
||||
// Dockerhub
|
||||
sh 'docker tag $TEMP_IMAGE_NAME_ARM docker.io/jc21/$IMAGE_NAME:$TAG_VERSION-armhf'
|
||||
sh 'docker tag $TEMP_IMAGE_NAME_ARM docker.io/jc21/$IMAGE_NAME:$MAJOR_VERSION-armhf'
|
||||
@ -98,6 +128,18 @@ pipeline {
|
||||
sh 'docker push docker.io/jc21/$IMAGE_NAME:latest-armhf'
|
||||
}
|
||||
|
||||
// Private Registry
|
||||
sh 'docker tag $TEMP_IMAGE_NAME_ARM $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:$TAG_VERSION-armhf'
|
||||
sh 'docker tag $TEMP_IMAGE_NAME_ARM $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:$MAJOR_VERSION-armhf'
|
||||
sh 'docker tag $TEMP_IMAGE_NAME_ARM $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:latest-armhf'
|
||||
|
||||
withCredentials([usernamePassword(credentialsId: 'jc21-private-registry', passwordVariable: 'dpass', usernameVariable: 'duser')]) {
|
||||
sh "docker login -u '${duser}' -p '$dpass' $DOCKER_PRIVATE_REGISTRY"
|
||||
sh 'docker push $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:$TAG_VERSION-armhf'
|
||||
sh 'docker push $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:$MAJOR_VERSION-armhf'
|
||||
sh 'docker push $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:latest-armhf'
|
||||
}
|
||||
|
||||
sh 'docker rmi $TEMP_IMAGE_NAME_ARM'
|
||||
}
|
||||
}
|
||||
|
16
README.md
16
README.md
@ -2,20 +2,22 @@
|
||||
|
||||
# Nginx Proxy Manager
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
This project comes as a pre-built docker image that enables you to easily forward to your websites
|
||||
running at home or otherwise, including free SSL, without having to know too much about Nginx or Letsencrypt.
|
||||
|
||||
----------
|
||||
|
||||
**WARNING: Version 2 a complete rewrite!** If you are using the `latest` docker tag and update to version 2
|
||||
without preparation, horrible things might happen. Refer to the [Importing Documentation](doc/IMPORTING.md).
|
||||
|
||||
----------
|
||||
|
||||
## Project Goal
|
||||
|
||||
I created this project to fill a personal need to provide users with a easy way to accomplish reverse
|
||||
proxying hosts with SSL termination and it had to be so easy that a monkey could do it. This goal hasn't changed.
|
||||
While there might be advanced options they are optional and the project should be as simple as possible
|
||||
so that the barrier for entry here is low.
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- Beautiful and Secure Admin Interface based on [Tabler](https://tabler.github.io/)
|
||||
|
17
TODO.md
17
TODO.md
@ -1,17 +0,0 @@
|
||||
# TODO
|
||||
|
||||
- Dashboard stats are caching instead of querying
|
||||
|
||||
Next version:
|
||||
|
||||
- UI Log tail
|
||||
- Enable/Disable a config
|
||||
|
||||
Testing:
|
||||
|
||||
- Access Levels
|
||||
- Adding a proxy host without access to read certs or access lists
|
||||
- Visibility
|
||||
- Forwarding
|
||||
- Cert renewals
|
||||
- Custom certs
|
26
bin/migrate_create
Executable file
26
bin/migrate_create
Executable file
@ -0,0 +1,26 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ "$1" == "" ]; then
|
||||
echo "Error: migrate name must be specified as first arg"
|
||||
exit 1
|
||||
else
|
||||
# Code path
|
||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
if hash realpath 2>/dev/null; then
|
||||
export CODEBASE=$(realpath $SCRIPT_DIR/..)
|
||||
elif hash grealpath 2>/dev/null; then
|
||||
export CODEBASE=$(grealpath $SCRIPT_DIR/..)
|
||||
else
|
||||
export CODEBASE=$(readlink -e $SCRIPT_DIR/..)
|
||||
fi
|
||||
|
||||
if [ -z "$CODEBASE" ]; then
|
||||
echo "Unable to determine absolute codebase directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd "$CODEBASE"
|
||||
|
||||
sudo /usr/local/bin/docker-compose run --rm --no-deps app node node_modules/knex/bin/cli.js migrate:make "$1"
|
||||
exit $?
|
||||
fi
|
@ -2,7 +2,7 @@
|
||||
|
||||
# Nginx Proxy Manager
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
|
@ -57,7 +57,7 @@ services:
|
||||
depends_on:
|
||||
- db
|
||||
db:
|
||||
image: mariadb
|
||||
image: jc21/mariadb-aria
|
||||
restart: always
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: "password123"
|
||||
|
@ -17,7 +17,7 @@ services:
|
||||
# if you want pretty colors in your docker logs:
|
||||
- FORCE_COLOR=1
|
||||
db:
|
||||
image: mariadb
|
||||
image: jc21/mariadb-aria
|
||||
restart: always
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: "password123"
|
||||
|
@ -11,6 +11,7 @@ services:
|
||||
- NODE_ENV=development
|
||||
- FORCE_COLOR=1
|
||||
volumes:
|
||||
- ./data:/data
|
||||
- ./data/letsencrypt:/etc/letsencrypt
|
||||
- .:/app
|
||||
- ./rootfs/etc/nginx:/etc/nginx
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "nginx-proxy-manager",
|
||||
"version": "2.0.2",
|
||||
"version": "2.0.8",
|
||||
"description": "A beautiful interface for creating Nginx endpoints",
|
||||
"main": "src/backend/index.js",
|
||||
"devDependencies": {
|
||||
@ -30,8 +30,8 @@
|
||||
"style-loader": "^0.22.1",
|
||||
"tabler-ui": "git+https://github.com/tabler/tabler.git",
|
||||
"underscore": "^1.8.3",
|
||||
"webpack": "^4.12.0",
|
||||
"webpack-cli": "^3.0.8",
|
||||
"webpack": "^4.25.1",
|
||||
"webpack-cli": "^3.1.2",
|
||||
"webpack-visualizer-plugin": "^0.1.11"
|
||||
},
|
||||
"dependencies": {
|
||||
|
@ -8,8 +8,9 @@ server {
|
||||
|
||||
include conf.d/include/block-exploits.conf;
|
||||
|
||||
set $server 127.0.0.1;
|
||||
set $port 81;
|
||||
set $forward_scheme http;
|
||||
set $server 127.0.0.1;
|
||||
set $port 81;
|
||||
|
||||
location /health {
|
||||
access_log off;
|
||||
@ -36,3 +37,17 @@ server {
|
||||
root /var/www/html;
|
||||
}
|
||||
}
|
||||
|
||||
# Default 443 Host
|
||||
server {
|
||||
listen 443 ssl default;
|
||||
server_name localhost;
|
||||
|
||||
access_log /data/logs/default.log proxy;
|
||||
|
||||
ssl_certificate /data/nginx/dummycert.pem;
|
||||
ssl_certificate_key /data/nginx/dummykey.pem;
|
||||
ssl_ciphers aNULL;
|
||||
|
||||
return 444;
|
||||
}
|
||||
|
2
rootfs/etc/nginx/conf.d/include/ip_ranges.conf
Normal file
2
rootfs/etc/nginx/conf.d/include/ip_ranges.conf
Normal file
@ -0,0 +1,2 @@
|
||||
# Intentionally left blank
|
||||
|
@ -1,6 +1,6 @@
|
||||
add_header X-Served-By $host;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-Scheme $scheme;
|
||||
proxy_set_header X-Forwarded-Protocol $scheme;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_pass http://$server:$port;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_pass $forward_scheme://$server:$port;
|
||||
|
1
rootfs/etc/nginx/conf.d/include/resolvers.conf
Normal file
1
rootfs/etc/nginx/conf.d/include/resolvers.conf
Normal file
@ -0,0 +1 @@
|
||||
# Intentionally blank
|
@ -51,6 +51,24 @@ http {
|
||||
|
||||
access_log /data/logs/default.log proxy;
|
||||
|
||||
# Dynamically generated resolvers file
|
||||
include /etc/nginx/conf.d/include/resolvers.conf;
|
||||
|
||||
# Default upstream scheme
|
||||
map $host $forward_scheme {
|
||||
default http;
|
||||
}
|
||||
|
||||
# Real IP Determination
|
||||
# Docker subnet:
|
||||
set_real_ip_from 172.0.0.0/8;
|
||||
# NPM generated CDN ip ranges:
|
||||
include conf.d/include/ip_ranges.conf;
|
||||
# always put the following 2 lines after ip subnets:
|
||||
real_ip_header X-Forwarded-For;
|
||||
real_ip_recursive on;
|
||||
|
||||
# Files generated by NPM
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
include /data/nginx/proxy_host/*.conf;
|
||||
include /data/nginx/redirection_host/*.conf;
|
||||
@ -59,6 +77,7 @@ http {
|
||||
}
|
||||
|
||||
stream {
|
||||
# Files generated by NPM
|
||||
include /data/nginx/stream/*.conf;
|
||||
}
|
||||
|
||||
|
@ -3,4 +3,9 @@
|
||||
mkdir -p /data/letsencrypt-acme-challenge
|
||||
|
||||
cd /app
|
||||
node --abort_on_uncaught_exception --max_old_space_size=250 /app/src/backend/index.js
|
||||
|
||||
while :
|
||||
do
|
||||
node --abort_on_uncaught_exception --max_old_space_size=250 /app/src/backend/index.js
|
||||
sleep 1
|
||||
done
|
||||
|
@ -1,5 +1,6 @@
|
||||
#!/usr/bin/with-contenv bash
|
||||
|
||||
# Create required folders
|
||||
mkdir -p /tmp/nginx/body \
|
||||
/var/log/nginx \
|
||||
/data/nginx \
|
||||
@ -12,9 +13,30 @@ mkdir -p /tmp/nginx/body \
|
||||
/data/nginx/dead_host \
|
||||
/data/nginx/temp \
|
||||
/var/lib/nginx/cache/public \
|
||||
/var/lib/nginx/cache/private
|
||||
/var/lib/nginx/cache/private \
|
||||
/var/cache/nginx/proxy_temp
|
||||
|
||||
touch /var/log/nginx/error.log && chmod 777 /var/log/nginx/error.log
|
||||
touch /var/log/nginx/error.log && chmod 777 /var/log/nginx/error.log && chmod -R 777 /var/cache/nginx
|
||||
chown root /tmp/nginx
|
||||
|
||||
# Dynamically generate resolvers file
|
||||
echo resolver $(awk 'BEGIN{ORS=" "} $1=="nameserver" {print $2}' /etc/resolv.conf) ";" > /etc/nginx/conf.d/include/resolvers.conf
|
||||
|
||||
# Generate dummy self-signed certificate.
|
||||
if [ ! -f /data/nginx/dummycert.pem ] || [ ! -f /data/nginx/dummykey.pem ]
|
||||
then
|
||||
echo "Generating dummy SSL certificate..."
|
||||
openssl req \
|
||||
-new \
|
||||
-newkey rsa:2048 \
|
||||
-days 3650 \
|
||||
-nodes \
|
||||
-x509 \
|
||||
-subj '/O=Nginx Proxy Manager/OU=Dummy Certificate/CN=localhost' \
|
||||
-keyout /data/nginx/dummykey.pem \
|
||||
-out /data/nginx/dummycert.pem
|
||||
echo "Complete"
|
||||
fi
|
||||
|
||||
# Run
|
||||
exec nginx
|
||||
|
@ -1,9 +1,10 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const logger = require('./logger').import;
|
||||
const utils = require('./lib/utils');
|
||||
const batchflow = require('batchflow');
|
||||
const fs = require('fs');
|
||||
const logger = require('./logger').import;
|
||||
const utils = require('./lib/utils');
|
||||
const batchflow = require('batchflow');
|
||||
const debug_mode = process.env.NODE_ENV !== 'production';
|
||||
|
||||
const internalProxyHost = require('./internal/proxy-host');
|
||||
const internalRedirectionHost = require('./internal/redirection-host');
|
||||
@ -353,7 +354,7 @@ module.exports = function () {
|
||||
.insertAndFetch({
|
||||
owner_user_id: 1,
|
||||
domain_names: [host.hostname],
|
||||
forward_ip: host.forward_server,
|
||||
forward_host: host.forward_server,
|
||||
forward_port: host.forward_port,
|
||||
access_list_id: access_list_id,
|
||||
certificate_id: certificate_id,
|
||||
@ -534,6 +535,10 @@ module.exports = function () {
|
||||
);
|
||||
|
||||
} else {
|
||||
if (debug_mode) {
|
||||
logger.debug('Importer skipped');
|
||||
}
|
||||
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
|
@ -11,6 +11,7 @@ function appStart () {
|
||||
const app = require('./app');
|
||||
const apiValidator = require('./lib/validator/api');
|
||||
const internalCertificate = require('./internal/certificate');
|
||||
const internalIpRanges = require('./internal/ip_ranges');
|
||||
|
||||
return migrate.latest()
|
||||
.then(setup)
|
||||
@ -18,9 +19,11 @@ function appStart () {
|
||||
.then(() => {
|
||||
return apiValidator.loadSchemas;
|
||||
})
|
||||
.then(internalIpRanges.fetch)
|
||||
.then(() => {
|
||||
|
||||
internalCertificate.initTimer();
|
||||
internalIpRanges.initTimer();
|
||||
|
||||
const server = app.listen(81, () => {
|
||||
logger.info('PID ' + process.pid + ' listening on port 81 ...');
|
||||
|
@ -103,7 +103,7 @@ const internalDeadHost = {
|
||||
/**
|
||||
* @param {Access} access
|
||||
* @param {Object} data
|
||||
* @param {Integer} data.id
|
||||
* @param {Number} data.id
|
||||
* @return {Promise}
|
||||
*/
|
||||
update: (access, data) => {
|
||||
@ -201,7 +201,7 @@ const internalDeadHost = {
|
||||
/**
|
||||
* @param {Access} access
|
||||
* @param {Object} data
|
||||
* @param {Integer} data.id
|
||||
* @param {Number} data.id
|
||||
* @param {Array} [data.expand]
|
||||
* @param {Array} [data.omit]
|
||||
* @return {Promise}
|
||||
@ -248,7 +248,7 @@ const internalDeadHost = {
|
||||
/**
|
||||
* @param {Access} access
|
||||
* @param {Object} data
|
||||
* @param {Integer} data.id
|
||||
* @param {Number} data.id
|
||||
* @param {String} [data.reason]
|
||||
* @returns {Promise}
|
||||
*/
|
||||
@ -290,6 +290,104 @@ const internalDeadHost = {
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Access} access
|
||||
* @param {Object} data
|
||||
* @param {Number} data.id
|
||||
* @param {String} [data.reason]
|
||||
* @returns {Promise}
|
||||
*/
|
||||
enable: (access, data) => {
|
||||
return access.can('dead_hosts:update', data.id)
|
||||
.then(() => {
|
||||
return internalDeadHost.get(access, {
|
||||
id: data.id,
|
||||
expand: ['certificate', 'owner']
|
||||
});
|
||||
})
|
||||
.then(row => {
|
||||
if (!row) {
|
||||
throw new error.ItemNotFoundError(data.id);
|
||||
} else if (row.enabled) {
|
||||
throw new error.ValidationError('Host is already enabled');
|
||||
}
|
||||
|
||||
row.enabled = 1;
|
||||
|
||||
return deadHostModel
|
||||
.query()
|
||||
.where('id', row.id)
|
||||
.patch({
|
||||
enabled: 1
|
||||
})
|
||||
.then(() => {
|
||||
// Configure nginx
|
||||
return internalNginx.configure(deadHostModel, 'dead_host', row);
|
||||
})
|
||||
.then(() => {
|
||||
// Add to audit log
|
||||
return internalAuditLog.add(access, {
|
||||
action: 'enabled',
|
||||
object_type: 'dead-host',
|
||||
object_id: row.id,
|
||||
meta: _.omit(row, omissions())
|
||||
});
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
return true;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Access} access
|
||||
* @param {Object} data
|
||||
* @param {Number} data.id
|
||||
* @param {String} [data.reason]
|
||||
* @returns {Promise}
|
||||
*/
|
||||
disable: (access, data) => {
|
||||
return access.can('dead_hosts:update', data.id)
|
||||
.then(() => {
|
||||
return internalDeadHost.get(access, {id: data.id});
|
||||
})
|
||||
.then(row => {
|
||||
if (!row) {
|
||||
throw new error.ItemNotFoundError(data.id);
|
||||
} else if (!row.enabled) {
|
||||
throw new error.ValidationError('Host is already disabled');
|
||||
}
|
||||
|
||||
row.enabled = 0;
|
||||
|
||||
return deadHostModel
|
||||
.query()
|
||||
.where('id', row.id)
|
||||
.patch({
|
||||
enabled: 0
|
||||
})
|
||||
.then(() => {
|
||||
// Delete Nginx Config
|
||||
return internalNginx.deleteConfig('dead_host', row)
|
||||
.then(() => {
|
||||
return internalNginx.reload();
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
// Add to audit log
|
||||
return internalAuditLog.add(access, {
|
||||
action: 'disabled',
|
||||
object_type: 'dead-host',
|
||||
object_id: row.id,
|
||||
meta: _.omit(row, omissions())
|
||||
});
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
return true;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* All Hosts
|
||||
*
|
||||
@ -338,7 +436,7 @@ const internalDeadHost = {
|
||||
/**
|
||||
* Report use
|
||||
*
|
||||
* @param {Integer} user_id
|
||||
* @param {Number} user_id
|
||||
* @param {String} visibility
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
150
src/backend/internal/ip_ranges.js
Normal file
150
src/backend/internal/ip_ranges.js
Normal file
@ -0,0 +1,150 @@
|
||||
'use strict';
|
||||
|
||||
const https = require('https');
|
||||
const fs = require('fs');
|
||||
const _ = require('lodash');
|
||||
const logger = require('../logger').ip_ranges;
|
||||
const error = require('../lib/error');
|
||||
const internalNginx = require('./nginx');
|
||||
const Liquid = require('liquidjs');
|
||||
|
||||
const CLOUDFRONT_URL = 'https://ip-ranges.amazonaws.com/ip-ranges.json';
|
||||
const CLOUDFARE_V4_URL = 'https://www.cloudflare.com/ips-v4';
|
||||
const CLOUDFARE_V6_URL = 'https://www.cloudflare.com/ips-v6';
|
||||
|
||||
const internalIpRanges = {
|
||||
|
||||
interval_timeout: 1000 * 60 * 60 * 6, // 6 hours
|
||||
interval: null,
|
||||
interval_processing: false,
|
||||
iteration_count: 0,
|
||||
|
||||
initTimer: () => {
|
||||
logger.info('IP Ranges Renewal Timer initialized');
|
||||
internalIpRanges.interval = setInterval(internalIpRanges.fetch, internalIpRanges.interval_timeout);
|
||||
},
|
||||
|
||||
fetchUrl: url => {
|
||||
return new Promise((resolve, reject) => {
|
||||
logger.info('Fetching ' + url);
|
||||
return https.get(url, res => {
|
||||
res.setEncoding('utf8');
|
||||
let raw_data = '';
|
||||
res.on('data', chunk => {
|
||||
raw_data += chunk;
|
||||
});
|
||||
|
||||
res.on('end', () => {
|
||||
resolve(raw_data);
|
||||
});
|
||||
}).on('error', err => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Triggered at startup and then later by a timer, this will fetch the ip ranges from services and apply them to nginx.
|
||||
*/
|
||||
fetch: () => {
|
||||
if (!internalIpRanges.interval_processing) {
|
||||
internalIpRanges.interval_processing = true;
|
||||
logger.info('Fetching IP Ranges from online services...');
|
||||
|
||||
let ip_ranges = [];
|
||||
|
||||
return internalIpRanges.fetchUrl(CLOUDFRONT_URL)
|
||||
.then(cloudfront_data => {
|
||||
let data = JSON.parse(cloudfront_data);
|
||||
|
||||
if (data && typeof data.prefixes !== 'undefined') {
|
||||
data.prefixes.map(item => {
|
||||
if (item.service === 'CLOUDFRONT') {
|
||||
ip_ranges.push(item.ip_prefix);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (data && typeof data.ipv6_prefixes !== 'undefined') {
|
||||
data.ipv6_prefixes.map(item => {
|
||||
if (item.service === 'CLOUDFRONT') {
|
||||
ip_ranges.push(item.ipv6_prefix);
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
return internalIpRanges.fetchUrl(CLOUDFARE_V4_URL);
|
||||
})
|
||||
.then(cloudfare_data => {
|
||||
let items = cloudfare_data.split('\n');
|
||||
ip_ranges = [... ip_ranges, ... items];
|
||||
})
|
||||
.then(() => {
|
||||
return internalIpRanges.fetchUrl(CLOUDFARE_V6_URL);
|
||||
})
|
||||
.then(cloudfare_data => {
|
||||
let items = cloudfare_data.split('\n');
|
||||
ip_ranges = [... ip_ranges, ... items];
|
||||
})
|
||||
.then(() => {
|
||||
let clean_ip_ranges = [];
|
||||
ip_ranges.map(range => {
|
||||
if (range) {
|
||||
clean_ip_ranges.push(range);
|
||||
}
|
||||
});
|
||||
|
||||
return internalIpRanges.generateConfig(clean_ip_ranges)
|
||||
.then(() => {
|
||||
if (internalIpRanges.iteration_count) {
|
||||
// Reload nginx
|
||||
return internalNginx.reload();
|
||||
}
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
internalIpRanges.interval_processing = false;
|
||||
internalIpRanges.iteration_count++;
|
||||
})
|
||||
.catch(err => {
|
||||
logger.error(err.message);
|
||||
internalIpRanges.interval_processing = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Array} ip_ranges
|
||||
* @returns {Promise}
|
||||
*/
|
||||
generateConfig: (ip_ranges) => {
|
||||
let renderEngine = Liquid({
|
||||
root: __dirname + '/../templates/'
|
||||
});
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let template = null;
|
||||
let filename = '/etc/nginx/conf.d/include/ip_ranges.conf';
|
||||
try {
|
||||
template = fs.readFileSync(__dirname + '/../templates/ip_ranges.conf', {encoding: 'utf8'});
|
||||
} catch (err) {
|
||||
reject(new error.ConfigurationError(err.message));
|
||||
return;
|
||||
}
|
||||
|
||||
renderEngine
|
||||
.parseAndRender(template, {ip_ranges: ip_ranges})
|
||||
.then(config_text => {
|
||||
fs.writeFileSync(filename, config_text, {encoding: 'utf8'});
|
||||
resolve(true);
|
||||
})
|
||||
.catch(err => {
|
||||
logger.warn('Could not write ' + filename + ':', err.message);
|
||||
reject(new error.ConfigurationError(err.message));
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = internalIpRanges;
|
@ -104,7 +104,7 @@ const internalProxyHost = {
|
||||
/**
|
||||
* @param {Access} access
|
||||
* @param {Object} data
|
||||
* @param {Integer} data.id
|
||||
* @param {Number} data.id
|
||||
* @return {Promise}
|
||||
*/
|
||||
update: (access, data) => {
|
||||
@ -163,7 +163,7 @@ const internalProxyHost = {
|
||||
// Add domain_names to the data in case it isn't there, so that the audit log renders correctly. The order is important here.
|
||||
data = _.assign({}, {
|
||||
domain_names: row.domain_names
|
||||
},data);
|
||||
}, data);
|
||||
|
||||
return proxyHostModel
|
||||
.query()
|
||||
@ -192,7 +192,7 @@ const internalProxyHost = {
|
||||
return internalNginx.configure(proxyHostModel, 'proxy_host', row)
|
||||
.then(new_meta => {
|
||||
row.meta = new_meta;
|
||||
row = internalHost.cleanRowCertificateMeta(row);
|
||||
row = internalHost.cleanRowCertificateMeta(row);
|
||||
return _.omit(row, omissions());
|
||||
});
|
||||
});
|
||||
@ -202,7 +202,7 @@ const internalProxyHost = {
|
||||
/**
|
||||
* @param {Access} access
|
||||
* @param {Object} data
|
||||
* @param {Integer} data.id
|
||||
* @param {Number} data.id
|
||||
* @param {Array} [data.expand]
|
||||
* @param {Array} [data.omit]
|
||||
* @return {Promise}
|
||||
@ -249,7 +249,7 @@ const internalProxyHost = {
|
||||
/**
|
||||
* @param {Access} access
|
||||
* @param {Object} data
|
||||
* @param {Integer} data.id
|
||||
* @param {Number} data.id
|
||||
* @param {String} [data.reason]
|
||||
* @returns {Promise}
|
||||
*/
|
||||
@ -291,6 +291,104 @@ const internalProxyHost = {
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Access} access
|
||||
* @param {Object} data
|
||||
* @param {Number} data.id
|
||||
* @param {String} [data.reason]
|
||||
* @returns {Promise}
|
||||
*/
|
||||
enable: (access, data) => {
|
||||
return access.can('proxy_hosts:update', data.id)
|
||||
.then(() => {
|
||||
return internalProxyHost.get(access, {
|
||||
id: data.id,
|
||||
expand: ['certificate', 'owner', 'access_list']
|
||||
});
|
||||
})
|
||||
.then(row => {
|
||||
if (!row) {
|
||||
throw new error.ItemNotFoundError(data.id);
|
||||
} else if (row.enabled) {
|
||||
throw new error.ValidationError('Host is already enabled');
|
||||
}
|
||||
|
||||
row.enabled = 1;
|
||||
|
||||
return proxyHostModel
|
||||
.query()
|
||||
.where('id', row.id)
|
||||
.patch({
|
||||
enabled: 1
|
||||
})
|
||||
.then(() => {
|
||||
// Configure nginx
|
||||
return internalNginx.configure(proxyHostModel, 'proxy_host', row);
|
||||
})
|
||||
.then(() => {
|
||||
// Add to audit log
|
||||
return internalAuditLog.add(access, {
|
||||
action: 'enabled',
|
||||
object_type: 'proxy-host',
|
||||
object_id: row.id,
|
||||
meta: _.omit(row, omissions())
|
||||
});
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
return true;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Access} access
|
||||
* @param {Object} data
|
||||
* @param {Number} data.id
|
||||
* @param {String} [data.reason]
|
||||
* @returns {Promise}
|
||||
*/
|
||||
disable: (access, data) => {
|
||||
return access.can('proxy_hosts:update', data.id)
|
||||
.then(() => {
|
||||
return internalProxyHost.get(access, {id: data.id});
|
||||
})
|
||||
.then(row => {
|
||||
if (!row) {
|
||||
throw new error.ItemNotFoundError(data.id);
|
||||
} else if (!row.enabled) {
|
||||
throw new error.ValidationError('Host is already disabled');
|
||||
}
|
||||
|
||||
row.enabled = 0;
|
||||
|
||||
return proxyHostModel
|
||||
.query()
|
||||
.where('id', row.id)
|
||||
.patch({
|
||||
enabled: 0
|
||||
})
|
||||
.then(() => {
|
||||
// Delete Nginx Config
|
||||
return internalNginx.deleteConfig('proxy_host', row)
|
||||
.then(() => {
|
||||
return internalNginx.reload();
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
// Add to audit log
|
||||
return internalAuditLog.add(access, {
|
||||
action: 'disabled',
|
||||
object_type: 'proxy-host',
|
||||
object_id: row.id,
|
||||
meta: _.omit(row, omissions())
|
||||
});
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
return true;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* All Hosts
|
||||
*
|
||||
@ -339,7 +437,7 @@ const internalProxyHost = {
|
||||
/**
|
||||
* Report use
|
||||
*
|
||||
* @param {Integer} user_id
|
||||
* @param {Number} user_id
|
||||
* @param {String} visibility
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
@ -103,7 +103,7 @@ const internalRedirectionHost = {
|
||||
/**
|
||||
* @param {Access} access
|
||||
* @param {Object} data
|
||||
* @param {Integer} data.id
|
||||
* @param {Number} data.id
|
||||
* @return {Promise}
|
||||
*/
|
||||
update: (access, data) => {
|
||||
@ -201,7 +201,7 @@ const internalRedirectionHost = {
|
||||
/**
|
||||
* @param {Access} access
|
||||
* @param {Object} data
|
||||
* @param {Integer} data.id
|
||||
* @param {Number} data.id
|
||||
* @param {Array} [data.expand]
|
||||
* @param {Array} [data.omit]
|
||||
* @return {Promise}
|
||||
@ -248,7 +248,7 @@ const internalRedirectionHost = {
|
||||
/**
|
||||
* @param {Access} access
|
||||
* @param {Object} data
|
||||
* @param {Integer} data.id
|
||||
* @param {Number} data.id
|
||||
* @param {String} [data.reason]
|
||||
* @returns {Promise}
|
||||
*/
|
||||
@ -290,6 +290,104 @@ const internalRedirectionHost = {
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Access} access
|
||||
* @param {Object} data
|
||||
* @param {Number} data.id
|
||||
* @param {String} [data.reason]
|
||||
* @returns {Promise}
|
||||
*/
|
||||
enable: (access, data) => {
|
||||
return access.can('redirection_hosts:update', data.id)
|
||||
.then(() => {
|
||||
return internalRedirectionHost.get(access, {
|
||||
id: data.id,
|
||||
expand: ['certificate', 'owner']
|
||||
});
|
||||
})
|
||||
.then(row => {
|
||||
if (!row) {
|
||||
throw new error.ItemNotFoundError(data.id);
|
||||
} else if (row.enabled) {
|
||||
throw new error.ValidationError('Host is already enabled');
|
||||
}
|
||||
|
||||
row.enabled = 1;
|
||||
|
||||
return redirectionHostModel
|
||||
.query()
|
||||
.where('id', row.id)
|
||||
.patch({
|
||||
enabled: 1
|
||||
})
|
||||
.then(() => {
|
||||
// Configure nginx
|
||||
return internalNginx.configure(redirectionHostModel, 'redirection_host', row);
|
||||
})
|
||||
.then(() => {
|
||||
// Add to audit log
|
||||
return internalAuditLog.add(access, {
|
||||
action: 'enabled',
|
||||
object_type: 'redirection-host',
|
||||
object_id: row.id,
|
||||
meta: _.omit(row, omissions())
|
||||
});
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
return true;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Access} access
|
||||
* @param {Object} data
|
||||
* @param {Number} data.id
|
||||
* @param {String} [data.reason]
|
||||
* @returns {Promise}
|
||||
*/
|
||||
disable: (access, data) => {
|
||||
return access.can('redirection_hosts:update', data.id)
|
||||
.then(() => {
|
||||
return internalRedirectionHost.get(access, {id: data.id});
|
||||
})
|
||||
.then(row => {
|
||||
if (!row) {
|
||||
throw new error.ItemNotFoundError(data.id);
|
||||
} else if (!row.enabled) {
|
||||
throw new error.ValidationError('Host is already disabled');
|
||||
}
|
||||
|
||||
row.enabled = 0;
|
||||
|
||||
return redirectionHostModel
|
||||
.query()
|
||||
.where('id', row.id)
|
||||
.patch({
|
||||
enabled: 0
|
||||
})
|
||||
.then(() => {
|
||||
// Delete Nginx Config
|
||||
return internalNginx.deleteConfig('redirection_host', row)
|
||||
.then(() => {
|
||||
return internalNginx.reload();
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
// Add to audit log
|
||||
return internalAuditLog.add(access, {
|
||||
action: 'disabled',
|
||||
object_type: 'redirection-host',
|
||||
object_id: row.id,
|
||||
meta: _.omit(row, omissions())
|
||||
});
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
return true;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* All Hosts
|
||||
*
|
||||
@ -338,7 +436,7 @@ const internalRedirectionHost = {
|
||||
/**
|
||||
* Report use
|
||||
*
|
||||
* @param {Integer} user_id
|
||||
* @param {Number} user_id
|
||||
* @param {String} visibility
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
@ -56,7 +56,7 @@ const internalStream = {
|
||||
/**
|
||||
* @param {Access} access
|
||||
* @param {Object} data
|
||||
* @param {Integer} data.id
|
||||
* @param {Number} data.id
|
||||
* @return {Promise}
|
||||
*/
|
||||
update: (access, data) => {
|
||||
@ -75,6 +75,12 @@ const internalStream = {
|
||||
.query()
|
||||
.omit(omissions())
|
||||
.patchAndFetchById(row.id, data)
|
||||
.then(saved_row => {
|
||||
return internalNginx.configure(streamModel, 'stream', saved_row)
|
||||
.then(() => {
|
||||
return internalStream.get(access, {id: row.id, expand: ['owner']});
|
||||
});
|
||||
})
|
||||
.then(saved_row => {
|
||||
// Add to audit log
|
||||
return internalAuditLog.add(access, {
|
||||
@ -93,7 +99,7 @@ const internalStream = {
|
||||
/**
|
||||
* @param {Access} access
|
||||
* @param {Object} data
|
||||
* @param {Integer} data.id
|
||||
* @param {Number} data.id
|
||||
* @param {Array} [data.expand]
|
||||
* @param {Array} [data.omit]
|
||||
* @return {Promise}
|
||||
@ -139,7 +145,7 @@ const internalStream = {
|
||||
/**
|
||||
* @param {Access} access
|
||||
* @param {Object} data
|
||||
* @param {Integer} data.id
|
||||
* @param {Number} data.id
|
||||
* @param {String} [data.reason]
|
||||
* @returns {Promise}
|
||||
*/
|
||||
@ -181,6 +187,104 @@ const internalStream = {
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Access} access
|
||||
* @param {Object} data
|
||||
* @param {Number} data.id
|
||||
* @param {String} [data.reason]
|
||||
* @returns {Promise}
|
||||
*/
|
||||
enable: (access, data) => {
|
||||
return access.can('streams:update', data.id)
|
||||
.then(() => {
|
||||
return internalStream.get(access, {
|
||||
id: data.id,
|
||||
expand: ['owner']
|
||||
});
|
||||
})
|
||||
.then(row => {
|
||||
if (!row) {
|
||||
throw new error.ItemNotFoundError(data.id);
|
||||
} else if (row.enabled) {
|
||||
throw new error.ValidationError('Host is already enabled');
|
||||
}
|
||||
|
||||
row.enabled = 1;
|
||||
|
||||
return streamModel
|
||||
.query()
|
||||
.where('id', row.id)
|
||||
.patch({
|
||||
enabled: 1
|
||||
})
|
||||
.then(() => {
|
||||
// Configure nginx
|
||||
return internalNginx.configure(streamModel, 'stream', row);
|
||||
})
|
||||
.then(() => {
|
||||
// Add to audit log
|
||||
return internalAuditLog.add(access, {
|
||||
action: 'enabled',
|
||||
object_type: 'stream',
|
||||
object_id: row.id,
|
||||
meta: _.omit(row, omissions())
|
||||
});
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
return true;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Access} access
|
||||
* @param {Object} data
|
||||
* @param {Number} data.id
|
||||
* @param {String} [data.reason]
|
||||
* @returns {Promise}
|
||||
*/
|
||||
disable: (access, data) => {
|
||||
return access.can('streams:update', data.id)
|
||||
.then(() => {
|
||||
return internalStream.get(access, {id: data.id});
|
||||
})
|
||||
.then(row => {
|
||||
if (!row) {
|
||||
throw new error.ItemNotFoundError(data.id);
|
||||
} else if (!row.enabled) {
|
||||
throw new error.ValidationError('Host is already disabled');
|
||||
}
|
||||
|
||||
row.enabled = 0;
|
||||
|
||||
return streamModel
|
||||
.query()
|
||||
.where('id', row.id)
|
||||
.patch({
|
||||
enabled: 0
|
||||
})
|
||||
.then(() => {
|
||||
// Delete Nginx Config
|
||||
return internalNginx.deleteConfig('stream', row)
|
||||
.then(() => {
|
||||
return internalNginx.reload();
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
// Add to audit log
|
||||
return internalAuditLog.add(access, {
|
||||
action: 'disabled',
|
||||
object_type: 'stream-host',
|
||||
object_id: row.id,
|
||||
meta: _.omit(row, omissions())
|
||||
});
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
return true;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* All Streams
|
||||
*
|
||||
@ -222,7 +326,7 @@ const internalStream = {
|
||||
/**
|
||||
* Report use
|
||||
*
|
||||
* @param {Integer} user_id
|
||||
* @param {Number} user_id
|
||||
* @param {String} visibility
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
@ -1,11 +1,13 @@
|
||||
const {Signale} = require('signale');
|
||||
|
||||
module.exports = {
|
||||
global: new Signale({scope: 'Global '}),
|
||||
migrate: new Signale({scope: 'Migrate '}),
|
||||
express: new Signale({scope: 'Express '}),
|
||||
access: new Signale({scope: 'Access '}),
|
||||
nginx: new Signale({scope: 'Nginx '}),
|
||||
ssl: new Signale({scope: 'SSL '}),
|
||||
import: new Signale({scope: 'Importer'}),
|
||||
global: new Signale({scope: 'Global '}),
|
||||
migrate: new Signale({scope: 'Migrate '}),
|
||||
express: new Signale({scope: 'Express '}),
|
||||
access: new Signale({scope: 'Access '}),
|
||||
nginx: new Signale({scope: 'Nginx '}),
|
||||
ssl: new Signale({scope: 'SSL '}),
|
||||
import: new Signale({scope: 'Importer '}),
|
||||
setup: new Signale({scope: 'Setup '}),
|
||||
ip_ranges: new Signale({scope: 'IP Ranges'})
|
||||
};
|
||||
|
37
src/backend/migrations/20180929054513_websockets.js
Normal file
37
src/backend/migrations/20180929054513_websockets.js
Normal file
@ -0,0 +1,37 @@
|
||||
'use strict';
|
||||
|
||||
const migrate_name = 'websockets';
|
||||
const logger = require('../logger').migrate;
|
||||
|
||||
/**
|
||||
* Migrate
|
||||
*
|
||||
* @see http://knexjs.org/#Schema
|
||||
*
|
||||
* @param {Object} knex
|
||||
* @param {Promise} Promise
|
||||
* @returns {Promise}
|
||||
*/
|
||||
exports.up = function (knex/*, Promise*/) {
|
||||
logger.info('[' + migrate_name + '] Migrating Up...');
|
||||
|
||||
return knex.schema.table('proxy_host', function (proxy_host) {
|
||||
proxy_host.integer('allow_websocket_upgrade').notNull().unsigned().defaultTo(0);
|
||||
})
|
||||
.then(() => {
|
||||
logger.info('[' + migrate_name + '] proxy_host Table altered');
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Undo Migrate
|
||||
*
|
||||
* @param {Object} knex
|
||||
* @param {Promise} Promise
|
||||
* @returns {Promise}
|
||||
*/
|
||||
exports.down = function (knex, Promise) {
|
||||
logger.warn('[' + migrate_name + '] You can\'t migrate down this one.');
|
||||
return Promise.resolve(true);
|
||||
};
|
36
src/backend/migrations/20181019052346_forward_host.js
Normal file
36
src/backend/migrations/20181019052346_forward_host.js
Normal file
@ -0,0 +1,36 @@
|
||||
'use strict';
|
||||
|
||||
const migrate_name = 'forward_host';
|
||||
const logger = require('../logger').migrate;
|
||||
|
||||
/**
|
||||
* Migrate
|
||||
*
|
||||
* @see http://knexjs.org/#Schema
|
||||
*
|
||||
* @param {Object} knex
|
||||
* @param {Promise} Promise
|
||||
* @returns {Promise}
|
||||
*/
|
||||
exports.up = function (knex/*, Promise*/) {
|
||||
logger.info('[' + migrate_name + '] Migrating Up...');
|
||||
|
||||
return knex.schema.table('proxy_host', function (proxy_host) {
|
||||
proxy_host.renameColumn('forward_ip', 'forward_host');
|
||||
})
|
||||
.then(() => {
|
||||
logger.info('[' + migrate_name + '] proxy_host Table altered');
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Undo Migrate
|
||||
*
|
||||
* @param {Object} knex
|
||||
* @param {Promise} Promise
|
||||
* @returns {Promise}
|
||||
*/
|
||||
exports.down = function (knex, Promise) {
|
||||
logger.warn('[' + migrate_name + '] You can\'t migrate down this one.');
|
||||
return Promise.resolve(true);
|
||||
};
|
51
src/backend/migrations/20181113041458_http2_support.js
Normal file
51
src/backend/migrations/20181113041458_http2_support.js
Normal file
@ -0,0 +1,51 @@
|
||||
'use strict';
|
||||
|
||||
const migrate_name = 'http2_support';
|
||||
const logger = require('../logger').migrate;
|
||||
|
||||
/**
|
||||
* Migrate
|
||||
*
|
||||
* @see http://knexjs.org/#Schema
|
||||
*
|
||||
* @param {Object} knex
|
||||
* @param {Promise} Promise
|
||||
* @returns {Promise}
|
||||
*/
|
||||
exports.up = function (knex/*, Promise*/) {
|
||||
logger.info('[' + migrate_name + '] Migrating Up...');
|
||||
|
||||
return knex.schema.table('proxy_host', function (proxy_host) {
|
||||
proxy_host.integer('http2_support').notNull().unsigned().defaultTo(0);
|
||||
})
|
||||
.then(() => {
|
||||
logger.info('[' + migrate_name + '] proxy_host Table altered');
|
||||
|
||||
return knex.schema.table('redirection_host', function (redirection_host) {
|
||||
redirection_host.integer('http2_support').notNull().unsigned().defaultTo(0);
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
logger.info('[' + migrate_name + '] redirection_host Table altered');
|
||||
|
||||
return knex.schema.table('dead_host', function (dead_host) {
|
||||
dead_host.integer('http2_support').notNull().unsigned().defaultTo(0);
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
logger.info('[' + migrate_name + '] dead_host Table altered');
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Undo Migrate
|
||||
*
|
||||
* @param {Object} knex
|
||||
* @param {Promise} Promise
|
||||
* @returns {Promise}
|
||||
*/
|
||||
exports.down = function (knex, Promise) {
|
||||
logger.warn('[' + migrate_name + '] You can\'t migrate down this one.');
|
||||
return Promise.resolve(true);
|
||||
};
|
||||
|
36
src/backend/migrations/20181213013211_forward_scheme.js
Normal file
36
src/backend/migrations/20181213013211_forward_scheme.js
Normal file
@ -0,0 +1,36 @@
|
||||
'use strict';
|
||||
|
||||
const migrate_name = 'forward_scheme';
|
||||
const logger = require('../logger').migrate;
|
||||
|
||||
/**
|
||||
* Migrate
|
||||
*
|
||||
* @see http://knexjs.org/#Schema
|
||||
*
|
||||
* @param {Object} knex
|
||||
* @param {Promise} Promise
|
||||
* @returns {Promise}
|
||||
*/
|
||||
exports.up = function (knex/*, Promise*/) {
|
||||
logger.info('[' + migrate_name + '] Migrating Up...');
|
||||
|
||||
return knex.schema.table('proxy_host', function (proxy_host) {
|
||||
proxy_host.string('forward_scheme').notNull().defaultTo('http');
|
||||
})
|
||||
.then(() => {
|
||||
logger.info('[' + migrate_name + '] proxy_host Table altered');
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Undo Migrate
|
||||
*
|
||||
* @param {Object} knex
|
||||
* @param {Promise} Promise
|
||||
* @returns {Promise}
|
||||
*/
|
||||
exports.down = function (knex, Promise) {
|
||||
logger.warn('[' + migrate_name + '] You can\'t migrate down this one.');
|
||||
return Promise.resolve(true);
|
||||
};
|
57
src/backend/migrations/20190104035154_disabled.js
Normal file
57
src/backend/migrations/20190104035154_disabled.js
Normal file
@ -0,0 +1,57 @@
|
||||
'use strict';
|
||||
|
||||
const migrate_name = 'disabled';
|
||||
const logger = require('../logger').migrate;
|
||||
|
||||
/**
|
||||
* Migrate
|
||||
*
|
||||
* @see http://knexjs.org/#Schema
|
||||
*
|
||||
* @param {Object} knex
|
||||
* @param {Promise} Promise
|
||||
* @returns {Promise}
|
||||
*/
|
||||
exports.up = function (knex/*, Promise*/) {
|
||||
logger.info('[' + migrate_name + '] Migrating Up...');
|
||||
|
||||
return knex.schema.table('proxy_host', function (proxy_host) {
|
||||
proxy_host.integer('enabled').notNull().unsigned().defaultTo(1);
|
||||
})
|
||||
.then(() => {
|
||||
logger.info('[' + migrate_name + '] proxy_host Table altered');
|
||||
|
||||
return knex.schema.table('redirection_host', function (redirection_host) {
|
||||
redirection_host.integer('enabled').notNull().unsigned().defaultTo(1);
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
logger.info('[' + migrate_name + '] redirection_host Table altered');
|
||||
|
||||
return knex.schema.table('dead_host', function (dead_host) {
|
||||
dead_host.integer('enabled').notNull().unsigned().defaultTo(1);
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
logger.info('[' + migrate_name + '] dead_host Table altered');
|
||||
|
||||
return knex.schema.table('stream', function (stream) {
|
||||
stream.integer('enabled').notNull().unsigned().defaultTo(1);
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
logger.info('[' + migrate_name + '] stream Table altered');
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Undo Migrate
|
||||
*
|
||||
* @param {Object} knex
|
||||
* @param {Promise} Promise
|
||||
* @returns {Promise}
|
||||
*/
|
||||
exports.down = function (knex, Promise) {
|
||||
logger.warn('[' + migrate_name + '] You can\'t migrate down this one.');
|
||||
return Promise.resolve(true);
|
||||
};
|
@ -20,7 +20,7 @@ router
|
||||
.options((req, res) => {
|
||||
res.sendStatus(204);
|
||||
})
|
||||
.all(jwtdecode()) // preferred so it doesn't apply to nonexistent routes
|
||||
.all(jwtdecode())
|
||||
|
||||
/**
|
||||
* GET /api/nginx/access-lists
|
||||
@ -79,7 +79,7 @@ router
|
||||
.options((req, res) => {
|
||||
res.sendStatus(204);
|
||||
})
|
||||
.all(jwtdecode()) // preferred so it doesn't apply to nonexistent routes
|
||||
.all(jwtdecode())
|
||||
|
||||
/**
|
||||
* GET /api/nginx/access-lists/123
|
||||
|
@ -20,7 +20,7 @@ router
|
||||
.options((req, res) => {
|
||||
res.sendStatus(204);
|
||||
})
|
||||
.all(jwtdecode()) // preferred so it doesn't apply to nonexistent routes
|
||||
.all(jwtdecode())
|
||||
|
||||
/**
|
||||
* GET /api/nginx/certificates
|
||||
@ -79,7 +79,7 @@ router
|
||||
.options((req, res) => {
|
||||
res.sendStatus(204);
|
||||
})
|
||||
.all(jwtdecode()) // preferred so it doesn't apply to nonexistent routes
|
||||
.all(jwtdecode())
|
||||
|
||||
/**
|
||||
* GET /api/nginx/certificates/123
|
||||
@ -157,7 +157,7 @@ router
|
||||
.options((req, res) => {
|
||||
res.sendStatus(204);
|
||||
})
|
||||
.all(jwtdecode()) // preferred so it doesn't apply to nonexistent routes
|
||||
.all(jwtdecode())
|
||||
|
||||
/**
|
||||
* POST /api/nginx/certificates/123/upload
|
||||
@ -191,7 +191,7 @@ router
|
||||
.options((req, res) => {
|
||||
res.sendStatus(204);
|
||||
})
|
||||
.all(jwtdecode()) // preferred so it doesn't apply to nonexistent routes
|
||||
.all(jwtdecode())
|
||||
|
||||
/**
|
||||
* POST /api/nginx/certificates/validate
|
||||
|
@ -20,7 +20,7 @@ router
|
||||
.options((req, res) => {
|
||||
res.sendStatus(204);
|
||||
})
|
||||
.all(jwtdecode()) // preferred so it doesn't apply to nonexistent routes
|
||||
.all(jwtdecode())
|
||||
|
||||
/**
|
||||
* GET /api/nginx/dead-hosts
|
||||
@ -79,7 +79,7 @@ router
|
||||
.options((req, res) => {
|
||||
res.sendStatus(204);
|
||||
})
|
||||
.all(jwtdecode()) // preferred so it doesn't apply to nonexistent routes
|
||||
.all(jwtdecode())
|
||||
|
||||
/**
|
||||
* GET /api/nginx/dead-hosts/123
|
||||
@ -147,4 +147,52 @@ router
|
||||
.catch(next);
|
||||
});
|
||||
|
||||
/**
|
||||
* Enable dead-host
|
||||
*
|
||||
* /api/nginx/dead-hosts/123/enable
|
||||
*/
|
||||
router
|
||||
.route('/:host_id/enable')
|
||||
.options((req, res) => {
|
||||
res.sendStatus(204);
|
||||
})
|
||||
.all(jwtdecode())
|
||||
|
||||
/**
|
||||
* POST /api/nginx/dead-hosts/123/enable
|
||||
*/
|
||||
.post((req, res, next) => {
|
||||
internalDeadHost.enable(res.locals.access, {id: parseInt(req.params.host_id, 10)})
|
||||
.then(result => {
|
||||
res.status(200)
|
||||
.send(result);
|
||||
})
|
||||
.catch(next);
|
||||
});
|
||||
|
||||
/**
|
||||
* Disable dead-host
|
||||
*
|
||||
* /api/nginx/dead-hosts/123/disable
|
||||
*/
|
||||
router
|
||||
.route('/:host_id/disable')
|
||||
.options((req, res) => {
|
||||
res.sendStatus(204);
|
||||
})
|
||||
.all(jwtdecode())
|
||||
|
||||
/**
|
||||
* POST /api/nginx/dead-hosts/123/disable
|
||||
*/
|
||||
.post((req, res, next) => {
|
||||
internalDeadHost.disable(res.locals.access, {id: parseInt(req.params.host_id, 10)})
|
||||
.then(result => {
|
||||
res.status(200)
|
||||
.send(result);
|
||||
})
|
||||
.catch(next);
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
|
@ -20,7 +20,7 @@ router
|
||||
.options((req, res) => {
|
||||
res.sendStatus(204);
|
||||
})
|
||||
.all(jwtdecode()) // preferred so it doesn't apply to nonexistent routes
|
||||
.all(jwtdecode())
|
||||
|
||||
/**
|
||||
* GET /api/nginx/proxy-hosts
|
||||
@ -79,7 +79,7 @@ router
|
||||
.options((req, res) => {
|
||||
res.sendStatus(204);
|
||||
})
|
||||
.all(jwtdecode()) // preferred so it doesn't apply to nonexistent routes
|
||||
.all(jwtdecode())
|
||||
|
||||
/**
|
||||
* GET /api/nginx/proxy-hosts/123
|
||||
@ -147,4 +147,52 @@ router
|
||||
.catch(next);
|
||||
});
|
||||
|
||||
/**
|
||||
* Enable proxy-host
|
||||
*
|
||||
* /api/nginx/proxy-hosts/123/enable
|
||||
*/
|
||||
router
|
||||
.route('/:host_id/enable')
|
||||
.options((req, res) => {
|
||||
res.sendStatus(204);
|
||||
})
|
||||
.all(jwtdecode())
|
||||
|
||||
/**
|
||||
* POST /api/nginx/proxy-hosts/123/enable
|
||||
*/
|
||||
.post((req, res, next) => {
|
||||
internalProxyHost.enable(res.locals.access, {id: parseInt(req.params.host_id, 10)})
|
||||
.then(result => {
|
||||
res.status(200)
|
||||
.send(result);
|
||||
})
|
||||
.catch(next);
|
||||
});
|
||||
|
||||
/**
|
||||
* Disable proxy-host
|
||||
*
|
||||
* /api/nginx/proxy-hosts/123/disable
|
||||
*/
|
||||
router
|
||||
.route('/:host_id/disable')
|
||||
.options((req, res) => {
|
||||
res.sendStatus(204);
|
||||
})
|
||||
.all(jwtdecode())
|
||||
|
||||
/**
|
||||
* POST /api/nginx/proxy-hosts/123/disable
|
||||
*/
|
||||
.post((req, res, next) => {
|
||||
internalProxyHost.disable(res.locals.access, {id: parseInt(req.params.host_id, 10)})
|
||||
.then(result => {
|
||||
res.status(200)
|
||||
.send(result);
|
||||
})
|
||||
.catch(next);
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
|
@ -20,7 +20,7 @@ router
|
||||
.options((req, res) => {
|
||||
res.sendStatus(204);
|
||||
})
|
||||
.all(jwtdecode()) // preferred so it doesn't apply to nonexistent routes
|
||||
.all(jwtdecode())
|
||||
|
||||
/**
|
||||
* GET /api/nginx/redirection-hosts
|
||||
@ -79,7 +79,7 @@ router
|
||||
.options((req, res) => {
|
||||
res.sendStatus(204);
|
||||
})
|
||||
.all(jwtdecode()) // preferred so it doesn't apply to nonexistent routes
|
||||
.all(jwtdecode())
|
||||
|
||||
/**
|
||||
* GET /api/nginx/redirection-hosts/123
|
||||
@ -147,4 +147,52 @@ router
|
||||
.catch(next);
|
||||
});
|
||||
|
||||
/**
|
||||
* Enable redirection-host
|
||||
*
|
||||
* /api/nginx/redirection-hosts/123/enable
|
||||
*/
|
||||
router
|
||||
.route('/:host_id/enable')
|
||||
.options((req, res) => {
|
||||
res.sendStatus(204);
|
||||
})
|
||||
.all(jwtdecode())
|
||||
|
||||
/**
|
||||
* POST /api/nginx/redirection-hosts/123/enable
|
||||
*/
|
||||
.post((req, res, next) => {
|
||||
internalRedirectionHost.enable(res.locals.access, {id: parseInt(req.params.host_id, 10)})
|
||||
.then(result => {
|
||||
res.status(200)
|
||||
.send(result);
|
||||
})
|
||||
.catch(next);
|
||||
});
|
||||
|
||||
/**
|
||||
* Disable redirection-host
|
||||
*
|
||||
* /api/nginx/redirection-hosts/123/disable
|
||||
*/
|
||||
router
|
||||
.route('/:host_id/disable')
|
||||
.options((req, res) => {
|
||||
res.sendStatus(204);
|
||||
})
|
||||
.all(jwtdecode())
|
||||
|
||||
/**
|
||||
* POST /api/nginx/redirection-hosts/123/disable
|
||||
*/
|
||||
.post((req, res, next) => {
|
||||
internalRedirectionHost.disable(res.locals.access, {id: parseInt(req.params.host_id, 10)})
|
||||
.then(result => {
|
||||
res.status(200)
|
||||
.send(result);
|
||||
})
|
||||
.catch(next);
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
|
@ -147,4 +147,52 @@ router
|
||||
.catch(next);
|
||||
});
|
||||
|
||||
/**
|
||||
* Enable stream
|
||||
*
|
||||
* /api/nginx/streams/123/enable
|
||||
*/
|
||||
router
|
||||
.route('/:host_id/enable')
|
||||
.options((req, res) => {
|
||||
res.sendStatus(204);
|
||||
})
|
||||
.all(jwtdecode())
|
||||
|
||||
/**
|
||||
* POST /api/nginx/streams/123/enable
|
||||
*/
|
||||
.post((req, res, next) => {
|
||||
internalStream.enable(res.locals.access, {id: parseInt(req.params.host_id, 10)})
|
||||
.then(result => {
|
||||
res.status(200)
|
||||
.send(result);
|
||||
})
|
||||
.catch(next);
|
||||
});
|
||||
|
||||
/**
|
||||
* Disable stream
|
||||
*
|
||||
* /api/nginx/streams/123/disable
|
||||
*/
|
||||
router
|
||||
.route('/:host_id/disable')
|
||||
.options((req, res) => {
|
||||
res.sendStatus(204);
|
||||
})
|
||||
.all(jwtdecode())
|
||||
|
||||
/**
|
||||
* POST /api/nginx/streams/123/disable
|
||||
*/
|
||||
.post((req, res, next) => {
|
||||
internalStream.disable(res.locals.access, {id: parseInt(req.params.host_id, 10)})
|
||||
.then(result => {
|
||||
res.status(200)
|
||||
.send(result);
|
||||
})
|
||||
.catch(next);
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
|
@ -21,7 +21,7 @@ router
|
||||
.options((req, res) => {
|
||||
res.sendStatus(204);
|
||||
})
|
||||
.all(jwtdecode()) // preferred so it doesn't apply to nonexistent routes
|
||||
.all(jwtdecode())
|
||||
|
||||
/**
|
||||
* GET /api/users
|
||||
@ -80,7 +80,7 @@ router
|
||||
.options((req, res) => {
|
||||
res.sendStatus(204);
|
||||
})
|
||||
.all(jwtdecode()) // preferred so it doesn't apply to nonexistent routes
|
||||
.all(jwtdecode())
|
||||
.all(userIdFromMe)
|
||||
|
||||
/**
|
||||
@ -160,7 +160,7 @@ router
|
||||
.options((req, res) => {
|
||||
res.sendStatus(204);
|
||||
})
|
||||
.all(jwtdecode()) // preferred so it doesn't apply to nonexistent routes
|
||||
.all(jwtdecode())
|
||||
.all(userIdFromMe)
|
||||
|
||||
/**
|
||||
@ -191,7 +191,7 @@ router
|
||||
.options((req, res) => {
|
||||
res.sendStatus(204);
|
||||
})
|
||||
.all(jwtdecode()) // preferred so it doesn't apply to nonexistent routes
|
||||
.all(jwtdecode())
|
||||
.all(userIdFromMe)
|
||||
|
||||
/**
|
||||
@ -222,7 +222,7 @@ router
|
||||
.options((req, res) => {
|
||||
res.sendStatus(204);
|
||||
})
|
||||
.all(jwtdecode()) // preferred so it doesn't apply to nonexistent routes
|
||||
.all(jwtdecode())
|
||||
|
||||
/**
|
||||
* POST /api/users/123/login
|
||||
|
@ -172,6 +172,11 @@
|
||||
"pattern": "^(?:\\*\\.)?(?:[^.*]+\\.?)+[^.]$"
|
||||
}
|
||||
},
|
||||
"enabled": {
|
||||
"description": "Is Enabled",
|
||||
"example": true,
|
||||
"type": "boolean"
|
||||
},
|
||||
"ssl_enabled": {
|
||||
"description": "Is SSL Enabled",
|
||||
"example": true,
|
||||
@ -186,6 +191,11 @@
|
||||
"type": "string",
|
||||
"pattern": "^(letsencrypt|other)$"
|
||||
},
|
||||
"http2_support": {
|
||||
"description": "HTTP2 Protocol Support",
|
||||
"example": false,
|
||||
"type": "boolean"
|
||||
},
|
||||
"block_exploits": {
|
||||
"description": "Should we block common exploits",
|
||||
"example": true,
|
||||
|
@ -24,9 +24,15 @@
|
||||
"ssl_forced": {
|
||||
"$ref": "../definitions.json#/definitions/ssl_forced"
|
||||
},
|
||||
"http2_support": {
|
||||
"$ref": "../definitions.json#/definitions/http2_support"
|
||||
},
|
||||
"advanced_config": {
|
||||
"type": "string"
|
||||
},
|
||||
"enabled": {
|
||||
"$ref": "../definitions.json#/definitions/enabled"
|
||||
},
|
||||
"meta": {
|
||||
"type": "object"
|
||||
}
|
||||
@ -50,9 +56,15 @@
|
||||
"ssl_forced": {
|
||||
"$ref": "#/definitions/ssl_forced"
|
||||
},
|
||||
"http2_support": {
|
||||
"$ref": "#/definitions/http2_support"
|
||||
},
|
||||
"advanced_config": {
|
||||
"$ref": "#/definitions/advanced_config"
|
||||
},
|
||||
"enabled": {
|
||||
"$ref": "#/definitions/enabled"
|
||||
},
|
||||
"meta": {
|
||||
"$ref": "#/definitions/meta"
|
||||
}
|
||||
@ -101,6 +113,9 @@
|
||||
"ssl_forced": {
|
||||
"$ref": "#/definitions/ssl_forced"
|
||||
},
|
||||
"http2_support": {
|
||||
"$ref": "#/definitions/http2_support"
|
||||
},
|
||||
"advanced_config": {
|
||||
"$ref": "#/definitions/advanced_config"
|
||||
},
|
||||
@ -138,6 +153,9 @@
|
||||
"ssl_forced": {
|
||||
"$ref": "#/definitions/ssl_forced"
|
||||
},
|
||||
"http2_support": {
|
||||
"$ref": "#/definitions/http2_support"
|
||||
},
|
||||
"advanced_config": {
|
||||
"$ref": "#/definitions/advanced_config"
|
||||
},
|
||||
@ -165,6 +183,34 @@
|
||||
"targetSchema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"title": "Enable",
|
||||
"description": "Enables a existing 404 Host",
|
||||
"href": "/nginx/dead-hosts/{definitions.identity.example}/enable",
|
||||
"access": "private",
|
||||
"method": "POST",
|
||||
"rel": "update",
|
||||
"http_header": {
|
||||
"$ref": "../examples.json#/definitions/auth_header"
|
||||
},
|
||||
"targetSchema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"title": "Disable",
|
||||
"description": "Disables a existing 404 Host",
|
||||
"href": "/nginx/dead-hosts/{definitions.identity.example}/disable",
|
||||
"access": "private",
|
||||
"method": "POST",
|
||||
"rel": "update",
|
||||
"http_header": {
|
||||
"$ref": "../examples.json#/definitions/auth_header"
|
||||
},
|
||||
"targetSchema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -18,9 +18,14 @@
|
||||
"domain_names": {
|
||||
"$ref": "../definitions.json#/definitions/domain_names"
|
||||
},
|
||||
"forward_ip": {
|
||||
"forward_scheme": {
|
||||
"type": "string",
|
||||
"format": "ipv4"
|
||||
"enum": ["http", "https"]
|
||||
},
|
||||
"forward_host": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 50
|
||||
},
|
||||
"forward_port": {
|
||||
"type": "integer",
|
||||
@ -33,18 +38,29 @@
|
||||
"ssl_forced": {
|
||||
"$ref": "../definitions.json#/definitions/ssl_forced"
|
||||
},
|
||||
"http2_support": {
|
||||
"$ref": "../definitions.json#/definitions/http2_support"
|
||||
},
|
||||
"block_exploits": {
|
||||
"$ref": "../definitions.json#/definitions/block_exploits"
|
||||
},
|
||||
"caching_enabled": {
|
||||
"$ref": "../definitions.json#/definitions/caching_enabled"
|
||||
},
|
||||
"allow_websocket_upgrade": {
|
||||
"description": "Allow Websocket Upgrade for all paths",
|
||||
"example": true,
|
||||
"type": "boolean"
|
||||
},
|
||||
"access_list_id": {
|
||||
"$ref": "../definitions.json#/definitions/access_list_id"
|
||||
},
|
||||
"advanced_config": {
|
||||
"type": "string"
|
||||
},
|
||||
"enabled": {
|
||||
"$ref": "../definitions.json#/definitions/enabled"
|
||||
},
|
||||
"meta": {
|
||||
"type": "object"
|
||||
}
|
||||
@ -62,8 +78,11 @@
|
||||
"domain_names": {
|
||||
"$ref": "#/definitions/domain_names"
|
||||
},
|
||||
"forward_ip": {
|
||||
"$ref": "#/definitions/forward_ip"
|
||||
"forward_scheme": {
|
||||
"$ref": "#/definitions/forward_scheme"
|
||||
},
|
||||
"forward_host": {
|
||||
"$ref": "#/definitions/forward_host"
|
||||
},
|
||||
"forward_port": {
|
||||
"$ref": "#/definitions/forward_port"
|
||||
@ -74,18 +93,27 @@
|
||||
"ssl_forced": {
|
||||
"$ref": "#/definitions/ssl_forced"
|
||||
},
|
||||
"http2_support": {
|
||||
"$ref": "#/definitions/http2_support"
|
||||
},
|
||||
"block_exploits": {
|
||||
"$ref": "#/definitions/block_exploits"
|
||||
},
|
||||
"caching_enabled": {
|
||||
"$ref": "#/definitions/caching_enabled"
|
||||
},
|
||||
"allow_websocket_upgrade": {
|
||||
"$ref": "#/definitions/allow_websocket_upgrade"
|
||||
},
|
||||
"access_list_id": {
|
||||
"$ref": "#/definitions/access_list_id"
|
||||
},
|
||||
"advanced_config": {
|
||||
"$ref": "#/definitions/advanced_config"
|
||||
},
|
||||
"enabled": {
|
||||
"$ref": "#/definitions/enabled"
|
||||
},
|
||||
"meta": {
|
||||
"$ref": "#/definitions/meta"
|
||||
}
|
||||
@ -123,15 +151,19 @@
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"domain_names",
|
||||
"forward_ip",
|
||||
"forward_scheme",
|
||||
"forward_host",
|
||||
"forward_port"
|
||||
],
|
||||
"properties": {
|
||||
"domain_names": {
|
||||
"$ref": "#/definitions/domain_names"
|
||||
},
|
||||
"forward_ip": {
|
||||
"$ref": "#/definitions/forward_ip"
|
||||
"forward_scheme": {
|
||||
"$ref": "#/definitions/forward_scheme"
|
||||
},
|
||||
"forward_host": {
|
||||
"$ref": "#/definitions/forward_host"
|
||||
},
|
||||
"forward_port": {
|
||||
"$ref": "#/definitions/forward_port"
|
||||
@ -142,18 +174,27 @@
|
||||
"ssl_forced": {
|
||||
"$ref": "#/definitions/ssl_forced"
|
||||
},
|
||||
"http2_support": {
|
||||
"$ref": "#/definitions/http2_support"
|
||||
},
|
||||
"block_exploits": {
|
||||
"$ref": "#/definitions/block_exploits"
|
||||
},
|
||||
"caching_enabled": {
|
||||
"$ref": "#/definitions/caching_enabled"
|
||||
},
|
||||
"allow_websocket_upgrade": {
|
||||
"$ref": "#/definitions/allow_websocket_upgrade"
|
||||
},
|
||||
"access_list_id": {
|
||||
"$ref": "#/definitions/access_list_id"
|
||||
},
|
||||
"advanced_config": {
|
||||
"$ref": "#/definitions/advanced_config"
|
||||
},
|
||||
"enabled": {
|
||||
"$ref": "#/definitions/enabled"
|
||||
},
|
||||
"meta": {
|
||||
"$ref": "#/definitions/meta"
|
||||
}
|
||||
@ -182,8 +223,11 @@
|
||||
"domain_names": {
|
||||
"$ref": "#/definitions/domain_names"
|
||||
},
|
||||
"forward_ip": {
|
||||
"$ref": "#/definitions/forward_ip"
|
||||
"forward_scheme": {
|
||||
"$ref": "#/definitions/forward_scheme"
|
||||
},
|
||||
"forward_host": {
|
||||
"$ref": "#/definitions/forward_host"
|
||||
},
|
||||
"forward_port": {
|
||||
"$ref": "#/definitions/forward_port"
|
||||
@ -194,18 +238,27 @@
|
||||
"ssl_forced": {
|
||||
"$ref": "#/definitions/ssl_forced"
|
||||
},
|
||||
"http2_support": {
|
||||
"$ref": "#/definitions/http2_support"
|
||||
},
|
||||
"block_exploits": {
|
||||
"$ref": "#/definitions/block_exploits"
|
||||
},
|
||||
"caching_enabled": {
|
||||
"$ref": "#/definitions/caching_enabled"
|
||||
},
|
||||
"allow_websocket_upgrade": {
|
||||
"$ref": "#/definitions/allow_websocket_upgrade"
|
||||
},
|
||||
"access_list_id": {
|
||||
"$ref": "#/definitions/access_list_id"
|
||||
},
|
||||
"advanced_config": {
|
||||
"$ref": "#/definitions/advanced_config"
|
||||
},
|
||||
"enabled": {
|
||||
"$ref": "#/definitions/enabled"
|
||||
},
|
||||
"meta": {
|
||||
"$ref": "#/definitions/meta"
|
||||
}
|
||||
@ -230,6 +283,34 @@
|
||||
"targetSchema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"title": "Enable",
|
||||
"description": "Enables a existing Proxy Host",
|
||||
"href": "/nginx/proxy-hosts/{definitions.identity.example}/enable",
|
||||
"access": "private",
|
||||
"method": "POST",
|
||||
"rel": "update",
|
||||
"http_header": {
|
||||
"$ref": "../examples.json#/definitions/auth_header"
|
||||
},
|
||||
"targetSchema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"title": "Disable",
|
||||
"description": "Disables a existing Proxy Host",
|
||||
"href": "/nginx/proxy-hosts/{definitions.identity.example}/disable",
|
||||
"access": "private",
|
||||
"method": "POST",
|
||||
"rel": "update",
|
||||
"http_header": {
|
||||
"$ref": "../examples.json#/definitions/auth_header"
|
||||
},
|
||||
"targetSchema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -32,12 +32,18 @@
|
||||
"ssl_forced": {
|
||||
"$ref": "../definitions.json#/definitions/ssl_forced"
|
||||
},
|
||||
"http2_support": {
|
||||
"$ref": "../definitions.json#/definitions/http2_support"
|
||||
},
|
||||
"block_exploits": {
|
||||
"$ref": "../definitions.json#/definitions/block_exploits"
|
||||
},
|
||||
"advanced_config": {
|
||||
"type": "string"
|
||||
},
|
||||
"enabled": {
|
||||
"$ref": "../definitions.json#/definitions/enabled"
|
||||
},
|
||||
"meta": {
|
||||
"type": "object"
|
||||
}
|
||||
@ -67,12 +73,18 @@
|
||||
"ssl_forced": {
|
||||
"$ref": "#/definitions/ssl_forced"
|
||||
},
|
||||
"http2_support": {
|
||||
"$ref": "#/definitions/http2_support"
|
||||
},
|
||||
"block_exploits": {
|
||||
"$ref": "#/definitions/block_exploits"
|
||||
},
|
||||
"advanced_config": {
|
||||
"$ref": "#/definitions/advanced_config"
|
||||
},
|
||||
"enabled": {
|
||||
"$ref": "#/definitions/enabled"
|
||||
},
|
||||
"meta": {
|
||||
"$ref": "#/definitions/meta"
|
||||
}
|
||||
@ -128,6 +140,9 @@
|
||||
"ssl_forced": {
|
||||
"$ref": "#/definitions/ssl_forced"
|
||||
},
|
||||
"http2_support": {
|
||||
"$ref": "#/definitions/http2_support"
|
||||
},
|
||||
"block_exploits": {
|
||||
"$ref": "#/definitions/block_exploits"
|
||||
},
|
||||
@ -174,6 +189,9 @@
|
||||
"ssl_forced": {
|
||||
"$ref": "#/definitions/ssl_forced"
|
||||
},
|
||||
"http2_support": {
|
||||
"$ref": "#/definitions/http2_support"
|
||||
},
|
||||
"block_exploits": {
|
||||
"$ref": "#/definitions/block_exploits"
|
||||
},
|
||||
@ -204,6 +222,34 @@
|
||||
"targetSchema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"title": "Enable",
|
||||
"description": "Enables a existing Redirection Host",
|
||||
"href": "/nginx/redirection-hosts/{definitions.identity.example}/enable",
|
||||
"access": "private",
|
||||
"method": "POST",
|
||||
"rel": "update",
|
||||
"http_header": {
|
||||
"$ref": "../examples.json#/definitions/auth_header"
|
||||
},
|
||||
"targetSchema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"title": "Disable",
|
||||
"description": "Disables a existing Redirection Host",
|
||||
"href": "/nginx/redirection-hosts/{definitions.identity.example}/disable",
|
||||
"access": "private",
|
||||
"method": "POST",
|
||||
"rel": "update",
|
||||
"http_header": {
|
||||
"$ref": "../examples.json#/definitions/auth_header"
|
||||
},
|
||||
"targetSchema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -35,6 +35,9 @@
|
||||
"udp_forwarding": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"enabled": {
|
||||
"$ref": "../definitions.json#/definitions/enabled"
|
||||
},
|
||||
"meta": {
|
||||
"type": "object"
|
||||
}
|
||||
@ -64,6 +67,9 @@
|
||||
"udp_forwarding": {
|
||||
"$ref": "#/definitions/udp_forwarding"
|
||||
},
|
||||
"enabled": {
|
||||
"$ref": "#/definitions/enabled"
|
||||
},
|
||||
"meta": {
|
||||
"$ref": "#/definitions/meta"
|
||||
}
|
||||
@ -184,6 +190,34 @@
|
||||
"targetSchema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"title": "Enable",
|
||||
"description": "Enables a existing Stream",
|
||||
"href": "/nginx/streams/{definitions.identity.example}/enable",
|
||||
"access": "private",
|
||||
"method": "POST",
|
||||
"rel": "update",
|
||||
"http_header": {
|
||||
"$ref": "../examples.json#/definitions/auth_header"
|
||||
},
|
||||
"targetSchema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"title": "Disable",
|
||||
"description": "Disables a existing Stream",
|
||||
"href": "/nginx/streams/{definitions.identity.example}/disable",
|
||||
"access": "private",
|
||||
"method": "POST",
|
||||
"rel": "update",
|
||||
"http_header": {
|
||||
"$ref": "../examples.json#/definitions/auth_header"
|
||||
},
|
||||
"targetSchema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -3,10 +3,11 @@
|
||||
const fs = require('fs');
|
||||
const NodeRSA = require('node-rsa');
|
||||
const config = require('config');
|
||||
const logger = require('./logger').global;
|
||||
const logger = require('./logger').setup;
|
||||
const userModel = require('./models/user');
|
||||
const userPermissionModel = require('./models/user_permission');
|
||||
const authModel = require('./models/auth');
|
||||
const debug_mode = process.env.NODE_ENV !== 'production';
|
||||
|
||||
module.exports = function () {
|
||||
return new Promise((resolve, reject) => {
|
||||
@ -22,6 +23,9 @@ module.exports = function () {
|
||||
config_data = require(filename);
|
||||
} catch (err) {
|
||||
// do nothing
|
||||
if (debug_mode) {
|
||||
logger.debug(filename + ' config file could not be required');
|
||||
}
|
||||
}
|
||||
|
||||
// Now create the keys and save them in the config.
|
||||
@ -40,12 +44,18 @@ module.exports = function () {
|
||||
reject(err);
|
||||
} else {
|
||||
logger.info('Wrote JWT key pair to config file: ' + filename);
|
||||
config.util.loadFileConfigs();
|
||||
resolve();
|
||||
|
||||
logger.warn('Restarting interface to apply new configuration');
|
||||
process.exit(0);
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
// JWT key pair exists
|
||||
if (debug_mode) {
|
||||
logger.debug('JWT Keypair already exists');
|
||||
}
|
||||
|
||||
resolve();
|
||||
}
|
||||
})
|
||||
@ -54,49 +64,54 @@ module.exports = function () {
|
||||
.query()
|
||||
.select(userModel.raw('COUNT(`id`) as `count`'))
|
||||
.where('is_deleted', 0)
|
||||
.first('count')
|
||||
.then(row => {
|
||||
if (!row.count) {
|
||||
// Create a new user and set password
|
||||
logger.info('Creating a new user: admin@example.com with password: changeme');
|
||||
.first();
|
||||
})
|
||||
.then(row => {
|
||||
if (!row.count) {
|
||||
// Create a new user and set password
|
||||
logger.info('Creating a new user: admin@example.com with password: changeme');
|
||||
|
||||
let data = {
|
||||
is_deleted: 0,
|
||||
email: 'admin@example.com',
|
||||
name: 'Administrator',
|
||||
nickname: 'Admin',
|
||||
avatar: '',
|
||||
roles: ['admin']
|
||||
};
|
||||
let data = {
|
||||
is_deleted: 0,
|
||||
email: 'admin@example.com',
|
||||
name: 'Administrator',
|
||||
nickname: 'Admin',
|
||||
avatar: '',
|
||||
roles: ['admin']
|
||||
};
|
||||
|
||||
return userModel
|
||||
return userModel
|
||||
.query()
|
||||
.insertAndFetch(data)
|
||||
.then(user => {
|
||||
return authModel
|
||||
.query()
|
||||
.insertAndFetch(data)
|
||||
.then(user => {
|
||||
return authModel
|
||||
.insert({
|
||||
user_id: user.id,
|
||||
type: 'password',
|
||||
secret: 'changeme',
|
||||
meta: {}
|
||||
})
|
||||
.then(() => {
|
||||
return userPermissionModel
|
||||
.query()
|
||||
.insert({
|
||||
user_id: user.id,
|
||||
type: 'password',
|
||||
secret: 'changeme',
|
||||
meta: {}
|
||||
})
|
||||
.then(() => {
|
||||
return userPermissionModel
|
||||
.query()
|
||||
.insert({
|
||||
user_id: user.id,
|
||||
visibility: 'all',
|
||||
proxy_hosts: 'manage',
|
||||
redirection_hosts: 'manage',
|
||||
dead_hosts: 'manage',
|
||||
streams: 'manage',
|
||||
access_lists: 'manage',
|
||||
certificates: 'manage'
|
||||
});
|
||||
user_id: user.id,
|
||||
visibility: 'all',
|
||||
proxy_hosts: 'manage',
|
||||
redirection_hosts: 'manage',
|
||||
dead_hosts: 'manage',
|
||||
streams: 'manage',
|
||||
access_lists: 'manage',
|
||||
certificates: 'manage'
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
logger.info('Initial setup completed');
|
||||
});
|
||||
} else if (debug_mode) {
|
||||
logger.debug('Admin user setup not required');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -1,5 +1,5 @@
|
||||
listen 80;
|
||||
{% if certificate -%}
|
||||
listen 443 ssl;
|
||||
listen 443 ssl{% if http2_support %} http2{% endif %};
|
||||
{% endif %}
|
||||
server_name {{ domain_names | join: " " }};
|
||||
server_name {{ domain_names | join: " " }};
|
||||
|
@ -1,5 +1,6 @@
|
||||
{% include "_header_comment.conf" %}
|
||||
|
||||
{% if enabled %}
|
||||
server {
|
||||
{% include "_listen.conf" %}
|
||||
{% include "_certificates.conf" %}
|
||||
@ -10,3 +11,4 @@ server {
|
||||
|
||||
return 404;
|
||||
}
|
||||
{% endif %}
|
||||
|
3
src/backend/templates/ip_ranges.conf
Normal file
3
src/backend/templates/ip_ranges.conf
Normal file
@ -0,0 +1,3 @@
|
||||
{% for range in ip_ranges %}
|
||||
set_real_ip_from {{ range }};
|
||||
{% endfor %}
|
@ -1,8 +1,10 @@
|
||||
{% include "_header_comment.conf" %}
|
||||
|
||||
{% if enabled %}
|
||||
server {
|
||||
set $server {{ forward_ip }};
|
||||
set $port {{ forward_port }};
|
||||
set $forward_scheme {{ forward_scheme }};
|
||||
set $server "{{ forward_host }}";
|
||||
set $port {{ forward_port }};
|
||||
|
||||
{% include "_listen.conf" %}
|
||||
{% include "_certificates.conf" %}
|
||||
@ -22,7 +24,14 @@ server {
|
||||
|
||||
{% include "_forced_ssl.conf" %}
|
||||
|
||||
{% if allow_websocket_upgrade == 1 or allow_websocket_upgrade == true %}
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_http_version 1.1;
|
||||
{% endif %}
|
||||
|
||||
# Proxy!
|
||||
include conf.d/include/proxy.conf;
|
||||
}
|
||||
}
|
||||
{% endif %}
|
||||
|
@ -1,5 +1,6 @@
|
||||
{% include "_header_comment.conf" %}
|
||||
|
||||
{% if enabled %}
|
||||
server {
|
||||
{% include "_listen.conf" %}
|
||||
{% include "_certificates.conf" %}
|
||||
@ -22,3 +23,4 @@ server {
|
||||
{% endif %}
|
||||
}
|
||||
}
|
||||
{% endif %}
|
||||
|
@ -2,6 +2,7 @@
|
||||
# {{ incoming_port }} TCP: {{ tcp_forwarding }} UDP: {{ udp_forwarding }}
|
||||
# ------------------------------------------------------------
|
||||
|
||||
{% if enabled %}
|
||||
{% if tcp_forwarding == 1 or tcp_forwarding == true -%}
|
||||
server {
|
||||
listen {{ incoming_port }};
|
||||
@ -14,3 +15,4 @@ server {
|
||||
proxy_pass {{ forward_ip }}:{{ forwarding_port }};
|
||||
}
|
||||
{% endif %}
|
||||
{% endif %}
|
@ -5,5 +5,5 @@
|
||||
<span class="loader"></span>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="/js/main.js?v=<%= version %>"></script>
|
||||
<script type="text/javascript" src="/js/main.bundle.js?v=<%= version %>"></script>
|
||||
<%- include partials/footer.ejs %>
|
||||
|
@ -5,5 +5,5 @@
|
||||
<span class="loader"></span>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="/js/login.js?v=<%= version %>"></script>
|
||||
<script type="text/javascript" src="/js/login.bundle.js?v=<%= version %>"></script>
|
||||
<%- include partials/footer.ejs %>
|
||||
|
@ -7,7 +7,7 @@ const Tokens = require('./tokens');
|
||||
/**
|
||||
* @param {String} message
|
||||
* @param {*} debug
|
||||
* @param {Integer} code
|
||||
* @param {Number} code
|
||||
* @constructor
|
||||
*/
|
||||
const ApiError = function (message, debug, code) {
|
||||
@ -129,7 +129,7 @@ function getAllObjects (path, expand, query) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {String} path
|
||||
* @param {String} path
|
||||
* @param {FormData} form_data
|
||||
* @returns {Promise}
|
||||
*/
|
||||
@ -241,7 +241,7 @@ module.exports = {
|
||||
|
||||
/**
|
||||
* @param {Object} data
|
||||
* @param {Integer} data.id
|
||||
* @param {Number} data.id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
update: function (data) {
|
||||
@ -251,7 +251,7 @@ module.exports = {
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Integer} id
|
||||
* @param {Number} id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
delete: function (id) {
|
||||
@ -260,7 +260,7 @@ module.exports = {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Integer} id
|
||||
* @param {Number} id
|
||||
* @param {Object} auth
|
||||
* @returns {Promise}
|
||||
*/
|
||||
@ -269,7 +269,7 @@ module.exports = {
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Integer} id
|
||||
* @param {Number} id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
loginAs: function (id) {
|
||||
@ -278,7 +278,7 @@ module.exports = {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Integer} id
|
||||
* @param {Number} id
|
||||
* @param {Object} perms
|
||||
* @returns {Promise}
|
||||
*/
|
||||
@ -308,7 +308,7 @@ module.exports = {
|
||||
|
||||
/**
|
||||
* @param {Object} data
|
||||
* @param {Integer} data.id
|
||||
* @param {Number} data.id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
update: function (data) {
|
||||
@ -318,11 +318,35 @@ module.exports = {
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Integer} id
|
||||
* @param {Number} id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
delete: function (id) {
|
||||
return fetch('delete', 'nginx/proxy-hosts/' + id);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
get: function (id) {
|
||||
return fetch('get', 'nginx/proxy-hosts/' + id);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
enable: function (id) {
|
||||
return fetch('post', 'nginx/proxy-hosts/' + id + '/enable');
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
disable: function (id) {
|
||||
return fetch('post', 'nginx/proxy-hosts/' + id + '/disable');
|
||||
}
|
||||
},
|
||||
|
||||
@ -345,7 +369,7 @@ module.exports = {
|
||||
|
||||
/**
|
||||
* @param {Object} data
|
||||
* @param {Integer} data.id
|
||||
* @param {Number} data.id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
update: function (data) {
|
||||
@ -355,7 +379,7 @@ module.exports = {
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Integer} id
|
||||
* @param {Number} id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
delete: function (id) {
|
||||
@ -363,12 +387,36 @@ module.exports = {
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Integer} id
|
||||
* @param {Number} id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
get: function (id) {
|
||||
return fetch('get', 'nginx/redirection-hosts/' + id);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} id
|
||||
* @param {FormData} form_data
|
||||
* @params {Promise}
|
||||
*/
|
||||
setCerts: function (id, form_data) {
|
||||
return FileUpload('nginx/redirection-hosts/' + id + '/certificates', form_data);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
enable: function (id) {
|
||||
return fetch('post', 'nginx/redirection-hosts/' + id + '/enable');
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
disable: function (id) {
|
||||
return fetch('post', 'nginx/redirection-hosts/' + id + '/disable');
|
||||
}
|
||||
},
|
||||
|
||||
@ -391,7 +439,7 @@ module.exports = {
|
||||
|
||||
/**
|
||||
* @param {Object} data
|
||||
* @param {Integer} data.id
|
||||
* @param {Number} data.id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
update: function (data) {
|
||||
@ -401,11 +449,35 @@ module.exports = {
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Integer} id
|
||||
* @param {Number} id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
delete: function (id) {
|
||||
return fetch('delete', 'nginx/streams/' + id);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
get: function (id) {
|
||||
return fetch('get', 'nginx/streams/' + id);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
enable: function (id) {
|
||||
return fetch('post', 'nginx/streams/' + id + '/enable');
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
disable: function (id) {
|
||||
return fetch('post', 'nginx/streams/' + id + '/disable');
|
||||
}
|
||||
},
|
||||
|
||||
@ -428,7 +500,7 @@ module.exports = {
|
||||
|
||||
/**
|
||||
* @param {Object} data
|
||||
* @param {Integer} data.id
|
||||
* @param {Number} data.id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
update: function (data) {
|
||||
@ -438,7 +510,7 @@ module.exports = {
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Integer} id
|
||||
* @param {Number} id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
delete: function (id) {
|
||||
@ -446,12 +518,36 @@ module.exports = {
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Integer} id
|
||||
* @param {Number} id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
get: function (id) {
|
||||
return fetch('get', 'nginx/dead-hosts/' + id);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} id
|
||||
* @param {FormData} form_data
|
||||
* @params {Promise}
|
||||
*/
|
||||
setCerts: function (id, form_data) {
|
||||
return FileUpload('nginx/dead-hosts/' + id + '/certificates', form_data);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
enable: function (id) {
|
||||
return fetch('post', 'nginx/dead-hosts/' + id + '/enable');
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
disable: function (id) {
|
||||
return fetch('post', 'nginx/dead-hosts/' + id + '/disable');
|
||||
}
|
||||
},
|
||||
|
||||
@ -474,7 +570,7 @@ module.exports = {
|
||||
|
||||
/**
|
||||
* @param {Object} data
|
||||
* @param {Integer} data.id
|
||||
* @param {Number} data.id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
update: function (data) {
|
||||
@ -484,7 +580,7 @@ module.exports = {
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Integer} id
|
||||
* @param {Number} id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
delete: function (id) {
|
||||
@ -511,7 +607,7 @@ module.exports = {
|
||||
|
||||
/**
|
||||
* @param {Object} data
|
||||
* @param {Integer} data.id
|
||||
* @param {Number} data.id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
update: function (data) {
|
||||
@ -521,7 +617,7 @@ module.exports = {
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Integer} id
|
||||
* @param {Number} id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
delete: function (id) {
|
||||
@ -529,7 +625,7 @@ module.exports = {
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Integer} id
|
||||
* @param {Number} id
|
||||
* @param {FormData} form_data
|
||||
* @params {Promise}
|
||||
*/
|
||||
|
@ -36,7 +36,7 @@
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="col-sm-6 col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="custom-switch">
|
||||
<input type="checkbox" class="custom-switch-input" name="ssl_forced" value="1"<%- ssl_forced ? ' checked' : '' %><%- certificate_id ? '' : ' disabled' %>>
|
||||
@ -45,6 +45,15 @@
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="custom-switch">
|
||||
<input type="checkbox" class="custom-switch-input" name="http2_support" value="1"<%- http2_support ? ' checked' : '' %><%- certificate_id ? '' : ' disabled' %>>
|
||||
<span class="custom-switch-indicator"></span>
|
||||
<span class="custom-switch-description"><%- i18n('all-hosts', 'http2-support') %></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Lets encrypt -->
|
||||
<div class="col-sm-12 col-md-12 letsencrypt">
|
||||
|
@ -22,6 +22,7 @@ module.exports = Mn.View.extend({
|
||||
save: 'button.save',
|
||||
certificate_select: 'select[name="certificate_id"]',
|
||||
ssl_forced: 'input[name="ssl_forced"]',
|
||||
http2_support: 'input[name="http2_support"]',
|
||||
letsencrypt: '.letsencrypt'
|
||||
},
|
||||
|
||||
@ -35,7 +36,11 @@ module.exports = Mn.View.extend({
|
||||
}
|
||||
|
||||
let enabled = id === 'new' || parseInt(id, 10) > 0;
|
||||
this.ui.ssl_forced.prop('disabled', !enabled).parents('.form-group').css('opacity', enabled ? 1 : 0.5);
|
||||
this.ui.ssl_forced.add(this.ui.http2_support)
|
||||
.prop('disabled', !enabled)
|
||||
.parents('.form-group')
|
||||
.css('opacity', enabled ? 1 : 0.5);
|
||||
this.ui.http2_support.prop('disabled', !enabled);
|
||||
},
|
||||
|
||||
'click @ui.save': function (e) {
|
||||
@ -54,6 +59,10 @@ module.exports = Mn.View.extend({
|
||||
data.ssl_forced = true;
|
||||
}
|
||||
|
||||
if (typeof data.http2_support !== 'undefined') {
|
||||
data.http2_support = !!data.http2_support;
|
||||
}
|
||||
|
||||
if (typeof data.domain_names === 'string' && data.domain_names) {
|
||||
data.domain_names = data.domain_names.split(',');
|
||||
}
|
||||
@ -74,7 +83,7 @@ module.exports = Mn.View.extend({
|
||||
|
||||
data.meta.letsencrypt_agree = data.meta.letsencrypt_agree === '1';
|
||||
} else {
|
||||
data.certificate_id = parseInt(data.certificate_id, 0);
|
||||
data.certificate_id = parseInt(data.certificate_id, 10);
|
||||
}
|
||||
|
||||
let method = App.Api.Nginx.DeadHosts.create;
|
||||
|
@ -6,9 +6,15 @@
|
||||
<td>
|
||||
<div>
|
||||
<% domain_names.map(function(host) {
|
||||
%>
|
||||
<span class="tag"><%- host %></span>
|
||||
<%
|
||||
if (host.indexOf('*') === -1) {
|
||||
%>
|
||||
<span class="tag host-link hover-red" rel="http<%- certificate_id ? 's' : '' %>://<%- host %>"><%- host %></span>
|
||||
<%
|
||||
} else {
|
||||
%>
|
||||
<span class="tag"><%- host %></span>
|
||||
<%
|
||||
}
|
||||
});
|
||||
%>
|
||||
</div>
|
||||
@ -22,7 +28,9 @@
|
||||
<td>
|
||||
<%
|
||||
var o = isOnline();
|
||||
if (o === true) { %>
|
||||
if (!enabled) { %>
|
||||
<span class="status-icon bg-warning"></span> <%- i18n('str', 'disabled') %>
|
||||
<% } else if (o === true) { %>
|
||||
<span class="status-icon bg-success"></span> <%- i18n('str', 'online') %>
|
||||
<% } else if (o === false) { %>
|
||||
<span title="<%- getOfflineError() %>"><span class="status-icon bg-danger"></span> <%- i18n('str', 'offline') %></span>
|
||||
@ -36,7 +44,7 @@
|
||||
<a href="#" data-toggle="dropdown" class="icon"><i class="fe fe-more-vertical"></i></a>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<a href="#" class="edit dropdown-item"><i class="dropdown-icon fe fe-edit"></i> <%- i18n('str', 'edit') %></a>
|
||||
<!--<a href="#" class="logs dropdown-item"><i class="dropdown-icon fe fe-book"></i> <%- i18n('str', 'logs') %></a>-->
|
||||
<a href="#" class="able dropdown-item"><i class="dropdown-icon fe fe-power"></i> <%- i18n('str', enabled ? 'disable' : 'enable') %></a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a href="#" class="delete dropdown-item"><i class="dropdown-icon fe fe-trash-2"></i> <%- i18n('str', 'delete') %></a>
|
||||
</div>
|
||||
|
@ -9,11 +9,25 @@ module.exports = Mn.View.extend({
|
||||
tagName: 'tr',
|
||||
|
||||
ui: {
|
||||
edit: 'a.edit',
|
||||
delete: 'a.delete'
|
||||
able: 'a.able',
|
||||
edit: 'a.edit',
|
||||
delete: 'a.delete',
|
||||
host_link: '.host-link'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click @ui.able': function (e) {
|
||||
e.preventDefault();
|
||||
let id = this.model.get('id');
|
||||
App.Api.Nginx.DeadHosts[this.model.get('enabled') ? 'disable' : 'enable'](id)
|
||||
.then(() => {
|
||||
return App.Api.Nginx.DeadHosts.get(id)
|
||||
.then(row => {
|
||||
this.model.set(row);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
'click @ui.edit': function (e) {
|
||||
e.preventDefault();
|
||||
App.Controller.showNginxDeadForm(this.model);
|
||||
@ -22,6 +36,12 @@ module.exports = Mn.View.extend({
|
||||
'click @ui.delete': function (e) {
|
||||
e.preventDefault();
|
||||
App.Controller.showNginxDeadDeleteConfirm(this.model);
|
||||
},
|
||||
|
||||
'click @ui.host_link': function (e) {
|
||||
e.preventDefault();
|
||||
let win = window.open($(e.currentTarget).attr('rel'), '_blank');
|
||||
win.focus();
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -20,10 +20,19 @@
|
||||
<input type="text" name="domain_names" class="form-control" id="input-domains" value="<%- domain_names.join(',') %>" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-8 col-md-8">
|
||||
<div class="col-sm-3 col-md-3">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('proxy-hosts', 'forward-ip') %><span class="form-required">*</span></label>
|
||||
<input type="text" name="forward_ip" class="form-control text-monospace" placeholder="000.000.000.000" value="<%- forward_ip %>" autocomplete="off" maxlength="15" required>
|
||||
<label class="form-label"><%- i18n('proxy-hosts', 'forward-scheme') %><span class="form-required">*</span></label>
|
||||
<select name="forward_scheme" class="form-control custom-select" placeholder="http">
|
||||
<option value="http" <%- forward_scheme === 'http' ? 'selected' : '' %>>http</option>
|
||||
<option value="https" <%- forward_scheme === 'https' ? 'selected' : '' %>>https</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-5 col-md-5">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('proxy-hosts', 'forward-host') %><span class="form-required">*</span></label>
|
||||
<input type="text" name="forward_host" class="form-control text-monospace" placeholder="" value="<%- forward_host %>" autocomplete="off" maxlength="50" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-4 col-md-4">
|
||||
@ -50,6 +59,16 @@
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="form-group">
|
||||
<label class="custom-switch">
|
||||
<input type="checkbox" class="custom-switch-input" name="allow_websocket_upgrade" value="1"<%- allow_websocket_upgrade ? ' checked' : '' %>>
|
||||
<span class="custom-switch-indicator"></span>
|
||||
<span class="custom-switch-description"><%- i18n('proxy-hosts', 'allow-websocket-upgrade') %></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('proxy-hosts', 'access-list') %></label>
|
||||
@ -73,7 +92,7 @@
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="col-sm-6 col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="custom-switch">
|
||||
<input type="checkbox" class="custom-switch-input" name="ssl_forced" value="1"<%- ssl_forced ? ' checked' : '' %><%- certificate_id ? '' : ' disabled' %>>
|
||||
@ -82,6 +101,15 @@
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="custom-switch">
|
||||
<input type="checkbox" class="custom-switch-input" name="http2_support" value="1"<%- http2_support ? ' checked' : '' %><%- certificate_id ? '' : ' disabled' %>>
|
||||
<span class="custom-switch-indicator"></span>
|
||||
<span class="custom-switch-description"><%- i18n('all-hosts', 'http2-support') %></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Lets encrypt -->
|
||||
<div class="col-sm-12 col-md-12 letsencrypt">
|
||||
|
@ -9,7 +9,6 @@ const accessListItemTemplate = require('./access-list-item.ejs');
|
||||
const Helpers = require('../../../lib/helpers');
|
||||
|
||||
require('jquery-serializejson');
|
||||
require('jquery-mask-plugin');
|
||||
require('selectize');
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
@ -19,13 +18,15 @@ module.exports = Mn.View.extend({
|
||||
ui: {
|
||||
form: 'form',
|
||||
domain_names: 'input[name="domain_names"]',
|
||||
forward_ip: 'input[name="forward_ip"]',
|
||||
forward_host: 'input[name="forward_host"]',
|
||||
buttons: '.modal-footer button',
|
||||
cancel: 'button.cancel',
|
||||
save: 'button.save',
|
||||
certificate_select: 'select[name="certificate_id"]',
|
||||
access_list_select: 'select[name="access_list_id"]',
|
||||
ssl_forced: 'input[name="ssl_forced"]',
|
||||
http2_support: 'input[name="http2_support"]',
|
||||
forward_scheme: 'select[name="forward_scheme"]',
|
||||
letsencrypt: '.letsencrypt'
|
||||
},
|
||||
|
||||
@ -39,7 +40,10 @@ module.exports = Mn.View.extend({
|
||||
}
|
||||
|
||||
let enabled = id === 'new' || parseInt(id, 10) > 0;
|
||||
this.ui.ssl_forced.prop('disabled', !enabled).parents('.form-group').css('opacity', enabled ? 1 : 0.5);
|
||||
this.ui.ssl_forced.add(this.ui.http2_support)
|
||||
.prop('disabled', !enabled)
|
||||
.parents('.form-group')
|
||||
.css('opacity', enabled ? 1 : 0.5);
|
||||
},
|
||||
|
||||
'click @ui.save': function (e) {
|
||||
@ -54,14 +58,19 @@ module.exports = Mn.View.extend({
|
||||
let data = this.ui.form.serializeJSON();
|
||||
|
||||
// Manipulate
|
||||
data.forward_port = parseInt(data.forward_port, 10);
|
||||
data.block_exploits = !!data.block_exploits;
|
||||
data.caching_enabled = !!data.caching_enabled;
|
||||
data.forward_port = parseInt(data.forward_port, 10);
|
||||
data.block_exploits = !!data.block_exploits;
|
||||
data.caching_enabled = !!data.caching_enabled;
|
||||
data.allow_websocket_upgrade = !!data.allow_websocket_upgrade;
|
||||
|
||||
if (typeof data.ssl_forced !== 'undefined' && data.ssl_forced === '1') {
|
||||
data.ssl_forced = true;
|
||||
}
|
||||
|
||||
if (typeof data.http2_support !== 'undefined') {
|
||||
data.http2_support = !!data.http2_support;
|
||||
}
|
||||
|
||||
if (typeof data.domain_names === 'string' && data.domain_names) {
|
||||
data.domain_names = data.domain_names.split(',');
|
||||
}
|
||||
@ -82,7 +91,7 @@ module.exports = Mn.View.extend({
|
||||
|
||||
data.meta.letsencrypt_agree = data.meta.letsencrypt_agree === '1';
|
||||
} else {
|
||||
data.certificate_id = parseInt(data.certificate_id, 0);
|
||||
data.certificate_id = parseInt(data.certificate_id, 10);
|
||||
}
|
||||
|
||||
let method = App.Api.Nginx.ProxyHosts.create;
|
||||
@ -122,12 +131,6 @@ module.exports = Mn.View.extend({
|
||||
onRender: function () {
|
||||
let view = this;
|
||||
|
||||
// IP Address
|
||||
this.ui.forward_ip.mask('099.099.099.099', {
|
||||
clearIfNotMatch: true,
|
||||
placeholder: '000.000.000.000'
|
||||
});
|
||||
|
||||
// Domain names
|
||||
this.ui.domain_names.selectize({
|
||||
delimiter: ',',
|
||||
@ -143,7 +146,6 @@ module.exports = Mn.View.extend({
|
||||
});
|
||||
|
||||
// Access Lists
|
||||
this.ui.letsencrypt.hide();
|
||||
this.ui.access_list_select.selectize({
|
||||
valueField: 'id',
|
||||
labelField: 'name',
|
||||
|
@ -6,9 +6,15 @@
|
||||
<td>
|
||||
<div>
|
||||
<% domain_names.map(function(host) {
|
||||
%>
|
||||
<span class="tag"><%- host %></span>
|
||||
<%
|
||||
if (host.indexOf('*') === -1) {
|
||||
%>
|
||||
<span class="tag host-link hover-green" rel="http<%- certificate_id ? 's' : '' %>://<%- host %>"><%- host %></span>
|
||||
<%
|
||||
} else {
|
||||
%>
|
||||
<span class="tag"><%- host %></span>
|
||||
<%
|
||||
}
|
||||
});
|
||||
%>
|
||||
</div>
|
||||
@ -17,7 +23,7 @@
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="text-monospace"><%- forward_ip %>:<%- forward_port %></div>
|
||||
<div class="text-monospace"><%- forward_scheme %>://<%- forward_host %>:<%- forward_port %></div>
|
||||
</td>
|
||||
<td>
|
||||
<div><%- certificate && certificate_id ? i18n('ssl', certificate.provider) : i18n('ssl', 'none') %></div>
|
||||
@ -28,7 +34,9 @@
|
||||
<td>
|
||||
<%
|
||||
var o = isOnline();
|
||||
if (o === true) { %>
|
||||
if (!enabled) { %>
|
||||
<span class="status-icon bg-warning"></span> <%- i18n('str', 'disabled') %>
|
||||
<% } else if (o === true) { %>
|
||||
<span class="status-icon bg-success"></span> <%- i18n('str', 'online') %>
|
||||
<% } else if (o === false) { %>
|
||||
<span title="<%- getOfflineError() %>"><span class="status-icon bg-danger"></span> <%- i18n('str', 'offline') %></span>
|
||||
@ -42,7 +50,7 @@
|
||||
<a href="#" data-toggle="dropdown" class="icon"><i class="fe fe-more-vertical"></i></a>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<a href="#" class="edit dropdown-item"><i class="dropdown-icon fe fe-edit"></i> <%- i18n('str', 'edit') %></a>
|
||||
<!--<a href="#" class="logs dropdown-item"><i class="dropdown-icon fe fe-book"></i> <%- i18n('str', 'logs') %></a>-->
|
||||
<a href="#" class="able dropdown-item"><i class="dropdown-icon fe fe-power"></i> <%- i18n('str', enabled ? 'disable' : 'enable') %></a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a href="#" class="delete dropdown-item"><i class="dropdown-icon fe fe-trash-2"></i> <%- i18n('str', 'delete') %></a>
|
||||
</div>
|
||||
|
@ -9,11 +9,25 @@ module.exports = Mn.View.extend({
|
||||
tagName: 'tr',
|
||||
|
||||
ui: {
|
||||
edit: 'a.edit',
|
||||
delete: 'a.delete'
|
||||
able: 'a.able',
|
||||
edit: 'a.edit',
|
||||
delete: 'a.delete',
|
||||
host_link: '.host-link'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click @ui.able': function (e) {
|
||||
e.preventDefault();
|
||||
let id = this.model.get('id');
|
||||
App.Api.Nginx.ProxyHosts[this.model.get('enabled') ? 'disable' : 'enable'](id)
|
||||
.then(() => {
|
||||
return App.Api.Nginx.ProxyHosts.get(id)
|
||||
.then(row => {
|
||||
this.model.set(row);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
'click @ui.edit': function (e) {
|
||||
e.preventDefault();
|
||||
App.Controller.showNginxProxyForm(this.model);
|
||||
@ -22,6 +36,12 @@ module.exports = Mn.View.extend({
|
||||
'click @ui.delete': function (e) {
|
||||
e.preventDefault();
|
||||
App.Controller.showNginxProxyDeleteConfirm(this.model);
|
||||
},
|
||||
|
||||
'click @ui.host_link': function (e) {
|
||||
e.preventDefault();
|
||||
let win = window.open($(e.currentTarget).attr('rel'), '_blank');
|
||||
win.focus();
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -60,7 +60,7 @@
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="col-sm-6 col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="custom-switch">
|
||||
<input type="checkbox" class="custom-switch-input" name="ssl_forced" value="1"<%- ssl_forced ? ' checked' : '' %><%- certificate_id ? '' : ' disabled' %>>
|
||||
@ -69,6 +69,15 @@
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="custom-switch">
|
||||
<input type="checkbox" class="custom-switch-input" name="http2_support" value="1"<%- http2_support ? ' checked' : '' %><%- certificate_id ? '' : ' disabled' %>>
|
||||
<span class="custom-switch-indicator"></span>
|
||||
<span class="custom-switch-description"><%- i18n('all-hosts', 'http2-support') %></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Lets encrypt -->
|
||||
<div class="col-sm-12 col-md-12 letsencrypt">
|
||||
|
@ -22,6 +22,7 @@ module.exports = Mn.View.extend({
|
||||
save: 'button.save',
|
||||
certificate_select: 'select[name="certificate_id"]',
|
||||
ssl_forced: 'input[name="ssl_forced"]',
|
||||
http2_support: 'input[name="http2_support"]',
|
||||
letsencrypt: '.letsencrypt'
|
||||
},
|
||||
|
||||
@ -35,7 +36,11 @@ module.exports = Mn.View.extend({
|
||||
}
|
||||
|
||||
let enabled = id === 'new' || parseInt(id, 10) > 0;
|
||||
this.ui.ssl_forced.prop('disabled', !enabled).parents('.form-group').css('opacity', enabled ? 1 : 0.5);
|
||||
this.ui.ssl_forced.add(this.ui.http2_support)
|
||||
.prop('disabled', !enabled)
|
||||
.parents('.form-group')
|
||||
.css('opacity', enabled ? 1 : 0.5);
|
||||
this.ui.http2_support.prop('disabled', !enabled);
|
||||
},
|
||||
|
||||
'click @ui.save': function (e) {
|
||||
@ -57,6 +62,10 @@ module.exports = Mn.View.extend({
|
||||
data.ssl_forced = true;
|
||||
}
|
||||
|
||||
if (typeof data.http2_support !== 'undefined') {
|
||||
data.http2_support = !!data.http2_support;
|
||||
}
|
||||
|
||||
if (typeof data.domain_names === 'string' && data.domain_names) {
|
||||
data.domain_names = data.domain_names.split(',');
|
||||
}
|
||||
@ -77,7 +86,7 @@ module.exports = Mn.View.extend({
|
||||
|
||||
data.meta.letsencrypt_agree = data.meta.letsencrypt_agree === '1';
|
||||
} else {
|
||||
data.certificate_id = parseInt(data.certificate_id, 0);
|
||||
data.certificate_id = parseInt(data.certificate_id, 10);
|
||||
}
|
||||
|
||||
let method = App.Api.Nginx.RedirectionHosts.create;
|
||||
|
@ -6,9 +6,15 @@
|
||||
<td>
|
||||
<div>
|
||||
<% domain_names.map(function(host) {
|
||||
%>
|
||||
<span class="tag"><%- host %></span>
|
||||
<%
|
||||
if (host.indexOf('*') === -1) {
|
||||
%>
|
||||
<span class="tag host-link hover-yellow" rel="http<%- certificate_id ? 's' : '' %>://<%- host %>"><%- host %></span>
|
||||
<%
|
||||
} else {
|
||||
%>
|
||||
<span class="tag"><%- host %></span>
|
||||
<%
|
||||
}
|
||||
});
|
||||
%>
|
||||
</div>
|
||||
@ -25,7 +31,9 @@
|
||||
<td>
|
||||
<%
|
||||
var o = isOnline();
|
||||
if (o === true) { %>
|
||||
if (!enabled) { %>
|
||||
<span class="status-icon bg-warning"></span> <%- i18n('str', 'disabled') %>
|
||||
<% } else if (o === true) { %>
|
||||
<span class="status-icon bg-success"></span> <%- i18n('str', 'online') %>
|
||||
<% } else if (o === false) { %>
|
||||
<span title="<%- getOfflineError() %>"><span class="status-icon bg-danger"></span> <%- i18n('str', 'offline') %></span>
|
||||
@ -39,7 +47,7 @@
|
||||
<a href="#" data-toggle="dropdown" class="icon"><i class="fe fe-more-vertical"></i></a>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<a href="#" class="edit dropdown-item"><i class="dropdown-icon fe fe-edit"></i> <%- i18n('str', 'edit') %></a>
|
||||
<!--<a href="#" class="logs dropdown-item"><i class="dropdown-icon fe fe-book"></i> <%- i18n('str', 'logs') %></a>-->
|
||||
<a href="#" class="able dropdown-item"><i class="dropdown-icon fe fe-power"></i> <%- i18n('str', enabled ? 'disable' : 'enable') %></a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a href="#" class="delete dropdown-item"><i class="dropdown-icon fe fe-trash-2"></i> <%- i18n('str', 'delete') %></a>
|
||||
</div>
|
||||
|
@ -9,11 +9,25 @@ module.exports = Mn.View.extend({
|
||||
tagName: 'tr',
|
||||
|
||||
ui: {
|
||||
edit: 'a.edit',
|
||||
delete: 'a.delete'
|
||||
able: 'a.able',
|
||||
edit: 'a.edit',
|
||||
delete: 'a.delete',
|
||||
host_link: '.host-link'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click @ui.able': function (e) {
|
||||
e.preventDefault();
|
||||
let id = this.model.get('id');
|
||||
App.Api.Nginx.RedirectionHosts[this.model.get('enabled') ? 'disable' : 'enable'](id)
|
||||
.then(() => {
|
||||
return App.Api.Nginx.RedirectionHosts.get(id)
|
||||
.then(row => {
|
||||
this.model.set(row);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
'click @ui.edit': function (e) {
|
||||
e.preventDefault();
|
||||
App.Controller.showNginxRedirectionForm(this.model);
|
||||
@ -22,6 +36,12 @@ module.exports = Mn.View.extend({
|
||||
'click @ui.delete': function (e) {
|
||||
e.preventDefault();
|
||||
App.Controller.showNginxRedirectionDeleteConfirm(this.model);
|
||||
},
|
||||
|
||||
'click @ui.host_link': function (e) {
|
||||
e.preventDefault();
|
||||
let win = window.open($(e.currentTarget).attr('rel'), '_blank');
|
||||
win.focus();
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -27,7 +27,9 @@
|
||||
<td>
|
||||
<%
|
||||
var o = isOnline();
|
||||
if (o === true) { %>
|
||||
if (!enabled) { %>
|
||||
<span class="status-icon bg-warning"></span> <%- i18n('str', 'disabled') %>
|
||||
<% } else if (o === true) { %>
|
||||
<span class="status-icon bg-success"></span> <%- i18n('str', 'online') %>
|
||||
<% } else if (o === false) { %>
|
||||
<span title="<%- getOfflineError() %>"><span class="status-icon bg-danger"></span> <%- i18n('str', 'offline') %></span>
|
||||
@ -41,6 +43,7 @@
|
||||
<a href="#" data-toggle="dropdown" class="icon"><i class="fe fe-more-vertical"></i></a>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<a href="#" class="edit dropdown-item"><i class="dropdown-icon fe fe-edit"></i> <%- i18n('str', 'edit') %></a>
|
||||
<a href="#" class="able dropdown-item"><i class="dropdown-icon fe fe-power"></i> <%- i18n('str', enabled ? 'disable' : 'enable') %></a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a href="#" class="delete dropdown-item"><i class="dropdown-icon fe fe-trash-2"></i> <%- i18n('str', 'delete') %></a>
|
||||
</div>
|
||||
|
@ -9,11 +9,24 @@ module.exports = Mn.View.extend({
|
||||
tagName: 'tr',
|
||||
|
||||
ui: {
|
||||
able: 'a.able',
|
||||
edit: 'a.edit',
|
||||
delete: 'a.delete'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click @ui.able': function (e) {
|
||||
e.preventDefault();
|
||||
let id = this.model.get('id');
|
||||
App.Api.Nginx.Streams[this.model.get('enabled') ? 'disable' : 'enable'](id)
|
||||
.then(() => {
|
||||
return App.Api.Nginx.Streams.get(id)
|
||||
.then(row => {
|
||||
this.model.set(row);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
'click @ui.edit': function (e) {
|
||||
e.preventDefault();
|
||||
App.Controller.showNginxStreamForm(this.model);
|
||||
|
@ -14,6 +14,8 @@
|
||||
"save": "Save",
|
||||
"cancel": "Cancel",
|
||||
"close": "Close",
|
||||
"enable": "Enable",
|
||||
"disable": "Disable",
|
||||
"sure": "Yes I'm Sure",
|
||||
"disabled": "Disabled",
|
||||
"choose-file": "Choose file",
|
||||
@ -65,6 +67,7 @@
|
||||
"details": "Details",
|
||||
"enable-ssl": "Enable SSL",
|
||||
"force-ssl": "Force SSL",
|
||||
"http2-support": "HTTP/2 Support",
|
||||
"domain-names": "Domain Names",
|
||||
"cert-provider": "Certificate Provider",
|
||||
"block-exploits": "Block Common Exploits",
|
||||
@ -92,13 +95,16 @@
|
||||
"empty": "There are no Proxy Hosts",
|
||||
"add": "Add Proxy Host",
|
||||
"form-title": "{id, select, undefined{New} other{Edit}} Proxy Host",
|
||||
"forward-ip": "Forward IP",
|
||||
"forward-scheme": "Scheme",
|
||||
"forward-host": "Forward Hostname / IP",
|
||||
"forward-port": "Forward Port",
|
||||
"delete": "Delete Proxy Host",
|
||||
"delete-confirm": "Are you sure you want to delete the Proxy host for: <strong>{domains}</strong>?",
|
||||
"help-title": "What is a Proxy Host?",
|
||||
"help-content": "A Proxy Host is the incoming endpoint for a web service that you want to forward.\nIt provides optional SSL termination for your service that might not have SSL support built in.\nProxy Hosts are the most common use for the Nginx Proxy Manager.",
|
||||
"access-list": "Access List"
|
||||
"access-list": "Access List",
|
||||
"allow-websocket-upgrade": "Websockets Support",
|
||||
"ignore-invalid-upstream-ssl": "Ignore Invalid SSL"
|
||||
},
|
||||
"redirection-hosts": {
|
||||
"title": "Redirection Hosts",
|
||||
@ -209,6 +215,8 @@
|
||||
"created": "Created {name}",
|
||||
"updated": "Updated {name}",
|
||||
"deleted": "Deleted {name}",
|
||||
"enabled": "Enabled {name}",
|
||||
"disabled": "Disabled {name}",
|
||||
"meta-title": "Details for Event",
|
||||
"view-meta": "View Details",
|
||||
"date": "Date"
|
||||
|
@ -13,6 +13,8 @@ const model = Backbone.Model.extend({
|
||||
domain_names: [],
|
||||
certificate_id: 0,
|
||||
ssl_forced: false,
|
||||
http2_support: false,
|
||||
enabled: true,
|
||||
meta: {},
|
||||
advanced_config: '',
|
||||
// The following are expansions:
|
||||
|
@ -7,23 +7,27 @@ const model = Backbone.Model.extend({
|
||||
|
||||
defaults: function () {
|
||||
return {
|
||||
id: undefined,
|
||||
created_on: null,
|
||||
modified_on: null,
|
||||
domain_names: [],
|
||||
forward_ip: '',
|
||||
forward_port: null,
|
||||
access_list_id: 0,
|
||||
certificate_id: 0,
|
||||
ssl_forced: false,
|
||||
caching_enabled: false,
|
||||
block_exploits: false,
|
||||
advanced_config: '',
|
||||
meta: {},
|
||||
id: undefined,
|
||||
created_on: null,
|
||||
modified_on: null,
|
||||
domain_names: [],
|
||||
forward_scheme: 'http',
|
||||
forward_host: '',
|
||||
forward_port: null,
|
||||
access_list_id: 0,
|
||||
certificate_id: 0,
|
||||
ssl_forced: false,
|
||||
caching_enabled: false,
|
||||
allow_websocket_upgrade: false,
|
||||
block_exploits: false,
|
||||
http2_support: false,
|
||||
advanced_config: '',
|
||||
enabled: true,
|
||||
meta: {},
|
||||
// The following are expansions:
|
||||
owner: null,
|
||||
access_list: null,
|
||||
certificate: null
|
||||
owner: null,
|
||||
access_list: null,
|
||||
certificate: null
|
||||
};
|
||||
}
|
||||
});
|
||||
|
@ -16,7 +16,9 @@ const model = Backbone.Model.extend({
|
||||
certificate_id: 0,
|
||||
ssl_forced: false,
|
||||
block_exploits: false,
|
||||
http2_support: false,
|
||||
advanced_config: '',
|
||||
enabled: true,
|
||||
meta: {},
|
||||
// The following are expansions:
|
||||
owner: null,
|
||||
|
@ -15,6 +15,7 @@ const model = Backbone.Model.extend({
|
||||
forwarding_port: null,
|
||||
tcp_forwarding: true,
|
||||
udp_forwarding: false,
|
||||
enabled: true,
|
||||
meta: {},
|
||||
// The following are expansions:
|
||||
owner: null
|
||||
|
@ -3,6 +3,18 @@ $yellow: #f1c40f;
|
||||
$blue: #467fcf;
|
||||
$pink: #f66d9b;
|
||||
|
||||
.tag.hover-green:hover, .tag.hover-green:active, .tag.hover-green:focus {
|
||||
background-color: #5eba00;
|
||||
cursor: pointer;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.tag.hover-red:hover, .tag.hover-red:active, .tag.hover-red:focus {
|
||||
background-color: #cd201f;
|
||||
cursor: pointer;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* For Card bodies where I don't want padding */
|
||||
.card-body.no-padding {
|
||||
padding: 0;
|
||||
@ -28,6 +40,12 @@ $pink: #f66d9b;
|
||||
border-color: $teal;
|
||||
}
|
||||
|
||||
.tag.hover-teal:hover, .tag.hover-teal:active, .tag.hover-teal:focus {
|
||||
background-color: $teal;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Yellow Outline Buttons */
|
||||
.btn-outline-yellow {
|
||||
color: $yellow;
|
||||
@ -48,6 +66,12 @@ $pink: #f66d9b;
|
||||
border-color: $yellow;
|
||||
}
|
||||
|
||||
.tag.hover-yellow:hover, .tag.hover-yellow:active, .tag.hover-yellow:focus {
|
||||
background-color: $yellow;
|
||||
cursor: pointer;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Blue Outline Buttons */
|
||||
.btn-outline-blue {
|
||||
color: $blue;
|
||||
@ -68,6 +92,12 @@ $pink: #f66d9b;
|
||||
border-color: $blue;
|
||||
}
|
||||
|
||||
.tag.hover-blue:hover, .tag.hover-blue:active, .tag.hover-blue:focus {
|
||||
background-color: $blue;
|
||||
cursor: pointer;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Pink Outline Buttons */
|
||||
.btn-outline-pink {
|
||||
color: $pink;
|
||||
@ -88,6 +118,11 @@ $pink: #f66d9b;
|
||||
border-color: $pink;
|
||||
}
|
||||
|
||||
.tag.hover-pink:hover, .tag.hover-pink:active, .tag.hover-pink:focus {
|
||||
background-color: $pink;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* dimmer */
|
||||
|
||||
.dimmer .loader {
|
||||
|
@ -10,9 +10,10 @@ module.exports = {
|
||||
login: './src/frontend/js/login.js'
|
||||
},
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
filename: 'js/[name].js',
|
||||
publicPath: '/'
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
filename: 'js/[name].bundle.js',
|
||||
chunkFilename: 'js/[name].bundle.[id].js',
|
||||
publicPath: '/'
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
@ -108,41 +109,6 @@ module.exports = {
|
||||
to: 'images',
|
||||
toType: 'dir',
|
||||
context: '/app'
|
||||
}]),
|
||||
new webpack.optimize.LimitChunkCountPlugin({
|
||||
maxChunks: 1, // Must be greater than or equal to one
|
||||
minChunkSize: 999999999
|
||||
})
|
||||
],
|
||||
/*
|
||||
optimization: {
|
||||
splitChunks: {
|
||||
chunks (chunk) {
|
||||
// exclude `my-excluded-chunk`
|
||||
return false;
|
||||
},
|
||||
minSize: 999999999,
|
||||
minChunks: 1,
|
||||
name: true,
|
||||
cacheGroups: {
|
||||
vendors: {
|
||||
test: /[\\/]node_modules[\\/]/,
|
||||
priority: -10
|
||||
},
|
||||
default: {
|
||||
minChunks: 2,
|
||||
priority: -20,
|
||||
reuseExistingChunk: true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
*/
|
||||
devServer: {
|
||||
contentBase: path.join(__dirname, 'dist'),
|
||||
compress: true,
|
||||
port: 8080,
|
||||
disableHostCheck: true,
|
||||
host: '0.0.0.0'
|
||||
}
|
||||
}])
|
||||
]
|
||||
};
|
||||
|
Reference in New Issue
Block a user