Compare commits
126 Commits
Author | SHA1 | Date | |
---|---|---|---|
74010acd85 | |||
7c7d255172 | |||
058f1e9835 | |||
b4fc629ec0 | |||
ae06b2da75 | |||
54d423a11f | |||
5da6c97a00 | |||
bf2f13443f | |||
9ce4c3fe2f | |||
4a07bf666d | |||
5be46b4b20 | |||
7fd825b76b | |||
b23d59dec7 | |||
492d450d26 | |||
04412f3624 | |||
c41057b28a | |||
8312bc0100 | |||
85ac43bc5e | |||
d1a0780c7a | |||
f9b8d76527 | |||
26f00eeae4 | |||
1bc2df2178 | |||
8dfbcef198 | |||
6690b7735d | |||
a9e7222e5e | |||
f8edeb2775 | |||
d1786fe159 | |||
157a12fb7c | |||
3f723b1638 | |||
e2e9835d01 | |||
7599617b67 | |||
18a5b11033 | |||
fff31b0f34 | |||
c02e30663a | |||
4c6527cafc | |||
55bddb12e5 | |||
d95861e1fb | |||
94754a5cb3 | |||
546f862236 | |||
f105e29e56 | |||
5c15993d06 | |||
a369ea1080 | |||
98068c0f57 | |||
e0ef8683a2 | |||
66412a75f9 | |||
84d8fb0899 | |||
c631537dbe | |||
8d2f49541c | |||
55a28e3437 | |||
67ea2d01c8 | |||
dab229e37c | |||
7084473330 | |||
dd2e335fae | |||
1ff87bbc12 | |||
2ebfdcf0c9 | |||
8ab161a3ee | |||
e74b9617be | |||
c3d88c83e3 | |||
3e912a7474 | |||
0d726a1d83 | |||
affabf065e | |||
e6ea77d263 | |||
df73c2a458 | |||
96c5c79aef | |||
64922f07ff | |||
bae21f3210 | |||
0702a4e58e | |||
31f1d304d6 | |||
291a74c295 | |||
c0e9d1eb2f | |||
a7cabdde3a | |||
3af560c2d0 | |||
1d23d5c761 | |||
995db12f22 | |||
4c60bfb66b | |||
1716747047 | |||
090b4d0388 | |||
a9f068daa8 | |||
f5ee91aeb3 | |||
e2ee2cbf2d | |||
dcf8364899 | |||
b783602786 | |||
005e64eb9f | |||
e9e5d293cc | |||
a57255350f | |||
781442bf1e | |||
604bd2c576 | |||
d9e1e1bbb7 | |||
907e9e182d | |||
0f238a5021 | |||
8d432bd60a | |||
fd932c7678 | |||
46a9f5cb96 | |||
f990d3f674 | |||
4a6de8deee | |||
9a7a216b23 | |||
fccaaaae4d | |||
a882b0be82 | |||
db7bbab768 | |||
030e553549 | |||
8b0ca8e367 | |||
83b2b07200 | |||
bdb591af9e | |||
2993a08777 | |||
2a2d3d57ec | |||
33c2c131c2 | |||
e4286c96a7 | |||
2d9486b6fd | |||
632ee2d0bd | |||
b09f201819 | |||
baaf39c23d | |||
b7467c10e8 | |||
701ef18606 | |||
3e7d2b216b | |||
41f16c20b6 | |||
96bc0b53c3 | |||
b80baa78ef | |||
ce88e0745d | |||
256bd2336f | |||
1b6993ee70 | |||
af319b4729 | |||
1a15b4f18d | |||
3ddd3b49fd | |||
e2c4b32311 | |||
8fb2821343 | |||
93f29734b7 |
16
.github/ISSUE_TEMPLATE/product_support.md
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
name: Product Support
|
||||||
|
about: Need help configuring the software?
|
||||||
|
title: ''
|
||||||
|
labels: product-support
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Checklist**
|
||||||
|
- Please read the [setup instructions](https://nginxproxymanager.com/setup/)
|
||||||
|
- Please read the [FAQ](https://nginxproxymanager.com/faq/)
|
||||||
|
|
||||||
|
**What is troubling you?**
|
||||||
|
|
||||||
|
_Clear and concise description of what you're trying to do and what isn't working for you_
|
2
.gitignore
vendored
@ -2,4 +2,4 @@
|
|||||||
.idea
|
.idea
|
||||||
._*
|
._*
|
||||||
.vscode
|
.vscode
|
||||||
|
certbot-help.txt
|
||||||
|
11
.jenkins/config-sqlite.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"database": {
|
||||||
|
"engine": "knex-native",
|
||||||
|
"knex": {
|
||||||
|
"client": "sqlite3",
|
||||||
|
"connection": {
|
||||||
|
"filename": "/data/database.sqlite"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
114
Jenkinsfile
vendored
@ -5,6 +5,7 @@ pipeline {
|
|||||||
options {
|
options {
|
||||||
buildDiscarder(logRotator(numToKeepStr: '5'))
|
buildDiscarder(logRotator(numToKeepStr: '5'))
|
||||||
disableConcurrentBuilds()
|
disableConcurrentBuilds()
|
||||||
|
ansiColor('xterm')
|
||||||
}
|
}
|
||||||
environment {
|
environment {
|
||||||
IMAGE = "nginx-proxy-manager"
|
IMAGE = "nginx-proxy-manager"
|
||||||
@ -42,18 +43,24 @@ pipeline {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
stage('Versions') {
|
||||||
|
steps {
|
||||||
|
sh 'cat frontend/package.json | jq --arg BUILD_VERSION "${BUILD_VERSION}" \'.version = $BUILD_VERSION\' | sponge frontend/package.json'
|
||||||
|
sh 'echo -e "\\E[1;36mFrontend Version is:\\E[1;33m $(cat frontend/package.json | jq -r .version)\\E[0m"'
|
||||||
|
sh 'cat backend/package.json | jq --arg BUILD_VERSION "${BUILD_VERSION}" \'.version = $BUILD_VERSION\' | sponge backend/package.json'
|
||||||
|
sh 'echo -e "\\E[1;36mBackend Version is:\\E[1;33m $(cat backend/package.json | jq -r .version)\\E[0m"'
|
||||||
|
sh 'sed -i -E "s/(version-)[0-9]+\\.[0-9]+\\.[0-9]+(-green)/\\1${BUILD_VERSION}\\2/" README.md'
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage('Frontend') {
|
stage('Frontend') {
|
||||||
steps {
|
steps {
|
||||||
ansiColor('xterm') {
|
|
||||||
sh './scripts/frontend-build'
|
sh './scripts/frontend-build'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
stage('Backend') {
|
stage('Backend') {
|
||||||
steps {
|
steps {
|
||||||
ansiColor('xterm') {
|
|
||||||
echo 'Checking Syntax ...'
|
echo 'Checking Syntax ...'
|
||||||
// See: https://github.com/yarnpkg/yarn/issues/3254
|
// See: https://github.com/yarnpkg/yarn/issues/3254
|
||||||
sh '''docker run --rm \\
|
sh '''docker run --rm \\
|
||||||
@ -76,32 +83,75 @@ pipeline {
|
|||||||
'''
|
'''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
stage('Integration Tests Sqlite') {
|
||||||
stage('Test') {
|
|
||||||
steps {
|
steps {
|
||||||
ansiColor('xterm') {
|
|
||||||
// Bring up a stack
|
// Bring up a stack
|
||||||
sh 'docker-compose up -d fullstack'
|
sh 'docker-compose up -d fullstack-sqlite'
|
||||||
sh './scripts/wait-healthy $(docker-compose ps -q fullstack) 120'
|
sh './scripts/wait-healthy $(docker-compose ps -q fullstack-sqlite) 120'
|
||||||
|
|
||||||
// Run tests
|
// Run tests
|
||||||
sh 'rm -rf test/results'
|
sh 'rm -rf test/results'
|
||||||
sh 'docker-compose up cypress'
|
sh 'docker-compose up cypress-sqlite'
|
||||||
// Get results
|
// Get results
|
||||||
sh 'docker cp -L "$(docker-compose ps -q cypress):/results" test/'
|
sh 'docker cp -L "$(docker-compose ps -q cypress-sqlite):/test/results" test/'
|
||||||
}
|
|
||||||
}
|
}
|
||||||
post {
|
post {
|
||||||
always {
|
always {
|
||||||
junit 'test/results/junit/*'
|
// Dumps to analyze later
|
||||||
|
sh 'mkdir -p debug'
|
||||||
|
sh 'docker-compose logs fullstack-sqlite | gzip > debug/docker_fullstack_sqlite.log.gz'
|
||||||
|
sh 'docker-compose logs db | gzip > debug/docker_db.log.gz'
|
||||||
// Cypress videos and screenshot artifacts
|
// Cypress videos and screenshot artifacts
|
||||||
dir(path: 'test/results') {
|
dir(path: 'test/results') {
|
||||||
archiveArtifacts allowEmptyArchive: true, artifacts: '**/*', excludes: '**/*.xml'
|
archiveArtifacts allowEmptyArchive: true, artifacts: '**/*', excludes: '**/*.xml'
|
||||||
}
|
}
|
||||||
|
junit 'test/results/junit/*'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Integration Tests Mysql') {
|
||||||
|
steps {
|
||||||
|
// Bring up a stack
|
||||||
|
sh 'docker-compose up -d fullstack-mysql'
|
||||||
|
sh './scripts/wait-healthy $(docker-compose ps -q fullstack-mysql) 120'
|
||||||
|
|
||||||
|
// Run tests
|
||||||
|
sh 'rm -rf test/results'
|
||||||
|
sh 'docker-compose up cypress-mysql'
|
||||||
|
// Get results
|
||||||
|
sh 'docker cp -L "$(docker-compose ps -q cypress-mysql):/test/results" test/'
|
||||||
|
}
|
||||||
|
post {
|
||||||
|
always {
|
||||||
// Dumps to analyze later
|
// Dumps to analyze later
|
||||||
sh 'mkdir -p debug'
|
sh 'mkdir -p debug'
|
||||||
sh 'docker-compose logs fullstack | gzip > debug/docker_fullstack.log.gz'
|
sh 'docker-compose logs fullstack-mysql | gzip > debug/docker_fullstack_mysql.log.gz'
|
||||||
|
sh 'docker-compose logs db | gzip > debug/docker_db.log.gz'
|
||||||
|
// Cypress videos and screenshot artifacts
|
||||||
|
dir(path: 'test/results') {
|
||||||
|
archiveArtifacts allowEmptyArchive: true, artifacts: '**/*', excludes: '**/*.xml'
|
||||||
}
|
}
|
||||||
|
junit 'test/results/junit/*'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Docs') {
|
||||||
|
when {
|
||||||
|
not {
|
||||||
|
equals expected: 'UNSTABLE', actual: currentBuild.result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
dir(path: 'docs') {
|
||||||
|
sh 'yarn install'
|
||||||
|
sh 'yarn build'
|
||||||
|
}
|
||||||
|
|
||||||
|
dir(path: 'docs/.vuepress/dist') {
|
||||||
|
sh 'tar -czf ../../docs.tgz *'
|
||||||
|
}
|
||||||
|
|
||||||
|
archiveArtifacts(artifacts: 'docs/docs.tgz', allowEmptyArchive: false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage('MultiArch Build') {
|
stage('MultiArch Build') {
|
||||||
@ -111,14 +161,45 @@ pipeline {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
steps {
|
steps {
|
||||||
ansiColor('xterm') {
|
|
||||||
withCredentials([usernamePassword(credentialsId: 'jc21-dockerhub', passwordVariable: 'dpass', usernameVariable: 'duser')]) {
|
withCredentials([usernamePassword(credentialsId: 'jc21-dockerhub', passwordVariable: 'dpass', usernameVariable: 'duser')]) {
|
||||||
|
// Docker Login
|
||||||
sh "docker login -u '${duser}' -p '${dpass}'"
|
sh "docker login -u '${duser}' -p '${dpass}'"
|
||||||
// Buildx with push
|
// Buildx with push from cache
|
||||||
sh "./scripts/buildx --push ${BUILDX_PUSH_TAGS}"
|
sh "./scripts/buildx --push ${BUILDX_PUSH_TAGS}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
stage('Docs Deploy') {
|
||||||
|
when {
|
||||||
|
allOf {
|
||||||
|
branch 'master'
|
||||||
|
not {
|
||||||
|
equals expected: 'UNSTABLE', actual: currentBuild.result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
withCredentials([[$class: 'AmazonWebServicesCredentialsBinding', accessKeyVariable: 'AWS_ACCESS_KEY_ID', credentialsId: 'npm-s3-docs', secretKeyVariable: 'AWS_SECRET_ACCESS_KEY']]) {
|
||||||
|
sh """docker run --rm \\
|
||||||
|
--name \${COMPOSE_PROJECT_NAME}-docs-upload \\
|
||||||
|
-e S3_BUCKET=jc21-npm-site \\
|
||||||
|
-e AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID \\
|
||||||
|
-e AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY \\
|
||||||
|
-v \$(pwd):/app \\
|
||||||
|
-w /app \\
|
||||||
|
jc21/ci-tools \\
|
||||||
|
scripts/docs-upload /app/docs/.vuepress/dist/
|
||||||
|
"""
|
||||||
|
|
||||||
|
sh """docker run --rm \\
|
||||||
|
--name \${COMPOSE_PROJECT_NAME}-docs-invalidate \\
|
||||||
|
-e AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID \\
|
||||||
|
-e AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY \\
|
||||||
|
jc21/ci-tools \\
|
||||||
|
aws cloudfront create-invalidation --distribution-id EN1G6DEWZUTDT --paths '/*'
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
stage('PR Comment') {
|
stage('PR Comment') {
|
||||||
when {
|
when {
|
||||||
@ -130,14 +211,12 @@ pipeline {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
steps {
|
steps {
|
||||||
ansiColor('xterm') {
|
|
||||||
script {
|
script {
|
||||||
def comment = pullRequest.comment("Docker Image for build ${BUILD_NUMBER} is available on [DockerHub](https://cloud.docker.com/repository/docker/jc21/${IMAGE}) as `jc21/${IMAGE}:github-${BRANCH_LOWER}`")
|
def comment = pullRequest.comment("Docker Image for build ${BUILD_NUMBER} is available on [DockerHub](https://cloud.docker.com/repository/docker/jc21/${IMAGE}) as `jc21/${IMAGE}:github-${BRANCH_LOWER}`")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
post {
|
post {
|
||||||
always {
|
always {
|
||||||
sh 'docker-compose down --rmi all --remove-orphans --volumes -t 30'
|
sh 'docker-compose down --rmi all --remove-orphans --volumes -t 30'
|
||||||
@ -149,6 +228,7 @@ pipeline {
|
|||||||
sh 'figlet "SUCCESS"'
|
sh 'figlet "SUCCESS"'
|
||||||
}
|
}
|
||||||
failure {
|
failure {
|
||||||
|
archiveArtifacts(artifacts: 'debug/**.*', allowEmptyArchive: true)
|
||||||
juxtapose event: 'failure'
|
juxtapose event: 'failure'
|
||||||
sh 'figlet "FAILURE"'
|
sh 'figlet "FAILURE"'
|
||||||
}
|
}
|
||||||
|
206
README.md
@ -1,16 +1,27 @@
|
|||||||

|
<p align="center">
|
||||||
|
<img src="https://nginxproxymanager.com/github.png">
|
||||||
# Nginx Proxy Manager
|
<br><br>
|
||||||
|
<img src="https://img.shields.io/badge/version-2.4.0-green.svg?style=for-the-badge">
|
||||||

|
<a href="https://hub.docker.com/repository/docker/jc21/nginx-proxy-manager">
|
||||||

|
<img src="https://img.shields.io/docker/stars/jc21/nginx-proxy-manager.svg?style=for-the-badge">
|
||||||

|
</a>
|
||||||
|
<a href="https://hub.docker.com/repository/docker/jc21/nginx-proxy-manager">
|
||||||
[](https://ci.nginxproxymanager.jc21.com/job/nginx-proxy-manager/job/master/)
|
<img src="https://img.shields.io/docker/pulls/jc21/nginx-proxy-manager.svg?style=for-the-badge">
|
||||||
|
</a>
|
||||||
|
<a href="https://ci.nginxproxymanager.com/blue/organizations/jenkins/nginx-proxy-manager/branches/">
|
||||||
|
<img src="https://img.shields.io/jenkins/build?jobUrl=https%3A%2F%2Fci.nginxproxymanager.com%2Fjob%2Fnginx-proxy-manager%2Fjob%2Fmaster&style=for-the-badge">
|
||||||
|
</a>
|
||||||
|
<a href="https://gitter.im/nginx-proxy-manager/community">
|
||||||
|
<img alt="Gitter" src="https://img.shields.io/gitter/room/nginx-proxy-manager/community?style=for-the-badge">
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
This project comes as a pre-built docker image that enables you to easily forward to your websites
|
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.
|
running at home or otherwise, including free SSL, without having to know too much about Nginx or Letsencrypt.
|
||||||
|
|
||||||
|
- [Quick Setup](https://nginxproxymanager.com#quick-setup)
|
||||||
|
- [Full Setup](https://nginxproxymanager.com/setup/)
|
||||||
|
- [Screenshots](https://nginxproxymanager.com/screenshots/)
|
||||||
|
|
||||||
## Project Goal
|
## Project Goal
|
||||||
|
|
||||||
@ -32,54 +43,6 @@ so that the barrier for entry here is low.
|
|||||||
- User management, permissions and audit log
|
- User management, permissions and audit log
|
||||||
|
|
||||||
|
|
||||||
## Screenshots
|
|
||||||
|
|
||||||
[](https://public.jc21.com/nginx-proxy-manager/v2/large/login.jpg)
|
|
||||||
[](https://public.jc21.com/nginx-proxy-manager/v2/large/dashboard.jpg)
|
|
||||||
[](https://public.jc21.com/nginx-proxy-manager/v2/large/proxy-hosts.jpg)
|
|
||||||
[](https://public.jc21.com/nginx-proxy-manager/v2/large/proxy-hosts-new1.jpg)
|
|
||||||
[](https://public.jc21.com/nginx-proxy-manager/v2/large/proxy-hosts-new2.jpg)
|
|
||||||
[](https://public.jc21.com/nginx-proxy-manager/v2/large/redirection-hosts.jpg)
|
|
||||||
[](https://public.jc21.com/nginx-proxy-manager/v2/large/redirection-hosts-new1.jpg)
|
|
||||||
[](https://public.jc21.com/nginx-proxy-manager/v2/large/streams.jpg)
|
|
||||||
[](https://public.jc21.com/nginx-proxy-manager/v2/large/streams-new1.jpg)
|
|
||||||
[](https://public.jc21.com/nginx-proxy-manager/v2/large/dead-hosts.jpg)
|
|
||||||
[](https://public.jc21.com/nginx-proxy-manager/v2/large/dead-hosts-new1.jpg)
|
|
||||||
[](https://public.jc21.com/nginx-proxy-manager/v2/large/certificates.jpg)
|
|
||||||
[](https://public.jc21.com/nginx-proxy-manager/v2/large/certificates-new1.jpg)
|
|
||||||
[](https://public.jc21.com/nginx-proxy-manager/v2/large/certificates-new2.jpg)
|
|
||||||
[](https://public.jc21.com/nginx-proxy-manager/v2/large/access-lists.jpg)
|
|
||||||
[](https://public.jc21.com/nginx-proxy-manager/v2/large/access-lists-new1.jpg)
|
|
||||||
[](https://public.jc21.com/nginx-proxy-manager/v2/large/users.jpg)
|
|
||||||
[](https://public.jc21.com/nginx-proxy-manager/v2/large/users-permissions.jpg)
|
|
||||||
[](https://public.jc21.com/nginx-proxy-manager/v2/large/audit-log.jpg)
|
|
||||||
|
|
||||||
|
|
||||||
## Getting started
|
|
||||||
|
|
||||||
Please consult the [installation instructions](doc/INSTALL.md) for a complete guide or
|
|
||||||
if you just want to get up and running in the quickest time possible, grab all the files in the `doc/example/` folder and run `docker-compose up -d`
|
|
||||||
|
|
||||||
|
|
||||||
## Administration
|
|
||||||
|
|
||||||
When your docker container is running, connect to it on port `81` for the admin interface.
|
|
||||||
|
|
||||||
[http://localhost:81](http://localhost:81)
|
|
||||||
|
|
||||||
Note: Requesting SSL Certificates won't work until this project is accessible from the outside world, as explained below.
|
|
||||||
|
|
||||||
|
|
||||||
### Default Administrator User
|
|
||||||
|
|
||||||
```
|
|
||||||
Email: admin@example.com
|
|
||||||
Password: changeme
|
|
||||||
```
|
|
||||||
|
|
||||||
Immediately after logging in with this default user you will be asked to modify your details and change your password.
|
|
||||||
|
|
||||||
|
|
||||||
## Hosting your home network
|
## Hosting your home network
|
||||||
|
|
||||||
I won't go in to too much detail here but here are the basics for someone new to this self-hosted world.
|
I won't go in to too much detail here but here are the basics for someone new to this self-hosted world.
|
||||||
@ -90,12 +53,127 @@ I won't go in to too much detail here but here are the basics for someone new to
|
|||||||
4. Use the Nginx Proxy Manager as your gateway to forward to your other web based services
|
4. Use the Nginx Proxy Manager as your gateway to forward to your other web based services
|
||||||
|
|
||||||
|
|
||||||
## Nginx Proxy Manager in the wild
|
## Contributors
|
||||||
|
|
||||||
As this software gains popularity it's common to see it integrated with other platforms. Please be aware that unless specifically mentioned in the documenation of those
|
Special thanks to the following contributors:
|
||||||
integrations, they are *not supported* by me and any donation links on the pages of those integrations will not come to me even though it looks like it.
|
|
||||||
|
|
||||||
Known integrations:
|
<!-- prettier-ignore-start -->
|
||||||
|
<!-- markdownlint-disable -->
|
||||||
- [HomeAssistant Hass.io plugin](https://github.com/hassio-addons/addon-nginx-proxy-manager)
|
<table>
|
||||||
- [UnRaid / Synology](https://github.com/jlesage/docker-nginx-proxy-manager)
|
<tr>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/Subv">
|
||||||
|
<img src="https://avatars1.githubusercontent.com/u/357072?s=460&u=d8adcdc91d749ae53e177973ed9b6bb6c4c894a3&v=4" width="80px;" alt=""/>
|
||||||
|
<br /><sub><b>Sebastian Valle</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/Indemnity83">
|
||||||
|
<img src="https://avatars3.githubusercontent.com/u/35218?s=460&u=7082004ff35138157c868d7d9c683ccebfce5968&v=4" width="80px;" alt=""/>
|
||||||
|
<br /><sub><b>Kyle Klaus</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/theraw">
|
||||||
|
<img src="https://avatars1.githubusercontent.com/u/32969774?s=460&u=6b359971e15685fb0359e6a8c065a399b40dc228&v=4" width="80px;" alt=""/>
|
||||||
|
<br /><sub><b>ƬHE ЯAW</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/spalger">
|
||||||
|
<img src="https://avatars2.githubusercontent.com/u/1329312?s=400&u=565223e38f1c052afb4c5dcca3fcf1c63ba17ae7&v=4" width="80px;" alt=""/>
|
||||||
|
<br /><sub><b>Spencer</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/Xantios">
|
||||||
|
<img src="https://avatars3.githubusercontent.com/u/1507836?s=460&v=4" width="80px;" alt=""/>
|
||||||
|
<br /><sub><b>Xantios Krugor</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/dpanesso">
|
||||||
|
<img src="https://avatars2.githubusercontent.com/u/2687121?s=460&v=4" width="80px;" alt=""/>
|
||||||
|
<br /><sub><b>David Panesso</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/IronTooch">
|
||||||
|
<img src="https://avatars3.githubusercontent.com/u/27360514?s=460&u=69bf854a6647c55725f62ecb8d39249c6c0b2602&v=4" width="80px;" alt=""/>
|
||||||
|
<br /><sub><b>IronTooch</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/damianog">
|
||||||
|
<img src="https://avatars1.githubusercontent.com/u/2786682?s=460&u=76c6136fae797abb76b951cd8a246dcaecaf21af&v=4" width="80px;" alt=""/>
|
||||||
|
<br /><sub><b>Damiano</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/tfmm">
|
||||||
|
<img src="https://avatars3.githubusercontent.com/u/6880538?s=460&u=ce0160821cc4aa802df8395200f2d4956a5bc541&v=4" width="80px;" alt=""/>
|
||||||
|
<br /><sub><b>Russ</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/margaale">
|
||||||
|
<img src="https://avatars3.githubusercontent.com/u/20794934?s=460&v=4" width="80px;" alt=""/>
|
||||||
|
<br /><sub><b>Marcelo Castagna</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/Steven-Harris">
|
||||||
|
<img src="https://avatars2.githubusercontent.com/u/7720242?s=460&v=4" width="80px;" alt=""/>
|
||||||
|
<br /><sub><b>Steven Harris</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/jlesage">
|
||||||
|
<img src="https://avatars0.githubusercontent.com/u/1791123?s=460&v=4" width="80px;" alt=""/>
|
||||||
|
<br /><sub><b>Jocelyn Le Sage</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/cmer">
|
||||||
|
<img src="https://avatars0.githubusercontent.com/u/412?s=460&u=67dd8b2e3661bfd6f68ec1eaa5b9821bd8a321cd&v=4" width="80px;" alt=""/>
|
||||||
|
<br /><sub><b>Carl Mercier</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/the1ts">
|
||||||
|
<img src="https://avatars1.githubusercontent.com/u/84956?s=460&v=4" width="80px;" alt=""/>
|
||||||
|
<br /><sub><b>Paul Mansfield</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/OhHeyAlan">
|
||||||
|
<img src="https://avatars0.githubusercontent.com/u/11955126?s=460&u=fbaa5a1a4f73ef8960132c703349bfd037fe2630&v=4" width="80px;" alt=""/>
|
||||||
|
<br /><sub><b>OhHeyAlan</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/dogmatic69">
|
||||||
|
<img src="https://avatars2.githubusercontent.com/u/94674?s=460&u=ca7647de53145c6283b6373ade5dc94ba99347db&v=4" width="80px;" alt=""/>
|
||||||
|
<br /><sub><b>Carl Sutton</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/tg44">
|
||||||
|
<img src="https://avatars0.githubusercontent.com/u/31839?s=460&u=ad32f4cadfef5e5fb09cdfa4b7b7b36a99ba6811&v=4" width="80px;" alt=""/>
|
||||||
|
<br /><sub><b>Gergő Törcsvári</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/vrenjith">
|
||||||
|
<img src="https://avatars3.githubusercontent.com/u/2093241?s=460&u=96ce93a9bebabdd0a60a2dc96cd093a41d5edaba&v=4" width="80px;" alt=""/>
|
||||||
|
<br /><sub><b>vrenjith</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<!-- markdownlint-enable -->
|
||||||
|
<!-- prettier-ignore-end -->
|
||||||
|
2
backend/.gitignore
vendored
@ -4,3 +4,5 @@ yarn-error.log
|
|||||||
tmp
|
tmp
|
||||||
certbot.log
|
certbot.log
|
||||||
node_modules
|
node_modules
|
||||||
|
core.*
|
||||||
|
|
||||||
|
8
backend/.vscode/settings.json
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"editor.insertSpaces": false,
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"files.trimTrailingWhitespace": true,
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.fixAll.eslint": true
|
||||||
|
}
|
||||||
|
}
|
26
backend/config/sqlite-test-db.json
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"database": {
|
||||||
|
"engine": "knex-native",
|
||||||
|
"knex": {
|
||||||
|
"client": "sqlite3",
|
||||||
|
"connection": {
|
||||||
|
"filename": "/app/backend/config/mydb.sqlite"
|
||||||
|
},
|
||||||
|
"pool": {
|
||||||
|
"min": 0,
|
||||||
|
"max": 1,
|
||||||
|
"createTimeoutMillis": 3000,
|
||||||
|
"acquireTimeoutMillis": 30000,
|
||||||
|
"idleTimeoutMillis": 30000,
|
||||||
|
"reapIntervalMillis": 1000,
|
||||||
|
"createRetryIntervalMillis": 100,
|
||||||
|
"propagateCreateError": false
|
||||||
|
},
|
||||||
|
"migrations": {
|
||||||
|
"tableName": "migrations",
|
||||||
|
"stub": "src/backend/lib/migrate_template.js",
|
||||||
|
"directory": "src/backend/migrations"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,7 +4,11 @@ if (!config.has('database')) {
|
|||||||
throw new Error('Database config does not exist! Please read the instructions: https://github.com/jc21/nginx-proxy-manager/blob/master/doc/INSTALL.md');
|
throw new Error('Database config does not exist! Please read the instructions: https://github.com/jc21/nginx-proxy-manager/blob/master/doc/INSTALL.md');
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = {
|
function generateDbConfig() {
|
||||||
|
if (config.database.engine === 'knex-native') {
|
||||||
|
return config.database.knex;
|
||||||
|
} else
|
||||||
|
return {
|
||||||
client: config.database.engine,
|
client: config.database.engine,
|
||||||
connection: {
|
connection: {
|
||||||
host: config.database.host,
|
host: config.database.host,
|
||||||
@ -16,7 +20,11 @@ let data = {
|
|||||||
migrations: {
|
migrations: {
|
||||||
tableName: 'migrations'
|
tableName: 'migrations'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let data = generateDbConfig();
|
||||||
|
|
||||||
if (typeof config.database.version !== 'undefined') {
|
if (typeof config.database.version !== 'undefined') {
|
||||||
data.version = config.database.version;
|
data.version = config.database.version;
|
||||||
|
@ -5,6 +5,7 @@ const logger = require('../logger').access;
|
|||||||
const error = require('../lib/error');
|
const error = require('../lib/error');
|
||||||
const accessListModel = require('../models/access_list');
|
const accessListModel = require('../models/access_list');
|
||||||
const accessListAuthModel = require('../models/access_list_auth');
|
const accessListAuthModel = require('../models/access_list_auth');
|
||||||
|
const accessListClientModel = require('../models/access_list_client');
|
||||||
const proxyHostModel = require('../models/proxy_host');
|
const proxyHostModel = require('../models/proxy_host');
|
||||||
const internalAuditLog = require('./audit-log');
|
const internalAuditLog = require('./audit-log');
|
||||||
const internalNginx = require('./nginx');
|
const internalNginx = require('./nginx');
|
||||||
@ -29,14 +30,16 @@ const internalAccessList = {
|
|||||||
.omit(omissions())
|
.omit(omissions())
|
||||||
.insertAndFetch({
|
.insertAndFetch({
|
||||||
name: data.name,
|
name: data.name,
|
||||||
|
satisfy_any: data.satisfy_any,
|
||||||
owner_user_id: access.token.getUserId(1)
|
owner_user_id: access.token.getUserId(1)
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.then((row) => {
|
.then((row) => {
|
||||||
data.id = row.id;
|
data.id = row.id;
|
||||||
|
|
||||||
// Now add the items
|
|
||||||
let promises = [];
|
let promises = [];
|
||||||
|
|
||||||
|
// Now add the items
|
||||||
data.items.map((item) => {
|
data.items.map((item) => {
|
||||||
promises.push(accessListAuthModel
|
promises.push(accessListAuthModel
|
||||||
.query()
|
.query()
|
||||||
@ -48,13 +51,27 @@ const internalAccessList = {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Now add the clients
|
||||||
|
if (typeof data.clients !== 'undefined' && data.clients) {
|
||||||
|
data.clients.map((client) => {
|
||||||
|
promises.push(accessListClientModel
|
||||||
|
.query()
|
||||||
|
.insert({
|
||||||
|
access_list_id: row.id,
|
||||||
|
address: client.address,
|
||||||
|
directive: client.directive
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return Promise.all(promises);
|
return Promise.all(promises);
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// re-fetch with expansions
|
// re-fetch with expansions
|
||||||
return internalAccessList.get(access, {
|
return internalAccessList.get(access, {
|
||||||
id: data.id,
|
id: data.id,
|
||||||
expand: ['owner', 'items']
|
expand: ['owner', 'items', 'clients', 'proxy_hosts.access_list.[clients,items]']
|
||||||
}, true /* <- skip masking */);
|
}, true /* <- skip masking */);
|
||||||
})
|
})
|
||||||
.then((row) => {
|
.then((row) => {
|
||||||
@ -64,7 +81,7 @@ const internalAccessList = {
|
|||||||
return internalAccessList.build(row)
|
return internalAccessList.build(row)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
if (row.proxy_host_count) {
|
if (row.proxy_host_count) {
|
||||||
return internalNginx.reload();
|
return internalNginx.bulkGenerateConfigs('proxy_host', row.proxy_hosts);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
@ -109,7 +126,8 @@ const internalAccessList = {
|
|||||||
.query()
|
.query()
|
||||||
.where({id: data.id})
|
.where({id: data.id})
|
||||||
.patch({
|
.patch({
|
||||||
name: data.name
|
name: data.name,
|
||||||
|
satisfy_any: data.satisfy_any,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -153,6 +171,38 @@ const internalAccessList = {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.then(() => {
|
||||||
|
// Check for clients and add/update/remove them
|
||||||
|
if (typeof data.clients !== 'undefined' && data.clients) {
|
||||||
|
let promises = [];
|
||||||
|
|
||||||
|
data.clients.map(function (client) {
|
||||||
|
if (client.address) {
|
||||||
|
promises.push(accessListClientModel
|
||||||
|
.query()
|
||||||
|
.insert({
|
||||||
|
access_list_id: data.id,
|
||||||
|
address: client.address,
|
||||||
|
directive: client.directive
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let query = accessListClientModel
|
||||||
|
.query()
|
||||||
|
.delete()
|
||||||
|
.where('access_list_id', data.id);
|
||||||
|
|
||||||
|
return query
|
||||||
|
.then(() => {
|
||||||
|
// Add new items
|
||||||
|
if (promises.length) {
|
||||||
|
return Promise.all(promises);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// Add to audit log
|
// Add to audit log
|
||||||
return internalAuditLog.add(access, {
|
return internalAuditLog.add(access, {
|
||||||
@ -166,14 +216,14 @@ const internalAccessList = {
|
|||||||
// re-fetch with expansions
|
// re-fetch with expansions
|
||||||
return internalAccessList.get(access, {
|
return internalAccessList.get(access, {
|
||||||
id: data.id,
|
id: data.id,
|
||||||
expand: ['owner', 'items']
|
expand: ['owner', 'items', 'clients', 'proxy_hosts.access_list.[clients,items]']
|
||||||
}, true /* <- skip masking */);
|
}, true /* <- skip masking */);
|
||||||
})
|
})
|
||||||
.then((row) => {
|
.then((row) => {
|
||||||
return internalAccessList.build(row)
|
return internalAccessList.build(row)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
if (row.proxy_host_count) {
|
if (row.proxy_host_count) {
|
||||||
return internalNginx.reload();
|
return internalNginx.bulkGenerateConfigs('proxy_host', row.proxy_hosts);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
@ -204,7 +254,7 @@ const internalAccessList = {
|
|||||||
.joinRaw('LEFT JOIN `proxy_host` ON `proxy_host`.`access_list_id` = `access_list`.`id` AND `proxy_host`.`is_deleted` = 0')
|
.joinRaw('LEFT JOIN `proxy_host` ON `proxy_host`.`access_list_id` = `access_list`.`id` AND `proxy_host`.`is_deleted` = 0')
|
||||||
.where('access_list.is_deleted', 0)
|
.where('access_list.is_deleted', 0)
|
||||||
.andWhere('access_list.id', data.id)
|
.andWhere('access_list.id', data.id)
|
||||||
.allowEager('[owner,items,proxy_hosts]')
|
.allowEager('[owner,items,clients,proxy_hosts.[*, access_list.[clients,items]]]')
|
||||||
.omit(['access_list.is_deleted'])
|
.omit(['access_list.is_deleted'])
|
||||||
.first();
|
.first();
|
||||||
|
|
||||||
@ -246,7 +296,7 @@ const internalAccessList = {
|
|||||||
delete: (access, data) => {
|
delete: (access, data) => {
|
||||||
return access.can('access_lists:delete', data.id)
|
return access.can('access_lists:delete', data.id)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return internalAccessList.get(access, {id: data.id, expand: ['proxy_hosts', 'items']});
|
return internalAccessList.get(access, {id: data.id, expand: ['proxy_hosts', 'items', 'clients']});
|
||||||
})
|
})
|
||||||
.then((row) => {
|
.then((row) => {
|
||||||
if (!row) {
|
if (!row) {
|
||||||
@ -330,7 +380,7 @@ const internalAccessList = {
|
|||||||
.where('access_list.is_deleted', 0)
|
.where('access_list.is_deleted', 0)
|
||||||
.groupBy('access_list.id')
|
.groupBy('access_list.id')
|
||||||
.omit(['access_list.is_deleted'])
|
.omit(['access_list.is_deleted'])
|
||||||
.allowEager('[owner,items]')
|
.allowEager('[owner,items,clients]')
|
||||||
.orderBy('access_list.name', 'ASC');
|
.orderBy('access_list.name', 'ASC');
|
||||||
|
|
||||||
if (access_data.permission_visibility !== 'all') {
|
if (access_data.permission_visibility !== 'all') {
|
||||||
|
@ -224,6 +224,9 @@ const internalNginx = {
|
|||||||
locationsPromise = Promise.resolve();
|
locationsPromise = Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set the IPv6 setting for the host
|
||||||
|
host.ipv6 = internalNginx.ipv6Enabled();
|
||||||
|
|
||||||
locationsPromise.then(() => {
|
locationsPromise.then(() => {
|
||||||
renderEngine
|
renderEngine
|
||||||
.parseAndRender(template, host)
|
.parseAndRender(template, host)
|
||||||
@ -270,6 +273,7 @@ const internalNginx = {
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let template = null;
|
let template = null;
|
||||||
let filename = '/data/nginx/temp/letsencrypt_' + certificate.id + '.conf';
|
let filename = '/data/nginx/temp/letsencrypt_' + certificate.id + '.conf';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
template = fs.readFileSync(__dirname + '/../templates/letsencrypt-request.conf', {encoding: 'utf8'});
|
template = fs.readFileSync(__dirname + '/../templates/letsencrypt-request.conf', {encoding: 'utf8'});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -277,6 +281,8 @@ const internalNginx = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
certificate.ipv6 = internalNginx.ipv6Enabled();
|
||||||
|
|
||||||
renderEngine
|
renderEngine
|
||||||
.parseAndRender(template, certificate)
|
.parseAndRender(template, certificate)
|
||||||
.then((config_text) => {
|
.then((config_text) => {
|
||||||
@ -396,6 +402,18 @@ const internalNginx = {
|
|||||||
*/
|
*/
|
||||||
advancedConfigHasDefaultLocation: function (config) {
|
advancedConfigHasDefaultLocation: function (config) {
|
||||||
return !!config.match(/^(?:.*;)?\s*?location\s*?\/\s*?{/im);
|
return !!config.match(/^(?:.*;)?\s*?location\s*?\/\s*?{/im);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
ipv6Enabled: function () {
|
||||||
|
if (typeof process.env.DISABLE_IPV6 !== 'undefined') {
|
||||||
|
const disabled = process.env.DISABLE_IPV6.toLowerCase();
|
||||||
|
return !(disabled === 'on' || disabled === 'true' || disabled === '1' || disabled === 'yes');
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -73,7 +73,7 @@ const internalProxyHost = {
|
|||||||
// re-fetch with cert
|
// re-fetch with cert
|
||||||
return internalProxyHost.get(access, {
|
return internalProxyHost.get(access, {
|
||||||
id: row.id,
|
id: row.id,
|
||||||
expand: ['certificate', 'owner', 'access_list']
|
expand: ['certificate', 'owner', 'access_list.[clients,items]']
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.then((row) => {
|
.then((row) => {
|
||||||
@ -186,7 +186,7 @@ const internalProxyHost = {
|
|||||||
.then(() => {
|
.then(() => {
|
||||||
return internalProxyHost.get(access, {
|
return internalProxyHost.get(access, {
|
||||||
id: data.id,
|
id: data.id,
|
||||||
expand: ['owner', 'certificate', 'access_list']
|
expand: ['owner', 'certificate', 'access_list.[clients,items]']
|
||||||
})
|
})
|
||||||
.then((row) => {
|
.then((row) => {
|
||||||
// Configure nginx
|
// Configure nginx
|
||||||
@ -219,7 +219,7 @@ const internalProxyHost = {
|
|||||||
.query()
|
.query()
|
||||||
.where('is_deleted', 0)
|
.where('is_deleted', 0)
|
||||||
.andWhere('id', data.id)
|
.andWhere('id', data.id)
|
||||||
.allowEager('[owner,access_list,certificate]')
|
.allowEager('[owner,access_list,access_list.[clients,items],certificate]')
|
||||||
.first();
|
.first();
|
||||||
|
|
||||||
if (access_data.permission_visibility !== 'all') {
|
if (access_data.permission_visibility !== 'all') {
|
||||||
|
@ -4,11 +4,21 @@ module.exports = function (req, res, next) {
|
|||||||
|
|
||||||
if (req.headers.origin) {
|
if (req.headers.origin) {
|
||||||
|
|
||||||
// very relaxed validation....
|
const originSchema = {
|
||||||
validator({
|
oneOf: [
|
||||||
|
{
|
||||||
type: 'string',
|
type: 'string',
|
||||||
pattern: '^[a-z\\-]+:\\/\\/(?:[\\w\\-\\.]+(:[0-9]+)?/?)?$'
|
pattern: '^[a-z\\-]+:\\/\\/(?:[\\w\\-\\.]+(:[0-9]+)?/?)?$'
|
||||||
}, req.headers.origin)
|
},
|
||||||
|
{
|
||||||
|
type: 'string',
|
||||||
|
pattern: '^[a-z\\-]+:\\/\\/(?:\\[([a-z0-9]{0,4}\\:?)+\\])?/?(:[0-9]+)?$'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
// very relaxed validation....
|
||||||
|
validator(originSchema, req.headers.origin)
|
||||||
.then(function () {
|
.then(function () {
|
||||||
res.set({
|
res.set({
|
||||||
'Access-Control-Allow-Origin': req.headers.origin,
|
'Access-Control-Allow-Origin': req.headers.origin,
|
||||||
|
@ -22,22 +22,6 @@ exports.up = function (knex/*, Promise*/) {
|
|||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
logger.info('[' + migrate_name + '] setting Table created');
|
logger.info('[' + migrate_name + '] setting Table created');
|
||||||
|
|
||||||
// TODO: add settings
|
|
||||||
let settingModel = require('../models/setting');
|
|
||||||
|
|
||||||
return settingModel
|
|
||||||
.query()
|
|
||||||
.insert({
|
|
||||||
id: 'default-site',
|
|
||||||
name: 'Default Site',
|
|
||||||
description: 'What to show when Nginx is hit with an unknown Host',
|
|
||||||
value: 'congratulations',
|
|
||||||
meta: {}
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
logger.info('[' + migrate_name + '] Default settings added');
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
53
backend/migrations/20200410143839_access_list_client.js
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
const migrate_name = 'access_list_client';
|
||||||
|
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.createTable('access_list_client', (table) => {
|
||||||
|
table.increments().primary();
|
||||||
|
table.dateTime('created_on').notNull();
|
||||||
|
table.dateTime('modified_on').notNull();
|
||||||
|
table.integer('access_list_id').notNull().unsigned();
|
||||||
|
table.string('address').notNull();
|
||||||
|
table.string('directive').notNull();
|
||||||
|
table.json('meta').notNull();
|
||||||
|
|
||||||
|
})
|
||||||
|
.then(function () {
|
||||||
|
logger.info('[' + migrate_name + '] access_list_client Table created');
|
||||||
|
|
||||||
|
return knex.schema.table('access_list', function (access_list) {
|
||||||
|
access_list.integer('satify_any').notNull().defaultTo(0);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
logger.info('[' + migrate_name + '] access_list Table altered');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undo Migrate
|
||||||
|
*
|
||||||
|
* @param {Object} knex
|
||||||
|
* @param {Promise} Promise
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
exports.down = function (knex/*, Promise*/) {
|
||||||
|
logger.info('[' + migrate_name + '] Migrating Down...');
|
||||||
|
|
||||||
|
return knex.schema.dropTable('access_list_client')
|
||||||
|
.then(() => {
|
||||||
|
logger.info('[' + migrate_name + '] access_list_client Table dropped');
|
||||||
|
});
|
||||||
|
};
|
34
backend/migrations/20200410143840_access_list_client_fix.js
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
const migrate_name = 'access_list_client_fix';
|
||||||
|
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('access_list', function (access_list) {
|
||||||
|
access_list.renameColumn('satify_any', 'satisfy_any');
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
logger.info('[' + migrate_name + '] access_list 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);
|
||||||
|
};
|
@ -5,13 +5,15 @@ const db = require('../db');
|
|||||||
const Model = require('objection').Model;
|
const Model = require('objection').Model;
|
||||||
const User = require('./user');
|
const User = require('./user');
|
||||||
const AccessListAuth = require('./access_list_auth');
|
const AccessListAuth = require('./access_list_auth');
|
||||||
|
const AccessListClient = require('./access_list_client');
|
||||||
|
const now = require('./now_helper');
|
||||||
|
|
||||||
Model.knex(db);
|
Model.knex(db);
|
||||||
|
|
||||||
class AccessList extends Model {
|
class AccessList extends Model {
|
||||||
$beforeInsert () {
|
$beforeInsert () {
|
||||||
this.created_on = Model.raw('NOW()');
|
this.created_on = now();
|
||||||
this.modified_on = Model.raw('NOW()');
|
this.modified_on = now();
|
||||||
|
|
||||||
// Default for meta
|
// Default for meta
|
||||||
if (typeof this.meta === 'undefined') {
|
if (typeof this.meta === 'undefined') {
|
||||||
@ -20,7 +22,7 @@ class AccessList extends Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$beforeUpdate () {
|
$beforeUpdate () {
|
||||||
this.modified_on = Model.raw('NOW()');
|
this.modified_on = now();
|
||||||
}
|
}
|
||||||
|
|
||||||
static get name () {
|
static get name () {
|
||||||
@ -62,6 +64,17 @@ class AccessList extends Model {
|
|||||||
qb.omit(['id', 'created_on', 'modified_on', 'access_list_id', 'meta']);
|
qb.omit(['id', 'created_on', 'modified_on', 'access_list_id', 'meta']);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
clients: {
|
||||||
|
relation: Model.HasManyRelation,
|
||||||
|
modelClass: AccessListClient,
|
||||||
|
join: {
|
||||||
|
from: 'access_list.id',
|
||||||
|
to: 'access_list_client.access_list_id'
|
||||||
|
},
|
||||||
|
modify: function (qb) {
|
||||||
|
qb.omit(['id', 'created_on', 'modified_on', 'access_list_id', 'meta']);
|
||||||
|
}
|
||||||
|
},
|
||||||
proxy_hosts: {
|
proxy_hosts: {
|
||||||
relation: Model.HasManyRelation,
|
relation: Model.HasManyRelation,
|
||||||
modelClass: ProxyHost,
|
modelClass: ProxyHost,
|
||||||
@ -76,6 +89,10 @@ class AccessList extends Model {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get satisfy() {
|
||||||
|
return this.satisfy_any ? 'satisfy any' : 'satisfy all';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = AccessList;
|
module.exports = AccessList;
|
||||||
|
@ -3,13 +3,14 @@
|
|||||||
|
|
||||||
const db = require('../db');
|
const db = require('../db');
|
||||||
const Model = require('objection').Model;
|
const Model = require('objection').Model;
|
||||||
|
const now = require('./now_helper');
|
||||||
|
|
||||||
Model.knex(db);
|
Model.knex(db);
|
||||||
|
|
||||||
class AccessListAuth extends Model {
|
class AccessListAuth extends Model {
|
||||||
$beforeInsert () {
|
$beforeInsert () {
|
||||||
this.created_on = Model.raw('NOW()');
|
this.created_on = now();
|
||||||
this.modified_on = Model.raw('NOW()');
|
this.modified_on = now();
|
||||||
|
|
||||||
// Default for meta
|
// Default for meta
|
||||||
if (typeof this.meta === 'undefined') {
|
if (typeof this.meta === 'undefined') {
|
||||||
@ -18,7 +19,7 @@ class AccessListAuth extends Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$beforeUpdate () {
|
$beforeUpdate () {
|
||||||
this.modified_on = Model.raw('NOW()');
|
this.modified_on = now();
|
||||||
}
|
}
|
||||||
|
|
||||||
static get name () {
|
static get name () {
|
||||||
|
59
backend/models/access_list_client.js
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
// Objection Docs:
|
||||||
|
// http://vincit.github.io/objection.js/
|
||||||
|
|
||||||
|
const db = require('../db');
|
||||||
|
const Model = require('objection').Model;
|
||||||
|
const now = require('./now_helper');
|
||||||
|
|
||||||
|
Model.knex(db);
|
||||||
|
|
||||||
|
class AccessListClient extends Model {
|
||||||
|
$beforeInsert () {
|
||||||
|
this.created_on = now();
|
||||||
|
this.modified_on = now();
|
||||||
|
|
||||||
|
// Default for meta
|
||||||
|
if (typeof this.meta === 'undefined') {
|
||||||
|
this.meta = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$beforeUpdate () {
|
||||||
|
this.modified_on = now();
|
||||||
|
}
|
||||||
|
|
||||||
|
static get name () {
|
||||||
|
return 'AccessListClient';
|
||||||
|
}
|
||||||
|
|
||||||
|
static get tableName () {
|
||||||
|
return 'access_list_client';
|
||||||
|
}
|
||||||
|
|
||||||
|
static get jsonAttributes () {
|
||||||
|
return ['meta'];
|
||||||
|
}
|
||||||
|
|
||||||
|
static get relationMappings () {
|
||||||
|
return {
|
||||||
|
access_list: {
|
||||||
|
relation: Model.HasOneRelation,
|
||||||
|
modelClass: require('./access_list'),
|
||||||
|
join: {
|
||||||
|
from: 'access_list_client.access_list_id',
|
||||||
|
to: 'access_list.id'
|
||||||
|
},
|
||||||
|
modify: function (qb) {
|
||||||
|
qb.where('access_list.is_deleted', 0);
|
||||||
|
qb.omit(['created_on', 'modified_on', 'is_deleted', 'access_list_id']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
get rule() {
|
||||||
|
return `${this.directive} ${this.address}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = AccessListClient;
|
@ -4,13 +4,14 @@
|
|||||||
const db = require('../db');
|
const db = require('../db');
|
||||||
const Model = require('objection').Model;
|
const Model = require('objection').Model;
|
||||||
const User = require('./user');
|
const User = require('./user');
|
||||||
|
const now = require('./now_helper');
|
||||||
|
|
||||||
Model.knex(db);
|
Model.knex(db);
|
||||||
|
|
||||||
class AuditLog extends Model {
|
class AuditLog extends Model {
|
||||||
$beforeInsert () {
|
$beforeInsert () {
|
||||||
this.created_on = Model.raw('NOW()');
|
this.created_on = now();
|
||||||
this.modified_on = Model.raw('NOW()');
|
this.modified_on = now();
|
||||||
|
|
||||||
// Default for meta
|
// Default for meta
|
||||||
if (typeof this.meta === 'undefined') {
|
if (typeof this.meta === 'undefined') {
|
||||||
@ -19,7 +20,7 @@ class AuditLog extends Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$beforeUpdate () {
|
$beforeUpdate () {
|
||||||
this.modified_on = Model.raw('NOW()');
|
this.modified_on = now();
|
||||||
}
|
}
|
||||||
|
|
||||||
static get name () {
|
static get name () {
|
||||||
|
@ -5,6 +5,7 @@ const bcrypt = require('bcrypt');
|
|||||||
const db = require('../db');
|
const db = require('../db');
|
||||||
const Model = require('objection').Model;
|
const Model = require('objection').Model;
|
||||||
const User = require('./user');
|
const User = require('./user');
|
||||||
|
const now = require('./now_helper');
|
||||||
|
|
||||||
Model.knex(db);
|
Model.knex(db);
|
||||||
|
|
||||||
@ -24,8 +25,8 @@ function encryptPassword () {
|
|||||||
|
|
||||||
class Auth extends Model {
|
class Auth extends Model {
|
||||||
$beforeInsert (queryContext) {
|
$beforeInsert (queryContext) {
|
||||||
this.created_on = Model.raw('NOW()');
|
this.created_on = now();
|
||||||
this.modified_on = Model.raw('NOW()');
|
this.modified_on = now();
|
||||||
|
|
||||||
// Default for meta
|
// Default for meta
|
||||||
if (typeof this.meta === 'undefined') {
|
if (typeof this.meta === 'undefined') {
|
||||||
@ -36,7 +37,7 @@ class Auth extends Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$beforeUpdate (queryContext) {
|
$beforeUpdate (queryContext) {
|
||||||
this.modified_on = Model.raw('NOW()');
|
this.modified_on = now();
|
||||||
return encryptPassword.apply(this, queryContext);
|
return encryptPassword.apply(this, queryContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,17 +4,18 @@
|
|||||||
const db = require('../db');
|
const db = require('../db');
|
||||||
const Model = require('objection').Model;
|
const Model = require('objection').Model;
|
||||||
const User = require('./user');
|
const User = require('./user');
|
||||||
|
const now = require('./now_helper');
|
||||||
|
|
||||||
Model.knex(db);
|
Model.knex(db);
|
||||||
|
|
||||||
class Certificate extends Model {
|
class Certificate extends Model {
|
||||||
$beforeInsert () {
|
$beforeInsert () {
|
||||||
this.created_on = Model.raw('NOW()');
|
this.created_on = now();
|
||||||
this.modified_on = Model.raw('NOW()');
|
this.modified_on = now();
|
||||||
|
|
||||||
// Default for expires_on
|
// Default for expires_on
|
||||||
if (typeof this.expires_on === 'undefined') {
|
if (typeof this.expires_on === 'undefined') {
|
||||||
this.expires_on = Model.raw('NOW()');
|
this.expires_on = now();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default for domain_names
|
// Default for domain_names
|
||||||
@ -31,7 +32,7 @@ class Certificate extends Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$beforeUpdate () {
|
$beforeUpdate () {
|
||||||
this.modified_on = Model.raw('NOW()');
|
this.modified_on = now();
|
||||||
|
|
||||||
// Sort domain_names
|
// Sort domain_names
|
||||||
if (typeof this.domain_names !== 'undefined') {
|
if (typeof this.domain_names !== 'undefined') {
|
||||||
|
@ -5,13 +5,14 @@ const db = require('../db');
|
|||||||
const Model = require('objection').Model;
|
const Model = require('objection').Model;
|
||||||
const User = require('./user');
|
const User = require('./user');
|
||||||
const Certificate = require('./certificate');
|
const Certificate = require('./certificate');
|
||||||
|
const now = require('./now_helper');
|
||||||
|
|
||||||
Model.knex(db);
|
Model.knex(db);
|
||||||
|
|
||||||
class DeadHost extends Model {
|
class DeadHost extends Model {
|
||||||
$beforeInsert () {
|
$beforeInsert () {
|
||||||
this.created_on = Model.raw('NOW()');
|
this.created_on = now();
|
||||||
this.modified_on = Model.raw('NOW()');
|
this.modified_on = now();
|
||||||
|
|
||||||
// Default for domain_names
|
// Default for domain_names
|
||||||
if (typeof this.domain_names === 'undefined') {
|
if (typeof this.domain_names === 'undefined') {
|
||||||
@ -27,7 +28,7 @@ class DeadHost extends Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$beforeUpdate () {
|
$beforeUpdate () {
|
||||||
this.modified_on = Model.raw('NOW()');
|
this.modified_on = now();
|
||||||
|
|
||||||
// Sort domain_names
|
// Sort domain_names
|
||||||
if (typeof this.domain_names !== 'undefined') {
|
if (typeof this.domain_names !== 'undefined') {
|
||||||
|
13
backend/models/now_helper.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
const db = require('../db');
|
||||||
|
const config = require('config');
|
||||||
|
const Model = require('objection').Model;
|
||||||
|
|
||||||
|
Model.knex(db);
|
||||||
|
|
||||||
|
module.exports = function () {
|
||||||
|
if (config.database.knex && config.database.knex.client === 'sqlite3') {
|
||||||
|
return Model.raw('date(\'now\')');
|
||||||
|
} else {
|
||||||
|
return Model.raw('NOW()');
|
||||||
|
}
|
||||||
|
};
|
@ -6,13 +6,14 @@ const Model = require('objection').Model;
|
|||||||
const User = require('./user');
|
const User = require('./user');
|
||||||
const AccessList = require('./access_list');
|
const AccessList = require('./access_list');
|
||||||
const Certificate = require('./certificate');
|
const Certificate = require('./certificate');
|
||||||
|
const now = require('./now_helper');
|
||||||
|
|
||||||
Model.knex(db);
|
Model.knex(db);
|
||||||
|
|
||||||
class ProxyHost extends Model {
|
class ProxyHost extends Model {
|
||||||
$beforeInsert () {
|
$beforeInsert () {
|
||||||
this.created_on = Model.raw('NOW()');
|
this.created_on = now();
|
||||||
this.modified_on = Model.raw('NOW()');
|
this.modified_on = now();
|
||||||
|
|
||||||
// Default for domain_names
|
// Default for domain_names
|
||||||
if (typeof this.domain_names === 'undefined') {
|
if (typeof this.domain_names === 'undefined') {
|
||||||
@ -28,7 +29,7 @@ class ProxyHost extends Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$beforeUpdate () {
|
$beforeUpdate () {
|
||||||
this.modified_on = Model.raw('NOW()');
|
this.modified_on = now();
|
||||||
|
|
||||||
// Sort domain_names
|
// Sort domain_names
|
||||||
if (typeof this.domain_names !== 'undefined') {
|
if (typeof this.domain_names !== 'undefined') {
|
||||||
|
@ -5,13 +5,14 @@ const db = require('../db');
|
|||||||
const Model = require('objection').Model;
|
const Model = require('objection').Model;
|
||||||
const User = require('./user');
|
const User = require('./user');
|
||||||
const Certificate = require('./certificate');
|
const Certificate = require('./certificate');
|
||||||
|
const now = require('./now_helper');
|
||||||
|
|
||||||
Model.knex(db);
|
Model.knex(db);
|
||||||
|
|
||||||
class RedirectionHost extends Model {
|
class RedirectionHost extends Model {
|
||||||
$beforeInsert () {
|
$beforeInsert () {
|
||||||
this.created_on = Model.raw('NOW()');
|
this.created_on = now();
|
||||||
this.modified_on = Model.raw('NOW()');
|
this.modified_on = now();
|
||||||
|
|
||||||
// Default for domain_names
|
// Default for domain_names
|
||||||
if (typeof this.domain_names === 'undefined') {
|
if (typeof this.domain_names === 'undefined') {
|
||||||
@ -27,7 +28,7 @@ class RedirectionHost extends Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$beforeUpdate () {
|
$beforeUpdate () {
|
||||||
this.modified_on = Model.raw('NOW()');
|
this.modified_on = now();
|
||||||
|
|
||||||
// Sort domain_names
|
// Sort domain_names
|
||||||
if (typeof this.domain_names !== 'undefined') {
|
if (typeof this.domain_names !== 'undefined') {
|
||||||
|
@ -4,13 +4,14 @@
|
|||||||
const db = require('../db');
|
const db = require('../db');
|
||||||
const Model = require('objection').Model;
|
const Model = require('objection').Model;
|
||||||
const User = require('./user');
|
const User = require('./user');
|
||||||
|
const now = require('./now_helper');
|
||||||
|
|
||||||
Model.knex(db);
|
Model.knex(db);
|
||||||
|
|
||||||
class Stream extends Model {
|
class Stream extends Model {
|
||||||
$beforeInsert () {
|
$beforeInsert () {
|
||||||
this.created_on = Model.raw('NOW()');
|
this.created_on = now();
|
||||||
this.modified_on = Model.raw('NOW()');
|
this.modified_on = now();
|
||||||
|
|
||||||
// Default for meta
|
// Default for meta
|
||||||
if (typeof this.meta === 'undefined') {
|
if (typeof this.meta === 'undefined') {
|
||||||
@ -19,7 +20,7 @@ class Stream extends Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$beforeUpdate () {
|
$beforeUpdate () {
|
||||||
this.modified_on = Model.raw('NOW()');
|
this.modified_on = now();
|
||||||
}
|
}
|
||||||
|
|
||||||
static get name () {
|
static get name () {
|
||||||
|
@ -4,13 +4,14 @@
|
|||||||
const db = require('../db');
|
const db = require('../db');
|
||||||
const Model = require('objection').Model;
|
const Model = require('objection').Model;
|
||||||
const UserPermission = require('./user_permission');
|
const UserPermission = require('./user_permission');
|
||||||
|
const now = require('./now_helper');
|
||||||
|
|
||||||
Model.knex(db);
|
Model.knex(db);
|
||||||
|
|
||||||
class User extends Model {
|
class User extends Model {
|
||||||
$beforeInsert () {
|
$beforeInsert () {
|
||||||
this.created_on = Model.raw('NOW()');
|
this.created_on = now();
|
||||||
this.modified_on = Model.raw('NOW()');
|
this.modified_on = now();
|
||||||
|
|
||||||
// Default for roles
|
// Default for roles
|
||||||
if (typeof this.roles === 'undefined') {
|
if (typeof this.roles === 'undefined') {
|
||||||
@ -19,7 +20,7 @@ class User extends Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$beforeUpdate () {
|
$beforeUpdate () {
|
||||||
this.modified_on = Model.raw('NOW()');
|
this.modified_on = now();
|
||||||
}
|
}
|
||||||
|
|
||||||
static get name () {
|
static get name () {
|
||||||
|
@ -3,17 +3,18 @@
|
|||||||
|
|
||||||
const db = require('../db');
|
const db = require('../db');
|
||||||
const Model = require('objection').Model;
|
const Model = require('objection').Model;
|
||||||
|
const now = require('./now_helper');
|
||||||
|
|
||||||
Model.knex(db);
|
Model.knex(db);
|
||||||
|
|
||||||
class UserPermission extends Model {
|
class UserPermission extends Model {
|
||||||
$beforeInsert () {
|
$beforeInsert () {
|
||||||
this.created_on = Model.raw('NOW()');
|
this.created_on = now();
|
||||||
this.modified_on = Model.raw('NOW()');
|
this.modified_on = now();
|
||||||
}
|
}
|
||||||
|
|
||||||
$beforeUpdate () {
|
$beforeUpdate () {
|
||||||
this.modified_on = Model.raw('NOW()');
|
this.modified_on = now();
|
||||||
}
|
}
|
||||||
|
|
||||||
static get name () {
|
static get name () {
|
||||||
|
@ -1,33 +1,35 @@
|
|||||||
{
|
{
|
||||||
"name": "nginx-proxy-manager",
|
"name": "nginx-proxy-manager",
|
||||||
"version": "2.1.1",
|
"version": "0.0.0",
|
||||||
"description": "A beautiful interface for creating Nginx endpoints",
|
"description": "A beautiful interface for creating Nginx endpoints",
|
||||||
"main": "js/index.js",
|
"main": "js/index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ajv": "^6.11.0",
|
"ajv": "^6.12.0",
|
||||||
"batchflow": "^0.4.0",
|
"batchflow": "^0.4.0",
|
||||||
"bcrypt": "^3.0.8",
|
"bcrypt": "^4.0.1",
|
||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"compression": "^1.7.4",
|
"compression": "^1.7.4",
|
||||||
"config": "^3.2.5",
|
"config": "^3.3.1",
|
||||||
"diskdb": "^0.1.17",
|
"diskdb": "^0.1.17",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"express-fileupload": "^1.1.6",
|
"express-fileupload": "^1.1.9",
|
||||||
"gravatar": "^1.8.0",
|
"gravatar": "^1.8.0",
|
||||||
"html-entities": "^1.2.1",
|
"html-entities": "^1.2.1",
|
||||||
"json-schema-ref-parser": "^7.1.3",
|
"json-schema-ref-parser": "^8.0.0",
|
||||||
"jsonwebtoken": "^8.5.1",
|
"jsonwebtoken": "^8.5.1",
|
||||||
"knex": "^0.20.10",
|
"knex": "^0.20.13",
|
||||||
"liquidjs": "^9.7.1",
|
"liquidjs": "^9.11.10",
|
||||||
"lodash": "^4.17.15",
|
"lodash": "^4.17.19",
|
||||||
"moment": "^2.24.0",
|
"moment": "^2.24.0",
|
||||||
"mysql": "^2.18.1",
|
"mysql": "^2.18.1",
|
||||||
"node-rsa": "^1.0.7",
|
"node-rsa": "^1.0.8",
|
||||||
"nodemon": "^2.0.2",
|
"nodemon": "^2.0.2",
|
||||||
"objection": "^2.1.3",
|
"objection": "^2.1.3",
|
||||||
"path": "^0.12.7",
|
"path": "^0.12.7",
|
||||||
|
"pg": "^7.12.1",
|
||||||
"restler": "^3.4.0",
|
"restler": "^3.4.0",
|
||||||
"signale": "^1.4.0",
|
"signale": "^1.4.0",
|
||||||
|
"sqlite3": "^4.1.1",
|
||||||
"temp-write": "^4.0.0",
|
"temp-write": "^4.0.0",
|
||||||
"unix-timestamp": "^0.2.0"
|
"unix-timestamp": "^0.2.0"
|
||||||
},
|
},
|
||||||
@ -40,6 +42,6 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint": "^6.8.0",
|
"eslint": "^6.8.0",
|
||||||
"eslint-plugin-align-assignments": "^1.1.2",
|
"eslint-plugin-align-assignments": "^1.1.2",
|
||||||
"prettier": "^1.19.1"
|
"prettier": "^2.0.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,29 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Name of the Access List"
|
"description": "Name of the Access List"
|
||||||
},
|
},
|
||||||
|
"directive": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["allow", "deny"]
|
||||||
|
},
|
||||||
|
"address": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^([0-9]{1,3}\\.){3}[0-9]{1,3}(/([0-9]|[1-2][0-9]|3[0-2]))?$"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*(/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8]))?$"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^all$"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"satisfy_any": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"meta": {
|
"meta": {
|
||||||
"type": "object"
|
"type": "object"
|
||||||
}
|
}
|
||||||
@ -71,16 +94,17 @@
|
|||||||
"schema": {
|
"schema": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"required": [
|
"required": ["name"],
|
||||||
"name"
|
|
||||||
],
|
|
||||||
"properties": {
|
"properties": {
|
||||||
"name": {
|
"name": {
|
||||||
"$ref": "#/definitions/name"
|
"$ref": "#/definitions/name"
|
||||||
},
|
},
|
||||||
|
"satisfy_any": {
|
||||||
|
"$ref": "#/definitions/satisfy_any"
|
||||||
|
},
|
||||||
"items": {
|
"items": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"minItems": 1,
|
"minItems": 0,
|
||||||
"items": {
|
"items": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
@ -96,6 +120,22 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"clients": {
|
||||||
|
"type": "array",
|
||||||
|
"minItems": 0,
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"address": {
|
||||||
|
"$ref": "#/definitions/address"
|
||||||
|
},
|
||||||
|
"directive": {
|
||||||
|
"$ref": "#/definitions/directive"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"meta": {
|
"meta": {
|
||||||
"$ref": "#/definitions/meta"
|
"$ref": "#/definitions/meta"
|
||||||
}
|
}
|
||||||
@ -124,9 +164,12 @@
|
|||||||
"name": {
|
"name": {
|
||||||
"$ref": "#/definitions/name"
|
"$ref": "#/definitions/name"
|
||||||
},
|
},
|
||||||
|
"satisfy_any": {
|
||||||
|
"$ref": "#/definitions/satisfy_any"
|
||||||
|
},
|
||||||
"items": {
|
"items": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"minItems": 1,
|
"minItems": 0,
|
||||||
"items": {
|
"items": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
@ -141,6 +184,22 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"clients": {
|
||||||
|
"type": "array",
|
||||||
|
"minItems": 0,
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"address": {
|
||||||
|
"$ref": "#/definitions/address"
|
||||||
|
},
|
||||||
|
"directive": {
|
||||||
|
"$ref": "#/definitions/directive"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
"forward_host": {
|
"forward_host": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"minLength": 1,
|
"minLength": 1,
|
||||||
"maxLength": 50
|
"maxLength": 255
|
||||||
},
|
},
|
||||||
"forward_port": {
|
"forward_port": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
|
@ -5,9 +5,15 @@ const logger = require('./logger').setup;
|
|||||||
const userModel = require('./models/user');
|
const userModel = require('./models/user');
|
||||||
const userPermissionModel = require('./models/user_permission');
|
const userPermissionModel = require('./models/user_permission');
|
||||||
const authModel = require('./models/auth');
|
const authModel = require('./models/auth');
|
||||||
|
const settingModel = require('./models/setting');
|
||||||
const debug_mode = process.env.NODE_ENV !== 'production' || !!process.env.DEBUG;
|
const debug_mode = process.env.NODE_ENV !== 'production' || !!process.env.DEBUG;
|
||||||
|
|
||||||
module.exports = function () {
|
/**
|
||||||
|
* Creates a new JWT RSA Keypair if not alread set on the config
|
||||||
|
*
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
const setupJwt = () => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
// Now go and check if the jwt gpg keys have been created and if not, create them
|
// Now go and check if the jwt gpg keys have been created and if not, create them
|
||||||
if (!config.has('jwt') || !config.has('jwt.key') || !config.has('jwt.pub')) {
|
if (!config.has('jwt') || !config.has('jwt.key') || !config.has('jwt.pub')) {
|
||||||
@ -27,12 +33,12 @@ module.exports = function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Now create the keys and save them in the config.
|
// Now create the keys and save them in the config.
|
||||||
let key = new NodeRSA({b: 2048});
|
let key = new NodeRSA({ b: 2048 });
|
||||||
key.generateKeyPair();
|
key.generateKeyPair();
|
||||||
|
|
||||||
config_data.jwt = {
|
config_data.jwt = {
|
||||||
key: key.exportKey('private').toString(),
|
key: key.exportKey('private').toString(),
|
||||||
pub: key.exportKey('public').toString()
|
pub: key.exportKey('public').toString(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Write config
|
// Write config
|
||||||
@ -47,7 +53,6 @@ module.exports = function () {
|
|||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// JWT key pair exists
|
// JWT key pair exists
|
||||||
if (debug_mode) {
|
if (debug_mode) {
|
||||||
@ -56,14 +61,20 @@ module.exports = function () {
|
|||||||
|
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
.then(() => {
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a default admin users if one doesn't already exist in the database
|
||||||
|
*
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
const setupDefaultUser = () => {
|
||||||
return userModel
|
return userModel
|
||||||
.query()
|
.query()
|
||||||
.select(userModel.raw('COUNT(`id`) as `count`'))
|
.select(userModel.raw('COUNT(`id`) as `count`'))
|
||||||
.where('is_deleted', 0)
|
.where('is_deleted', 0)
|
||||||
.first();
|
.first()
|
||||||
})
|
|
||||||
.then((row) => {
|
.then((row) => {
|
||||||
if (!row.count) {
|
if (!row.count) {
|
||||||
// Create a new user and set password
|
// Create a new user and set password
|
||||||
@ -75,7 +86,7 @@ module.exports = function () {
|
|||||||
name: 'Administrator',
|
name: 'Administrator',
|
||||||
nickname: 'Admin',
|
nickname: 'Admin',
|
||||||
avatar: '',
|
avatar: '',
|
||||||
roles: ['admin']
|
roles: ['admin'],
|
||||||
};
|
};
|
||||||
|
|
||||||
return userModel
|
return userModel
|
||||||
@ -88,12 +99,10 @@ module.exports = function () {
|
|||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
type: 'password',
|
type: 'password',
|
||||||
secret: 'changeme',
|
secret: 'changeme',
|
||||||
meta: {}
|
meta: {},
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return userPermissionModel
|
return userPermissionModel.query().insert({
|
||||||
.query()
|
|
||||||
.insert({
|
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
visibility: 'all',
|
visibility: 'all',
|
||||||
proxy_hosts: 'manage',
|
proxy_hosts: 'manage',
|
||||||
@ -101,15 +110,53 @@ module.exports = function () {
|
|||||||
dead_hosts: 'manage',
|
dead_hosts: 'manage',
|
||||||
streams: 'manage',
|
streams: 'manage',
|
||||||
access_lists: 'manage',
|
access_lists: 'manage',
|
||||||
certificates: 'manage'
|
certificates: 'manage',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
logger.info('Initial setup completed');
|
logger.info('Initial admin setup completed');
|
||||||
});
|
});
|
||||||
} else if (debug_mode) {
|
} else if (debug_mode) {
|
||||||
logger.debug('Admin user setup not required');
|
logger.debug('Admin user setup not required');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates default settings if they don't already exist in the database
|
||||||
|
*
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
const setupDefaultSettings = () => {
|
||||||
|
return settingModel
|
||||||
|
.query()
|
||||||
|
.select(settingModel.raw('COUNT(`id`) as `count`'))
|
||||||
|
.where({id: 'default-site'})
|
||||||
|
.first()
|
||||||
|
.then((row) => {
|
||||||
|
if (!row.count) {
|
||||||
|
settingModel
|
||||||
|
.query()
|
||||||
|
.insert({
|
||||||
|
id: 'default-site',
|
||||||
|
name: 'Default Site',
|
||||||
|
description: 'What to show when Nginx is hit with an unknown Host',
|
||||||
|
value: 'congratulations',
|
||||||
|
meta: {},
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
logger.info('Default settings added');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (debug_mode) {
|
||||||
|
logger.debug('Default setting setup not required');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = function () {
|
||||||
|
return setupJwt()
|
||||||
|
.then(setupDefaultUser)
|
||||||
|
.then(setupDefaultSettings);
|
||||||
|
};
|
||||||
|
@ -1,5 +1,15 @@
|
|||||||
listen 80;
|
listen 80;
|
||||||
|
{% if ipv6 -%}
|
||||||
|
listen [::]:80;
|
||||||
|
{% else -%}
|
||||||
|
#listen [::]:80;
|
||||||
|
{% endif %}
|
||||||
{% if certificate -%}
|
{% if certificate -%}
|
||||||
listen 443 ssl{% if http2_support %} http2{% endif %};
|
listen 443 ssl{% if http2_support %} http2{% endif %};
|
||||||
|
{% if ipv6 -%}
|
||||||
|
listen [::]:443;
|
||||||
|
{% else -%}
|
||||||
|
#listen [::]:443;
|
||||||
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
server_name {{ domain_names | join: " " }};
|
server_name {{ domain_names | join: " " }};
|
||||||
|
@ -2,6 +2,10 @@
|
|||||||
|
|
||||||
server {
|
server {
|
||||||
listen 80;
|
listen 80;
|
||||||
|
{% if ipv6 -%}
|
||||||
|
listen [::]:80;
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
server_name {{ domain_names | join: " " }};
|
server_name {{ domain_names | join: " " }};
|
||||||
|
|
||||||
access_log /data/logs/letsencrypt-requests.log standard;
|
access_log /data/logs/letsencrypt-requests.log standard;
|
||||||
|
@ -21,18 +21,30 @@ server {
|
|||||||
{% if use_default_location %}
|
{% if use_default_location %}
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
{%- if access_list_id > 0 -%}
|
|
||||||
# Access List
|
{% if access_list_id > 0 %}
|
||||||
|
{% if access_list.items.length > 0 %}
|
||||||
|
# Authorization
|
||||||
auth_basic "Authorization required";
|
auth_basic "Authorization required";
|
||||||
auth_basic_user_file /data/access/{{ access_list_id }};
|
auth_basic_user_file /data/access/{{ access_list_id }};
|
||||||
{%- endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
# Access Rules
|
||||||
|
{% for client in access_list.clients %}
|
||||||
|
{{- client.rule -}};
|
||||||
|
{% endfor %}deny all;
|
||||||
|
|
||||||
|
# Access checks must...
|
||||||
|
{{ access_list.satisfy }};
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% include "_forced_ssl.conf" %}
|
{% include "_forced_ssl.conf" %}
|
||||||
{% include "_hsts.conf" %}
|
{% include "_hsts.conf" %}
|
||||||
|
|
||||||
{% if allow_websocket_upgrade == 1 or allow_websocket_upgrade == true %}
|
{% if allow_websocket_upgrade == 1 or allow_websocket_upgrade == true %}
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
proxy_set_header Connection "upgrade";
|
proxy_set_header Connection $http_connection;
|
||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
@ -6,6 +6,12 @@
|
|||||||
{% if tcp_forwarding == 1 or tcp_forwarding == true -%}
|
{% if tcp_forwarding == 1 or tcp_forwarding == true -%}
|
||||||
server {
|
server {
|
||||||
listen {{ incoming_port }};
|
listen {{ incoming_port }};
|
||||||
|
{% if ipv6 -%}
|
||||||
|
listen [::]:{{ incoming_port }};
|
||||||
|
{% else -%}
|
||||||
|
#listen [::]:{{ incoming_port }};
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
proxy_pass {{ forward_ip }}:{{ forwarding_port }};
|
proxy_pass {{ forward_ip }}:{{ forwarding_port }};
|
||||||
|
|
||||||
# Custom
|
# Custom
|
||||||
@ -16,6 +22,11 @@ server {
|
|||||||
{% if udp_forwarding == 1 or udp_forwarding == true %}
|
{% if udp_forwarding == 1 or udp_forwarding == true %}
|
||||||
server {
|
server {
|
||||||
listen {{ incoming_port }} udp;
|
listen {{ incoming_port }} udp;
|
||||||
|
{% if ipv6 -%}
|
||||||
|
listen [::]:{{ incoming_port }} udp;
|
||||||
|
{% else -%}
|
||||||
|
#listen [::]:{{ incoming_port }} udp;
|
||||||
|
{% endif %}
|
||||||
proxy_pass {{ forward_ip }}:{{ forwarding_port }};
|
proxy_pass {{ forward_ip }}:{{ forwarding_port }};
|
||||||
|
|
||||||
# Custom
|
# Custom
|
||||||
|
1515
backend/yarn.lock
@ -1,17 +0,0 @@
|
|||||||
## Advanced Nginx Configuration
|
|
||||||
|
|
||||||
If you are a more advanced user, you might be itching for extra Nginx customizability.
|
|
||||||
|
|
||||||
NPM has the ability to include different custom configuration snippets in different places.
|
|
||||||
|
|
||||||
You can add your custom configuration snippet files at `/data/nginx/custom` as follow:
|
|
||||||
|
|
||||||
`/data/nginx/custom/root.conf`: Included at the very end of nginx.conf
|
|
||||||
`/data/nginx/custom/http.conf`: Included at the end of the main http block
|
|
||||||
`/data/nginx/custom/server_proxy.conf`: Included at the end of every proxy server block
|
|
||||||
`/data/nginx/custom/server_redirect.conf`: Included at the end of every redirection server block
|
|
||||||
`/data/nginx/custom/server_stream.conf`: Included at the end of every stream server block
|
|
||||||
`/data/nginx/custom/server_stream_tcp.conf`: Included at the end of every TCP stream server block
|
|
||||||
`/data/nginx/custom/server_stream_udp.conf`: Included at the end of every UDP stream server block
|
|
||||||
|
|
||||||
Every file is optional.
|
|
@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"database": {
|
|
||||||
"engine": "mysql",
|
|
||||||
"host": "db",
|
|
||||||
"name": "npm",
|
|
||||||
"user": "npm",
|
|
||||||
"password": "npm",
|
|
||||||
"port": 3306
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
version: "3"
|
|
||||||
services:
|
|
||||||
app:
|
|
||||||
image: jc21/nginx-proxy-manager:latest
|
|
||||||
restart: always
|
|
||||||
ports:
|
|
||||||
- 80:80
|
|
||||||
- 81:81
|
|
||||||
- 443:443
|
|
||||||
volumes:
|
|
||||||
- ./config.json:/app/config/production.json
|
|
||||||
- ./data:/data
|
|
||||||
- ./letsencrypt:/etc/letsencrypt
|
|
||||||
depends_on:
|
|
||||||
- db
|
|
||||||
environment:
|
|
||||||
# if you want pretty colors in your docker logs:
|
|
||||||
- FORCE_COLOR=1
|
|
||||||
db:
|
|
||||||
image: mariadb:latest
|
|
||||||
restart: always
|
|
||||||
environment:
|
|
||||||
MYSQL_ROOT_PASSWORD: "npm"
|
|
||||||
MYSQL_DATABASE: "npm"
|
|
||||||
MYSQL_USER: "npm"
|
|
||||||
MYSQL_PASSWORD: "npm"
|
|
||||||
volumes:
|
|
||||||
- ./data/mysql:/var/lib/mysql
|
|
@ -16,7 +16,6 @@ ENV S6_FIX_ATTRS_HIDDEN=1
|
|||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
|
|
||||||
RUN echo "fs.file-max = 65535" > /etc/sysctl.conf \
|
RUN echo "fs.file-max = 65535" > /etc/sysctl.conf \
|
||||||
&& rm -rf /etc/nginx \
|
|
||||||
&& apk update \
|
&& apk update \
|
||||||
&& apk add python2 certbot jq \
|
&& apk add python2 certbot jq \
|
||||||
&& rm -rf /var/cache/apk/*
|
&& rm -rf /var/cache/apk/*
|
||||||
@ -30,7 +29,6 @@ RUN /tmp/install-s6 "${TARGETPLATFORM}" && rm -f /tmp/install-s6
|
|||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
EXPOSE 81
|
EXPOSE 81
|
||||||
EXPOSE 443
|
EXPOSE 443
|
||||||
EXPOSE 9876
|
|
||||||
|
|
||||||
COPY docker/rootfs /
|
COPY docker/rootfs /
|
||||||
ADD backend /app
|
ADD backend /app
|
||||||
|
@ -6,7 +6,6 @@ ENV SUPPRESS_NO_CONFIG_WARNING=1
|
|||||||
ENV S6_FIX_ATTRS_HIDDEN=1
|
ENV S6_FIX_ATTRS_HIDDEN=1
|
||||||
|
|
||||||
RUN echo "fs.file-max = 65535" > /etc/sysctl.conf \
|
RUN echo "fs.file-max = 65535" > /etc/sysctl.conf \
|
||||||
&& rm -rf /etc/nginx \
|
|
||||||
&& apk update \
|
&& apk update \
|
||||||
&& apk add python2 certbot jq \
|
&& apk add python2 certbot jq \
|
||||||
&& rm -rf /var/cache/apk/*
|
&& rm -rf /var/cache/apk/*
|
||||||
|
@ -2,14 +2,14 @@
|
|||||||
version: "3"
|
version: "3"
|
||||||
services:
|
services:
|
||||||
|
|
||||||
fullstack:
|
fullstack-mysql:
|
||||||
image: ${IMAGE}:ci-${BUILD_NUMBER}
|
image: ${IMAGE}:ci-${BUILD_NUMBER}
|
||||||
environment:
|
environment:
|
||||||
- NODE_ENV=development
|
- NODE_ENV=development
|
||||||
- FORCE_COLOR=1
|
- FORCE_COLOR=1
|
||||||
volumes:
|
volumes:
|
||||||
- npm_data:/data
|
- npm_data:/data
|
||||||
- ../.jenkins/config.json:/app/config/production.json
|
- ../.jenkins/config-mysql.json:/app/config/development.json
|
||||||
expose:
|
expose:
|
||||||
- 81
|
- 81
|
||||||
- 80
|
- 80
|
||||||
@ -17,6 +17,19 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
- db
|
- db
|
||||||
|
|
||||||
|
fullstack-sqlite:
|
||||||
|
image: ${IMAGE}:ci-${BUILD_NUMBER}
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=development
|
||||||
|
- FORCE_COLOR=1
|
||||||
|
volumes:
|
||||||
|
- npm_data:/data
|
||||||
|
- ../.jenkins/config-sqlite.json:/app/config/development.json
|
||||||
|
expose:
|
||||||
|
- 81
|
||||||
|
- 80
|
||||||
|
- 443
|
||||||
|
|
||||||
db:
|
db:
|
||||||
image: jc21/mariadb-aria
|
image: jc21/mariadb-aria
|
||||||
environment:
|
environment:
|
||||||
@ -27,13 +40,24 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- db_data:/var/lib/mysql
|
- db_data:/var/lib/mysql
|
||||||
|
|
||||||
cypress:
|
cypress-mysql:
|
||||||
image: ${IMAGE}-cypress:ci-${BUILD_NUMBER}
|
image: ${IMAGE}-cypress:ci-${BUILD_NUMBER}
|
||||||
build:
|
build:
|
||||||
context: ../
|
context: ../
|
||||||
dockerfile: test/cypress/Dockerfile
|
dockerfile: test/cypress/Dockerfile
|
||||||
environment:
|
environment:
|
||||||
CYPRESS_baseUrl: "http://fullstack:81"
|
CYPRESS_baseUrl: "http://fullstack-mysql:81"
|
||||||
|
volumes:
|
||||||
|
- cypress-logs:/results
|
||||||
|
command: cypress run --browser chrome --config-file=${CYPRESS_CONFIG:-cypress/config/ci.json}
|
||||||
|
|
||||||
|
cypress-sqlite:
|
||||||
|
image: ${IMAGE}-cypress:ci-${BUILD_NUMBER}
|
||||||
|
build:
|
||||||
|
context: ../
|
||||||
|
dockerfile: test/cypress/Dockerfile
|
||||||
|
environment:
|
||||||
|
CYPRESS_baseUrl: "http://fullstack-sqlite:81"
|
||||||
volumes:
|
volumes:
|
||||||
- cypress-logs:/results
|
- cypress-logs:/results
|
||||||
command: cypress run --browser chrome --config-file=${CYPRESS_CONFIG:-cypress/config/ci.json}
|
command: cypress run --browser chrome --config-file=${CYPRESS_CONFIG:-cypress/config/ci.json}
|
||||||
|
@ -15,6 +15,7 @@ services:
|
|||||||
- NODE_ENV=development
|
- NODE_ENV=development
|
||||||
- FORCE_COLOR=1
|
- FORCE_COLOR=1
|
||||||
- DEVELOPMENT=true
|
- DEVELOPMENT=true
|
||||||
|
#- DISABLE_IPV6=true
|
||||||
volumes:
|
volumes:
|
||||||
- npm_data:/data
|
- npm_data:/data
|
||||||
- le_data:/etc/letsencrypt
|
- le_data:/etc/letsencrypt
|
||||||
|
46
docker/rootfs/bin/handle-ipv6-setting
Executable file
@ -0,0 +1,46 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# This command reads the `DISABLE_IPV6` env var and will either enable
|
||||||
|
# or disable ipv6 in all nginx configs based on this setting.
|
||||||
|
|
||||||
|
# Lowercase
|
||||||
|
DISABLE_IPV6=$(echo "${DISABLE_IPV6:-}" | tr '[:upper:]' '[:lower:]')
|
||||||
|
|
||||||
|
CYAN='\E[1;36m'
|
||||||
|
BLUE='\E[1;34m'
|
||||||
|
YELLOW='\E[1;33m'
|
||||||
|
RED='\E[1;31m'
|
||||||
|
RESET='\E[0m'
|
||||||
|
|
||||||
|
FOLDER=$1
|
||||||
|
if [ "$FOLDER" == "" ]; then
|
||||||
|
echo -e "${RED}❯ $0 requires a absolute folder path as the first argument!${RESET}"
|
||||||
|
echo -e "${YELLOW} ie: $0 /data/nginx${RESET}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
FILES=$(find "$FOLDER" -type f -name "*.conf")
|
||||||
|
if [ "$DISABLE_IPV6" == "true" ] || [ "$DISABLE_IPV6" == "on" ] || [ "$DISABLE_IPV6" == "1" ] || [ "$DISABLE_IPV6" == "yes" ]; then
|
||||||
|
# IPV6 is disabled
|
||||||
|
echo "Disabling IPV6 in hosts"
|
||||||
|
echo -e "${BLUE}❯ ${CYAN}Disabling IPV6 in hosts: ${YELLOW}${FOLDER}${RESET}"
|
||||||
|
|
||||||
|
# Iterate over configs and run the regex
|
||||||
|
for FILE in $FILES
|
||||||
|
do
|
||||||
|
echo -e " ${BLUE}❯ ${YELLOW}${FILE}${RESET}"
|
||||||
|
sed -E -i 's/^([^#]*)listen \[::\]/\1#listen [::]/g' "$FILE"
|
||||||
|
done
|
||||||
|
|
||||||
|
else
|
||||||
|
# IPV6 is enabled
|
||||||
|
echo -e "${BLUE}❯ ${CYAN}Enabling IPV6 in hosts: ${YELLOW}${FOLDER}${RESET}"
|
||||||
|
|
||||||
|
# Iterate over configs and run the regex
|
||||||
|
for FILE in $FILES
|
||||||
|
do
|
||||||
|
echo -e " ${BLUE}❯ ${YELLOW}${FILE}${RESET}"
|
||||||
|
sed -E -i 's/^(\s*)#listen \[::\]/\1listen [::]/g' "$FILE"
|
||||||
|
done
|
||||||
|
|
||||||
|
fi
|
@ -26,12 +26,15 @@ http {
|
|||||||
tcp_nopush on;
|
tcp_nopush on;
|
||||||
tcp_nodelay on;
|
tcp_nodelay on;
|
||||||
client_body_temp_path /tmp/nginx/body 1 2;
|
client_body_temp_path /tmp/nginx/body 1 2;
|
||||||
keepalive_timeout 65;
|
keepalive_timeout 90s;
|
||||||
|
proxy_connect_timeout 90s;
|
||||||
|
proxy_send_timeout 90s;
|
||||||
|
proxy_read_timeout 90s;
|
||||||
ssl_prefer_server_ciphers on;
|
ssl_prefer_server_ciphers on;
|
||||||
gzip on;
|
gzip on;
|
||||||
proxy_ignore_client_abort off;
|
proxy_ignore_client_abort off;
|
||||||
client_max_body_size 2000m;
|
client_max_body_size 2000m;
|
||||||
server_names_hash_bucket_size 64;
|
server_names_hash_bucket_size 1024;
|
||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
proxy_set_header X-Forwarded-Scheme $scheme;
|
proxy_set_header X-Forwarded-Scheme $scheme;
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
@ -42,4 +42,8 @@ then
|
|||||||
echo "Complete"
|
echo "Complete"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Handle IPV6 settings
|
||||||
|
/bin/handle-ipv6-setting /etc/nginx/conf.d
|
||||||
|
/bin/handle-ipv6-setting /data/nginx
|
||||||
|
|
||||||
exec nginx
|
exec nginx
|
||||||
|
@ -16,5 +16,5 @@ alias h='cd ~;clear;'
|
|||||||
|
|
||||||
echo -e -n '\E[1;34m'
|
echo -e -n '\E[1;34m'
|
||||||
figlet -w 120 "NginxProxyManager"
|
figlet -w 120 "NginxProxyManager"
|
||||||
echo -e "\E[1;36mVersion \E[1;32m${NPM_BUILD_VERSION:-2.0.0-dev}\E[1;36m (${NPM_BUILD_COMMIT:-dev}) ${NPM_BUILD_DATE:-0000-00-00}, Nginx \E[1;32m${NGINX_VERSION:-unknown}\E[1;36m, Alpine \E[1;32m${VERSION_ID:-unknown}\E[1;36m, Kernel \E[1;32m$(uname -r)\E[0m"
|
echo -e "\E[1;36mVersion \E[1;32m${NPM_BUILD_VERSION:-2.0.0-dev} (${NPM_BUILD_COMMIT:-dev}) ${NPM_BUILD_DATE:-0000-00-00}\E[1;36m, OpenResty \E[1;32m${OPENRESTY_VERSION:-unknown}\E[1;36m, Alpine \E[1;32m${VERSION_ID:-unknown}\E[1;36m, Kernel \E[1;32m$(uname -r)\E[0m"
|
||||||
echo
|
echo
|
||||||
|
3
docs/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
.vuepress/dist
|
||||||
|
node_modules
|
||||||
|
ts
|
81
docs/.vuepress/config.js
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
module.exports = {
|
||||||
|
locales: {
|
||||||
|
"/": {
|
||||||
|
lang: "en-US",
|
||||||
|
title: "Nginx Proxy Manager",
|
||||||
|
description: "Expose your services easily and securely"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
head: [
|
||||||
|
["link", { rel: "icon", href: "/icon.png" }],
|
||||||
|
["meta", { name: "description", content: "Docker container and built in Web Application for managing Nginx proxy hosts with a simple, powerful interface, providing free SSL support via Let's Encrypt" }],
|
||||||
|
["meta", { property: "og:title", content: "Nginx Proxy Manager" }],
|
||||||
|
["meta", { property: "og:description", content: "Docker container and built in Web Application for managing Nginx proxy hosts with a simple, powerful interface, providing free SSL support via Let's Encrypt"}],
|
||||||
|
["meta", { property: "og:type", content: "website" }],
|
||||||
|
["meta", { property: "og:url", content: "https://nginxproxymanager.com/" }],
|
||||||
|
["meta", { property: "og:image", content: "https://nginxproxymanager.com/icon.png" }],
|
||||||
|
["meta", { name: "twitter:card", content: "summary"}],
|
||||||
|
["meta", { name: "twitter:title", content: "Nginx Proxy Manager"}],
|
||||||
|
["meta", { name: "twitter:description", content: "Docker container and built in Web Application for managing Nginx proxy hosts with a simple, powerful interface, providing free SSL support via Let's Encrypt"}],
|
||||||
|
["meta", { name: "twitter:image", content: "https://nginxproxymanager.com/icon.png"}],
|
||||||
|
["meta", { name: "twitter:alt", content: "Nginx Proxy Manager"}],
|
||||||
|
],
|
||||||
|
themeConfig: {
|
||||||
|
logo: "/icon.png",
|
||||||
|
// the GitHub repo path
|
||||||
|
repo: "jc21/nginx-proxy-manager",
|
||||||
|
// the label linking to the repo
|
||||||
|
repoLabel: "GitHub",
|
||||||
|
// if your docs are not at the root of the repo:
|
||||||
|
docsDir: "docs",
|
||||||
|
// defaults to false, set to true to enable
|
||||||
|
editLinks: true,
|
||||||
|
locales: {
|
||||||
|
"/": {
|
||||||
|
// text for the language dropdown
|
||||||
|
selectText: "Languages",
|
||||||
|
// label for this locale in the language dropdown
|
||||||
|
label: "English",
|
||||||
|
// Custom text for edit link. Defaults to "Edit this page"
|
||||||
|
editLinkText: "Edit this page on GitHub",
|
||||||
|
// Custom navbar values
|
||||||
|
nav: [{ text: "Setup", link: "/setup/" }],
|
||||||
|
// Custom sidebar values
|
||||||
|
sidebar: [
|
||||||
|
"/",
|
||||||
|
["/guide/", "Guide"],
|
||||||
|
["/screenshots/", "Screenshots"],
|
||||||
|
["/setup/", "Setup Instructions"],
|
||||||
|
["/advanced-config/", "Advanced Configuration"],
|
||||||
|
["/faq/", "Frequently Asked Questions"],
|
||||||
|
["/third-party/", "Third Party"]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
[
|
||||||
|
"@vuepress/google-analytics",
|
||||||
|
{
|
||||||
|
ga: "UA-99675467-4"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"sitemap",
|
||||||
|
{
|
||||||
|
hostname: "https://nginxproxymanager.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'vuepress-plugin-zooming',
|
||||||
|
{
|
||||||
|
selector: '.zooming',
|
||||||
|
delay: 1000,
|
||||||
|
options: {
|
||||||
|
bgColor: 'black',
|
||||||
|
zIndex: 10000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]
|
||||||
|
};
|
BIN
docs/.vuepress/public/github.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
docs/.vuepress/public/icon.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
docs/.vuepress/public/logo.png
Normal file
After Width: | Height: | Size: 57 KiB |
1
docs/.vuepress/public/logo.svg
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
docs/.vuepress/public/nerd-font.woff2
Normal file
2
docs/.vuepress/public/robots.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
User-agent: *
|
||||||
|
Disallow:
|
BIN
docs/.vuepress/public/screenshots/access-lists.png
Normal file
After Width: | Height: | Size: 106 KiB |
BIN
docs/.vuepress/public/screenshots/audit-log.png
Normal file
After Width: | Height: | Size: 178 KiB |
BIN
docs/.vuepress/public/screenshots/certificates.png
Normal file
After Width: | Height: | Size: 173 KiB |
BIN
docs/.vuepress/public/screenshots/custom-settings.png
Normal file
After Width: | Height: | Size: 141 KiB |
BIN
docs/.vuepress/public/screenshots/dashboard.png
Normal file
After Width: | Height: | Size: 102 KiB |
BIN
docs/.vuepress/public/screenshots/dead-hosts.png
Normal file
After Width: | Height: | Size: 150 KiB |
BIN
docs/.vuepress/public/screenshots/login.png
Normal file
After Width: | Height: | Size: 92 KiB |
BIN
docs/.vuepress/public/screenshots/permissions.png
Normal file
After Width: | Height: | Size: 151 KiB |
BIN
docs/.vuepress/public/screenshots/proxy-hosts-add.png
Normal file
After Width: | Height: | Size: 207 KiB |
BIN
docs/.vuepress/public/screenshots/proxy-hosts.png
Normal file
After Width: | Height: | Size: 181 KiB |
BIN
docs/.vuepress/public/screenshots/redirection-hosts.png
Normal file
After Width: | Height: | Size: 162 KiB |
23
docs/.vuepress/styles/index.styl
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
.home .hero img
|
||||||
|
max-width: 500px !important
|
||||||
|
min-width: 300px
|
||||||
|
width: 100%
|
||||||
|
|
||||||
|
.center
|
||||||
|
margin 0 auto;
|
||||||
|
width: 80%
|
||||||
|
|
||||||
|
#main-title
|
||||||
|
display: none
|
||||||
|
|
||||||
|
.hero
|
||||||
|
margin: 150px 25px 70px
|
||||||
|
|
||||||
|
@font-face
|
||||||
|
font-family: 'Nerd Font';
|
||||||
|
src: url("/nerd-font.woff2") format("woff2");
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal
|
||||||
|
|
||||||
|
code
|
||||||
|
font-family: 'Nerd Font', source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;
|
4
docs/.vuepress/styles/palette.styl
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
$accentColor = #f15833
|
||||||
|
$textColor = #663015
|
||||||
|
$borderColor = #eaecef
|
||||||
|
$codeBgColor = #282c34
|
108
docs/README.md
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
---
|
||||||
|
home: true
|
||||||
|
heroImage: /logo.png
|
||||||
|
actionText: Get Started →
|
||||||
|
actionLink: /guide/
|
||||||
|
footer: MIT Licensed | Copyright © 2016-present jc21.com
|
||||||
|
---
|
||||||
|
|
||||||
|
<div class="features">
|
||||||
|
<div class="feature">
|
||||||
|
<h2>Get Connected</h2>
|
||||||
|
<p>
|
||||||
|
Expose web services on your network ·
|
||||||
|
Free SSL with Let's Encrypt ·
|
||||||
|
Designed with security in mind ·
|
||||||
|
Perfect for home networks
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="feature">
|
||||||
|
<h2>Proxy Hosts</h2>
|
||||||
|
<p>Expose your private network Web services and get connected anywhere.</p>
|
||||||
|
</div>
|
||||||
|
<div class="feature">
|
||||||
|
<h2>Beautiful UI</h2>
|
||||||
|
<p>Based on Tabler, the interface is a pleasure to use. Configuring a server has never been so fun.</p>
|
||||||
|
</div>
|
||||||
|
<div class="feature">
|
||||||
|
<h2>Free SSL</h2>
|
||||||
|
<p>Built in Let’s Encrypt support allows you to secure your Web services at no cost to you. The certificates even renew themselves!</p>
|
||||||
|
</div>
|
||||||
|
<div class="feature">
|
||||||
|
<h2>Docker FTW</h2>
|
||||||
|
<p>Built as a Docker Image, Nginx Proxy Manager only requires a database.</p>
|
||||||
|
</div>
|
||||||
|
<div class="feature">
|
||||||
|
<h2>Multiple Users</h2>
|
||||||
|
<p>Configure other users to either view or manage their own hosts. Full access permissions are available.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
### Quick Setup
|
||||||
|
|
||||||
|
1. Install Docker and Docker-Compose
|
||||||
|
|
||||||
|
- [Docker Install documentation](https://docs.docker.com/install/)
|
||||||
|
- [Docker-Compose Install documentation](https://docs.docker.com/compose/install/)
|
||||||
|
|
||||||
|
2. Create a config file for example
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"database": {
|
||||||
|
"engine": "mysql",
|
||||||
|
"host": "db",
|
||||||
|
"name": "npm",
|
||||||
|
"user": "npm",
|
||||||
|
"password": "npm",
|
||||||
|
"port": 3306
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Create a docker-compose.yml file similar to this:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
version: '3'
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
image: 'jc21/nginx-proxy-manager:latest'
|
||||||
|
ports:
|
||||||
|
- '80:80'
|
||||||
|
- '81:81'
|
||||||
|
- '443:443'
|
||||||
|
volumes:
|
||||||
|
- ./config.json:/app/config/production.json
|
||||||
|
- ./data:/data
|
||||||
|
- ./letsencrypt:/etc/letsencrypt
|
||||||
|
db:
|
||||||
|
image: 'jc21/mariadb-aria:10.4'
|
||||||
|
environment:
|
||||||
|
MYSQL_ROOT_PASSWORD: 'npm'
|
||||||
|
MYSQL_DATABASE: 'npm'
|
||||||
|
MYSQL_USER: 'npm'
|
||||||
|
MYSQL_PASSWORD: 'npm'
|
||||||
|
volumes:
|
||||||
|
- ./data/mysql:/var/lib/mysql
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Bring up your stack
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker-compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
5. Log in to the Admin UI
|
||||||
|
|
||||||
|
When your docker container is running, connect to it on port `81` for the admin interface.
|
||||||
|
Sometimes this can take a little bit because of the entropy of keys.
|
||||||
|
|
||||||
|
[http://127.0.0.1:81](http://127.0.0.1:81)
|
||||||
|
|
||||||
|
Default Admin User:
|
||||||
|
|
||||||
|
```
|
||||||
|
Email: admin@example.com
|
||||||
|
Password: changeme
|
||||||
|
```
|
||||||
|
|
||||||
|
Immediately after logging in with this default user you will be asked to modify your details and change your password.
|
46
docs/advanced-config/README.md
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# Advanced Configuration
|
||||||
|
|
||||||
|
## Disabling IPv6
|
||||||
|
|
||||||
|
On some docker hosts IPv6 may not be enabled. In these cases, the following message may be seen in the log:
|
||||||
|
|
||||||
|
> Address family not supported by protocol
|
||||||
|
|
||||||
|
The easy fix is to add a Docker environment variable to the Nginx Proxy Manager stack:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
environment:
|
||||||
|
DISABLE_IPV6: 'true'
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Custom Nginx Configurations
|
||||||
|
|
||||||
|
If you are a more advanced user, you might be itching for extra Nginx customizability.
|
||||||
|
|
||||||
|
NPM has the ability to include different custom configuration snippets in different places.
|
||||||
|
|
||||||
|
You can add your custom configuration snippet files at `/data/nginx/custom` as follow:
|
||||||
|
|
||||||
|
- `/data/nginx/custom/root.conf`: Included at the very end of nginx.conf
|
||||||
|
- `/data/nginx/custom/http.conf`: Included at the end of the main http block
|
||||||
|
- `/data/nginx/custom/server_proxy.conf`: Included at the end of every proxy server block
|
||||||
|
- `/data/nginx/custom/server_redirect.conf`: Included at the end of every redirection server block
|
||||||
|
- `/data/nginx/custom/server_stream.conf`: Included at the end of every stream server block
|
||||||
|
- `/data/nginx/custom/server_stream_tcp.conf`: Included at the end of every TCP stream server block
|
||||||
|
- `/data/nginx/custom/server_stream_udp.conf`: Included at the end of every UDP stream server block
|
||||||
|
|
||||||
|
Every file is optional.
|
||||||
|
|
||||||
|
|
||||||
|
## X-FRAME-OPTIONS Header
|
||||||
|
|
||||||
|
You can configure the [`X-FRAME-OPTIONS`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options) header
|
||||||
|
value by specifying it as a Docker environment variable. The default if not specified is `deny`.
|
||||||
|
|
||||||
|
```yml
|
||||||
|
...
|
||||||
|
environment:
|
||||||
|
X_FRAME_OPTIONS: "sameorigin"
|
||||||
|
...
|
||||||
|
```
|
16
docs/faq/README.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# FAQ
|
||||||
|
|
||||||
|
## Do I have to use Docker?
|
||||||
|
|
||||||
|
Yes, that's how this project is packaged.
|
||||||
|
|
||||||
|
This makes it easier to support the project when I have control over the version of Nginx and NodeJS
|
||||||
|
being used. In future this could change if the backend was no longer using NodeJS and it's long list
|
||||||
|
of dependencies.
|
||||||
|
|
||||||
|
|
||||||
|
## Can I run it on a Raspberry Pi?
|
||||||
|
|
||||||
|
Yes! The docker image is multi-arch and is built for a variety of architectures. If yours is
|
||||||
|
[not listed](https://hub.docker.com/r/jc21/nginx-proxy-manager/tags) please open a
|
||||||
|
[GitHub issue](https://github.com/jc21/nginx-proxy-manager/issues/new?assignees=&labels=enhancement&template=feature_request.md&title=).
|
1
docs/guide/README.md
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../../README.md
|
777
docs/package.json
Normal file
@ -0,0 +1,777 @@
|
|||||||
|
{
|
||||||
|
"name": "docs",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"dependencies": {
|
||||||
|
"@vuepress/plugin-google-analytics": "^1.5.3",
|
||||||
|
"abbrev": "^1.1.1",
|
||||||
|
"accepts": "^1.3.7",
|
||||||
|
"acorn": "^7.4.0",
|
||||||
|
"agentkeepalive": "^4.1.3",
|
||||||
|
"ajv": "^6.12.3",
|
||||||
|
"ajv-errors": "^1.0.1",
|
||||||
|
"ajv-keywords": "^3.5.2",
|
||||||
|
"algoliasearch": "^4.3.1",
|
||||||
|
"alphanum-sort": "^1.0.2",
|
||||||
|
"ansi-colors": "^4.1.1",
|
||||||
|
"ansi-escapes": "^4.3.1",
|
||||||
|
"ansi-html": "^0.0.7",
|
||||||
|
"ansi-regex": "^5.0.0",
|
||||||
|
"ansi-styles": "^4.2.1",
|
||||||
|
"anymatch": "^3.1.1",
|
||||||
|
"aproba": "^2.0.0",
|
||||||
|
"argparse": "^1.0.10",
|
||||||
|
"arr-diff": "^4.0.0",
|
||||||
|
"arr-flatten": "^1.1.0",
|
||||||
|
"arr-union": "^3.1.0",
|
||||||
|
"array-flatten": "^3.0.0",
|
||||||
|
"array-union": "^2.1.0",
|
||||||
|
"array-uniq": "^2.1.0",
|
||||||
|
"array-unique": "^0.3.2",
|
||||||
|
"asn1": "^0.2.4",
|
||||||
|
"asn1.js": "^5.4.1",
|
||||||
|
"assert": "^2.0.0",
|
||||||
|
"assert-plus": "^1.0.0",
|
||||||
|
"assign-symbols": "^2.0.2",
|
||||||
|
"async": "^3.2.0",
|
||||||
|
"async-each": "^1.0.3",
|
||||||
|
"async-limiter": "^2.0.0",
|
||||||
|
"asynckit": "^0.4.0",
|
||||||
|
"atob": "^2.1.2",
|
||||||
|
"autocomplete.js": "^0.37.1",
|
||||||
|
"autoprefixer": "^9.8.6",
|
||||||
|
"aws-sign2": "^0.7.0",
|
||||||
|
"aws4": "^1.10.0",
|
||||||
|
"babel-loader": "^8.1.0",
|
||||||
|
"babel-plugin-dynamic-import-node": "^2.3.3",
|
||||||
|
"babel-plugin-module-resolver": "^4.0.0",
|
||||||
|
"balanced-match": "^1.0.0",
|
||||||
|
"base": "^3.0.0",
|
||||||
|
"base64-js": "^1.3.1",
|
||||||
|
"batch": "^0.6.1",
|
||||||
|
"bcrypt-pbkdf": "^1.0.2",
|
||||||
|
"big.js": "^5.2.2",
|
||||||
|
"binary-extensions": "^2.1.0",
|
||||||
|
"bluebird": "^3.7.2",
|
||||||
|
"bn.js": "^5.1.2",
|
||||||
|
"body-parser": "^1.19.0",
|
||||||
|
"bonjour": "^3.5.0",
|
||||||
|
"boolbase": "^1.0.0",
|
||||||
|
"brace-expansion": "^1.1.11",
|
||||||
|
"braces": "^3.0.2",
|
||||||
|
"brorand": "^1.1.0",
|
||||||
|
"browserify-aes": "^1.2.0",
|
||||||
|
"browserify-cipher": "^1.0.1",
|
||||||
|
"browserify-des": "^1.0.2",
|
||||||
|
"browserify-rsa": "^4.0.1",
|
||||||
|
"browserify-sign": "^4.2.1",
|
||||||
|
"browserify-zlib": "^0.2.0",
|
||||||
|
"browserslist": "^4.13.0",
|
||||||
|
"buffer": "^5.6.0",
|
||||||
|
"buffer-from": "^1.1.1",
|
||||||
|
"buffer-indexof": "^1.1.1",
|
||||||
|
"buffer-json": "^2.0.0",
|
||||||
|
"buffer-xor": "^2.0.2",
|
||||||
|
"builtin-status-codes": "^3.0.0",
|
||||||
|
"bytes": "^3.1.0",
|
||||||
|
"cac": "^6.6.1",
|
||||||
|
"cacache": "^15.0.5",
|
||||||
|
"cache-base": "^4.0.0",
|
||||||
|
"cache-loader": "^4.1.0",
|
||||||
|
"call-me-maybe": "^1.0.1",
|
||||||
|
"caller-callsite": "^4.1.0",
|
||||||
|
"caller-path": "^3.0.0",
|
||||||
|
"callsites": "^3.1.0",
|
||||||
|
"camel-case": "^4.1.1",
|
||||||
|
"camelcase": "^6.0.0",
|
||||||
|
"caniuse-api": "^3.0.0",
|
||||||
|
"caniuse-lite": "^1.0.30001111",
|
||||||
|
"caseless": "^0.12.0",
|
||||||
|
"chalk": "^4.1.0",
|
||||||
|
"chokidar": "^3.4.1",
|
||||||
|
"chownr": "^2.0.0",
|
||||||
|
"chrome-trace-event": "^1.0.2",
|
||||||
|
"ci-info": "^2.0.0",
|
||||||
|
"cipher-base": "^1.0.4",
|
||||||
|
"class-utils": "^0.3.6",
|
||||||
|
"clean-css": "^4.2.3",
|
||||||
|
"clipboard": "^2.0.6",
|
||||||
|
"cliui": "^6.0.0",
|
||||||
|
"coa": "^2.0.2",
|
||||||
|
"code-point-at": "^1.1.0",
|
||||||
|
"collection-visit": "^1.0.0",
|
||||||
|
"color": "^3.1.2",
|
||||||
|
"color-convert": "^2.0.1",
|
||||||
|
"color-name": "^1.1.4",
|
||||||
|
"color-string": "^1.5.3",
|
||||||
|
"combined-stream": "^1.0.8",
|
||||||
|
"commander": "^6.0.0",
|
||||||
|
"commondir": "^1.0.1",
|
||||||
|
"component-emitter": "^1.3.0",
|
||||||
|
"compressible": "^2.0.18",
|
||||||
|
"compression": "^1.7.4",
|
||||||
|
"concat-map": "^0.0.1",
|
||||||
|
"concat-stream": "^2.0.0",
|
||||||
|
"connect-history-api-fallback": "^1.6.0",
|
||||||
|
"consola": "^2.15.0",
|
||||||
|
"console-browserify": "^1.2.0",
|
||||||
|
"consolidate": "^0.15.1",
|
||||||
|
"constants-browserify": "^1.0.0",
|
||||||
|
"content-disposition": "^0.5.3",
|
||||||
|
"content-type": "^1.0.4",
|
||||||
|
"convert-source-map": "^1.7.0",
|
||||||
|
"cookie": "^0.4.1",
|
||||||
|
"cookie-signature": "^1.1.0",
|
||||||
|
"copy-concurrently": "^1.0.5",
|
||||||
|
"copy-descriptor": "^0.1.1",
|
||||||
|
"copy-webpack-plugin": "^6.0.3",
|
||||||
|
"core-js": "^3.6.5",
|
||||||
|
"core-util-is": "^1.0.2",
|
||||||
|
"cosmiconfig": "^7.0.0",
|
||||||
|
"create-ecdh": "^4.0.4",
|
||||||
|
"create-hash": "^1.2.0",
|
||||||
|
"create-hmac": "^1.1.7",
|
||||||
|
"cross-spawn": "^7.0.3",
|
||||||
|
"crypto-browserify": "^3.12.0",
|
||||||
|
"css": "^3.0.0",
|
||||||
|
"css-color-names": "^1.0.1",
|
||||||
|
"css-declaration-sorter": "^5.1.2",
|
||||||
|
"css-loader": "^4.2.0",
|
||||||
|
"css-parse": "^2.0.0",
|
||||||
|
"css-select": "^2.1.0",
|
||||||
|
"css-select-base-adapter": "^0.1.1",
|
||||||
|
"css-tree": "^1.0.0-alpha.39",
|
||||||
|
"css-unit-converter": "^1.1.2",
|
||||||
|
"css-what": "^3.3.0",
|
||||||
|
"cssesc": "^3.0.0",
|
||||||
|
"cssnano": "^4.1.10",
|
||||||
|
"cssnano-preset-default": "^4.0.7",
|
||||||
|
"cssnano-util-get-arguments": "^4.0.0",
|
||||||
|
"cssnano-util-get-match": "^4.0.0",
|
||||||
|
"cssnano-util-raw-cache": "^4.0.1",
|
||||||
|
"cssnano-util-same-parent": "^4.0.1",
|
||||||
|
"csso": "^4.0.3",
|
||||||
|
"cyclist": "^1.0.1",
|
||||||
|
"dashdash": "^1.14.1",
|
||||||
|
"de-indent": "^1.0.2",
|
||||||
|
"debug": "^4.1.1",
|
||||||
|
"decamelize": "^4.0.0",
|
||||||
|
"decode-uri-component": "^0.2.0",
|
||||||
|
"deep-equal": "^2.0.3",
|
||||||
|
"deepmerge": "^4.2.2",
|
||||||
|
"default-gateway": "^6.0.1",
|
||||||
|
"define-properties": "^1.1.3",
|
||||||
|
"define-property": "^2.0.2",
|
||||||
|
"del": "^5.1.0",
|
||||||
|
"delayed-stream": "^1.0.0",
|
||||||
|
"delegate": "^3.2.0",
|
||||||
|
"depd": "^2.0.0",
|
||||||
|
"des.js": "^1.0.1",
|
||||||
|
"destroy": "^1.0.4",
|
||||||
|
"detect-node": "^2.0.4",
|
||||||
|
"diacritics": "^1.3.0",
|
||||||
|
"diffie-hellman": "^5.0.3",
|
||||||
|
"dir-glob": "^3.0.1",
|
||||||
|
"dns-equal": "^1.0.0",
|
||||||
|
"dns-packet": "^5.2.1",
|
||||||
|
"dns-txt": "^2.0.2",
|
||||||
|
"docsearch.js": "^2.6.3",
|
||||||
|
"dom-converter": "^0.2.0",
|
||||||
|
"dom-serializer": "^1.0.1",
|
||||||
|
"dom-walk": "^0.1.2",
|
||||||
|
"domain-browser": "^4.16.0",
|
||||||
|
"domelementtype": "^2.0.1",
|
||||||
|
"domhandler": "^3.0.0",
|
||||||
|
"domutils": "^2.1.0",
|
||||||
|
"dot-prop": "^5.2.0",
|
||||||
|
"duplexify": "^4.1.1",
|
||||||
|
"ecc-jsbn": "^0.2.0",
|
||||||
|
"ee-first": "^1.1.1",
|
||||||
|
"electron-to-chromium": "^1.3.522",
|
||||||
|
"elliptic": "^6.5.3",
|
||||||
|
"emoji-regex": "^9.0.0",
|
||||||
|
"emojis-list": "^3.0.0",
|
||||||
|
"encodeurl": "^1.0.2",
|
||||||
|
"end-of-stream": "^1.4.4",
|
||||||
|
"enhanced-resolve": "^4.3.0",
|
||||||
|
"entities": "^2.0.3",
|
||||||
|
"envify": "^4.1.0",
|
||||||
|
"envinfo": "^7.7.2",
|
||||||
|
"errno": "^0.1.7",
|
||||||
|
"error-ex": "^1.3.2",
|
||||||
|
"es-abstract": "^1.17.6",
|
||||||
|
"es-to-primitive": "^1.2.1",
|
||||||
|
"es6-promise": "^4.2.8",
|
||||||
|
"escape-html": "^1.0.3",
|
||||||
|
"escape-string-regexp": "^4.0.0",
|
||||||
|
"eslint-scope": "^5.1.0",
|
||||||
|
"esprima": "^4.0.1",
|
||||||
|
"esrecurse": "^4.2.1",
|
||||||
|
"estraverse": "^5.2.0",
|
||||||
|
"esutils": "^2.0.3",
|
||||||
|
"etag": "^1.8.1",
|
||||||
|
"eventemitter3": "^4.0.4",
|
||||||
|
"events": "^3.2.0",
|
||||||
|
"eventsource": "^1.0.7",
|
||||||
|
"evp_bytestokey": "^1.0.3",
|
||||||
|
"execa": "^4.0.3",
|
||||||
|
"expand-brackets": "^4.0.0",
|
||||||
|
"express": "^4.17.1",
|
||||||
|
"extend": "^3.0.2",
|
||||||
|
"extend-shallow": "^3.0.2",
|
||||||
|
"extglob": "^3.0.0",
|
||||||
|
"extsprintf": "^1.4.0",
|
||||||
|
"fast-deep-equal": "^3.1.3",
|
||||||
|
"fast-glob": "^3.2.4",
|
||||||
|
"fast-json-stable-stringify": "^2.1.0",
|
||||||
|
"faye-websocket": "^0.11.3",
|
||||||
|
"figgy-pudding": "^3.5.2",
|
||||||
|
"figures": "^3.2.0",
|
||||||
|
"file-loader": "^6.0.0",
|
||||||
|
"fill-range": "^7.0.1",
|
||||||
|
"finalhandler": "^1.1.2",
|
||||||
|
"find-babel-config": "^1.2.0",
|
||||||
|
"find-cache-dir": "^3.3.1",
|
||||||
|
"find-up": "^4.1.0",
|
||||||
|
"flush-write-stream": "^2.0.0",
|
||||||
|
"follow-redirects": "^1.12.1",
|
||||||
|
"for-in": "^1.0.2",
|
||||||
|
"foreach": "^2.0.5",
|
||||||
|
"forever-agent": "^0.6.1",
|
||||||
|
"form-data": "^3.0.0",
|
||||||
|
"forwarded": "^0.1.2",
|
||||||
|
"fragment-cache": "^0.2.1",
|
||||||
|
"fresh": "^0.5.2",
|
||||||
|
"from2": "^2.3.0",
|
||||||
|
"fs-extra": "^9.0.1",
|
||||||
|
"fs-write-stream-atomic": "^1.0.10",
|
||||||
|
"fs.realpath": "^1.0.0",
|
||||||
|
"function-bind": "^1.1.1",
|
||||||
|
"gensync": "^1.0.0-beta.1",
|
||||||
|
"get-caller-file": "^2.0.5",
|
||||||
|
"get-stream": "^5.1.0",
|
||||||
|
"get-value": "^3.0.1",
|
||||||
|
"getpass": "^0.1.7",
|
||||||
|
"glob": "^7.1.6",
|
||||||
|
"glob-parent": "^5.1.1",
|
||||||
|
"glob-to-regexp": "^0.4.1",
|
||||||
|
"global": "^4.4.0",
|
||||||
|
"globals": "^13.1.0",
|
||||||
|
"globby": "^11.0.1",
|
||||||
|
"good-listener": "^1.2.2",
|
||||||
|
"graceful-fs": "^4.2.4",
|
||||||
|
"gray-matter": "^4.0.2",
|
||||||
|
"handle-thing": "^2.0.1",
|
||||||
|
"har-schema": "^2.0.0",
|
||||||
|
"har-validator": "^5.1.5",
|
||||||
|
"has": "^1.0.3",
|
||||||
|
"has-ansi": "^4.0.0",
|
||||||
|
"has-flag": "^4.0.0",
|
||||||
|
"has-symbols": "^1.0.1",
|
||||||
|
"has-value": "^2.0.2",
|
||||||
|
"has-values": "^2.0.1",
|
||||||
|
"hash-base": "^3.1.0",
|
||||||
|
"hash-sum": "^2.0.0",
|
||||||
|
"hash.js": "^1.1.7",
|
||||||
|
"he": "^1.2.0",
|
||||||
|
"hex-color-regex": "^1.1.0",
|
||||||
|
"hmac-drbg": "^1.0.1",
|
||||||
|
"hogan.js": "^3.0.2",
|
||||||
|
"hpack.js": "^2.1.6",
|
||||||
|
"hsl-regex": "^1.0.0",
|
||||||
|
"hsla-regex": "^1.0.0",
|
||||||
|
"html-comment-regex": "^1.1.2",
|
||||||
|
"html-entities": "^1.3.1",
|
||||||
|
"html-minifier": "^4.0.0",
|
||||||
|
"html-tags": "^3.1.0",
|
||||||
|
"htmlparser2": "^4.1.0",
|
||||||
|
"http-deceiver": "^1.2.7",
|
||||||
|
"http-errors": "^1.8.0",
|
||||||
|
"http-parser-js": "^0.5.2",
|
||||||
|
"http-proxy": "^1.18.1",
|
||||||
|
"http-proxy-middleware": "^1.0.5",
|
||||||
|
"http-signature": "^1.3.4",
|
||||||
|
"https-browserify": "^1.0.0",
|
||||||
|
"iconv-lite": "^0.6.2",
|
||||||
|
"icss-replace-symbols": "^1.1.0",
|
||||||
|
"icss-utils": "^4.1.1",
|
||||||
|
"ieee754": "^1.1.13",
|
||||||
|
"iferr": "^1.0.2",
|
||||||
|
"ignore": "^5.1.8",
|
||||||
|
"immediate": "^3.3.0",
|
||||||
|
"import-cwd": "^3.0.0",
|
||||||
|
"import-fresh": "^3.2.1",
|
||||||
|
"import-from": "^3.0.0",
|
||||||
|
"import-local": "^3.0.2",
|
||||||
|
"imurmurhash": "^0.1.4",
|
||||||
|
"indexes-of": "^1.0.1",
|
||||||
|
"infer-owner": "^1.0.4",
|
||||||
|
"inflight": "^1.0.6",
|
||||||
|
"inherits": "^2.0.4",
|
||||||
|
"internal-ip": "^6.1.0",
|
||||||
|
"invariant": "^2.2.4",
|
||||||
|
"invert-kv": "^3.0.1",
|
||||||
|
"ip": "^1.1.5",
|
||||||
|
"ip-regex": "^4.1.0",
|
||||||
|
"ipaddr.js": "^1.9.1",
|
||||||
|
"is-absolute-url": "^3.0.3",
|
||||||
|
"is-accessor-descriptor": "^3.0.1",
|
||||||
|
"is-arguments": "^1.0.4",
|
||||||
|
"is-arrayish": "^0.3.2",
|
||||||
|
"is-binary-path": "^2.1.0",
|
||||||
|
"is-buffer": "^2.0.4",
|
||||||
|
"is-callable": "^1.2.0",
|
||||||
|
"is-color-stop": "^1.1.0",
|
||||||
|
"is-data-descriptor": "^2.0.0",
|
||||||
|
"is-date-object": "^1.0.2",
|
||||||
|
"is-descriptor": "^3.0.0",
|
||||||
|
"is-directory": "^0.3.1",
|
||||||
|
"is-extendable": "^1.0.1",
|
||||||
|
"is-extglob": "^2.1.1",
|
||||||
|
"is-fullwidth-code-point": "^3.0.0",
|
||||||
|
"is-glob": "^4.0.1",
|
||||||
|
"is-number": "^7.0.0",
|
||||||
|
"is-obj": "^2.0.0",
|
||||||
|
"is-path-cwd": "^2.2.0",
|
||||||
|
"is-path-in-cwd": "^3.0.0",
|
||||||
|
"is-path-inside": "^3.0.2",
|
||||||
|
"is-plain-obj": "^2.1.0",
|
||||||
|
"is-plain-object": "^4.1.1",
|
||||||
|
"is-regex": "^1.1.1",
|
||||||
|
"is-resolvable": "^1.1.0",
|
||||||
|
"is-stream": "^2.0.0",
|
||||||
|
"is-svg": "^4.2.1",
|
||||||
|
"is-symbol": "^1.0.3",
|
||||||
|
"is-typedarray": "^1.0.0",
|
||||||
|
"is-windows": "^1.0.2",
|
||||||
|
"is-wsl": "^2.2.0",
|
||||||
|
"isarray": "^2.0.5",
|
||||||
|
"isexe": "^2.0.0",
|
||||||
|
"isobject": "^4.0.0",
|
||||||
|
"isstream": "^0.1.2",
|
||||||
|
"javascript-stringify": "^2.0.1",
|
||||||
|
"js-levenshtein": "^1.1.6",
|
||||||
|
"js-tokens": "^6.0.0",
|
||||||
|
"js-yaml": "^3.14.0",
|
||||||
|
"jsbn": "^1.1.0",
|
||||||
|
"jsesc": "^3.0.1",
|
||||||
|
"json-parse-better-errors": "^1.0.2",
|
||||||
|
"json-schema": "^0.2.5",
|
||||||
|
"json-schema-traverse": "^0.4.1",
|
||||||
|
"json-stringify-safe": "^5.0.1",
|
||||||
|
"json3": "^3.3.3",
|
||||||
|
"json5": "^2.1.3",
|
||||||
|
"jsonfile": "^6.0.1",
|
||||||
|
"jsprim": "^2.0.0",
|
||||||
|
"killable": "^1.0.1",
|
||||||
|
"kind-of": "^6.0.3",
|
||||||
|
"last-call-webpack-plugin": "^3.0.0",
|
||||||
|
"lcid": "^3.1.1",
|
||||||
|
"linkify-it": "^3.0.2",
|
||||||
|
"load-script": "^1.0.0",
|
||||||
|
"loader-runner": "^4.0.0",
|
||||||
|
"loader-utils": "^2.0.0",
|
||||||
|
"locate-path": "^5.0.0",
|
||||||
|
"lodash": "^4.17.19",
|
||||||
|
"lodash._reinterpolate": "^3.0.0",
|
||||||
|
"lodash.chunk": "^4.2.0",
|
||||||
|
"lodash.clonedeep": "^4.5.0",
|
||||||
|
"lodash.debounce": "^4.0.8",
|
||||||
|
"lodash.kebabcase": "^4.1.1",
|
||||||
|
"lodash.memoize": "^4.1.2",
|
||||||
|
"lodash.padstart": "^4.6.1",
|
||||||
|
"lodash.sortby": "^4.7.0",
|
||||||
|
"lodash.template": "^4.5.0",
|
||||||
|
"lodash.templatesettings": "^4.2.0",
|
||||||
|
"lodash.uniq": "^4.5.0",
|
||||||
|
"loglevel": "^1.6.8",
|
||||||
|
"loose-envify": "^1.4.0",
|
||||||
|
"lower-case": "^2.0.1",
|
||||||
|
"lru-cache": "^6.0.0",
|
||||||
|
"make-dir": "^3.1.0",
|
||||||
|
"mamacro": "^0.0.7",
|
||||||
|
"map-age-cleaner": "^0.1.3",
|
||||||
|
"map-cache": "^0.2.2",
|
||||||
|
"map-visit": "^1.0.0",
|
||||||
|
"markdown-it": "^11.0.0",
|
||||||
|
"markdown-it-anchor": "^5.3.0",
|
||||||
|
"markdown-it-chain": "^1.3.0",
|
||||||
|
"markdown-it-container": "^3.0.0",
|
||||||
|
"markdown-it-emoji": "^1.4.0",
|
||||||
|
"markdown-it-table-of-contents": "^0.4.4",
|
||||||
|
"md5.js": "^1.3.5",
|
||||||
|
"mdn-data": "^2.0.11",
|
||||||
|
"mdurl": "^1.0.1",
|
||||||
|
"media-typer": "^1.1.0",
|
||||||
|
"mem": "^6.1.0",
|
||||||
|
"memory-fs": "^0.5.0",
|
||||||
|
"merge-descriptors": "^1.0.1",
|
||||||
|
"merge-source-map": "^1.1.0",
|
||||||
|
"merge2": "^1.4.1",
|
||||||
|
"methods": "^1.1.2",
|
||||||
|
"micromatch": "^4.0.2",
|
||||||
|
"miller-rabin": "^4.0.1",
|
||||||
|
"mime": "^2.4.6",
|
||||||
|
"mime-db": "^1.44.0",
|
||||||
|
"mime-types": "^2.1.27",
|
||||||
|
"mimic-fn": "^3.1.0",
|
||||||
|
"min-document": "^2.19.0",
|
||||||
|
"mini-css-extract-plugin": "^0.9.0",
|
||||||
|
"minimalistic-assert": "^1.0.1",
|
||||||
|
"minimalistic-crypto-utils": "^1.0.1",
|
||||||
|
"minimatch": "^3.0.4",
|
||||||
|
"minimist": "^1.2.5",
|
||||||
|
"mississippi": "^4.0.0",
|
||||||
|
"mixin-deep": "^2.0.1",
|
||||||
|
"mkdirp": "^1.0.4",
|
||||||
|
"move-concurrently": "^1.0.1",
|
||||||
|
"ms": "^2.1.2",
|
||||||
|
"multicast-dns": "^7.2.2",
|
||||||
|
"multicast-dns-service-types": "^1.1.0",
|
||||||
|
"nanomatch": "^1.2.13",
|
||||||
|
"negotiator": "^0.6.2",
|
||||||
|
"neo-async": "^2.6.2",
|
||||||
|
"nice-try": "^2.0.1",
|
||||||
|
"no-case": "^3.0.3",
|
||||||
|
"node-forge": "^0.9.1",
|
||||||
|
"node-libs-browser": "^2.2.1",
|
||||||
|
"node-releases": "^1.1.60",
|
||||||
|
"nopt": "^4.0.3",
|
||||||
|
"normalize-path": "^3.0.0",
|
||||||
|
"normalize-range": "^0.1.2",
|
||||||
|
"normalize-url": "^5.1.0",
|
||||||
|
"npm-run-path": "^4.0.1",
|
||||||
|
"nprogress": "^0.2.0",
|
||||||
|
"nth-check": "^1.0.2",
|
||||||
|
"num2fraction": "^1.2.2",
|
||||||
|
"number-is-nan": "^2.0.0",
|
||||||
|
"oauth-sign": "^0.9.0",
|
||||||
|
"object-assign": "^4.1.1",
|
||||||
|
"object-copy": "^1.0.0",
|
||||||
|
"object-inspect": "^1.8.0",
|
||||||
|
"object-is": "^1.1.2",
|
||||||
|
"object-keys": "^1.1.1",
|
||||||
|
"object-visit": "^1.0.1",
|
||||||
|
"object.assign": "^4.1.0",
|
||||||
|
"object.getownpropertydescriptors": "^2.1.0",
|
||||||
|
"object.pick": "^1.3.0",
|
||||||
|
"object.values": "^1.1.1",
|
||||||
|
"obuf": "^1.1.2",
|
||||||
|
"on-finished": "^2.3.0",
|
||||||
|
"on-headers": "^1.0.2",
|
||||||
|
"once": "^1.4.0",
|
||||||
|
"opencollective-postinstall": "^2.0.3",
|
||||||
|
"opn": "^6.0.0",
|
||||||
|
"optimize-css-assets-webpack-plugin": "^5.0.3",
|
||||||
|
"original": "^1.0.2",
|
||||||
|
"os-browserify": "^0.3.0",
|
||||||
|
"os-locale": "^5.0.0",
|
||||||
|
"p-defer": "^3.0.0",
|
||||||
|
"p-finally": "^2.0.1",
|
||||||
|
"p-is-promise": "^3.0.0",
|
||||||
|
"p-limit": "^3.0.2",
|
||||||
|
"p-locate": "^4.1.0",
|
||||||
|
"p-map": "^4.0.0",
|
||||||
|
"p-retry": "^4.2.0",
|
||||||
|
"p-try": "^2.2.0",
|
||||||
|
"pako": "^1.0.11",
|
||||||
|
"parallel-transform": "^1.2.0",
|
||||||
|
"param-case": "^3.0.3",
|
||||||
|
"parse-asn1": "^5.1.5",
|
||||||
|
"parse-json": "^5.0.1",
|
||||||
|
"parseurl": "^1.3.3",
|
||||||
|
"pascalcase": "^1.0.0",
|
||||||
|
"path-browserify": "^1.0.1",
|
||||||
|
"path-dirname": "^1.0.2",
|
||||||
|
"path-exists": "^4.0.0",
|
||||||
|
"path-is-absolute": "^2.0.0",
|
||||||
|
"path-is-inside": "^1.0.2",
|
||||||
|
"path-key": "^3.1.1",
|
||||||
|
"path-parse": "^1.0.6",
|
||||||
|
"path-to-regexp": "^6.1.0",
|
||||||
|
"path-type": "^4.0.0",
|
||||||
|
"pbkdf2": "^3.1.1",
|
||||||
|
"performance-now": "^2.1.0",
|
||||||
|
"pify": "^5.0.0",
|
||||||
|
"pinkie": "^2.0.4",
|
||||||
|
"pinkie-promise": "^2.0.1",
|
||||||
|
"pkg-dir": "^4.2.0",
|
||||||
|
"pkg-up": "^3.1.0",
|
||||||
|
"portfinder": "^1.0.28",
|
||||||
|
"posix-character-classes": "^1.0.0",
|
||||||
|
"postcss": "^7.0.32",
|
||||||
|
"postcss-calc": "^7.0.2",
|
||||||
|
"postcss-colormin": "^4.0.3",
|
||||||
|
"postcss-convert-values": "^4.0.1",
|
||||||
|
"postcss-discard-comments": "^4.0.2",
|
||||||
|
"postcss-discard-duplicates": "^4.0.2",
|
||||||
|
"postcss-discard-empty": "^4.0.1",
|
||||||
|
"postcss-discard-overridden": "^4.0.1",
|
||||||
|
"postcss-load-config": "^2.1.0",
|
||||||
|
"postcss-loader": "^3.0.0",
|
||||||
|
"postcss-merge-longhand": "^4.0.11",
|
||||||
|
"postcss-merge-rules": "^4.0.3",
|
||||||
|
"postcss-minify-font-values": "^4.0.2",
|
||||||
|
"postcss-minify-gradients": "^4.0.2",
|
||||||
|
"postcss-minify-params": "^4.0.2",
|
||||||
|
"postcss-minify-selectors": "^4.0.2",
|
||||||
|
"postcss-modules-extract-imports": "^2.0.0",
|
||||||
|
"postcss-modules-local-by-default": "^3.0.3",
|
||||||
|
"postcss-modules-scope": "^2.2.0",
|
||||||
|
"postcss-modules-values": "^3.0.0",
|
||||||
|
"postcss-normalize-charset": "^4.0.1",
|
||||||
|
"postcss-normalize-display-values": "^4.0.2",
|
||||||
|
"postcss-normalize-positions": "^4.0.2",
|
||||||
|
"postcss-normalize-repeat-style": "^4.0.2",
|
||||||
|
"postcss-normalize-string": "^4.0.2",
|
||||||
|
"postcss-normalize-timing-functions": "^4.0.2",
|
||||||
|
"postcss-normalize-unicode": "^4.0.1",
|
||||||
|
"postcss-normalize-url": "^4.0.1",
|
||||||
|
"postcss-normalize-whitespace": "^4.0.2",
|
||||||
|
"postcss-ordered-values": "^4.1.2",
|
||||||
|
"postcss-reduce-initial": "^4.0.3",
|
||||||
|
"postcss-reduce-transforms": "^4.0.2",
|
||||||
|
"postcss-safe-parser": "^4.0.2",
|
||||||
|
"postcss-selector-parser": "^6.0.2",
|
||||||
|
"postcss-svgo": "^4.0.2",
|
||||||
|
"postcss-unique-selectors": "^4.0.1",
|
||||||
|
"postcss-value-parser": "^4.1.0",
|
||||||
|
"prepend-http": "^3.0.1",
|
||||||
|
"prettier": "^2.0.5",
|
||||||
|
"pretty-error": "^2.1.1",
|
||||||
|
"pretty-time": "^1.1.0",
|
||||||
|
"prismjs": "^1.20.0",
|
||||||
|
"private": "^0.1.8",
|
||||||
|
"process": "^0.11.10",
|
||||||
|
"process-nextick-args": "^2.0.1",
|
||||||
|
"promise-inflight": "^1.0.1",
|
||||||
|
"proxy-addr": "^2.0.6",
|
||||||
|
"prr": "^1.0.1",
|
||||||
|
"pseudomap": "^1.0.2",
|
||||||
|
"psl": "^1.8.0",
|
||||||
|
"public-encrypt": "^4.0.3",
|
||||||
|
"pump": "^3.0.0",
|
||||||
|
"pumpify": "^2.0.1",
|
||||||
|
"punycode": "^2.1.1",
|
||||||
|
"q": "^1.5.1",
|
||||||
|
"qs": "^6.9.4",
|
||||||
|
"query-string": "^6.13.1",
|
||||||
|
"querystring": "^0.2.0",
|
||||||
|
"querystring-es3": "^0.2.1",
|
||||||
|
"querystringify": "^2.1.1",
|
||||||
|
"randombytes": "^2.1.0",
|
||||||
|
"randomfill": "^1.0.4",
|
||||||
|
"range-parser": "^1.2.1",
|
||||||
|
"raw-body": "^2.4.1",
|
||||||
|
"readable-stream": "^3.6.0",
|
||||||
|
"readdirp": "^3.4.0",
|
||||||
|
"reduce": "^1.0.2",
|
||||||
|
"regenerate": "^1.4.1",
|
||||||
|
"regenerate-unicode-properties": "^8.2.0",
|
||||||
|
"regenerator-runtime": "^0.13.7",
|
||||||
|
"regenerator-transform": "^0.14.5",
|
||||||
|
"regex-not": "^1.0.2",
|
||||||
|
"regexp.prototype.flags": "^1.3.0",
|
||||||
|
"regexpu-core": "^4.7.0",
|
||||||
|
"regjsgen": "^0.5.2",
|
||||||
|
"regjsparser": "^0.6.4",
|
||||||
|
"relateurl": "^0.2.7",
|
||||||
|
"remove-trailing-separator": "^1.1.0",
|
||||||
|
"renderkid": "^2.0.3",
|
||||||
|
"repeat-element": "^1.1.3",
|
||||||
|
"repeat-string": "^1.6.1",
|
||||||
|
"request": "^2.88.2",
|
||||||
|
"require-directory": "^2.1.1",
|
||||||
|
"require-main-filename": "^2.0.0",
|
||||||
|
"requires-port": "^1.0.0",
|
||||||
|
"reselect": "^4.0.0",
|
||||||
|
"resolve": "^1.17.0",
|
||||||
|
"resolve-cwd": "^3.0.0",
|
||||||
|
"resolve-from": "^5.0.0",
|
||||||
|
"resolve-url": "^0.2.1",
|
||||||
|
"ret": "^0.3.1",
|
||||||
|
"retry": "^0.12.0",
|
||||||
|
"rgb-regex": "^1.0.1",
|
||||||
|
"rgba-regex": "^1.0.0",
|
||||||
|
"rimraf": "^3.0.2",
|
||||||
|
"ripemd160": "^2.0.2",
|
||||||
|
"run-queue": "^2.0.1",
|
||||||
|
"safe-buffer": "^5.2.1",
|
||||||
|
"safe-regex": "^2.1.1",
|
||||||
|
"safer-buffer": "^2.1.2",
|
||||||
|
"sax": "^1.2.4",
|
||||||
|
"schema-utils": "^2.7.0",
|
||||||
|
"section-matter": "^1.0.0",
|
||||||
|
"select": "^1.1.2",
|
||||||
|
"select-hose": "^2.0.0",
|
||||||
|
"selfsigned": "^1.10.7",
|
||||||
|
"semver": "^7.3.2",
|
||||||
|
"send": "^0.17.1",
|
||||||
|
"serialize-javascript": "^4.0.0",
|
||||||
|
"serve-index": "^1.9.1",
|
||||||
|
"serve-static": "^1.14.1",
|
||||||
|
"set-blocking": "^2.0.0",
|
||||||
|
"set-value": "^3.0.2",
|
||||||
|
"setimmediate": "^1.0.5",
|
||||||
|
"setprototypeof": "^1.2.0",
|
||||||
|
"sha.js": "^2.4.11",
|
||||||
|
"shebang-command": "^2.0.0",
|
||||||
|
"shebang-regex": "^3.0.0",
|
||||||
|
"signal-exit": "^3.0.3",
|
||||||
|
"simple-swizzle": "^0.2.2",
|
||||||
|
"sitemap": "^6.2.0",
|
||||||
|
"slash": "^3.0.0",
|
||||||
|
"smoothscroll-polyfill": "^0.4.4",
|
||||||
|
"snapdragon": "^0.12.0",
|
||||||
|
"snapdragon-node": "^3.0.0",
|
||||||
|
"snapdragon-util": "^5.0.1",
|
||||||
|
"sockjs": "^0.3.21",
|
||||||
|
"sockjs-client": "^1.5.0",
|
||||||
|
"sort-keys": "^4.0.0",
|
||||||
|
"source-list-map": "^2.0.1",
|
||||||
|
"source-map": "^0.7.3",
|
||||||
|
"source-map-resolve": "^0.6.0",
|
||||||
|
"source-map-support": "^0.5.19",
|
||||||
|
"source-map-url": "^0.4.0",
|
||||||
|
"spdy": "^4.0.2",
|
||||||
|
"spdy-transport": "^3.0.0",
|
||||||
|
"split-string": "^6.1.0",
|
||||||
|
"sprintf-js": "^1.1.2",
|
||||||
|
"sshpk": "^1.16.1",
|
||||||
|
"ssri": "^8.0.0",
|
||||||
|
"stable": "^0.1.8",
|
||||||
|
"stack-utils": "^2.0.2",
|
||||||
|
"static-extend": "^0.1.2",
|
||||||
|
"statuses": "^2.0.0",
|
||||||
|
"std-env": "^2.2.1",
|
||||||
|
"stream-browserify": "^3.0.0",
|
||||||
|
"stream-each": "^1.2.3",
|
||||||
|
"stream-http": "^3.1.1",
|
||||||
|
"stream-shift": "^1.0.1",
|
||||||
|
"strict-uri-encode": "^2.0.0",
|
||||||
|
"string-width": "^4.2.0",
|
||||||
|
"string.prototype.trimleft": "^2.1.2",
|
||||||
|
"string.prototype.trimright": "^2.1.2",
|
||||||
|
"string_decoder": "^1.3.0",
|
||||||
|
"strip-ansi": "^6.0.0",
|
||||||
|
"strip-bom-string": "^1.0.0",
|
||||||
|
"strip-eof": "^2.0.0",
|
||||||
|
"stylehacks": "^4.0.3",
|
||||||
|
"stylus": "^0.54.8",
|
||||||
|
"stylus-loader": "^3.0.2",
|
||||||
|
"supports-color": "^7.1.0",
|
||||||
|
"svg-tags": "^1.0.0",
|
||||||
|
"svgo": "^1.3.2",
|
||||||
|
"tapable": "^1.1.3",
|
||||||
|
"terser": "^5.0.0",
|
||||||
|
"terser-webpack-plugin": "^4.0.0",
|
||||||
|
"text-table": "^0.2.0",
|
||||||
|
"through": "^2.3.8",
|
||||||
|
"through2": "^4.0.2",
|
||||||
|
"thunky": "^1.1.0",
|
||||||
|
"timers-browserify": "^2.0.11",
|
||||||
|
"timsort": "^0.3.0",
|
||||||
|
"tiny-emitter": "^2.1.0",
|
||||||
|
"to-arraybuffer": "^1.0.1",
|
||||||
|
"to-factory": "^1.0.0",
|
||||||
|
"to-fast-properties": "^3.0.1",
|
||||||
|
"to-object-path": "^0.3.0",
|
||||||
|
"to-regex": "^3.0.2",
|
||||||
|
"to-regex-range": "^5.0.1",
|
||||||
|
"toidentifier": "^1.0.0",
|
||||||
|
"toml": "^3.0.0",
|
||||||
|
"toposort": "^2.0.2",
|
||||||
|
"tough-cookie": "^4.0.0",
|
||||||
|
"tr46": "^2.0.2",
|
||||||
|
"tslib": "^2.0.0",
|
||||||
|
"tty-browserify": "^0.0.1",
|
||||||
|
"tunnel-agent": "^0.6.0",
|
||||||
|
"tweetnacl": "^1.0.3",
|
||||||
|
"type-fest": "^0.16.0",
|
||||||
|
"type-is": "^1.6.18",
|
||||||
|
"typedarray": "^0.0.6",
|
||||||
|
"uc.micro": "^1.0.6",
|
||||||
|
"uglify-js": "^3.10.1",
|
||||||
|
"unicode-canonical-property-names-ecmascript": "^1.0.4",
|
||||||
|
"unicode-match-property-ecmascript": "^1.0.4",
|
||||||
|
"unicode-match-property-value-ecmascript": "^1.2.0",
|
||||||
|
"unicode-property-aliases-ecmascript": "^1.1.0",
|
||||||
|
"union-value": "^2.0.1",
|
||||||
|
"uniq": "^1.0.1",
|
||||||
|
"uniqs": "^2.0.0",
|
||||||
|
"unique-filename": "^1.1.1",
|
||||||
|
"unique-slug": "^2.0.2",
|
||||||
|
"universalify": "^2.0.0",
|
||||||
|
"unpipe": "^1.0.0",
|
||||||
|
"unquote": "^1.1.1",
|
||||||
|
"unset-value": "^1.0.0",
|
||||||
|
"upath": "^1.2.0",
|
||||||
|
"upper-case": "^2.0.1",
|
||||||
|
"uri-js": "^4.2.2",
|
||||||
|
"urix": "^0.1.0",
|
||||||
|
"url": "^0.11.0",
|
||||||
|
"url-loader": "^4.1.0",
|
||||||
|
"url-parse": "^1.4.7",
|
||||||
|
"use": "^3.1.1",
|
||||||
|
"util": "^0.12.3",
|
||||||
|
"util-deprecate": "^1.0.2",
|
||||||
|
"util.promisify": "^1.0.1",
|
||||||
|
"utila": "^0.4.0",
|
||||||
|
"utils-merge": "^1.0.1",
|
||||||
|
"uuid": "^8.3.0",
|
||||||
|
"vary": "^1.1.2",
|
||||||
|
"vendors": "^1.0.4",
|
||||||
|
"verror": "^1.10.0",
|
||||||
|
"vm-browserify": "^1.1.2",
|
||||||
|
"vue": "^2.6.11",
|
||||||
|
"vue-hot-reload-api": "^2.3.4",
|
||||||
|
"vue-loader": "^15.9.3",
|
||||||
|
"vue-router": "^3.4.0",
|
||||||
|
"vue-server-renderer": "^2.6.11",
|
||||||
|
"vue-style-loader": "^4.1.2",
|
||||||
|
"vue-template-compiler": "^2.6.11",
|
||||||
|
"vue-template-es2015-compiler": "^1.9.1",
|
||||||
|
"vuepress": "^1.5.3",
|
||||||
|
"vuepress-html-webpack-plugin": "^3.2.0",
|
||||||
|
"vuepress-plugin-container": "^2.1.4",
|
||||||
|
"vuepress-plugin-sitemap": "^2.3.1",
|
||||||
|
"vuepress-plugin-smooth-scroll": "^0.0.9",
|
||||||
|
"vuepress-plugin-zooming": "^1.1.7",
|
||||||
|
"watchpack": "^1.7.4",
|
||||||
|
"wbuf": "^1.7.3",
|
||||||
|
"webidl-conversions": "^6.1.0",
|
||||||
|
"webpack": "^4.44.1",
|
||||||
|
"webpack-chain": "^6.5.1",
|
||||||
|
"webpack-dev-middleware": "^3.7.2",
|
||||||
|
"webpack-dev-server": "^3.11.0",
|
||||||
|
"webpack-log": "^3.0.1",
|
||||||
|
"webpack-merge": "^5.1.1",
|
||||||
|
"webpack-sources": "^1.4.3",
|
||||||
|
"webpackbar": "^4.0.0",
|
||||||
|
"websocket-driver": "^0.7.4",
|
||||||
|
"websocket-extensions": "^0.1.4",
|
||||||
|
"whatwg-url": "^8.1.0",
|
||||||
|
"when": "^3.7.8",
|
||||||
|
"which": "^2.0.2",
|
||||||
|
"which-module": "^2.0.0",
|
||||||
|
"worker-farm": "^1.7.0",
|
||||||
|
"wrap-ansi": "^7.0.0",
|
||||||
|
"wrappy": "^1.0.2",
|
||||||
|
"ws": "^7.3.1",
|
||||||
|
"xmlbuilder": "^15.1.1",
|
||||||
|
"xtend": "^4.0.2",
|
||||||
|
"y18n": "^4.0.0",
|
||||||
|
"yallist": "^4.0.0",
|
||||||
|
"yargs": "^15.4.1",
|
||||||
|
"yargs-parser": "^18.1.3",
|
||||||
|
"zepto": "^1.2.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {},
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vuepress dev",
|
||||||
|
"build": "vuepress build"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC"
|
||||||
|
}
|
12
docs/screenshots/README.md
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# Screenshots
|
||||||
|
|
||||||
|
<img class="no-medium-zoom zooming" src="/screenshots/login.png" alt="Login" title="Login" width="200"/>
|
||||||
|
<img class="no-medium-zoom zooming" src="/screenshots/dashboard.png" alt="Dashboard" title="Dashboard" width="200"/>
|
||||||
|
<img class="no-medium-zoom zooming" src="/screenshots/proxy-hosts.png" alt="Proxy Hosts" title="Proxy Hosts" width="200"/>
|
||||||
|
<img class="no-medium-zoom zooming" src="/screenshots/proxy-hosts-add.png" alt="Add Proxy Host" title="Add Proxy Host" width="200"/>
|
||||||
|
<img class="no-medium-zoom zooming" src="/screenshots/redirection-hosts.png" alt="Redirection Hosts" title="Redirection Hosts" width="200"/>
|
||||||
|
<img class="no-medium-zoom zooming" src="/screenshots/dead-hosts.png" alt="404 Hosts" title="404 Hosts" width="200"/>
|
||||||
|
<img class="no-medium-zoom zooming" src="/screenshots/permissions.png" alt="User Permissions" title="User Permissions" width="200"/>
|
||||||
|
<img class="no-medium-zoom zooming" src="/screenshots/certificates.png" alt="Certificates" title="Certificates" width="200"/>
|
||||||
|
<img class="no-medium-zoom zooming" src="/screenshots/audit-log.png" alt="Audit Log" title="Audit Log" width="200"/>
|
||||||
|
<img class="no-medium-zoom zooming" src="/screenshots/custom-settings.png" alt="Custom Settings" title="Custom Settings" width="200"/>
|
@ -1,13 +1,4 @@
|
|||||||
## Installation and Configuration
|
# Full Setup Instructions
|
||||||
|
|
||||||
If you just want to get up and running in the quickest time possible, grab all the files in
|
|
||||||
the [doc/example/](https://github.com/jc21/nginx-proxy-manager/tree/master/doc/example)
|
|
||||||
folder and run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker-compose up -d
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
### Configuration File
|
### Configuration File
|
||||||
|
|
||||||
@ -15,7 +6,7 @@ docker-compose up -d
|
|||||||
|
|
||||||
Don't worry, this is easy to do.
|
Don't worry, this is easy to do.
|
||||||
|
|
||||||
The app requires a configuration file to let it know what database you're using.
|
The app requires a configuration file to let it know what database you're using. By default, this file is called `config.json`
|
||||||
|
|
||||||
Here's an example configuration for `mysql` (or mariadb) that is compatible with the docker-compose example below:
|
Here's an example configuration for `mysql` (or mariadb) that is compatible with the docker-compose example below:
|
||||||
|
|
||||||
@ -32,15 +23,31 @@ Here's an example configuration for `mysql` (or mariadb) that is compatible with
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Alternatively if you would like to use a Sqlite database file:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"database": {
|
||||||
|
"engine": "knex-native",
|
||||||
|
"knex": {
|
||||||
|
"client": "sqlite3",
|
||||||
|
"connection": {
|
||||||
|
"filename": "/data/database.sqlite"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Once you've created your configuration file it's easy to mount it in the docker container.
|
Once you've created your configuration file it's easy to mount it in the docker container.
|
||||||
|
|
||||||
**Note:** After the first run of the application, the config file will be altered to include generated encryption keys unique to your installation. These keys
|
**Note:** After the first run of the application, the config file will be altered to include generated encryption keys unique to your installation. These keys
|
||||||
affect the login and session management of the application. If these keys change for any reason, all users will be logged out.
|
affect the login and session management of the application. If these keys change for any reason, all users will be logged out.
|
||||||
|
|
||||||
|
|
||||||
### Database
|
### MySQL Database
|
||||||
|
|
||||||
This app doesn't come with a database, you have to provide one yourself. Currently only `mysql/mariadb` is supported for the minimum versions:
|
If you opt for the MySQL configuration you will have to provide the database server yourself. You can also use MariaDB. Here are the minimum supported versions:
|
||||||
|
|
||||||
- MySQL v5.7.8+
|
- MySQL v5.7.8+
|
||||||
- MariaDB v10.2.7+
|
- MariaDB v10.2.7+
|
||||||
@ -48,6 +55,12 @@ This app doesn't come with a database, you have to provide one yourself. Current
|
|||||||
It's easy to use another docker container for your database also and link it as part of the docker stack, so that's what the following examples
|
It's easy to use another docker container for your database also and link it as part of the docker stack, so that's what the following examples
|
||||||
are going to use.
|
are going to use.
|
||||||
|
|
||||||
|
::: warning
|
||||||
|
|
||||||
|
When using a `mariadb` database, the NPM configuration file should still use the `mysql` engine!
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
|
||||||
### Running the App
|
### Running the App
|
||||||
|
|
||||||
@ -61,11 +74,14 @@ services:
|
|||||||
restart: always
|
restart: always
|
||||||
ports:
|
ports:
|
||||||
# Public HTTP Port:
|
# Public HTTP Port:
|
||||||
- 80:80
|
- '80:80'
|
||||||
# Public HTTPS Port:
|
# Public HTTPS Port:
|
||||||
- 443:443
|
- '443:443'
|
||||||
# Admin Web Port:
|
# Admin Web Port:
|
||||||
- 81:81
|
- '81:81'
|
||||||
|
environment:
|
||||||
|
# Uncomment this if IPv6 is not enabled on your host
|
||||||
|
# DISABLE_IPV6: 'true'
|
||||||
volumes:
|
volumes:
|
||||||
# Make sure this config.json file exists as per instructions above:
|
# Make sure this config.json file exists as per instructions above:
|
||||||
- ./config.json:/app/config/production.json
|
- ./config.json:/app/config/production.json
|
||||||
@ -74,13 +90,13 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
- db
|
- db
|
||||||
db:
|
db:
|
||||||
image: mariadb:latest
|
image: jc21/mariadb-aria:10.4
|
||||||
restart: always
|
restart: always
|
||||||
environment:
|
environment:
|
||||||
MYSQL_ROOT_PASSWORD: "npm"
|
MYSQL_ROOT_PASSWORD: 'npm'
|
||||||
MYSQL_DATABASE: "npm"
|
MYSQL_DATABASE: 'npm'
|
||||||
MYSQL_USER: "npm"
|
MYSQL_USER: 'npm'
|
||||||
MYSQL_PASSWORD: "npm"
|
MYSQL_PASSWORD: 'npm'
|
||||||
volumes:
|
volumes:
|
||||||
- ./data/mysql:/var/lib/mysql
|
- ./data/mysql:/var/lib/mysql
|
||||||
```
|
```
|
||||||
@ -91,6 +107,7 @@ Then:
|
|||||||
docker-compose up -d
|
docker-compose up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The config file (config.json) must be present in this directory.
|
||||||
|
|
||||||
### Running on Raspberry PI / ARM devices
|
### Running on Raspberry PI / ARM devices
|
||||||
|
|
||||||
@ -102,7 +119,7 @@ The docker images support the following architectures:
|
|||||||
The docker images are a manifest of all the architecture docker builds supported, so this means
|
The docker images are a manifest of all the architecture docker builds supported, so this means
|
||||||
you don't have to worry about doing anything special and you can follow the common instructions above.
|
you don't have to worry about doing anything special and you can follow the common instructions above.
|
||||||
|
|
||||||
Check out the [dockerhub tags](https://cloud.docker.com/repository/registry-1.docker.io/jc21/nginx-proxy-manager/tags)
|
Check out the [dockerhub tags](https://hub.docker.com/r/jc21/nginx-proxy-manager/tags)
|
||||||
for a list of supported architectures and if you want one that doesn't exist,
|
for a list of supported architectures and if you want one that doesn't exist,
|
||||||
[create a feature request](https://github.com/jc21/nginx-proxy-manager/issues/new?assignees=&labels=enhancement&template=feature_request.md&title=).
|
[create a feature request](https://github.com/jc21/nginx-proxy-manager/issues/new?assignees=&labels=enhancement&template=feature_request.md&title=).
|
||||||
|
|
||||||
@ -114,9 +131,9 @@ on Raspbian.
|
|||||||
|
|
||||||
After the app is running for the first time, the following will happen:
|
After the app is running for the first time, the following will happen:
|
||||||
|
|
||||||
- The database will initialize with table structures
|
1. The database will initialize with table structures
|
||||||
- GPG keys will be generated and saved in the configuration file
|
2. GPG keys will be generated and saved in the configuration file
|
||||||
- A default admin user will be created
|
3. A default admin user will be created
|
||||||
|
|
||||||
This process can take a couple of minutes depending on your machine.
|
This process can take a couple of minutes depending on your machine.
|
||||||
|
|
||||||
@ -129,22 +146,3 @@ Password: changeme
|
|||||||
```
|
```
|
||||||
|
|
||||||
Immediately after logging in with this default user you will be asked to modify your details and change your password.
|
Immediately after logging in with this default user you will be asked to modify your details and change your password.
|
||||||
|
|
||||||
|
|
||||||
### Advanced Options
|
|
||||||
|
|
||||||
#### X-FRAME-OPTIONS Header
|
|
||||||
|
|
||||||
You can configure the [`X-FRAME-OPTIONS`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options) header
|
|
||||||
value by specifying it as a Docker environment variable. The default if not specified is `deny`.
|
|
||||||
|
|
||||||
```yml
|
|
||||||
...
|
|
||||||
environment:
|
|
||||||
X_FRAME_OPTIONS: "sameorigin"
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
... -e "X_FRAME_OPTIONS=sameorigin" ...
|
|
||||||
```
|
|
12
docs/third-party/README.md
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# Third Party
|
||||||
|
|
||||||
|
As this software gains popularity it's common to see it integrated with other platforms. Please be aware that unless specifically mentioned in the documenation of those
|
||||||
|
integrations, they are *not supported* by me.
|
||||||
|
|
||||||
|
Known integrations:
|
||||||
|
|
||||||
|
- [HomeAssistant Hass.io plugin](https://github.com/hassio-addons/addon-nginx-proxy-manager)
|
||||||
|
- [UnRaid / Synology](https://github.com/jlesage/docker-nginx-proxy-manager)
|
||||||
|
|
||||||
|
If you would like your integration of NPM listed, please open a
|
||||||
|
[Github issue](https://github.com/jc21/nginx-proxy-manager/issues/new?assignees=&labels=enhancement&template=feature_request.md&title=)
|
10348
docs/yarn.lock
Normal file
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 56 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 16 KiB |
@ -2,8 +2,8 @@
|
|||||||
<browserconfig>
|
<browserconfig>
|
||||||
<msapplication>
|
<msapplication>
|
||||||
<tile>
|
<tile>
|
||||||
<square150x150logo src="/images/favicon/mstile-150x150.png"/>
|
<square150x150logo src="/images/favicons/mstile-150x150.png"/>
|
||||||
<TileColor>#f0ad00</TileColor>
|
<TileColor>#333333</TileColor>
|
||||||
</tile>
|
</tile>
|
||||||
</msapplication>
|
</msapplication>
|
||||||
</browserconfig>
|
</browserconfig>
|
||||||
|
Before Width: | Height: | Size: 958 B After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 12 KiB |
@ -9,24 +9,77 @@ Created by potrace 1.11, written by Peter Selinger 2001-2013
|
|||||||
</metadata>
|
</metadata>
|
||||||
<g transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"
|
<g transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"
|
||||||
fill="#000000" stroke="none">
|
fill="#000000" stroke="none">
|
||||||
<path d="M2384 5110 c-1036 -74 -1922 -761 -2248 -1743 -146 -437 -170 -915
|
<path d="M2348 4766 c-109 -63 -203 -116 -210 -120 -7 -3 -20 -10 -28 -16 -19
|
||||||
-70 -1367 193 -869 838 -1582 1687 -1864 437 -146 915 -170 1367 -70 632 141
|
-12 -114 -68 -410 -238 -481 -276 -1017 -586 -1087 -628 -39 -23 -73 -40 -76
|
||||||
1204 533 1564 1074 222 333 358 697 412 1100 22 167 22 473 0 640 -96 722
|
-38 -3 3 -5 -519 -5 -1160 l0 -1165 292 -169 c293 -170 727 -421 801 -462 22
|
||||||
-473 1348 -1062 1767 -472 335 -1074 504 -1650 463z m1514 -1050 c173 -18 201
|
-12 99 -56 170 -98 167 -96 690 -399 732 -423 l32 -18 207 120 c114 66 226
|
||||||
-52 210 -250 8 -190 -30 -414 -110 -655 -112 -338 -282 -619 -509 -842 -131
|
131 248 143 23 13 214 124 426 246 212 122 401 231 420 242 19 11 127 73 240
|
||||||
-130 -233 -203 -384 -278 -211 -105 -410 -162 -663 -188 l-114 -12 -97 -109
|
138 213 123 225 130 383 220 61 35 95 60 92 68 -3 7 0 10 5 7 11 -7 7 36 -4
|
||||||
c-201 -228 -505 -449 -846 -616 -210 -102 -316 -133 -338 -98 -25 39 106 345
|
54 -4 5 -2 13 4 16 8 5 7 12 -2 23 -10 12 -10 14 0 8 6 -4 12 -2 12 3 0 6 -6
|
||||||
248 578 166 275 296 430 548 657 36 32 47 49 47 72 l0 30 -137 -66 c-229 -109
|
11 -12 11 -10 0 -9 3 1 9 10 6 10 10 1 16 -9 6 -9 9 0 15 9 6 9 12 0 30 -9 17
|
||||||
-615 -273 -644 -273 -15 0 -35 7 -43 16 -24 24 -20 109 8 169 38 82 425 784
|
-9 21 0 15 14 -9 10 27 -5 37 -6 5 -6 8 2 8 11 0 10 30 -2 49 -3 5 -1 13 5 16
|
||||||
473 860 88 136 163 193 292 221 71 15 247 18 324 5 l48 -8 49 61 c305 381 900
|
8 5 7 11 -1 22 -10 11 -10 16 0 22 10 7 11 13 1 31 -8 15 -8 20 -1 16 6 -4 11
|
||||||
682 1434 726 50 4 96 8 101 8 6 1 52 -3 103 -8z m-558 -2245 c-21 -148 -73
|
-3 11 3 0 5 -5 13 -11 17 -8 5 -8 9 1 14 9 6 9 12 0 30 -8 15 -8 20 -1 16 15
|
||||||
-226 -214 -319 -97 -64 -842 -472 -899 -492 -47 -17 -110 -18 -138 -4 -13 7
|
-10 14 7 -1 22 -9 9 -9 12 0 12 12 0 12 26 -1 46 -3 6 -3 14 0 18 10 10 11
|
||||||
-19 21 -19 44 0 29 102 278 230 563 36 81 28 76 165 88 273 25 602 139 810
|
256 1 262 -4 3 -4 13 2 23 6 12 5 22 -3 31 -9 10 -9 11 1 6 6 -4 12 -2 12 3 0
|
||||||
283 l70 48 3 -65 c2 -36 -2 -102 -8 -146z"/>
|
6 -5 11 -10 11 -7 0 -7 6 0 20 6 11 7 20 1 20 -12 0 -9 33 4 43 6 6 5 10 -3
|
||||||
<path d="M3580 3711 c-72 -23 -112 -76 -112 -151 0 -88 62 -152 150 -153 194
|
14 -10 4 -10 8 0 21 10 12 10 14 1 8 -7 -4 -13 -2 -13 3 0 6 6 11 13 11 9 0 8
|
||||||
-3 214 278 22 307 -19 3 -46 1 -60 -3z"/>
|
3 -2 9 -11 7 -11 10 0 15 11 5 11 7 1 12 -10 5 -10 9 0 22 10 12 10 14 1 8 -7
|
||||||
<path d="M3035 3392 c-68 -33 -128 -93 -158 -161 -31 -69 -29 -178 5 -252 54
|
-4 -13 -2 -13 3 0 6 6 11 13 11 9 0 8 3 -2 9 -11 7 -11 10 0 15 11 5 11 7 1
|
||||||
-117 159 -184 288 -184 96 1 169 33 234 106 60 66 80 129 74 228 -7 118 -59
|
12 -10 5 -10 9 0 22 10 12 10 14 1 8 -7 -4 -13 -2 -13 3 0 6 6 11 13 11 9 0 8
|
||||||
201 -160 256 -44 24 -67 30 -138 33 -77 3 -90 1 -145 -26z"/>
|
3 -2 9 -11 7 -11 10 0 15 11 5 11 7 1 12 -10 5 -10 9 0 22 10 12 10 14 1 8 -7
|
||||||
|
-4 -13 -3 -13 3 0 5 6 12 13 15 9 5 9 7 0 12 -9 4 -10 11 -3 25 7 13 7 19 1
|
||||||
|
19 -7 0 -7 8 -1 24 6 16 5 27 -3 35 -8 8 -8 14 -2 19 15 10 17 32 5 46 -5 7
|
||||||
|
-7 16 -3 20 12 12 10 190 -2 198 -7 5 -7 8 1 8 12 0 12 29 -1 49 -5 10 -4 12
|
||||||
|
3 7 7 -4 12 -3 12 3 0 5 -5 13 -11 17 -8 5 -8 9 1 14 9 6 9 12 0 30 -8 15 -8
|
||||||
|
20 -1 16 6 -4 11 -3 11 3 0 5 -5 13 -11 17 -8 5 -8 9 1 14 9 6 9 12 0 30 -8
|
||||||
|
16 -9 20 -1 15 12 -7 9 34 -3 54 -4 5 -2 13 4 16 8 5 7 12 -2 23 -10 12 -10
|
||||||
|
14 0 8 6 -4 12 -2 12 3 0 6 -6 11 -12 11 -10 0 -9 3 1 9 10 6 10 10 1 16 -9 6
|
||||||
|
-9 9 0 15 9 6 9 12 0 30 -9 17 -9 21 0 15 14 -9 10 27 -5 37 -6 5 -6 8 2 8 11
|
||||||
|
0 10 36 -2 48 -2 3 0 11 6 18 7 8 7 14 -1 19 -5 3 -10 12 -10 18 0 7 4 6 10
|
||||||
|
-3 8 -12 9 -9 8 12 -2 24 -34 45 -313 206 -458 265 -1024 592 -1070 617 -22
|
||||||
|
13 -174 100 -338 194 -165 94 -304 171 -310 171 -7 0 -101 -52 -209 -114z
|
||||||
|
m347 -191 c33 -19 85 -49 116 -66 l56 -32 0 -174 -1 -174 -68 -40 c-80 -47
|
||||||
|
-184 -105 -214 -120 -18 -9 -43 1 -165 71 -79 46 -146 84 -148 86 -2 2 -4 17
|
||||||
|
-4 34 0 36 -6 36 -102 11 -204 -54 -409 -148 -565 -259 -164 -116 -329 -287
|
||||||
|
-433 -447 -105 -163 -197 -382 -232 -554 -16 -78 -20 -102 -30 -191 -7 -50 -6
|
||||||
|
-247 0 -300 3 -25 7 -69 10 -99 3 -29 8 -57 10 -61 7 -11 -5 -40 -16 -40 -4 0
|
||||||
|
-35 -16 -68 -35 -64 -38 -77 -42 -78 -22 -1 19 -14 91 -18 105 -25 73 -25 511
|
||||||
|
0 584 2 7 6 27 8 43 12 77 66 267 97 340 116 269 219 425 399 606 173 172 297
|
||||||
|
262 506 364 144 71 261 112 412 146 l101 23 -2 50 -1 50 149 88 150 87 35 -20
|
||||||
|
c20 -10 63 -35 96 -54z m512 -300 c296 -108 565 -305 798 -586 96 -115 237
|
||||||
|
-374 288 -529 100 -300 129 -643 77 -900 -6 -30 -13 -68 -16 -85 -4 -28 -23
|
||||||
|
-98 -57 -207 l-15 -48 47 -25 46 -25 0 -175 0 -174 -110 -62 c-60 -35 -127
|
||||||
|
-74 -148 -88 -45 -29 -21 -38 -253 97 l-91 53 1 174 c1 95 3 175 6 177 3 3 69
|
||||||
|
41 148 86 141 80 144 81 173 67 16 -9 29 -13 29 -10 0 3 0 7 0 10 0 3 7 22 14
|
||||||
|
42 21 55 55 211 63 290 9 85 10 330 0 403 -21 171 -95 421 -159 534 -5 11 -22
|
||||||
|
40 -36 65 -140 253 -346 467 -597 621 -75 46 -204 108 -270 131 -22 7 -43 16
|
||||||
|
-48 19 -6 5 -12 143 -8 173 2 12 16 9 118 -28z m-231 -776 c645 -372 604 -346
|
||||||
|
605 -377 0 -15 1 -278 1 -586 l1 -558 -29 -24 c-16 -13 -32 -23 -35 -24 -4 0
|
||||||
|
-421 -236 -529 -300 -8 -4 -109 -63 -223 -129 l-208 -121 -132 76 c-73 42
|
||||||
|
-148 85 -167 96 -28 16 -222 127 -473 271 -18 11 -82 48 -142 83 l-111 64 1
|
||||||
|
589 c0 324 2 591 5 594 3 2 145 84 315 182 171 98 378 217 461 265 83 48 157
|
||||||
|
90 165 93 11 6 41 24 75 46 1 1 190 -108 420 -240z m-1788 -1533 c71 -41 138
|
||||||
|
-80 149 -87 19 -11 20 -23 20 -186 l0 -174 -29 -15 c-15 -8 -28 -18 -28 -23 0
|
||||||
|
-13 167 -174 235 -227 125 -97 270 -180 406 -235 74 -29 194 -67 233 -73 18
|
||||||
|
-3 39 -8 47 -10 73 -26 352 -39 514 -24 95 8 261 43 354 74 163 55 413 195
|
||||||
|
535 302 15 12 32 22 38 22 7 0 45 -19 85 -42 l74 -42 -23 -21 c-260 -239 -658
|
||||||
|
-426 -983 -460 -27 -3 -63 -8 -79 -11 -32 -7 -285 -7 -361 0 -200 18 -437 87
|
||||||
|
-633 184 -194 96 -315 185 -487 355 l-120 118 -38 -21 -38 -22 -137 78 c-75
|
||||||
|
42 -144 83 -152 90 -12 10 -15 43 -14 178 0 99 4 170 10 176 8 8 283 168 291
|
||||||
|
170 1 0 60 -33 131 -74z"/>
|
||||||
|
<path d="M2475 4451 l-80 -47 -3 -84 c-3 -111 0 -122 46 -147 21 -11 59 -32
|
||||||
|
83 -46 l44 -26 80 46 c44 25 83 51 87 56 3 6 6 53 5 106 l-2 94 -72 43 c-39
|
||||||
|
23 -80 44 -90 47 -10 3 -51 -15 -98 -42z"/>
|
||||||
|
<path d="M3978 1843 l-78 -45 0 -102 0 -102 83 -47 c46 -26 87 -47 92 -47 4 0
|
||||||
|
44 21 89 47 l83 46 -1 101 -1 101 -80 47 c-44 27 -87 47 -95 47 -8 -1 -50 -22
|
||||||
|
-92 -46z"/>
|
||||||
|
<path d="M2307 3015 c-137 -80 -253 -145 -258 -145 -11 0 -10 21 -12 -318 -2
|
||||||
|
-241 0 -285 13 -296 14 -11 435 -258 487 -285 20 -10 50 4 275 135 139 80 256
|
||||||
|
151 260 157 3 6 6 142 6 303 l0 293 -132 77 c-72 42 -147 86 -166 96 -19 11
|
||||||
|
-76 45 -127 74 -50 30 -93 54 -95 54 -1 -1 -115 -66 -251 -145z"/>
|
||||||
|
<path d="M971 1846 c-40 -24 -76 -48 -80 -54 -5 -8 -6 -160 -2 -194 1 -8 156
|
||||||
|
-98 169 -98 5 0 46 21 90 47 l81 47 3 70 c6 126 4 131 -60 168 -90 51 -104 58
|
||||||
|
-116 58 -6 -1 -44 -20 -85 -44z"/>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 5.6 KiB |
@ -1,13 +1,14 @@
|
|||||||
{
|
{
|
||||||
"name": "",
|
"name": "",
|
||||||
|
"short_name": "",
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"src": "/images/favicon/android-chrome-192x192.png",
|
"src": "/images/favicons/android-chrome-192x192.png",
|
||||||
"sizes": "192x192",
|
"sizes": "192x192",
|
||||||
"type": "image/png"
|
"type": "image/png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/images/favicon/android-chrome-512x512.png",
|
"src": "/images/favicons/android-chrome-512x512.png",
|
||||||
"sizes": "512x512",
|
"sizes": "512x512",
|
||||||
"type": "image/png"
|
"type": "image/png"
|
||||||
}
|
}
|
BIN
frontend/app-images/logo-256.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
frontend/app-images/logo-text-vertical-grey.png
Normal file
After Width: | Height: | Size: 14 KiB |
@ -5,22 +5,20 @@
|
|||||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||||
<meta http-equiv="Content-Language" content="en">
|
<meta http-equiv="Content-Language" content="en">
|
||||||
<meta name="msapplication-TileColor" content="#2d89ef">
|
|
||||||
<meta name="theme-color" content="#4188c9">
|
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
<meta name="mobile-web-app-capable" content="yes">
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
<meta name="HandheldFriendly" content="True">
|
<meta name="HandheldFriendly" content="True">
|
||||||
<meta name="MobileOptimized" content="320">
|
<meta name="MobileOptimized" content="320">
|
||||||
<title><%- title %></title>
|
<title><%- title %></title>
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/images/favicons/apple-touch-icon.png?v=<%= version %>">
|
<link rel="apple-touch-icon" sizes="180x180" href="/images/favicons/apple-touch-icon.png">
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="/images/favicons/favicon-32x32.png?v=<%= version %>">
|
<link rel="icon" type="image/png" sizes="32x32" href="/images/favicons/favicon-32x32.png">
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="/images/favicons/favicon-16x16.png?v=<%= version %>">
|
<link rel="icon" type="image/png" sizes="16x16" href="/images/favicons/favicon-16x16.png">
|
||||||
<link rel="manifest" href="/images/favicons/site.webmanifest?v=<%= version %>">
|
<link rel="manifest" href="/images/favicons/site.webmanifest">
|
||||||
<link rel="mask-icon" href="/images/favicons/safari-pinned-tab.svg?v=<%= version %>" color="#5bbad5">
|
<link rel="mask-icon" href="/images/favicons/safari-pinned-tab.svg" color="#5bbad5">
|
||||||
<link rel="shortcut icon" href="/images/favicons/favicon.ico?v=<%= version %>">
|
<link rel="shortcut icon" href="/images/favicons/favicon.ico">
|
||||||
<meta name="msapplication-TileColor" content="#f5f5f5">
|
<meta name="msapplication-TileColor" content="#333333">
|
||||||
<meta name="msapplication-config" content="/images/favicons/browserconfig.xml?v=<%= version %>">
|
<meta name="msapplication-config" content="/images/favicons/browserconfig.xml">
|
||||||
<meta name="theme-color" content="#ffffff">
|
<meta name="theme-color" content="#ffffff">
|
||||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,300i,400,400i,500,500i,600,600i,700,700i&subset=latin-ext">
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,300i,400,400i,500,500i,600,600i,700,700i&subset=latin-ext">
|
||||||
<link href="/css/main.css?v=<%= version %>" rel="stylesheet">
|
<link href="/css/main.css?v=<%= version %>" rel="stylesheet">
|
||||||
|
@ -3,8 +3,17 @@
|
|||||||
<h5 class="modal-title"><%- i18n('access-lists', 'form-title', {id: id}) %></h5>
|
<h5 class="modal-title"><%- i18n('access-lists', 'form-title', {id: id}) %></h5>
|
||||||
<button type="button" class="close cancel" aria-label="Close" data-dismiss="modal"> </button>
|
<button type="button" class="close cancel" aria-label="Close" data-dismiss="modal"> </button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body has-tabs">
|
||||||
<form>
|
<form>
|
||||||
|
<ul class="nav nav-tabs" role="tablist">
|
||||||
|
<li role="presentation" class="nav-item"><a href="#details" aria-controls="tab1" role="tab" data-toggle="tab" class="nav-link active show" aria-selected="true"><i class="fe fe-zap"></i> <%- i18n('access-lists', 'details') %></a></li>
|
||||||
|
<li role="presentation" class="nav-item"><a href="#auth" aria-controls="tab4" role="tab" data-toggle="tab" class="nav-link" aria-selected="false"><i class="fe fe-users"></i> <%- i18n('access-lists', 'authorization') %></a></li>
|
||||||
|
<li role="presentation" class="nav-item"><a href="#access" aria-controls="tab2" role="tab" data-toggle="tab" class="nav-link" aria-selected="false"><i class="fe fe-radio"></i> <%- i18n('access-lists', 'access') %></a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="tab-content">
|
||||||
|
<!-- Details -->
|
||||||
|
<div role="tabpanel" class="tab-pane active show" id="details">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-12 col-md-12">
|
<div class="col-sm-12 col-md-12">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@ -12,6 +21,22 @@
|
|||||||
<input type="text" name="name" class="form-control" value="<%- name %>" required>
|
<input type="text" name="name" class="form-control" value="<%- name %>" required>
|
||||||
</div>
|
</div>
|
||||||
</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="satisfy_any" value="1"<%- typeof satisfy_any !== 'undefined' && satisfy_any ? ' checked' : '' %>>
|
||||||
|
<span class="custom-switch-indicator"></span>
|
||||||
|
<span class="custom-switch-description"><%- i18n('access-lists', 'satisfy-any') %></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Authorization -->
|
||||||
|
<div class="tab-pane" id="auth">
|
||||||
|
<div class="row">
|
||||||
<div class="col-sm-6 col-md-6">
|
<div class="col-sm-6 col-md-6">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label"><%- i18n('str', 'username') %></label>
|
<label class="form-label"><%- i18n('str', 'username') %></label>
|
||||||
@ -25,6 +50,27 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="items"><!-- items --></div>
|
<div class="items"><!-- items --></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Access -->
|
||||||
|
<div class="tab-pane" id="access">
|
||||||
|
<div class="clients"><!-- clients --></div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-3 col-md-3">
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" class="form-control disabled" value="deny" disabled>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-9 col-md-9">
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" class="form-control disabled" value="all" disabled>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-muted">Note that the <code>allow</code> and <code>deny</code> directives will be applied in the order they are defined.</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
|
@ -3,6 +3,7 @@ const App = require('../../main');
|
|||||||
const AccessListModel = require('../../../models/access-list');
|
const AccessListModel = require('../../../models/access-list');
|
||||||
const template = require('./form.ejs');
|
const template = require('./form.ejs');
|
||||||
const ItemView = require('./form/item');
|
const ItemView = require('./form/item');
|
||||||
|
const ClientView = require('./form/client');
|
||||||
|
|
||||||
require('jquery-serializejson');
|
require('jquery-serializejson');
|
||||||
|
|
||||||
@ -10,12 +11,17 @@ const ItemsView = Mn.CollectionView.extend({
|
|||||||
childView: ItemView
|
childView: ItemView
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const ClientsView = Mn.CollectionView.extend({
|
||||||
|
childView: ClientView
|
||||||
|
});
|
||||||
|
|
||||||
module.exports = Mn.View.extend({
|
module.exports = Mn.View.extend({
|
||||||
template: template,
|
template: template,
|
||||||
className: 'modal-dialog',
|
className: 'modal-dialog',
|
||||||
|
|
||||||
ui: {
|
ui: {
|
||||||
items_region: '.items',
|
items_region: '.items',
|
||||||
|
clients_region: '.clients',
|
||||||
form: 'form',
|
form: 'form',
|
||||||
buttons: '.modal-footer button',
|
buttons: '.modal-footer button',
|
||||||
cancel: 'button.cancel',
|
cancel: 'button.cancel',
|
||||||
@ -23,7 +29,8 @@ module.exports = Mn.View.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
regions: {
|
regions: {
|
||||||
items_region: '@ui.items_region'
|
items_region: '@ui.items_region',
|
||||||
|
clients_region: '@ui.clients_region'
|
||||||
},
|
},
|
||||||
|
|
||||||
events: {
|
events: {
|
||||||
@ -38,6 +45,7 @@ module.exports = Mn.View.extend({
|
|||||||
let view = this;
|
let view = this;
|
||||||
let form_data = this.ui.form.serializeJSON();
|
let form_data = this.ui.form.serializeJSON();
|
||||||
let items_data = [];
|
let items_data = [];
|
||||||
|
let clients_data = [];
|
||||||
|
|
||||||
form_data.username.map(function (val, idx) {
|
form_data.username.map(function (val, idx) {
|
||||||
if (val.trim().length) {
|
if (val.trim().length) {
|
||||||
@ -48,16 +56,29 @@ module.exports = Mn.View.extend({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!items_data.length) {
|
form_data.address.map(function (val, idx) {
|
||||||
alert('You must specify at least 1 Username and Password combination');
|
if (val.trim().length) {
|
||||||
|
clients_data.push({
|
||||||
|
address: val.trim(),
|
||||||
|
directive: form_data.directive[idx]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!items_data.length && !clients_data.length) {
|
||||||
|
alert('You must specify at least 1 Authorization or Access rule');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = {
|
let data = {
|
||||||
name: form_data.name,
|
name: form_data.name,
|
||||||
items: items_data
|
satisfy_any: !!form_data.satisfy_any,
|
||||||
|
items: items_data,
|
||||||
|
clients: clients_data
|
||||||
};
|
};
|
||||||
|
|
||||||
|
console.log(data);
|
||||||
|
|
||||||
let method = App.Api.Nginx.AccessLists.create;
|
let method = App.Api.Nginx.AccessLists.create;
|
||||||
let is_new = true;
|
let is_new = true;
|
||||||
|
|
||||||
@ -88,6 +109,7 @@ module.exports = Mn.View.extend({
|
|||||||
|
|
||||||
onRender: function () {
|
onRender: function () {
|
||||||
let items = this.model.get('items');
|
let items = this.model.get('items');
|
||||||
|
let clients = this.model.get('clients');
|
||||||
|
|
||||||
// Add empty items to the end of the list. This is cheating but hey I don't have the time to do it right
|
// Add empty items to the end of the list. This is cheating but hey I don't have the time to do it right
|
||||||
let items_to_add = 5 - items.length;
|
let items_to_add = 5 - items.length;
|
||||||
@ -97,9 +119,20 @@ module.exports = Mn.View.extend({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let clients_to_add = 4 - clients.length;
|
||||||
|
if (clients_to_add) {
|
||||||
|
for (let i = 0; i < clients_to_add; i++) {
|
||||||
|
clients.push({});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.showChildView('items_region', new ItemsView({
|
this.showChildView('items_region', new ItemsView({
|
||||||
collection: new Backbone.Collection(items)
|
collection: new Backbone.Collection(items)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
this.showChildView('clients_region', new ClientsView({
|
||||||
|
collection: new Backbone.Collection(clients)
|
||||||
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
initialize: function (options) {
|
initialize: function (options) {
|
||||||
|
13
frontend/js/app/nginx/access/form/client.ejs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<div class="col-sm-3 col-md-3">
|
||||||
|
<div class="form-group">
|
||||||
|
<select name="directive[]" class="form-control custom-select" placeholder="http">
|
||||||
|
<option value="allow" <%- typeof directive == 'undefined' || directive === 'allow' ? 'selected' : '' %>>allow</option>
|
||||||
|
<option value="deny" <%- typeof directive !== 'undefined' && directive === 'deny' ? 'selected' : '' %>>deny</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-9 col-md-9">
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" name="address[]" class="form-control" value="<%- typeof address !== 'undefined' ? address : '' %>" value="">
|
||||||
|
</div>
|
||||||
|
</div>
|
7
frontend/js/app/nginx/access/form/client.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
const Mn = require('backbone.marionette');
|
||||||
|
const template = require('./client.ejs');
|
||||||
|
|
||||||
|
module.exports = Mn.View.extend({
|
||||||
|
template: template,
|
||||||
|
className: 'row'
|
||||||
|
});
|
@ -14,6 +14,16 @@
|
|||||||
<td>
|
<td>
|
||||||
<%- i18n('access-lists', 'item-count', {count: items.length || 0}) %>
|
<%- i18n('access-lists', 'item-count', {count: items.length || 0}) %>
|
||||||
</td>
|
</td>
|
||||||
|
<td>
|
||||||
|
<%- i18n('access-lists', 'client-count', {count: clients.length || 0}) %>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<% if (satisfy_any) { %>
|
||||||
|
<%- i18n('str', 'any') %>
|
||||||
|
<%} else { %>
|
||||||
|
<%- i18n('str', 'all') %>
|
||||||
|
<% } %>
|
||||||
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<%- i18n('access-lists', 'proxy-host-count', {count: proxy_host_count}) %>
|
<%- i18n('access-lists', 'proxy-host-count', {count: proxy_host_count}) %>
|
||||||
</td>
|
</td>
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<th width="30"> </th>
|
<th width="30"> </th>
|
||||||
<th><%- i18n('str', 'name') %></th>
|
<th><%- i18n('str', 'name') %></th>
|
||||||
<th><%- i18n('users', 'title') %></th>
|
<th><%- i18n('access-lists', 'authorization') %></th>
|
||||||
|
<th><%- i18n('access-lists', 'access') %></th>
|
||||||
|
<th><%- i18n('access-lists', 'satisfy') %></th>
|
||||||
<th><%- i18n('proxy-hosts', 'title') %></th>
|
<th><%- i18n('proxy-hosts', 'title') %></th>
|
||||||
<% if (canManage) { %>
|
<% if (canManage) { %>
|
||||||
<th> </th>
|
<th> </th>
|
||||||
|