Compare commits
277 Commits
v2.8.1
...
ssl-passth
Author | SHA1 | Date | |
---|---|---|---|
70163a66fb | |||
6e82161987 | |||
f650137c84 | |||
02d3093d88 | |||
ab026e5e18 | |||
725ba83606 | |||
281906c0b5 | |||
8ed121f43d | |||
81a9cab2b3 | |||
8d98a417c5 | |||
6fa81b179b | |||
9e169fbb42 | |||
27f84f880a | |||
5a2548c89d | |||
5b1f0cead1 | |||
0d9c941b4e | |||
8865aa9c8c | |||
6d8c4218f1 | |||
c134a43337 | |||
780759dc27 | |||
85128f08f3 | |||
d2f8c1e5f1 | |||
9c88b9c1e9 | |||
13fd2ce4e2 | |||
9979f516d6 | |||
39a5cd2d6e | |||
784516283f | |||
ce503232c3 | |||
f2edf9130f | |||
413ab50fc4 | |||
c1880bd3ff | |||
0f0a672275 | |||
babc5b7a38 | |||
b96c996a45 | |||
fb8f2c2f9a | |||
6794937391 | |||
f022e84979 | |||
fd5ac952cc | |||
07f60e5c77 | |||
628b8a7e1f | |||
30a442807d | |||
1626c8edd1 | |||
ca6561bf6c | |||
273a81471d | |||
8b07a67133 | |||
32089ea272 | |||
658acd147c | |||
ca3370a6ac | |||
c4e2557de2 | |||
6f2b4fdf86 | |||
f302ff71c9 | |||
fee87a44d6 | |||
8944609b63 | |||
be87c45f27 | |||
1b1807c79a | |||
a8f4699816 | |||
ac3df6dd77 | |||
5c67908460 | |||
7b67ef3015 | |||
e5a3b5ee2f | |||
5e9ff4d2bf | |||
daa71764b6 | |||
6a6c2ef192 | |||
320315956d | |||
4f10d129c2 | |||
62eb3fcd85 | |||
ab40e4e2cf | |||
0bb9450642 | |||
a6e15532b9 | |||
9a89a8a77e | |||
fe3675dc7a | |||
5c9acc2bff | |||
c94e937a50 | |||
3e4e10e644 | |||
ba7bb57ca2 | |||
14c125150a | |||
053701a702 | |||
3fc3e43042 | |||
b0dc68d7d4 | |||
e895baaeb4 | |||
c47f6fdb21 | |||
9e188e441a | |||
f6efcdf9f9 | |||
b1ceda3af4 | |||
cd3a0684d0 | |||
f25e54c6cb | |||
66f86cf497 | |||
d260edc547 | |||
ba1e6fa984 | |||
6b59f36213 | |||
1894960762 | |||
83c5c55f32 | |||
fb8c0b9a48 | |||
d34691152c | |||
cea80b482e | |||
c460a8fa5c | |||
5f852437fe | |||
8aded1a685 | |||
f2acb9e150 | |||
6f3a00c9b8 | |||
fbae107c04 | |||
6c1ae77a2a | |||
67e8ca6714 | |||
a56d976947 | |||
ac0bb6bee2 | |||
dee67dac75 | |||
9458cfbd1a | |||
4b8bdd22b3 | |||
a4c143e2d1 | |||
e91019feb9 | |||
8a37ec72b7 | |||
c263a33095 | |||
4b2c0115db | |||
673f40bd85 | |||
b9f8108cd3 | |||
a16ecf656b | |||
842cff130b | |||
346b9b4b79 | |||
56c317d223 | |||
b7b150a979 | |||
fae848bd1b | |||
a5b8087dc5 | |||
7aa078e025 | |||
4b6b276b64 | |||
0373daae5c | |||
7f30dd0475 | |||
d2a77c2371 | |||
104f65b541 | |||
d0fb4b6914 | |||
42c3272def | |||
2812889d61 | |||
bd3a13b2a5 | |||
289d179142 | |||
deca493912 | |||
d16bf7d6c0 | |||
3f1415dad1 | |||
3e744b6b2d | |||
56c4f8a106 | |||
99ef8bae4c | |||
b7f0343730 | |||
c807b59fb4 | |||
60fc57431a | |||
d988a3a307 | |||
de763d3fa9 | |||
21bfb61cc8 | |||
a79adeb280 | |||
9b7a019222 | |||
e65143d276 | |||
61bb183eda | |||
f3d3a6db91 | |||
9ebb443cb7 | |||
fa4c4d0a8d | |||
3e1518fef6 | |||
e59db84721 | |||
c281b31fc8 | |||
1c93ca9896 | |||
df5836e573 | |||
41ef35f0d0 | |||
5e6ce8643f | |||
f575400bc8 | |||
6d9a335b0e | |||
f94eb5f318 | |||
245fa421a2 | |||
655094a816 | |||
d544650b05 | |||
d9d682585c | |||
44a202552e | |||
fd6673420b | |||
0e9cd5db9c | |||
6da6e6f145 | |||
bdaa7460e4 | |||
d6d1c1ac35 | |||
67958155fc | |||
198bd74ec6 | |||
92eec95dad | |||
b73a034fca | |||
11b508f021 | |||
c2b5be37f9 | |||
1a04863a08 | |||
077d3eb993 | |||
ac38221bdf | |||
9f146b8fc3 | |||
9a2d9659a7 | |||
6faae5350a | |||
2616709e7f | |||
bed9ff084c | |||
fb3082b0bb | |||
c8439b8429 | |||
4c3632d3e7 | |||
24d8cd57da | |||
b7c0a8b0c2 | |||
d6d7b22b1e | |||
5269c957ce | |||
bf8d2672c8 | |||
8461acab9b | |||
731ae43fe8 | |||
5354c85352 | |||
22a37bb923 | |||
07bbece704 | |||
9af2f50d81 | |||
7b148f7970 | |||
a1e52d919f | |||
899b487daa | |||
86eeb5fc44 | |||
62a708b416 | |||
a7ce8704b3 | |||
7319a13077 | |||
95bd4d93c5 | |||
69c33f0395 | |||
cd4caea2dc | |||
c9daf19940 | |||
7c2540b193 | |||
3e600552dc | |||
69ee6b1699 | |||
ca1ea042b2 | |||
f26df7d9bb | |||
ba45705571 | |||
bf8ea71c77 | |||
7deb64a5de | |||
e283865d3d | |||
a32be3e96b | |||
0cfd6eab3f | |||
c2361f13e6 | |||
bc81de54b9 | |||
07884bc9b1 | |||
58c3204187 | |||
19d3deddd4 | |||
f0233b947e | |||
c6a90a2fd0 | |||
3607c30d98 | |||
717105f243 | |||
2373e4a06d | |||
4c76803f13 | |||
6a46e88f8f | |||
e4e94d5be0 | |||
a3b896fa70 | |||
60347a90e9 | |||
a02d4ec46f | |||
485bae8f22 | |||
655477316b | |||
e22f87dee7 | |||
d3337322dd | |||
6202f4f943 | |||
b42cc9ed3e | |||
fbf72c0f61 | |||
cbd0b0c070 | |||
874f049323 | |||
42ab4020e2 | |||
7ab9683b87 | |||
865facfd05 | |||
0951f4a202 | |||
882ec27969 | |||
a84158c1ff | |||
161d3ec817 | |||
5b15249689 | |||
602fce1c7e | |||
f2f653e345 | |||
b55738bd6e | |||
b39a59ce72 | |||
9872daf29f | |||
91044e730b | |||
656a7dceef | |||
d636502eaa | |||
389fd158ad | |||
30fa63b379 | |||
691063545c | |||
421934efed | |||
f056b9dc7f | |||
48d421ba28 | |||
096b714117 | |||
0d25dc1aaa | |||
63d3c2d06f | |||
5fc704ccad | |||
61d99561c1 | |||
c26ce2083f | |||
847d71f72a | |||
3c35039445 |
30
.github/ISSUE_TEMPLATE/bug_report.md
vendored
30
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -6,20 +6,30 @@ labels: bug
|
|||||||
assignees: ''
|
assignees: ''
|
||||||
|
|
||||||
---
|
---
|
||||||
|
<!--
|
||||||
|
|
||||||
**Are you in the right place?**
|
Are you in the right place?
|
||||||
- If you are looking for support on how to get your upstream server forwarding, please consider asking the community on Reddit.
|
- If you are looking for support on how to get your upstream server forwarding, please consider asking the community on Reddit.
|
||||||
- If you are writing code changes to contribute and need to ask about the internals of the software, Gitter is the best place to ask.
|
- If you are writing code changes to contribute and need to ask about the internals of the software, Gitter is the best place to ask.
|
||||||
- If you think you found a bug with NPM (not Nginx, or your upstream server or MySql) then you are in the *right place.*
|
- If you think you found a bug with NPM (not Nginx, or your upstream server or MySql) then you are in the *right place.*
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
**Checklist**
|
**Checklist**
|
||||||
- Have you pulled and found the error with `jc21/nginx-proxy-manager:latest` docker image?
|
- Have you pulled and found the error with `jc21/nginx-proxy-manager:latest` docker image?
|
||||||
|
- Yes / No
|
||||||
- Are you sure you're not using someone else's docker image?
|
- Are you sure you're not using someone else's docker image?
|
||||||
- If having problems with Lets Encrypt, have you made absolutely sure your site is accessible from outside of your network?
|
- Yes / No
|
||||||
|
- Have you searched for similar issues (both open and closed)?
|
||||||
|
- Yes / No
|
||||||
|
|
||||||
**Describe the bug**
|
**Describe the bug**
|
||||||
- A clear and concise description of what the bug is.
|
<!-- A clear and concise description of what the bug is. -->
|
||||||
- What version of Nginx Proxy Manager is reported on the login page?
|
|
||||||
|
|
||||||
|
**Nginx Proxy Manager Version**
|
||||||
|
<!-- What version of Nginx Proxy Manager is reported on the login page? -->
|
||||||
|
|
||||||
|
|
||||||
**To Reproduce**
|
**To Reproduce**
|
||||||
Steps to reproduce the behavior:
|
Steps to reproduce the behavior:
|
||||||
@ -28,14 +38,18 @@ Steps to reproduce the behavior:
|
|||||||
3. Scroll down to '....'
|
3. Scroll down to '....'
|
||||||
4. See error
|
4. See error
|
||||||
|
|
||||||
|
|
||||||
**Expected behavior**
|
**Expected behavior**
|
||||||
A clear and concise description of what you expected to happen.
|
<!-- A clear and concise description of what you expected to happen. -->
|
||||||
|
|
||||||
|
|
||||||
**Screenshots**
|
**Screenshots**
|
||||||
If applicable, add screenshots to help explain your problem.
|
<!-- If applicable, add screenshots to help explain your problem. -->
|
||||||
|
|
||||||
|
|
||||||
**Operating System**
|
**Operating System**
|
||||||
- Please specify if using a Rpi, Mac, orchestration tool or any other setups that might affect the reproduction of this error.
|
<!-- Please specify if using a Rpi, Mac, orchestration tool or any other setups that might affect the reproduction of this error. -->
|
||||||
|
|
||||||
|
|
||||||
**Additional context**
|
**Additional context**
|
||||||
Add any other context about the problem here, docker version, browser version if applicable to the problem. Too much info is better than too little.
|
<!-- Add any other context about the problem here, docker version, browser version, logs if applicable to the problem. Too much info is better than too little. -->
|
||||||
|
18
.github/ISSUE_TEMPLATE/dns_challenge_request.md
vendored
Normal file
18
.github/ISSUE_TEMPLATE/dns_challenge_request.md
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
name: DNS challenge provider request
|
||||||
|
about: Suggest a new provider to be available for a certificate DNS challenge
|
||||||
|
title: ''
|
||||||
|
labels: dns provider request
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**What provider would you like to see added to NPM?**
|
||||||
|
<!-- What is this provider called? -->
|
||||||
|
|
||||||
|
|
||||||
|
**Have you checked if a certbot plugin exists?**
|
||||||
|
<!--
|
||||||
|
Currently NPM only supports DNS challenge providers for which a certbot plugin exists.
|
||||||
|
You can visit pypi.org, and search for a package with the name `certbot-dns-<privider>`.
|
||||||
|
-->
|
19
.github/ISSUE_TEMPLATE/feature_request.md
vendored
19
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@ -7,19 +7,26 @@ assignees: ''
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Are you in the right place?**
|
<!--
|
||||||
|
|
||||||
|
Are you in the right place?
|
||||||
- If you are looking for support on how to get your upstream server forwarding, please consider asking the community on Reddit.
|
- If you are looking for support on how to get your upstream server forwarding, please consider asking the community on Reddit.
|
||||||
- If you are writing code changes to contribute and need to ask about the internals of the software, Gitter is the best place to ask.
|
- If you are writing code changes to contribute and need to ask about the internals of the software, Gitter is the best place to ask.
|
||||||
- If you have a feature request for NPM then you are in the *right place.*
|
- If you think you found a bug with NPM (not Nginx, or your upstream server or MySql) then you are in the *right place.*
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
**Is your feature request related to a problem? Please describe.**
|
**Is your feature request related to a problem? Please describe.**
|
||||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
|
||||||
|
|
||||||
|
|
||||||
**Describe the solution you'd like**
|
**Describe the solution you'd like**
|
||||||
A clear and concise description of what you want to happen.
|
<!-- A clear and concise description of what you want to happen. -->
|
||||||
|
|
||||||
|
|
||||||
**Describe alternatives you've considered**
|
**Describe alternatives you've considered**
|
||||||
A clear and concise description of any alternative solutions or features you've considered.
|
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
|
||||||
|
|
||||||
|
|
||||||
**Additional context**
|
**Additional context**
|
||||||
Add any other context or screenshots about the feature request here.
|
<!-- Add any other context or screenshots about the feature request here. -->
|
||||||
|
2
Jenkinsfile
vendored
2
Jenkinsfile
vendored
@ -213,7 +213,7 @@ pipeline {
|
|||||||
}
|
}
|
||||||
steps {
|
steps {
|
||||||
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("This is an automated message from CI:\n\nDocker Image for build ${BUILD_NUMBER} is available on [DockerHub](https://cloud.docker.com/repository/docker/jc21/${IMAGE}) as `jc21/${IMAGE}:github-${BRANCH_LOWER}`\n\n**Note:** ensure you backup your NPM instance before testing this PR image! Especially if this PR contains database changes.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
318
README.md
318
README.md
@ -1,7 +1,7 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="https://nginxproxymanager.com/github.png">
|
<img src="https://nginxproxymanager.com/github.png">
|
||||||
<br><br>
|
<br><br>
|
||||||
<img src="https://img.shields.io/badge/version-2.8.1-green.svg?style=for-the-badge">
|
<img src="https://img.shields.io/badge/version-2.9.9-green.svg?style=for-the-badge">
|
||||||
<a href="https://hub.docker.com/repository/docker/jc21/nginx-proxy-manager">
|
<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">
|
<img src="https://img.shields.io/docker/stars/jc21/nginx-proxy-manager.svg?style=for-the-badge">
|
||||||
</a>
|
</a>
|
||||||
@ -14,12 +14,15 @@
|
|||||||
<a href="https://gitter.im/nginx-proxy-manager/community">
|
<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">
|
<img alt="Gitter" src="https://img.shields.io/gitter/room/nginx-proxy-manager/community?style=for-the-badge">
|
||||||
</a>
|
</a>
|
||||||
|
<a href="https://reddit.com/r/nginxproxymanager">
|
||||||
|
<img alt="Reddit" src="https://img.shields.io/reddit/subreddit-subscribers/nginxproxymanager?label=Reddit%20Community&style=for-the-badge">
|
||||||
|
</a>
|
||||||
</p>
|
</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)
|
- [Quick Setup](#quick-setup)
|
||||||
- [Full Setup](https://nginxproxymanager.com/setup/)
|
- [Full Setup](https://nginxproxymanager.com/setup/)
|
||||||
- [Screenshots](https://nginxproxymanager.com/screenshots/)
|
- [Screenshots](https://nginxproxymanager.com/screenshots/)
|
||||||
|
|
||||||
@ -52,6 +55,67 @@ I won't go in to too much detail here but here are the basics for someone new to
|
|||||||
3. Configure your domain name details to point to your home, either with a static ip or a service like DuckDNS or [Amazon Route53](https://github.com/jc21/route53-ddns)
|
3. Configure your domain name details to point to your home, either with a static ip or a service like DuckDNS or [Amazon Route53](https://github.com/jc21/route53-ddns)
|
||||||
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
|
||||||
|
|
||||||
|
## 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 docker-compose.yml file similar to this:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
version: '3'
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
image: 'jc21/nginx-proxy-manager:latest'
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- '80:80'
|
||||||
|
- '81:81'
|
||||||
|
- '443:443'
|
||||||
|
environment:
|
||||||
|
DB_MYSQL_HOST: "db"
|
||||||
|
DB_MYSQL_PORT: 3306
|
||||||
|
DB_MYSQL_USER: "npm"
|
||||||
|
DB_MYSQL_PASSWORD: "npm"
|
||||||
|
DB_MYSQL_NAME: "npm"
|
||||||
|
volumes:
|
||||||
|
- ./data:/data
|
||||||
|
- ./letsencrypt:/etc/letsencrypt
|
||||||
|
db:
|
||||||
|
image: 'jc21/mariadb-aria:latest'
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
MYSQL_ROOT_PASSWORD: 'npm'
|
||||||
|
MYSQL_DATABASE: 'npm'
|
||||||
|
MYSQL_USER: 'npm'
|
||||||
|
MYSQL_PASSWORD: 'npm'
|
||||||
|
volumes:
|
||||||
|
- ./data/mysql:/var/lib/mysql
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Bring up your stack
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker-compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
4. 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.
|
||||||
|
|
||||||
|
|
||||||
## Contributors
|
## Contributors
|
||||||
|
|
||||||
@ -63,43 +127,43 @@ Special thanks to the following contributors:
|
|||||||
<tr>
|
<tr>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/Subv">
|
<a href="https://github.com/Subv">
|
||||||
<img src="https://avatars1.githubusercontent.com/u/357072?s=460&u=d8adcdc91d749ae53e177973ed9b6bb6c4c894a3&v=4" width="80px;" alt=""/>
|
<img src="https://avatars1.githubusercontent.com/u/357072?s=460&u=d8adcdc91d749ae53e177973ed9b6bb6c4c894a3&v=4" width="80" alt=""/>
|
||||||
<br /><sub><b>Sebastian Valle</b></sub>
|
<br /><sub><b>Sebastian Valle</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/Indemnity83">
|
<a href="https://github.com/Indemnity83">
|
||||||
<img src="https://avatars3.githubusercontent.com/u/35218?s=460&u=7082004ff35138157c868d7d9c683ccebfce5968&v=4" width="80px;" alt=""/>
|
<img src="https://avatars3.githubusercontent.com/u/35218?s=460&u=7082004ff35138157c868d7d9c683ccebfce5968&v=4" width="80" alt=""/>
|
||||||
<br /><sub><b>Kyle Klaus</b></sub>
|
<br /><sub><b>Kyle Klaus</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/theraw">
|
<a href="https://github.com/theraw">
|
||||||
<img src="https://avatars1.githubusercontent.com/u/32969774?s=460&u=6b359971e15685fb0359e6a8c065a399b40dc228&v=4" width="80px;" alt=""/>
|
<img src="https://avatars1.githubusercontent.com/u/32969774?s=460&u=6b359971e15685fb0359e6a8c065a399b40dc228&v=4" width="80" alt=""/>
|
||||||
<br /><sub><b>ƬHE ЯAW</b></sub>
|
<br /><sub><b>ƬHE ЯAW</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/spalger">
|
<a href="https://github.com/spalger">
|
||||||
<img src="https://avatars2.githubusercontent.com/u/1329312?s=400&u=565223e38f1c052afb4c5dcca3fcf1c63ba17ae7&v=4" width="80px;" alt=""/>
|
<img src="https://avatars2.githubusercontent.com/u/1329312?s=400&u=565223e38f1c052afb4c5dcca3fcf1c63ba17ae7&v=4" width="80" alt=""/>
|
||||||
<br /><sub><b>Spencer</b></sub>
|
<br /><sub><b>Spencer</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/Xantios">
|
<a href="https://github.com/Xantios">
|
||||||
<img src="https://avatars3.githubusercontent.com/u/1507836?s=460&v=4" width="80px;" alt=""/>
|
<img src="https://avatars3.githubusercontent.com/u/1507836?s=460&v=4" width="80" alt=""/>
|
||||||
<br /><sub><b>Xantios Krugor</b></sub>
|
<br /><sub><b>Xantios Krugor</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/dpanesso">
|
<a href="https://github.com/dpanesso">
|
||||||
<img src="https://avatars2.githubusercontent.com/u/2687121?s=460&v=4" width="80px;" alt=""/>
|
<img src="https://avatars2.githubusercontent.com/u/2687121?s=460&v=4" width="80" alt=""/>
|
||||||
<br /><sub><b>David Panesso</b></sub>
|
<br /><sub><b>David Panesso</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/IronTooch">
|
<a href="https://github.com/IronTooch">
|
||||||
<img src="https://avatars3.githubusercontent.com/u/27360514?s=460&u=69bf854a6647c55725f62ecb8d39249c6c0b2602&v=4" width="80px;" alt=""/>
|
<img src="https://avatars3.githubusercontent.com/u/27360514?s=460&u=69bf854a6647c55725f62ecb8d39249c6c0b2602&v=4" width="80" alt=""/>
|
||||||
<br /><sub><b>IronTooch</b></sub>
|
<br /><sub><b>IronTooch</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
@ -107,43 +171,43 @@ Special thanks to the following contributors:
|
|||||||
<tr>
|
<tr>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/damianog">
|
<a href="https://github.com/damianog">
|
||||||
<img src="https://avatars1.githubusercontent.com/u/2786682?s=460&u=76c6136fae797abb76b951cd8a246dcaecaf21af&v=4" width="80px;" alt=""/>
|
<img src="https://avatars1.githubusercontent.com/u/2786682?s=460&u=76c6136fae797abb76b951cd8a246dcaecaf21af&v=4" width="80" alt=""/>
|
||||||
<br /><sub><b>Damiano</b></sub>
|
<br /><sub><b>Damiano</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/tfmm">
|
<a href="https://github.com/tfmm">
|
||||||
<img src="https://avatars3.githubusercontent.com/u/6880538?s=460&u=ce0160821cc4aa802df8395200f2d4956a5bc541&v=4" width="80px;" alt=""/>
|
<img src="https://avatars3.githubusercontent.com/u/6880538?s=460&u=ce0160821cc4aa802df8395200f2d4956a5bc541&v=4" width="80" alt=""/>
|
||||||
<br /><sub><b>Russ</b></sub>
|
<br /><sub><b>Russ</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/margaale">
|
<a href="https://github.com/margaale">
|
||||||
<img src="https://avatars3.githubusercontent.com/u/20794934?s=460&v=4" width="80px;" alt=""/>
|
<img src="https://avatars3.githubusercontent.com/u/20794934?s=460&v=4" width="80" alt=""/>
|
||||||
<br /><sub><b>Marcelo Castagna</b></sub>
|
<br /><sub><b>Marcelo Castagna</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/Steven-Harris">
|
<a href="https://github.com/Steven-Harris">
|
||||||
<img src="https://avatars2.githubusercontent.com/u/7720242?s=460&v=4" width="80px;" alt=""/>
|
<img src="https://avatars2.githubusercontent.com/u/7720242?s=460&v=4" width="80" alt=""/>
|
||||||
<br /><sub><b>Steven Harris</b></sub>
|
<br /><sub><b>Steven Harris</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/jlesage">
|
<a href="https://github.com/jlesage">
|
||||||
<img src="https://avatars0.githubusercontent.com/u/1791123?s=460&v=4" width="80px;" alt=""/>
|
<img src="https://avatars0.githubusercontent.com/u/1791123?s=460&v=4" width="80" alt=""/>
|
||||||
<br /><sub><b>Jocelyn Le Sage</b></sub>
|
<br /><sub><b>Jocelyn Le Sage</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/cmer">
|
<a href="https://github.com/cmer">
|
||||||
<img src="https://avatars0.githubusercontent.com/u/412?s=460&u=67dd8b2e3661bfd6f68ec1eaa5b9821bd8a321cd&v=4" width="80px;" alt=""/>
|
<img src="https://avatars0.githubusercontent.com/u/412?s=460&u=67dd8b2e3661bfd6f68ec1eaa5b9821bd8a321cd&v=4" width="80" alt=""/>
|
||||||
<br /><sub><b>Carl Mercier</b></sub>
|
<br /><sub><b>Carl Mercier</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/the1ts">
|
<a href="https://github.com/the1ts">
|
||||||
<img src="https://avatars1.githubusercontent.com/u/84956?s=460&v=4" width="80px;" alt=""/>
|
<img src="https://avatars1.githubusercontent.com/u/84956?s=460&v=4" width="80" alt=""/>
|
||||||
<br /><sub><b>Paul Mansfield</b></sub>
|
<br /><sub><b>Paul Mansfield</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
@ -151,43 +215,43 @@ Special thanks to the following contributors:
|
|||||||
<tr>
|
<tr>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/OhHeyAlan">
|
<a href="https://github.com/OhHeyAlan">
|
||||||
<img src="https://avatars0.githubusercontent.com/u/11955126?s=460&u=fbaa5a1a4f73ef8960132c703349bfd037fe2630&v=4" width="80px;" alt=""/>
|
<img src="https://avatars0.githubusercontent.com/u/11955126?s=460&u=fbaa5a1a4f73ef8960132c703349bfd037fe2630&v=4" width="80" alt=""/>
|
||||||
<br /><sub><b>OhHeyAlan</b></sub>
|
<br /><sub><b>OhHeyAlan</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/dogmatic69">
|
<a href="https://github.com/dogmatic69">
|
||||||
<img src="https://avatars2.githubusercontent.com/u/94674?s=460&u=ca7647de53145c6283b6373ade5dc94ba99347db&v=4" width="80px;" alt=""/>
|
<img src="https://avatars2.githubusercontent.com/u/94674?s=460&u=ca7647de53145c6283b6373ade5dc94ba99347db&v=4" width="80" alt=""/>
|
||||||
<br /><sub><b>Carl Sutton</b></sub>
|
<br /><sub><b>Carl Sutton</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/tg44">
|
<a href="https://github.com/tg44">
|
||||||
<img src="https://avatars0.githubusercontent.com/u/31839?s=460&u=ad32f4cadfef5e5fb09cdfa4b7b7b36a99ba6811&v=4" width="80px;" alt=""/>
|
<img src="https://avatars0.githubusercontent.com/u/31839?s=460&u=ad32f4cadfef5e5fb09cdfa4b7b7b36a99ba6811&v=4" width="80" alt=""/>
|
||||||
<br /><sub><b>Gergő Törcsvári</b></sub>
|
<br /><sub><b>Gergő Törcsvári</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/vrenjith">
|
<a href="https://github.com/vrenjith">
|
||||||
<img src="https://avatars3.githubusercontent.com/u/2093241?s=460&u=96ce93a9bebabdd0a60a2dc96cd093a41d5edaba&v=4" width="80px;" alt=""/>
|
<img src="https://avatars3.githubusercontent.com/u/2093241?s=460&u=96ce93a9bebabdd0a60a2dc96cd093a41d5edaba&v=4" width="80" alt=""/>
|
||||||
<br /><sub><b>vrenjith</b></sub>
|
<br /><sub><b>vrenjith</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/duhruh">
|
<a href="https://github.com/duhruh">
|
||||||
<img src="https://avatars2.githubusercontent.com/u/1133969?s=460&u=c0691e6131ec6d516416c1c6fcedb5034f877bbe&v=4" width="80px;" alt=""/>
|
<img src="https://avatars2.githubusercontent.com/u/1133969?s=460&u=c0691e6131ec6d516416c1c6fcedb5034f877bbe&v=4" width="80" alt=""/>
|
||||||
<br /><sub><b>David Rivera</b></sub>
|
<br /><sub><b>David Rivera</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/jipjan">
|
<a href="https://github.com/jipjan">
|
||||||
<img src="https://avatars2.githubusercontent.com/u/1384618?s=460&v=4" width="80px;" alt=""/>
|
<img src="https://avatars2.githubusercontent.com/u/1384618?s=460&v=4" width="80" alt=""/>
|
||||||
<br /><sub><b>Jaap-Jan de Wit</b></sub>
|
<br /><sub><b>Jaap-Jan de Wit</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/jmwebslave">
|
<a href="https://github.com/jmwebslave">
|
||||||
<img src="https://avatars2.githubusercontent.com/u/6118262?s=460&u=7db409c47135b1e141c366bbb03ed9fae6ac2638&v=4" width="80px;" alt=""/>
|
<img src="https://avatars2.githubusercontent.com/u/6118262?s=460&u=7db409c47135b1e141c366bbb03ed9fae6ac2638&v=4" width="80" alt=""/>
|
||||||
<br /><sub><b>James Morgan</b></sub>
|
<br /><sub><b>James Morgan</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
@ -195,43 +259,43 @@ Special thanks to the following contributors:
|
|||||||
<tr>
|
<tr>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/chaptergy">
|
<a href="https://github.com/chaptergy">
|
||||||
<img src="https://avatars2.githubusercontent.com/u/26956711?s=460&u=7d9adebabb6b4e7af7cb05d98d751087a372304b&v=4" width="80px;" alt=""/>
|
<img src="https://avatars2.githubusercontent.com/u/26956711?s=460&u=7d9adebabb6b4e7af7cb05d98d751087a372304b&v=4" width="80" alt=""/>
|
||||||
<br /><sub><b>chaptergy</b></sub>
|
<br /><sub><b>chaptergy</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/Philip-Mooney">
|
<a href="https://github.com/Philip-Mooney">
|
||||||
<img src="https://avatars0.githubusercontent.com/u/48624631?s=460&v=4" width="80px;" alt=""/>
|
<img src="https://avatars0.githubusercontent.com/u/48624631?s=460&v=4" width="80" alt=""/>
|
||||||
<br /><sub><b>Philip Mooney</b></sub>
|
<br /><sub><b>Philip Mooney</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/WaterCalm">
|
<a href="https://github.com/WaterCalm">
|
||||||
<img src="https://avatars1.githubusercontent.com/u/23502129?s=400&v=4" width="80px;" alt=""/>
|
<img src="https://avatars1.githubusercontent.com/u/23502129?s=400&v=4" width="80" alt=""/>
|
||||||
<br /><sub><b>WaterCalm</b></sub>
|
<br /><sub><b>WaterCalm</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/lebrou34">
|
<a href="https://github.com/lebrou34">
|
||||||
<img src="https://avatars1.githubusercontent.com/u/16373103?s=460&v=4" width="80px;" alt=""/>
|
<img src="https://avatars1.githubusercontent.com/u/16373103?s=460&v=4" width="80" alt=""/>
|
||||||
<br /><sub><b>lebrou34</b></sub>
|
<br /><sub><b>lebrou34</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/lightglitch">
|
<a href="https://github.com/lightglitch">
|
||||||
<img src="https://avatars0.githubusercontent.com/u/196953?s=460&v=4" width="80px;" alt=""/>
|
<img src="https://avatars0.githubusercontent.com/u/196953?s=460&v=4" width="80" alt=""/>
|
||||||
<br /><sub><b>Mário Franco</b></sub>
|
<br /><sub><b>Mário Franco</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/klutchell">
|
<a href="https://github.com/klutchell">
|
||||||
<img src="https://avatars3.githubusercontent.com/u/20458272?s=460&v=4" width="80px;" alt=""/>
|
<img src="https://avatars3.githubusercontent.com/u/20458272?s=460&v=4" width="80" alt=""/>
|
||||||
<br /><sub><b>Kyle Harding</b></sub>
|
<br /><sub><b>Kyle Harding</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/ahgraber">
|
<a href="https://github.com/ahgraber">
|
||||||
<img src="https://avatars.githubusercontent.com/u/24922003?s=460&u=8376c9f00af9b6057ba4d2fb03b4f1b20a75277f&v=4" width="80px;" alt=""/>
|
<img src="https://avatars.githubusercontent.com/u/24922003?s=460&u=8376c9f00af9b6057ba4d2fb03b4f1b20a75277f&v=4" width="80" alt=""/>
|
||||||
<br /><sub><b>Alex Graber</b></sub>
|
<br /><sub><b>Alex Graber</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
@ -239,22 +303,210 @@ Special thanks to the following contributors:
|
|||||||
<tr>
|
<tr>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/MooBaloo">
|
<a href="https://github.com/MooBaloo">
|
||||||
<img src="https://avatars.githubusercontent.com/u/9493496?s=460&v=4" width="80px;" alt=""/>
|
<img src="https://avatars.githubusercontent.com/u/9493496?s=460&v=4" width="80" alt=""/>
|
||||||
<br /><sub><b>MooBaloo</b></sub>
|
<br /><sub><b>MooBaloo</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/Shuro">
|
<a href="https://github.com/Shuro">
|
||||||
<img src="https://avatars.githubusercontent.com/u/944030?s=460&v=4" width="80px;" alt=""/>
|
<img src="https://avatars.githubusercontent.com/u/944030?s=460&v=4" width="80" alt=""/>
|
||||||
<br /><sub><b>Shuro</b></sub>
|
<br /><sub><b>Shuro</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/lorisbergeron">
|
<a href="https://github.com/lorisbergeron">
|
||||||
<img src="https://avatars.githubusercontent.com/u/51918567?s=460&u=778e4ff284b7d7304450f98421c99f79298371fb&v=4" width="80px;" alt=""/>
|
<img src="https://avatars.githubusercontent.com/u/51918567?s=460&u=778e4ff284b7d7304450f98421c99f79298371fb&v=4" width="80" alt=""/>
|
||||||
<br /><sub><b>Loris Bergeron</b></sub>
|
<br /><sub><b>Loris Bergeron</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/hepelayo">
|
||||||
|
<img src="https://avatars.githubusercontent.com/u/8243119?v=4" width="80" alt=""/>
|
||||||
|
<br /><sub><b>hepelayo</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/jonasled">
|
||||||
|
<img src="https://avatars.githubusercontent.com/u/46790650?v=4" width="80" alt=""/>
|
||||||
|
<br /><sub><b>Jonas Leder</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/stegmannb">
|
||||||
|
<img src="https://avatars.githubusercontent.com/u/12850482?v=4" width="80" alt=""/>
|
||||||
|
<br /><sub><b>Bastian Stegmann</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/Stealthii">
|
||||||
|
<img src="https://avatars.githubusercontent.com/u/998920?v=4" width="80" alt=""/>
|
||||||
|
<br /><sub><b>Stealthii</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/thegamingninja">
|
||||||
|
<img src="https://avatars.githubusercontent.com/u/8020534?v=4" width="80" alt=""/>
|
||||||
|
<br /><sub><b>THEGamingninja</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/italobb">
|
||||||
|
<img src="https://avatars.githubusercontent.com/u/1801687?v=4" width="80" alt=""/>
|
||||||
|
<br /><sub><b>Italo Borssatto</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/GurjinderSingh">
|
||||||
|
<img src="https://avatars.githubusercontent.com/u/3470709?v=4" width="80" alt=""/>
|
||||||
|
<br /><sub><b>Gurjinder Singh</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/phantomski77">
|
||||||
|
<img src="https://avatars.githubusercontent.com/u/69464125?v=4" width="80" alt=""/>
|
||||||
|
<br /><sub><b>David Dosoudil</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/ijaron">
|
||||||
|
<img src="https://avatars.githubusercontent.com/u/5156472?v=4" width="80" alt=""/>
|
||||||
|
<br /><sub><b>ijaron</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/nielscil">
|
||||||
|
<img src="https://avatars.githubusercontent.com/u/9073152?v=4" width="80" alt=""/>
|
||||||
|
<br /><sub><b>Niels Bouma</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/ogarai">
|
||||||
|
<img src="https://avatars.githubusercontent.com/u/2949572?v=4" width="80" alt=""/>
|
||||||
|
<br /><sub><b>Orko Garai</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/baruffaldi">
|
||||||
|
<img src="https://avatars.githubusercontent.com/u/36949?v=4" width="80" alt=""/>
|
||||||
|
<br /><sub><b>Filippo Baruffaldi</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/bikram990">
|
||||||
|
<img src="https://avatars.githubusercontent.com/u/6782131?v=4" width="80" alt=""/>
|
||||||
|
<br /><sub><b>Bikramjeet Singh</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/razvanstoica89">
|
||||||
|
<img src="https://avatars.githubusercontent.com/u/28236583?v=4" width="80" alt=""/>
|
||||||
|
<br /><sub><b>Razvan Stoica</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/psharma04">
|
||||||
|
<img src="https://avatars.githubusercontent.com/u/22587474?v=4" width="80" alt=""/>
|
||||||
|
<br /><sub><b>RBXII3</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/demize">
|
||||||
|
<img src="https://avatars.githubusercontent.com/u/264914?v=4" width="80" alt=""/>
|
||||||
|
<br /><sub><b>demize</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/PUP-Loki">
|
||||||
|
<img src="https://avatars.githubusercontent.com/u/75944209?v=4" width="80" alt=""/>
|
||||||
|
<br /><sub><b>PUP-Loki</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/DSorlov">
|
||||||
|
<img src="https://avatars.githubusercontent.com/u/8133650?v=4" width="80" alt=""/>
|
||||||
|
<br /><sub><b>Daniel Sörlöv</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/Theyooo">
|
||||||
|
<img src="https://avatars.githubusercontent.com/u/58510131?v=4" width="80" alt=""/>
|
||||||
|
<br /><sub><b>Theyooo</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/mrdink">
|
||||||
|
<img src="https://avatars.githubusercontent.com/u/514751?v=4" width="80" alt=""/>
|
||||||
|
<br /><sub><b>Justin Peacock</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/ChrisTracy">
|
||||||
|
<img src="https://avatars.githubusercontent.com/u/58871574?v=4" width="80" alt=""/>
|
||||||
|
<br /><sub><b>Chris Tracy</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/Fuechslein">
|
||||||
|
<img src="https://avatars.githubusercontent.com/u/15112818?v=4" width="80" alt=""/>
|
||||||
|
<br /><sub><b>Fuechslein</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/nightah">
|
||||||
|
<img src="https://avatars.githubusercontent.com/u/3339418?v=4" width="80" alt=""/>
|
||||||
|
<br /><sub><b>Amir Zarrinkafsh</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/gabbe">
|
||||||
|
<img src="https://avatars.githubusercontent.com/u/156397?v=4" width="80" alt=""/>
|
||||||
|
<br /><sub><b>gabbe</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/bmbvenom">
|
||||||
|
<img src="https://avatars.githubusercontent.com/u/20530371?v=4" width="80" alt=""/>
|
||||||
|
<br /><sub><b>bmbvenom</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/FMeinicke">
|
||||||
|
<img src="https://avatars.githubusercontent.com/u/42121639?v=4" width="80" alt=""/>
|
||||||
|
<br /><sub><b>Florian Meinicke</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/ssrahul96">
|
||||||
|
<img src="https://avatars.githubusercontent.com/u/15570570?v=4" width="80" alt=""/>
|
||||||
|
<br /><sub><b>Rahul Somasundaram</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/BjoernAkAManf">
|
||||||
|
<img src="https://avatars.githubusercontent.com/u/833043?v=4" width="80" alt=""/>
|
||||||
|
<br /><sub><b>Björn Heinrichs</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/realJoshByrnes">
|
||||||
|
<img src="https://avatars.githubusercontent.com/u/204185?v=4" width="80" alt=""/>
|
||||||
|
<br /><sub><b>Josh Byrnes</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/bergi9">
|
||||||
|
<img src="https://avatars.githubusercontent.com/u/5556750?v=4" width="80" alt=""/>
|
||||||
|
<br /><sub><b>bergi9</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<!-- markdownlint-enable -->
|
<!-- markdownlint-enable -->
|
||||||
|
@ -74,13 +74,11 @@ app.use(function (err, req, res, next) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Not every error is worth logging - but this is good for now until it gets annoying.
|
// Not every error is worth logging - but this is good for now until it gets annoying.
|
||||||
if (typeof err.stack !== 'undefined' && err.stack) {
|
if (process.env.NODE_ENV === 'development' || process.env.DEBUG) {
|
||||||
if (process.env.NODE_ENV === 'development') {
|
log.debug(err);
|
||||||
log.debug(err.stack);
|
} else if (typeof err.stack !== 'undefined' && err.stack && (typeof err.public == 'undefined' || !err.public)) {
|
||||||
} else if (typeof err.public == 'undefined' || !err.public) {
|
|
||||||
log.warn(err.message);
|
log.warn(err.message);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
res
|
res
|
||||||
.status(err.status || 500)
|
.status(err.status || 500)
|
||||||
|
@ -95,7 +95,8 @@ async function createDbConfigFromEnvironment() {
|
|||||||
client: 'sqlite3',
|
client: 'sqlite3',
|
||||||
connection: {
|
connection: {
|
||||||
filename: envSqliteFile
|
filename: envSqliteFile
|
||||||
}
|
},
|
||||||
|
useNullAsDefault: true
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (JSON.stringify(configData.database) === JSON.stringify(newConfig)) {
|
if (JSON.stringify(configData.database) === JSON.stringify(newConfig)) {
|
||||||
|
@ -118,7 +118,6 @@ const internalAccessList = {
|
|||||||
// Sanity check that something crazy hasn't happened
|
// Sanity check that something crazy hasn't happened
|
||||||
throw new error.InternalValidationError('Access List could not be updated, IDs do not match: ' + row.id + ' !== ' + data.id);
|
throw new error.InternalValidationError('Access List could not be updated, IDs do not match: ' + row.id + ' !== ' + data.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// patch name if specified
|
// patch name if specified
|
||||||
@ -205,6 +204,7 @@ const internalAccessList = {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.then(internalNginx.reload)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// Add to audit log
|
// Add to audit log
|
||||||
return internalAuditLog.add(access, {
|
return internalAuditLog.add(access, {
|
||||||
|
@ -1,19 +1,20 @@
|
|||||||
const fs = require('fs');
|
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
|
const fs = require('fs');
|
||||||
|
const tempWrite = require('temp-write');
|
||||||
|
const moment = require('moment');
|
||||||
const logger = require('../logger').ssl;
|
const logger = require('../logger').ssl;
|
||||||
const error = require('../lib/error');
|
const error = require('../lib/error');
|
||||||
const certificateModel = require('../models/certificate');
|
|
||||||
const internalAuditLog = require('./audit-log');
|
|
||||||
const tempWrite = require('temp-write');
|
|
||||||
const utils = require('../lib/utils');
|
const utils = require('../lib/utils');
|
||||||
const moment = require('moment');
|
const certificateModel = require('../models/certificate');
|
||||||
const debug_mode = process.env.NODE_ENV !== 'production' || !!process.env.DEBUG;
|
const dnsPlugins = require('../global/certbot-dns-plugins');
|
||||||
const le_staging = process.env.NODE_ENV !== 'production';
|
const internalAuditLog = require('./audit-log');
|
||||||
const internalNginx = require('./nginx');
|
const internalNginx = require('./nginx');
|
||||||
const internalHost = require('./host');
|
const internalHost = require('./host');
|
||||||
const certbot_command = '/usr/bin/certbot';
|
const letsencryptStaging = process.env.NODE_ENV !== 'production';
|
||||||
const le_config = '/etc/letsencrypt.ini';
|
const letsencryptConfig = '/etc/letsencrypt.ini';
|
||||||
const dns_plugins = require('../global/certbot-dns-plugins');
|
const certbotCommand = 'certbot';
|
||||||
|
const archiver = require('archiver');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
function omissions() {
|
function omissions() {
|
||||||
return ['is_deleted'];
|
return ['is_deleted'];
|
||||||
@ -21,14 +22,14 @@ function omissions() {
|
|||||||
|
|
||||||
const internalCertificate = {
|
const internalCertificate = {
|
||||||
|
|
||||||
allowed_ssl_files: ['certificate', 'certificate_key', 'intermediate_certificate'],
|
allowedSslFiles: ['certificate', 'certificate_key', 'intermediate_certificate'],
|
||||||
interval_timeout: 1000 * 60 * 60, // 1 hour
|
intervalTimeout: 1000 * 60 * 60, // 1 hour
|
||||||
interval: null,
|
interval: null,
|
||||||
interval_processing: false,
|
intervalProcessing: false,
|
||||||
|
|
||||||
initTimer: () => {
|
initTimer: () => {
|
||||||
logger.info('Let\'s Encrypt Renewal Timer initialized');
|
logger.info('Let\'s Encrypt Renewal Timer initialized');
|
||||||
internalCertificate.interval = setInterval(internalCertificate.processExpiringHosts, internalCertificate.interval_timeout);
|
internalCertificate.interval = setInterval(internalCertificate.processExpiringHosts, internalCertificate.intervalTimeout);
|
||||||
// And do this now as well
|
// And do this now as well
|
||||||
internalCertificate.processExpiringHosts();
|
internalCertificate.processExpiringHosts();
|
||||||
},
|
},
|
||||||
@ -37,15 +38,15 @@ const internalCertificate = {
|
|||||||
* Triggered by a timer, this will check for expiring hosts and renew their ssl certs if required
|
* Triggered by a timer, this will check for expiring hosts and renew their ssl certs if required
|
||||||
*/
|
*/
|
||||||
processExpiringHosts: () => {
|
processExpiringHosts: () => {
|
||||||
if (!internalCertificate.interval_processing) {
|
if (!internalCertificate.intervalProcessing) {
|
||||||
internalCertificate.interval_processing = true;
|
internalCertificate.intervalProcessing = true;
|
||||||
logger.info('Renewing SSL certs close to expiry...');
|
logger.info('Renewing SSL certs close to expiry...');
|
||||||
|
|
||||||
let cmd = certbot_command + ' renew --non-interactive --quiet ' +
|
const cmd = certbotCommand + ' renew --non-interactive --quiet ' +
|
||||||
'--config "' + le_config + '" ' +
|
'--config "' + letsencryptConfig + '" ' +
|
||||||
'--preferred-challenges "dns,http" ' +
|
'--preferred-challenges "dns,http" ' +
|
||||||
'--disable-hook-validation ' +
|
'--disable-hook-validation ' +
|
||||||
(le_staging ? '--staging' : '');
|
(letsencryptStaging ? '--staging' : '');
|
||||||
|
|
||||||
return utils.exec(cmd)
|
return utils.exec(cmd)
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
@ -93,11 +94,11 @@ const internalCertificate = {
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
internalCertificate.interval_processing = false;
|
internalCertificate.intervalProcessing = false;
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
logger.error(err);
|
logger.error(err);
|
||||||
internalCertificate.interval_processing = false;
|
internalCertificate.intervalProcessing = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -336,6 +337,71 @@ const internalCertificate = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Access} access
|
||||||
|
* @param {Object} data
|
||||||
|
* @param {Number} data.id
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
download: (access, data) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
access.can('certificates:get', data)
|
||||||
|
.then(() => {
|
||||||
|
return internalCertificate.get(access, data);
|
||||||
|
})
|
||||||
|
.then((certificate) => {
|
||||||
|
if (certificate.provider === 'letsencrypt') {
|
||||||
|
const zipDirectory = '/etc/letsencrypt/live/npm-' + data.id;
|
||||||
|
|
||||||
|
if (!fs.existsSync(zipDirectory)) {
|
||||||
|
throw new error.ItemNotFoundError('Certificate ' + certificate.nice_name + ' does not exists');
|
||||||
|
}
|
||||||
|
|
||||||
|
let certFiles = fs.readdirSync(zipDirectory)
|
||||||
|
.filter((fn) => fn.endsWith('.pem'))
|
||||||
|
.map((fn) => fs.realpathSync(path.join(zipDirectory, fn)));
|
||||||
|
const downloadName = 'npm-' + data.id + '-' + `${Date.now()}.zip`;
|
||||||
|
const opName = '/tmp/' + downloadName;
|
||||||
|
internalCertificate.zipFiles(certFiles, opName)
|
||||||
|
.then(() => {
|
||||||
|
logger.debug('zip completed : ', opName);
|
||||||
|
const resp = {
|
||||||
|
fileName: opName
|
||||||
|
};
|
||||||
|
resolve(resp);
|
||||||
|
}).catch((err) => reject(err));
|
||||||
|
} else {
|
||||||
|
throw new error.ValidationError('Only Let\'sEncrypt certificates can be downloaded');
|
||||||
|
}
|
||||||
|
}).catch((err) => reject(err));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {String} source
|
||||||
|
* @param {String} out
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
zipFiles(source, out) {
|
||||||
|
const archive = archiver('zip', { zlib: { level: 9 } });
|
||||||
|
const stream = fs.createWriteStream(out);
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
source
|
||||||
|
.map((fl) => {
|
||||||
|
let fileName = path.basename(fl);
|
||||||
|
logger.debug(fl, 'added to certificate zip');
|
||||||
|
archive.file(fl, { name: fileName });
|
||||||
|
});
|
||||||
|
archive
|
||||||
|
.on('error', (err) => reject(err))
|
||||||
|
.pipe(stream);
|
||||||
|
|
||||||
|
stream.on('close', () => resolve());
|
||||||
|
archive.finalize();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Access} access
|
* @param {Access} access
|
||||||
* @param {Object} data
|
* @param {Object} data
|
||||||
@ -448,11 +514,9 @@ const internalCertificate = {
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
writeCustomCert: (certificate) => {
|
writeCustomCert: (certificate) => {
|
||||||
if (debug_mode) {
|
|
||||||
logger.info('Writing Custom Certificate:', certificate);
|
logger.info('Writing Custom Certificate:', certificate);
|
||||||
}
|
|
||||||
|
|
||||||
let dir = '/data/custom_ssl/npm-' + certificate.id;
|
const dir = '/data/custom_ssl/npm-' + certificate.id;
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (certificate.provider === 'letsencrypt') {
|
if (certificate.provider === 'letsencrypt') {
|
||||||
@ -460,9 +524,9 @@ const internalCertificate = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let cert_data = certificate.meta.certificate;
|
let certData = certificate.meta.certificate;
|
||||||
if (typeof certificate.meta.intermediate_certificate !== 'undefined') {
|
if (typeof certificate.meta.intermediate_certificate !== 'undefined') {
|
||||||
cert_data = cert_data + '\n' + certificate.meta.intermediate_certificate;
|
certData = certData + '\n' + certificate.meta.intermediate_certificate;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -474,7 +538,7 @@ const internalCertificate = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fs.writeFile(dir + '/fullchain.pem', cert_data, function (err) {
|
fs.writeFile(dir + '/fullchain.pem', certData, function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
} else {
|
} else {
|
||||||
@ -524,7 +588,7 @@ const internalCertificate = {
|
|||||||
// Put file contents into an object
|
// Put file contents into an object
|
||||||
let files = {};
|
let files = {};
|
||||||
_.map(data.files, (file, name) => {
|
_.map(data.files, (file, name) => {
|
||||||
if (internalCertificate.allowed_ssl_files.indexOf(name) !== -1) {
|
if (internalCertificate.allowedSslFiles.indexOf(name) !== -1) {
|
||||||
files[name] = file.data.toString();
|
files[name] = file.data.toString();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -582,7 +646,7 @@ const internalCertificate = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_.map(data.files, (file, name) => {
|
_.map(data.files, (file, name) => {
|
||||||
if (internalCertificate.allowed_ssl_files.indexOf(name) !== -1) {
|
if (internalCertificate.allowedSslFiles.indexOf(name) !== -1) {
|
||||||
row.meta[name] = file.data.toString();
|
row.meta[name] = file.data.toString();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -601,7 +665,7 @@ const internalCertificate = {
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return _.pick(row.meta, internalCertificate.allowed_ssl_files);
|
return _.pick(row.meta, internalCertificate.allowedSslFiles);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -649,9 +713,9 @@ const internalCertificate = {
|
|||||||
return tempWrite(certificate, '/tmp')
|
return tempWrite(certificate, '/tmp')
|
||||||
.then((filepath) => {
|
.then((filepath) => {
|
||||||
return internalCertificate.getCertificateInfoFromFile(filepath, throw_expired)
|
return internalCertificate.getCertificateInfoFromFile(filepath, throw_expired)
|
||||||
.then((cert_data) => {
|
.then((certData) => {
|
||||||
fs.unlinkSync(filepath);
|
fs.unlinkSync(filepath);
|
||||||
return cert_data;
|
return certData;
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
fs.unlinkSync(filepath);
|
fs.unlinkSync(filepath);
|
||||||
throw err;
|
throw err;
|
||||||
@ -667,33 +731,33 @@ const internalCertificate = {
|
|||||||
* @param {Boolean} [throw_expired] Throw when the certificate is out of date
|
* @param {Boolean} [throw_expired] Throw when the certificate is out of date
|
||||||
*/
|
*/
|
||||||
getCertificateInfoFromFile: (certificate_file, throw_expired) => {
|
getCertificateInfoFromFile: (certificate_file, throw_expired) => {
|
||||||
let cert_data = {};
|
let certData = {};
|
||||||
|
|
||||||
return utils.exec('openssl x509 -in ' + certificate_file + ' -subject -noout')
|
return utils.exec('openssl x509 -in ' + certificate_file + ' -subject -noout')
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
// subject=CN = something.example.com
|
// subject=CN = something.example.com
|
||||||
let regex = /(?:subject=)?[^=]+=\s+(\S+)/gim;
|
const regex = /(?:subject=)?[^=]+=\s+(\S+)/gim;
|
||||||
let match = regex.exec(result);
|
const match = regex.exec(result);
|
||||||
|
|
||||||
if (typeof match[1] === 'undefined') {
|
if (typeof match[1] === 'undefined') {
|
||||||
throw new error.ValidationError('Could not determine subject from certificate: ' + result);
|
throw new error.ValidationError('Could not determine subject from certificate: ' + result);
|
||||||
}
|
}
|
||||||
|
|
||||||
cert_data['cn'] = match[1];
|
certData['cn'] = match[1];
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return utils.exec('openssl x509 -in ' + certificate_file + ' -issuer -noout');
|
return utils.exec('openssl x509 -in ' + certificate_file + ' -issuer -noout');
|
||||||
})
|
})
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
// issuer=C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
|
// issuer=C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
|
||||||
let regex = /^(?:issuer=)?(.*)$/gim;
|
const regex = /^(?:issuer=)?(.*)$/gim;
|
||||||
let match = regex.exec(result);
|
const match = regex.exec(result);
|
||||||
|
|
||||||
if (typeof match[1] === 'undefined') {
|
if (typeof match[1] === 'undefined') {
|
||||||
throw new error.ValidationError('Could not determine issuer from certificate: ' + result);
|
throw new error.ValidationError('Could not determine issuer from certificate: ' + result);
|
||||||
}
|
}
|
||||||
|
|
||||||
cert_data['issuer'] = match[1];
|
certData['issuer'] = match[1];
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return utils.exec('openssl x509 -in ' + certificate_file + ' -dates -noout');
|
return utils.exec('openssl x509 -in ' + certificate_file + ' -dates -noout');
|
||||||
@ -701,39 +765,39 @@ const internalCertificate = {
|
|||||||
.then((result) => {
|
.then((result) => {
|
||||||
// notBefore=Jul 14 04:04:29 2018 GMT
|
// notBefore=Jul 14 04:04:29 2018 GMT
|
||||||
// notAfter=Oct 12 04:04:29 2018 GMT
|
// notAfter=Oct 12 04:04:29 2018 GMT
|
||||||
let valid_from = null;
|
let validFrom = null;
|
||||||
let valid_to = null;
|
let validTo = null;
|
||||||
|
|
||||||
let lines = result.split('\n');
|
const lines = result.split('\n');
|
||||||
lines.map(function (str) {
|
lines.map(function (str) {
|
||||||
let regex = /^(\S+)=(.*)$/gim;
|
const regex = /^(\S+)=(.*)$/gim;
|
||||||
let match = regex.exec(str.trim());
|
const match = regex.exec(str.trim());
|
||||||
|
|
||||||
if (match && typeof match[2] !== 'undefined') {
|
if (match && typeof match[2] !== 'undefined') {
|
||||||
let date = parseInt(moment(match[2], 'MMM DD HH:mm:ss YYYY z').format('X'), 10);
|
const date = parseInt(moment(match[2], 'MMM DD HH:mm:ss YYYY z').format('X'), 10);
|
||||||
|
|
||||||
if (match[1].toLowerCase() === 'notbefore') {
|
if (match[1].toLowerCase() === 'notbefore') {
|
||||||
valid_from = date;
|
validFrom = date;
|
||||||
} else if (match[1].toLowerCase() === 'notafter') {
|
} else if (match[1].toLowerCase() === 'notafter') {
|
||||||
valid_to = date;
|
validTo = date;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!valid_from || !valid_to) {
|
if (!validFrom || !validTo) {
|
||||||
throw new error.ValidationError('Could not determine dates from certificate: ' + result);
|
throw new error.ValidationError('Could not determine dates from certificate: ' + result);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (throw_expired && valid_to < parseInt(moment().format('X'), 10)) {
|
if (throw_expired && validTo < parseInt(moment().format('X'), 10)) {
|
||||||
throw new error.ValidationError('Certificate has expired');
|
throw new error.ValidationError('Certificate has expired');
|
||||||
}
|
}
|
||||||
|
|
||||||
cert_data['dates'] = {
|
certData['dates'] = {
|
||||||
from: valid_from,
|
from: validFrom,
|
||||||
to: valid_to
|
to: validTo
|
||||||
};
|
};
|
||||||
|
|
||||||
return cert_data;
|
return certData;
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
throw new error.ValidationError('Certificate is not valid (' + err.message + ')', err);
|
throw new error.ValidationError('Certificate is not valid (' + err.message + ')', err);
|
||||||
});
|
});
|
||||||
@ -747,7 +811,7 @@ const internalCertificate = {
|
|||||||
* @returns {Object}
|
* @returns {Object}
|
||||||
*/
|
*/
|
||||||
cleanMeta: function (meta, remove) {
|
cleanMeta: function (meta, remove) {
|
||||||
internalCertificate.allowed_ssl_files.map((key) => {
|
internalCertificate.allowedSslFiles.map((key) => {
|
||||||
if (typeof meta[key] !== 'undefined' && meta[key]) {
|
if (typeof meta[key] !== 'undefined' && meta[key]) {
|
||||||
if (remove) {
|
if (remove) {
|
||||||
delete meta[key];
|
delete meta[key];
|
||||||
@ -761,24 +825,24 @@ const internalCertificate = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Request a certificate using the http challenge
|
||||||
* @param {Object} certificate the certificate row
|
* @param {Object} certificate the certificate row
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
requestLetsEncryptSsl: (certificate) => {
|
requestLetsEncryptSsl: (certificate) => {
|
||||||
logger.info('Requesting Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
|
logger.info('Requesting Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
|
||||||
|
|
||||||
let cmd = certbot_command + ' certonly --non-interactive ' +
|
const cmd = certbotCommand + ' certonly --non-interactive ' +
|
||||||
'--config "' + le_config + '" ' +
|
'--config "' + letsencryptConfig + '" ' +
|
||||||
'--cert-name "npm-' + certificate.id + '" ' +
|
'--cert-name "npm-' + certificate.id + '" ' +
|
||||||
'--agree-tos ' +
|
'--agree-tos ' +
|
||||||
|
'--authenticator webroot ' +
|
||||||
'--email "' + certificate.meta.letsencrypt_email + '" ' +
|
'--email "' + certificate.meta.letsencrypt_email + '" ' +
|
||||||
'--preferred-challenges "dns,http" ' +
|
'--preferred-challenges "dns,http" ' +
|
||||||
'--domains "' + certificate.domain_names.join(',') + '" ' +
|
'--domains "' + certificate.domain_names.join(',') + '" ' +
|
||||||
(le_staging ? '--staging' : '');
|
(letsencryptStaging ? '--staging' : '');
|
||||||
|
|
||||||
if (debug_mode) {
|
|
||||||
logger.info('Command:', cmd);
|
logger.info('Command:', cmd);
|
||||||
}
|
|
||||||
|
|
||||||
return utils.exec(cmd)
|
return utils.exec(cmd)
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
@ -795,7 +859,7 @@ const internalCertificate = {
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
requestLetsEncryptSslWithDnsChallenge: (certificate) => {
|
requestLetsEncryptSslWithDnsChallenge: (certificate) => {
|
||||||
const dns_plugin = dns_plugins[certificate.meta.dns_provider];
|
const dns_plugin = dnsPlugins[certificate.meta.dns_provider];
|
||||||
|
|
||||||
if (!dns_plugin) {
|
if (!dns_plugin) {
|
||||||
throw Error(`Unknown DNS provider '${certificate.meta.dns_provider}'`);
|
throw Error(`Unknown DNS provider '${certificate.meta.dns_provider}'`);
|
||||||
@ -803,23 +867,22 @@ const internalCertificate = {
|
|||||||
|
|
||||||
logger.info(`Requesting Let'sEncrypt certificates via ${dns_plugin.display_name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);
|
logger.info(`Requesting Let'sEncrypt certificates via ${dns_plugin.display_name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);
|
||||||
|
|
||||||
const credentials_loc = '/etc/letsencrypt/credentials/credentials-' + certificate.id;
|
const credentialsLocation = '/etc/letsencrypt/credentials/credentials-' + certificate.id;
|
||||||
const credentials_cmd = 'mkdir -p /etc/letsencrypt/credentials 2> /dev/null; echo \'' + certificate.meta.dns_provider_credentials.replace('\'', '\\\'') + '\' > \'' + credentials_loc + '\' && chmod 600 \'' + credentials_loc + '\'';
|
const credentialsCmd = 'mkdir -p /etc/letsencrypt/credentials 2> /dev/null; echo \'' + certificate.meta.dns_provider_credentials.replace('\'', '\\\'') + '\' > \'' + credentialsLocation + '\' && chmod 600 \'' + credentialsLocation + '\'';
|
||||||
const prepare_cmd = 'pip3 install ' + dns_plugin.package_name + '==' + dns_plugin.package_version + ' ' + dns_plugin.dependencies;
|
const prepareCmd = 'pip install ' + dns_plugin.package_name + (dns_plugin.version_requirement || '') + ' ' + dns_plugin.dependencies;
|
||||||
|
|
||||||
// Whether the plugin has a --<name>-credentials argument
|
// Whether the plugin has a --<name>-credentials argument
|
||||||
const has_config_arg = certificate.meta.dns_provider !== 'route53';
|
const hasConfigArg = certificate.meta.dns_provider !== 'route53';
|
||||||
|
|
||||||
let main_cmd =
|
let mainCmd = certbotCommand + ' certonly --non-interactive ' +
|
||||||
certbot_command + ' certonly --non-interactive ' +
|
|
||||||
'--cert-name "npm-' + certificate.id + '" ' +
|
'--cert-name "npm-' + certificate.id + '" ' +
|
||||||
'--agree-tos ' +
|
'--agree-tos ' +
|
||||||
'--email "' + certificate.meta.letsencrypt_email + '" ' +
|
'--email "' + certificate.meta.letsencrypt_email + '" ' +
|
||||||
'--domains "' + certificate.domain_names.join(',') + '" ' +
|
'--domains "' + certificate.domain_names.join(',') + '" ' +
|
||||||
'--authenticator ' + dns_plugin.full_plugin_name + ' ' +
|
'--authenticator ' + dns_plugin.full_plugin_name + ' ' +
|
||||||
(
|
(
|
||||||
has_config_arg
|
hasConfigArg
|
||||||
? '--' + dns_plugin.full_plugin_name + '-credentials "' + credentials_loc + '"'
|
? '--' + dns_plugin.full_plugin_name + '-credentials "' + credentialsLocation + '"'
|
||||||
: ''
|
: ''
|
||||||
) +
|
) +
|
||||||
(
|
(
|
||||||
@ -827,22 +890,20 @@ const internalCertificate = {
|
|||||||
? ' --' + dns_plugin.full_plugin_name + '-propagation-seconds ' + certificate.meta.propagation_seconds
|
? ' --' + dns_plugin.full_plugin_name + '-propagation-seconds ' + certificate.meta.propagation_seconds
|
||||||
: ''
|
: ''
|
||||||
) +
|
) +
|
||||||
(le_staging ? ' --staging' : '');
|
(letsencryptStaging ? ' --staging' : '');
|
||||||
|
|
||||||
// Prepend the path to the credentials file as an environment variable
|
// Prepend the path to the credentials file as an environment variable
|
||||||
if (certificate.meta.dns_provider === 'route53') {
|
if (certificate.meta.dns_provider === 'route53') {
|
||||||
main_cmd = 'AWS_CONFIG_FILE=\'' + credentials_loc + '\' ' + main_cmd;
|
mainCmd = 'AWS_CONFIG_FILE=\'' + credentialsLocation + '\' ' + mainCmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (debug_mode) {
|
logger.info('Command:', `${credentialsCmd} && ${prepareCmd} && ${mainCmd}`);
|
||||||
logger.info('Command:', `${credentials_cmd} && ${prepare_cmd} && ${main_cmd}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return utils.exec(credentials_cmd)
|
return utils.exec(credentialsCmd)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return utils.exec(prepare_cmd)
|
return utils.exec(prepareCmd)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return utils.exec(main_cmd)
|
return utils.exec(mainCmd)
|
||||||
.then(async (result) => {
|
.then(async (result) => {
|
||||||
logger.info(result);
|
logger.info(result);
|
||||||
return result;
|
return result;
|
||||||
@ -850,8 +911,8 @@ const internalCertificate = {
|
|||||||
});
|
});
|
||||||
}).catch(async (err) => {
|
}).catch(async (err) => {
|
||||||
// Don't fail if file does not exist
|
// Don't fail if file does not exist
|
||||||
const delete_credentials_cmd = `rm -f '${credentials_loc}' || true`;
|
const delete_credentialsCmd = `rm -f '${credentialsLocation}' || true`;
|
||||||
await utils.exec(delete_credentials_cmd);
|
await utils.exec(delete_credentialsCmd);
|
||||||
throw err;
|
throw err;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -870,7 +931,7 @@ const internalCertificate = {
|
|||||||
})
|
})
|
||||||
.then((certificate) => {
|
.then((certificate) => {
|
||||||
if (certificate.provider === 'letsencrypt') {
|
if (certificate.provider === 'letsencrypt') {
|
||||||
let renewMethod = certificate.meta.dns_challenge ? internalCertificate.renewLetsEncryptSslWithDnsChallenge : internalCertificate.renewLetsEncryptSsl;
|
const renewMethod = certificate.meta.dns_challenge ? internalCertificate.renewLetsEncryptSslWithDnsChallenge : internalCertificate.renewLetsEncryptSsl;
|
||||||
|
|
||||||
return renewMethod(certificate)
|
return renewMethod(certificate)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
@ -908,16 +969,14 @@ const internalCertificate = {
|
|||||||
renewLetsEncryptSsl: (certificate) => {
|
renewLetsEncryptSsl: (certificate) => {
|
||||||
logger.info('Renewing Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
|
logger.info('Renewing Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
|
||||||
|
|
||||||
let cmd = certbot_command + ' renew --non-interactive ' +
|
const cmd = certbotCommand + ' renew --force-renewal --non-interactive ' +
|
||||||
'--config "' + le_config + '" ' +
|
'--config "' + letsencryptConfig + '" ' +
|
||||||
'--cert-name "npm-' + certificate.id + '" ' +
|
'--cert-name "npm-' + certificate.id + '" ' +
|
||||||
'--preferred-challenges "dns,http" ' +
|
'--preferred-challenges "dns,http" ' +
|
||||||
'--disable-hook-validation ' +
|
'--disable-hook-validation ' +
|
||||||
(le_staging ? '--staging' : '');
|
(letsencryptStaging ? '--staging' : '');
|
||||||
|
|
||||||
if (debug_mode) {
|
|
||||||
logger.info('Command:', cmd);
|
logger.info('Command:', cmd);
|
||||||
}
|
|
||||||
|
|
||||||
return utils.exec(cmd)
|
return utils.exec(cmd)
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
@ -931,7 +990,7 @@ const internalCertificate = {
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
renewLetsEncryptSslWithDnsChallenge: (certificate) => {
|
renewLetsEncryptSslWithDnsChallenge: (certificate) => {
|
||||||
const dns_plugin = dns_plugins[certificate.meta.dns_provider];
|
const dns_plugin = dnsPlugins[certificate.meta.dns_provider];
|
||||||
|
|
||||||
if (!dns_plugin) {
|
if (!dns_plugin) {
|
||||||
throw Error(`Unknown DNS provider '${certificate.meta.dns_provider}'`);
|
throw Error(`Unknown DNS provider '${certificate.meta.dns_provider}'`);
|
||||||
@ -939,23 +998,20 @@ const internalCertificate = {
|
|||||||
|
|
||||||
logger.info(`Renewing Let'sEncrypt certificates via ${dns_plugin.display_name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);
|
logger.info(`Renewing Let'sEncrypt certificates via ${dns_plugin.display_name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);
|
||||||
|
|
||||||
let main_cmd =
|
let mainCmd = certbotCommand + ' renew --non-interactive ' +
|
||||||
certbot_command + ' renew --non-interactive ' +
|
|
||||||
'--cert-name "npm-' + certificate.id + '" ' +
|
'--cert-name "npm-' + certificate.id + '" ' +
|
||||||
'--disable-hook-validation' +
|
'--disable-hook-validation' +
|
||||||
(le_staging ? ' --staging' : '');
|
(letsencryptStaging ? ' --staging' : '');
|
||||||
|
|
||||||
// Prepend the path to the credentials file as an environment variable
|
// Prepend the path to the credentials file as an environment variable
|
||||||
if (certificate.meta.dns_provider === 'route53') {
|
if (certificate.meta.dns_provider === 'route53') {
|
||||||
const credentials_loc = '/etc/letsencrypt/credentials/credentials-' + certificate.id;
|
const credentialsLocation = '/etc/letsencrypt/credentials/credentials-' + certificate.id;
|
||||||
main_cmd = 'AWS_CONFIG_FILE=\'' + credentials_loc + '\' ' + main_cmd;
|
mainCmd = 'AWS_CONFIG_FILE=\'' + credentialsLocation + '\' ' + mainCmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (debug_mode) {
|
logger.info('Command:', mainCmd);
|
||||||
logger.info('Command:', main_cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
return utils.exec(main_cmd)
|
return utils.exec(mainCmd)
|
||||||
.then(async (result) => {
|
.then(async (result) => {
|
||||||
logger.info(result);
|
logger.info(result);
|
||||||
return result;
|
return result;
|
||||||
@ -970,28 +1026,24 @@ const internalCertificate = {
|
|||||||
revokeLetsEncryptSsl: (certificate, throw_errors) => {
|
revokeLetsEncryptSsl: (certificate, throw_errors) => {
|
||||||
logger.info('Revoking Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
|
logger.info('Revoking Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
|
||||||
|
|
||||||
const main_cmd = certbot_command + ' revoke --non-interactive ' +
|
const mainCmd = certbotCommand + ' revoke --non-interactive ' +
|
||||||
'--cert-path "/etc/letsencrypt/live/npm-' + certificate.id + '/fullchain.pem" ' +
|
'--cert-path "/etc/letsencrypt/live/npm-' + certificate.id + '/fullchain.pem" ' +
|
||||||
'--delete-after-revoke ' +
|
'--delete-after-revoke ' +
|
||||||
(le_staging ? '--staging' : '');
|
(letsencryptStaging ? '--staging' : '');
|
||||||
|
|
||||||
// Don't fail command if file does not exist
|
// Don't fail command if file does not exist
|
||||||
const delete_credentials_cmd = `rm -f '/etc/letsencrypt/credentials/credentials-${certificate.id}' || true`;
|
const delete_credentialsCmd = `rm -f '/etc/letsencrypt/credentials/credentials-${certificate.id}' || true`;
|
||||||
|
|
||||||
if (debug_mode) {
|
logger.info('Command:', mainCmd + '; ' + delete_credentialsCmd);
|
||||||
logger.info('Command:', main_cmd + '; ' + delete_credentials_cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
return utils.exec(main_cmd)
|
return utils.exec(mainCmd)
|
||||||
.then(async (result) => {
|
.then(async (result) => {
|
||||||
await utils.exec(delete_credentials_cmd);
|
await utils.exec(delete_credentialsCmd);
|
||||||
logger.info(result);
|
logger.info(result);
|
||||||
return result;
|
return result;
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
if (debug_mode) {
|
|
||||||
logger.error(err.message);
|
logger.error(err.message);
|
||||||
}
|
|
||||||
|
|
||||||
if (throw_errors) {
|
if (throw_errors) {
|
||||||
throw err;
|
throw err;
|
||||||
@ -1004,9 +1056,9 @@ const internalCertificate = {
|
|||||||
* @returns {Boolean}
|
* @returns {Boolean}
|
||||||
*/
|
*/
|
||||||
hasLetsEncryptSslCerts: (certificate) => {
|
hasLetsEncryptSslCerts: (certificate) => {
|
||||||
let le_path = '/etc/letsencrypt/live/npm-' + certificate.id;
|
const letsencryptPath = '/etc/letsencrypt/live/npm-' + certificate.id;
|
||||||
|
|
||||||
return fs.existsSync(le_path + '/fullchain.pem') && fs.existsSync(le_path + '/privkey.pem');
|
return fs.existsSync(letsencryptPath + '/fullchain.pem') && fs.existsSync(letsencryptPath + '/privkey.pem');
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2,6 +2,7 @@ const _ = require('lodash');
|
|||||||
const proxyHostModel = require('../models/proxy_host');
|
const proxyHostModel = require('../models/proxy_host');
|
||||||
const redirectionHostModel = require('../models/redirection_host');
|
const redirectionHostModel = require('../models/redirection_host');
|
||||||
const deadHostModel = require('../models/dead_host');
|
const deadHostModel = require('../models/dead_host');
|
||||||
|
const sslPassthroughHostModel = require('../models/ssl_passthrough_host');
|
||||||
|
|
||||||
const internalHost = {
|
const internalHost = {
|
||||||
|
|
||||||
@ -81,6 +82,9 @@ const internalHost = {
|
|||||||
.query()
|
.query()
|
||||||
.where('is_deleted', 0),
|
.where('is_deleted', 0),
|
||||||
deadHostModel
|
deadHostModel
|
||||||
|
.query()
|
||||||
|
.where('is_deleted', 0),
|
||||||
|
sslPassthroughHostModel
|
||||||
.query()
|
.query()
|
||||||
.where('is_deleted', 0)
|
.where('is_deleted', 0)
|
||||||
];
|
];
|
||||||
@ -112,6 +116,12 @@ const internalHost = {
|
|||||||
response_object.total_count += response_object.dead_hosts.length;
|
response_object.total_count += response_object.dead_hosts.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (promises_results[3]) {
|
||||||
|
// SSL Passthrough Hosts
|
||||||
|
response_object.ssl_passthrough_hosts = internalHost._getHostsWithDomains(promises_results[3], domain_names);
|
||||||
|
response_object.total_count += response_object.ssl_passthrough_hosts.length;
|
||||||
|
}
|
||||||
|
|
||||||
return response_object;
|
return response_object;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -137,7 +147,11 @@ const internalHost = {
|
|||||||
deadHostModel
|
deadHostModel
|
||||||
.query()
|
.query()
|
||||||
.where('is_deleted', 0)
|
.where('is_deleted', 0)
|
||||||
.andWhere('domain_names', 'like', '%' + hostname + '%')
|
.andWhere('domain_names', 'like', '%' + hostname + '%'),
|
||||||
|
sslPassthroughHostModel
|
||||||
|
.query()
|
||||||
|
.where('is_deleted', 0)
|
||||||
|
.andWhere('domain_name', '=', hostname),
|
||||||
];
|
];
|
||||||
|
|
||||||
return Promise.all(promises)
|
return Promise.all(promises)
|
||||||
@ -165,6 +179,13 @@ const internalHost = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (promises_results[3]) {
|
||||||
|
// SSL Passthrough Hosts
|
||||||
|
if (internalHost._checkHostnameRecordsTaken(hostname, promises_results[3], ignore_type === 'ssl_passthrough' && ignore_id ? ignore_id : 0)) {
|
||||||
|
is_taken = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
hostname: hostname,
|
hostname: hostname,
|
||||||
is_taken: is_taken
|
is_taken: is_taken
|
||||||
@ -185,14 +206,21 @@ const internalHost = {
|
|||||||
|
|
||||||
if (existing_rows && existing_rows.length) {
|
if (existing_rows && existing_rows.length) {
|
||||||
existing_rows.map(function (existing_row) {
|
existing_rows.map(function (existing_row) {
|
||||||
existing_row.domain_names.map(function (existing_hostname) {
|
|
||||||
|
function checkHostname(existing_hostname) {
|
||||||
// Does this domain match?
|
// Does this domain match?
|
||||||
if (existing_hostname.toLowerCase() === hostname.toLowerCase()) {
|
if (existing_hostname.toLowerCase() === hostname.toLowerCase()) {
|
||||||
if (!ignore_id || ignore_id !== existing_row.id) {
|
if (!ignore_id || ignore_id !== existing_row.id) {
|
||||||
is_taken = true;
|
is_taken = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
|
if (existing_row.domain_names) {
|
||||||
|
existing_row.domain_names.map(checkHostname);
|
||||||
|
} else if (existing_row.domain_name) {
|
||||||
|
checkHostname(existing_row.domain_name);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ const logger = require('../logger').nginx;
|
|||||||
const utils = require('../lib/utils');
|
const utils = require('../lib/utils');
|
||||||
const error = require('../lib/error');
|
const error = require('../lib/error');
|
||||||
const { Liquid } = require('liquidjs');
|
const { Liquid } = require('liquidjs');
|
||||||
|
const passthroughHostModel = require('../models/ssl_passthrough_host');
|
||||||
const debug_mode = process.env.NODE_ENV !== 'production' || !!process.env.DEBUG;
|
const debug_mode = process.env.NODE_ENV !== 'production' || !!process.env.DEBUG;
|
||||||
|
|
||||||
const internalNginx = {
|
const internalNginx = {
|
||||||
@ -24,6 +25,7 @@ const internalNginx = {
|
|||||||
*/
|
*/
|
||||||
configure: (model, host_type, host) => {
|
configure: (model, host_type, host) => {
|
||||||
let combined_meta = {};
|
let combined_meta = {};
|
||||||
|
const sslPassthroughEnabled = internalNginx.sslPassthroughEnabled();
|
||||||
|
|
||||||
return internalNginx.test()
|
return internalNginx.test()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
@ -32,7 +34,25 @@ const internalNginx = {
|
|||||||
return internalNginx.deleteConfig(host_type, host); // Don't throw errors, as the file may not exist at all
|
return internalNginx.deleteConfig(host_type, host); // Don't throw errors, as the file may not exist at all
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
if (host_type === 'ssl_passthrough_host' && !sslPassthroughEnabled){
|
||||||
|
// ssl passthrough is disabled
|
||||||
|
const meta = {
|
||||||
|
nginx_online: false,
|
||||||
|
nginx_err: 'SSL passthrough is not enabled in environment'
|
||||||
|
};
|
||||||
|
|
||||||
|
return passthroughHostModel
|
||||||
|
.query()
|
||||||
|
.where('is_deleted', 0)
|
||||||
|
.andWhere('enabled', 1)
|
||||||
|
.patch({
|
||||||
|
meta
|
||||||
|
}).then(() => {
|
||||||
|
return internalNginx.deleteConfig('ssl_passthrough_host', host, false);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
return internalNginx.generateConfig(host_type, host);
|
return internalNginx.generateConfig(host_type, host);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// Test nginx again and update meta with result
|
// Test nginx again and update meta with result
|
||||||
@ -44,12 +64,27 @@ const internalNginx = {
|
|||||||
nginx_err: null
|
nginx_err: null
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (host_type === 'ssl_passthrough_host'){
|
||||||
|
// If passthrough is disabled we have already marked the hosts as offline
|
||||||
|
if (sslPassthroughEnabled) {
|
||||||
|
return passthroughHostModel
|
||||||
|
.query()
|
||||||
|
.where('is_deleted', 0)
|
||||||
|
.andWhere('enabled', 1)
|
||||||
|
.patch({
|
||||||
|
meta: combined_meta
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
return model
|
return model
|
||||||
.query()
|
.query()
|
||||||
.where('id', host.id)
|
.where('id', host.id)
|
||||||
.patch({
|
.patch({
|
||||||
meta: combined_meta
|
meta: combined_meta
|
||||||
});
|
});
|
||||||
|
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
// Remove the error_log line because it's a docker-ism false positive that doesn't need to be reported.
|
// Remove the error_log line because it's a docker-ism false positive that doesn't need to be reported.
|
||||||
@ -74,6 +109,18 @@ const internalNginx = {
|
|||||||
nginx_err: valid_lines.join('\n')
|
nginx_err: valid_lines.join('\n')
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (host_type === 'ssl_passthrough_host'){
|
||||||
|
return passthroughHostModel
|
||||||
|
.query()
|
||||||
|
.where('is_deleted', 0)
|
||||||
|
.andWhere('enabled', 1)
|
||||||
|
.patch({
|
||||||
|
meta: combined_meta
|
||||||
|
}).then(() => {
|
||||||
|
return internalNginx.deleteConfig('ssl_passthrough_host', host, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return model
|
return model
|
||||||
.query()
|
.query()
|
||||||
.where('id', host.id)
|
.where('id', host.id)
|
||||||
@ -125,6 +172,8 @@ const internalNginx = {
|
|||||||
|
|
||||||
if (host_type === 'default') {
|
if (host_type === 'default') {
|
||||||
return '/data/nginx/default_host/site.conf';
|
return '/data/nginx/default_host/site.conf';
|
||||||
|
} else if (host_type === 'ssl_passthrough_host') {
|
||||||
|
return '/data/nginx/ssl_passthrough_host/hosts.conf';
|
||||||
}
|
}
|
||||||
|
|
||||||
return '/data/nginx/' + host_type + '/' + host_id + '.conf';
|
return '/data/nginx/' + host_type + '/' + host_id + '.conf';
|
||||||
@ -136,6 +185,8 @@ const internalNginx = {
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
renderLocations: (host) => {
|
renderLocations: (host) => {
|
||||||
|
|
||||||
|
//logger.info('host = ' + JSON.stringify(host, null, 2));
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let template;
|
let template;
|
||||||
|
|
||||||
@ -146,12 +197,18 @@ const internalNginx = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let renderer = new Liquid();
|
let renderer = new Liquid({
|
||||||
|
root: __dirname + '/../templates/'
|
||||||
|
});
|
||||||
let renderedLocations = '';
|
let renderedLocations = '';
|
||||||
|
|
||||||
const locationRendering = async () => {
|
const locationRendering = async () => {
|
||||||
for (let i = 0; i < host.locations.length; i++) {
|
for (let i = 0; i < host.locations.length; i++) {
|
||||||
let locationCopy = Object.assign({}, host.locations[i]);
|
let locationCopy = Object.assign({}, {access_list_id: host.access_list_id}, {certificate_id: host.certificate_id},
|
||||||
|
{ssl_forced: host.ssl_forced}, {caching_enabled: host.caching_enabled}, {block_exploits: host.block_exploits},
|
||||||
|
{allow_websocket_upgrade: host.allow_websocket_upgrade}, {http2_support: host.http2_support},
|
||||||
|
{hsts_enabled: host.hsts_enabled}, {hsts_subdomains: host.hsts_subdomains}, {access_list: host.access_list},
|
||||||
|
{certificate: host.certificate}, host.locations[i]);
|
||||||
|
|
||||||
if (locationCopy.forward_host.indexOf('/') > -1) {
|
if (locationCopy.forward_host.indexOf('/') > -1) {
|
||||||
const splitted = locationCopy.forward_host.split('/');
|
const splitted = locationCopy.forward_host.split('/');
|
||||||
@ -160,12 +217,16 @@ const internalNginx = {
|
|||||||
locationCopy.forward_path = `/${splitted.join('/')}`;
|
locationCopy.forward_path = `/${splitted.join('/')}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//logger.info('locationCopy = ' + JSON.stringify(locationCopy, null, 2));
|
||||||
|
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
renderedLocations += await renderer.parseAndRender(template, locationCopy);
|
renderedLocations += await renderer.parseAndRender(template, locationCopy);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
locationRendering().then(() => resolve(renderedLocations));
|
locationRendering().then(() => resolve(renderedLocations));
|
||||||
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -174,33 +235,51 @@ const internalNginx = {
|
|||||||
* @param {Object} host
|
* @param {Object} host
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
generateConfig: (host_type, host) => {
|
generateConfig: async (host_type, host) => {
|
||||||
host_type = host_type.replace(new RegExp('-', 'g'), '_');
|
host_type = host_type.replace(new RegExp('-', 'g'), '_');
|
||||||
|
|
||||||
if (debug_mode) {
|
if (debug_mode) {
|
||||||
logger.info('Generating ' + host_type + ' Config:', host);
|
logger.info('Generating ' + host_type + ' Config:', host);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// logger.info('host = ' + JSON.stringify(host, null, 2));
|
||||||
|
|
||||||
let renderEngine = new Liquid({
|
let renderEngine = new Liquid({
|
||||||
root: __dirname + '/../templates/'
|
root: __dirname + '/../templates/'
|
||||||
});
|
});
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
let template = null;
|
let template = null;
|
||||||
let filename = internalNginx.getConfigName(host_type, host.id);
|
let filename = internalNginx.getConfigName(host_type, host.id);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
template = fs.readFileSync(__dirname + '/../templates/' + host_type + '.conf', {encoding: 'utf8'});
|
template = fs.readFileSync(__dirname + '/../templates/' + host_type + '.conf', {encoding: 'utf8'});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
reject(new error.ConfigurationError(err.message));
|
throw new error.ConfigurationError(err.message);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let locationsPromise;
|
let locationsPromise;
|
||||||
let origLocations;
|
let origLocations;
|
||||||
|
|
||||||
// Manipulate the data a bit before sending it to the template
|
// Manipulate the data a bit before sending it to the template
|
||||||
if (host_type !== 'default') {
|
if (host_type === 'ssl_passthrough_host') {
|
||||||
|
if (internalNginx.sslPassthroughEnabled()){
|
||||||
|
const allHosts = await passthroughHostModel
|
||||||
|
.query()
|
||||||
|
.where('is_deleted', 0)
|
||||||
|
.groupBy('id')
|
||||||
|
.omit(['is_deleted']);
|
||||||
|
host = {
|
||||||
|
all_passthrough_hosts: allHosts.map((host) => {
|
||||||
|
// Replace dots in domain
|
||||||
|
host.forwarding_host = internalNginx.addIpv6Brackets(host.forwarding_host);
|
||||||
|
return host;
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
internalNginx.deleteConfig(host_type, host, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (host_type !== 'default') {
|
||||||
host.use_default_location = true;
|
host.use_default_location = true;
|
||||||
if (typeof host.advanced_config !== 'undefined' && host.advanced_config) {
|
if (typeof host.advanced_config !== 'undefined' && host.advanced_config) {
|
||||||
host.use_default_location = !internalNginx.advancedConfigHasDefaultLocation(host.advanced_config);
|
host.use_default_location = !internalNginx.advancedConfigHasDefaultLocation(host.advanced_config);
|
||||||
@ -208,6 +287,7 @@ const internalNginx = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (host.locations) {
|
if (host.locations) {
|
||||||
|
//logger.info ('host.locations = ' + JSON.stringify(host.locations, null, 2));
|
||||||
origLocations = [].concat(host.locations);
|
origLocations = [].concat(host.locations);
|
||||||
locationsPromise = internalNginx.renderLocations(host).then((renderedLocations) => {
|
locationsPromise = internalNginx.renderLocations(host).then((renderedLocations) => {
|
||||||
host.locations = renderedLocations;
|
host.locations = renderedLocations;
|
||||||
@ -227,7 +307,7 @@ const internalNginx = {
|
|||||||
// Set the IPv6 setting for the host
|
// Set the IPv6 setting for the host
|
||||||
host.ipv6 = internalNginx.ipv6Enabled();
|
host.ipv6 = internalNginx.ipv6Enabled();
|
||||||
|
|
||||||
locationsPromise.then(() => {
|
return locationsPromise.then(() => {
|
||||||
renderEngine
|
renderEngine
|
||||||
.parseAndRender(template, host)
|
.parseAndRender(template, host)
|
||||||
.then((config_text) => {
|
.then((config_text) => {
|
||||||
@ -240,15 +320,14 @@ const internalNginx = {
|
|||||||
// Restore locations array
|
// Restore locations array
|
||||||
host.locations = origLocations;
|
host.locations = origLocations;
|
||||||
|
|
||||||
resolve(true);
|
return true;
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
if (debug_mode) {
|
if (debug_mode) {
|
||||||
logger.warn('Could not write ' + filename + ':', err.message);
|
logger.warn('Could not write ' + filename + ':', err.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
reject(new error.ConfigurationError(err.message));
|
throw new error.ConfigurationError(err.message);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -414,6 +493,33 @@ const internalNginx = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
sslPassthroughEnabled: function () {
|
||||||
|
if (typeof process.env.ENABLE_SSL_PASSTHROUGH !== 'undefined') {
|
||||||
|
const enabled = process.env.ENABLE_SSL_PASSTHROUGH.toLowerCase();
|
||||||
|
return (enabled === 'on' || enabled === 'true' || enabled === '1' || enabled === 'yes');
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to add brackets to an IP if it is IPv6
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
addIpv6Brackets: function (ip) {
|
||||||
|
// Only run check if ipv6 is enabled
|
||||||
|
if (internalNginx.ipv6Enabled()) {
|
||||||
|
const ipv6Regex = /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/gi;
|
||||||
|
if (ipv6Regex.test(ip)){
|
||||||
|
return `[${ip}]`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ip;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
365
backend/internal/ssl-passthrough-host.js
Normal file
365
backend/internal/ssl-passthrough-host.js
Normal file
@ -0,0 +1,365 @@
|
|||||||
|
const _ = require('lodash');
|
||||||
|
const error = require('../lib/error');
|
||||||
|
const passthroughHostModel = require('../models/ssl_passthrough_host');
|
||||||
|
const internalHost = require('./host');
|
||||||
|
const internalNginx = require('./nginx');
|
||||||
|
const internalAuditLog = require('./audit-log');
|
||||||
|
|
||||||
|
function omissions () {
|
||||||
|
return ['is_deleted'];
|
||||||
|
}
|
||||||
|
|
||||||
|
const internalPassthroughHost = {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Access} access
|
||||||
|
* @param {Object} data
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
create: (access, data) => {
|
||||||
|
return access.can('ssl_passthrough_hosts:create', data)
|
||||||
|
.then(() => {
|
||||||
|
// Get the domain name and check it against existing records
|
||||||
|
return internalHost.isHostnameTaken(data.domain_name)
|
||||||
|
.then((result) => {
|
||||||
|
if (result.is_taken) {
|
||||||
|
throw new error.ValidationError(result.hostname + ' is already in use');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}).then((/*access_data*/) => {
|
||||||
|
data.owner_user_id = access.token.getUserId(1);
|
||||||
|
|
||||||
|
if (typeof data.meta === 'undefined') {
|
||||||
|
data.meta = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return passthroughHostModel
|
||||||
|
.query()
|
||||||
|
.omit(omissions())
|
||||||
|
.insertAndFetch(data);
|
||||||
|
})
|
||||||
|
.then((row) => {
|
||||||
|
// Configure nginx
|
||||||
|
return internalNginx.configure(passthroughHostModel, 'ssl_passthrough_host', {})
|
||||||
|
.then(() => {
|
||||||
|
return internalPassthroughHost.get(access, {id: row.id, expand: ['owner']});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then((row) => {
|
||||||
|
// Add to audit log
|
||||||
|
return internalAuditLog.add(access, {
|
||||||
|
action: 'created',
|
||||||
|
object_type: 'ssl-passthrough-host',
|
||||||
|
object_id: row.id,
|
||||||
|
meta: data
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
return row;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Access} access
|
||||||
|
* @param {Object} data
|
||||||
|
* @param {Number} data.id
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
|
update: (access, data) => {
|
||||||
|
return access.can('ssl_passthrough_hosts:update', data.id)
|
||||||
|
.then((/*access_data*/) => {
|
||||||
|
// Get the domain name and check it against existing records
|
||||||
|
if (typeof data.domain_name !== 'undefined') {
|
||||||
|
return internalHost.isHostnameTaken(data.domain_name, 'ssl_passthrough', data.id)
|
||||||
|
.then((result) => {
|
||||||
|
if (result.is_taken) {
|
||||||
|
throw new error.ValidationError(result.hostname + ' is already in use');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).then((/*access_data*/) => {
|
||||||
|
return internalPassthroughHost.get(access, {id: data.id});
|
||||||
|
})
|
||||||
|
.then((row) => {
|
||||||
|
if (row.id !== data.id) {
|
||||||
|
// Sanity check that something crazy hasn't happened
|
||||||
|
throw new error.InternalValidationError('SSL Passthrough Host could not be updated, IDs do not match: ' + row.id + ' !== ' + data.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return passthroughHostModel
|
||||||
|
.query()
|
||||||
|
.omit(omissions())
|
||||||
|
.patchAndFetchById(row.id, data)
|
||||||
|
.then(() => {
|
||||||
|
return internalNginx.configure(passthroughHostModel, 'ssl_passthrough_host', {})
|
||||||
|
.then(() => {
|
||||||
|
return internalPassthroughHost.get(access, {id: row.id, expand: ['owner']});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then((saved_row) => {
|
||||||
|
// Add to audit log
|
||||||
|
return internalAuditLog.add(access, {
|
||||||
|
action: 'updated',
|
||||||
|
object_type: 'ssl-passthrough-host',
|
||||||
|
object_id: row.id,
|
||||||
|
meta: data
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
return _.omit(saved_row, omissions());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Access} access
|
||||||
|
* @param {Object} data
|
||||||
|
* @param {Number} data.id
|
||||||
|
* @param {Array} [data.expand]
|
||||||
|
* @param {Array} [data.omit]
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
|
get: (access, data) => {
|
||||||
|
if (typeof data === 'undefined') {
|
||||||
|
data = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return access.can('ssl_passthrough_hosts:get', data.id)
|
||||||
|
.then((access_data) => {
|
||||||
|
let query = passthroughHostModel
|
||||||
|
.query()
|
||||||
|
.where('is_deleted', 0)
|
||||||
|
.andWhere('id', data.id)
|
||||||
|
.allowEager('[owner]')
|
||||||
|
.first();
|
||||||
|
|
||||||
|
if (access_data.permission_visibility !== 'all') {
|
||||||
|
query.andWhere('owner_user_id', access.token.getUserId(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom omissions
|
||||||
|
if (typeof data.omit !== 'undefined' && data.omit !== null) {
|
||||||
|
query.omit(data.omit);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof data.expand !== 'undefined' && data.expand !== null) {
|
||||||
|
query.eager('[' + data.expand.join(', ') + ']');
|
||||||
|
}
|
||||||
|
|
||||||
|
return query;
|
||||||
|
})
|
||||||
|
.then((row) => {
|
||||||
|
if (row) {
|
||||||
|
return _.omit(row, omissions());
|
||||||
|
} else {
|
||||||
|
throw new error.ItemNotFoundError(data.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Access} access
|
||||||
|
* @param {Object} data
|
||||||
|
* @param {Number} data.id
|
||||||
|
* @param {String} [data.reason]
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
delete: (access, data) => {
|
||||||
|
return access.can('ssl_passthrough_hosts:delete', data.id)
|
||||||
|
.then(() => {
|
||||||
|
return internalPassthroughHost.get(access, {id: data.id});
|
||||||
|
})
|
||||||
|
.then((row) => {
|
||||||
|
if (!row) {
|
||||||
|
throw new error.ItemNotFoundError(data.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return passthroughHostModel
|
||||||
|
.query()
|
||||||
|
.where('id', row.id)
|
||||||
|
.patch({
|
||||||
|
is_deleted: 1
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
// Update Nginx Config
|
||||||
|
return internalNginx.configure(passthroughHostModel, 'ssl_passthrough_host', {})
|
||||||
|
.then(() => {
|
||||||
|
return internalNginx.reload();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
// Add to audit log
|
||||||
|
return internalAuditLog.add(access, {
|
||||||
|
action: 'deleted',
|
||||||
|
object_type: 'ssl-passthrough-host',
|
||||||
|
object_id: row.id,
|
||||||
|
meta: _.omit(row, omissions())
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Access} access
|
||||||
|
* @param {Object} data
|
||||||
|
* @param {Number} data.id
|
||||||
|
* @param {String} [data.reason]
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
enable: (access, data) => {
|
||||||
|
return access.can('ssl_passthrough_hosts:update', data.id)
|
||||||
|
.then(() => {
|
||||||
|
return internalPassthroughHost.get(access, {
|
||||||
|
id: data.id,
|
||||||
|
expand: ['owner']
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then((row) => {
|
||||||
|
if (!row) {
|
||||||
|
throw new error.ItemNotFoundError(data.id);
|
||||||
|
} else if (row.enabled) {
|
||||||
|
throw new error.ValidationError('Host is already enabled');
|
||||||
|
}
|
||||||
|
|
||||||
|
row.enabled = 1;
|
||||||
|
|
||||||
|
return passthroughHostModel
|
||||||
|
.query()
|
||||||
|
.where('id', row.id)
|
||||||
|
.patch({
|
||||||
|
enabled: 1
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
// Configure nginx
|
||||||
|
return internalNginx.configure(passthroughHostModel, 'ssl_passthrough_host', {});
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
// Add to audit log
|
||||||
|
return internalAuditLog.add(access, {
|
||||||
|
action: 'enabled',
|
||||||
|
object_type: 'ssl-passthrough-host',
|
||||||
|
object_id: row.id,
|
||||||
|
meta: _.omit(row, omissions())
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Access} access
|
||||||
|
* @param {Object} data
|
||||||
|
* @param {Number} data.id
|
||||||
|
* @param {String} [data.reason]
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
disable: (access, data) => {
|
||||||
|
return access.can('ssl_passthrough_hosts:update', data.id)
|
||||||
|
.then(() => {
|
||||||
|
return internalPassthroughHost.get(access, {id: data.id});
|
||||||
|
})
|
||||||
|
.then((row) => {
|
||||||
|
if (!row) {
|
||||||
|
throw new error.ItemNotFoundError(data.id);
|
||||||
|
} else if (!row.enabled) {
|
||||||
|
throw new error.ValidationError('Host is already disabled');
|
||||||
|
}
|
||||||
|
|
||||||
|
row.enabled = 0;
|
||||||
|
|
||||||
|
return passthroughHostModel
|
||||||
|
.query()
|
||||||
|
.where('id', row.id)
|
||||||
|
.patch({
|
||||||
|
enabled: 0
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
// Update Nginx Config
|
||||||
|
return internalNginx.configure(passthroughHostModel, 'ssl_passthrough_host', {})
|
||||||
|
.then(() => {
|
||||||
|
return internalNginx.reload();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
// Add to audit log
|
||||||
|
return internalAuditLog.add(access, {
|
||||||
|
action: 'disabled',
|
||||||
|
object_type: 'ssl-passthrough-host',
|
||||||
|
object_id: row.id,
|
||||||
|
meta: _.omit(row, omissions())
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All SSL Passthrough Hosts
|
||||||
|
*
|
||||||
|
* @param {Access} access
|
||||||
|
* @param {Array} [expand]
|
||||||
|
* @param {String} [search_query]
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
getAll: (access, expand, search_query) => {
|
||||||
|
return access.can('ssl_passthrough_hosts:list')
|
||||||
|
.then((access_data) => {
|
||||||
|
let query = passthroughHostModel
|
||||||
|
.query()
|
||||||
|
.where('is_deleted', 0)
|
||||||
|
.groupBy('id')
|
||||||
|
.omit(['is_deleted'])
|
||||||
|
.allowEager('[owner]')
|
||||||
|
.orderBy('domain_name', 'ASC');
|
||||||
|
|
||||||
|
if (access_data.permission_visibility !== 'all') {
|
||||||
|
query.andWhere('owner_user_id', access.token.getUserId(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query is used for searching
|
||||||
|
if (typeof search_query === 'string') {
|
||||||
|
query.where(function () {
|
||||||
|
this.where('domain_name', 'like', '%' + search_query + '%');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof expand !== 'undefined' && expand !== null) {
|
||||||
|
query.eager('[' + expand.join(', ') + ']');
|
||||||
|
}
|
||||||
|
|
||||||
|
return query;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Report use
|
||||||
|
*
|
||||||
|
* @param {Number} user_id
|
||||||
|
* @param {String} visibility
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
getCount: (user_id, visibility) => {
|
||||||
|
let query = passthroughHostModel
|
||||||
|
.query()
|
||||||
|
.count('id as count')
|
||||||
|
.where('is_deleted', 0);
|
||||||
|
|
||||||
|
if (visibility !== 'all') {
|
||||||
|
query.andWhere('owner_user_id', user_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return query.first()
|
||||||
|
.then((row) => {
|
||||||
|
return parseInt(row.count, 10);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = internalPassthroughHost;
|
@ -67,6 +67,7 @@ const internalUser = {
|
|||||||
proxy_hosts: 'manage',
|
proxy_hosts: 'manage',
|
||||||
redirection_hosts: 'manage',
|
redirection_hosts: 'manage',
|
||||||
dead_hosts: 'manage',
|
dead_hosts: 'manage',
|
||||||
|
ssl_passthrough_hosts: 'manage',
|
||||||
streams: 'manage',
|
streams: 'manage',
|
||||||
access_lists: 'manage',
|
access_lists: 'manage',
|
||||||
certificates: 'manage'
|
certificates: 'manage'
|
||||||
|
23
backend/lib/access/ssl_passthrough_hosts-create.json
Normal file
23
backend/lib/access/ssl_passthrough_hosts-create.json
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "roles#/definitions/admin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": ["permission_ssl_passthrough_hosts", "roles"],
|
||||||
|
"properties": {
|
||||||
|
"permission_ssl_passthrough_hosts": {
|
||||||
|
"$ref": "perms#/definitions/manage"
|
||||||
|
},
|
||||||
|
"roles": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["user"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
23
backend/lib/access/ssl_passthrough_hosts-delete.json
Normal file
23
backend/lib/access/ssl_passthrough_hosts-delete.json
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "roles#/definitions/admin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": ["permission_ssl_passthrough_hosts", "roles"],
|
||||||
|
"properties": {
|
||||||
|
"permission_ssl_passthrough_hosts": {
|
||||||
|
"$ref": "perms#/definitions/manage"
|
||||||
|
},
|
||||||
|
"roles": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["user"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
23
backend/lib/access/ssl_passthrough_hosts-get.json
Normal file
23
backend/lib/access/ssl_passthrough_hosts-get.json
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "roles#/definitions/admin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": ["permission_ssl_passthrough_hosts", "roles"],
|
||||||
|
"properties": {
|
||||||
|
"permission_ssl_passthrough_hosts": {
|
||||||
|
"$ref": "perms#/definitions/view"
|
||||||
|
},
|
||||||
|
"roles": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["user"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
23
backend/lib/access/ssl_passthrough_hosts-list.json
Normal file
23
backend/lib/access/ssl_passthrough_hosts-list.json
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "roles#/definitions/admin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": ["permission_ssl_passthrough_hosts", "roles"],
|
||||||
|
"properties": {
|
||||||
|
"permission_ssl_passthrough_hosts": {
|
||||||
|
"$ref": "perms#/definitions/view"
|
||||||
|
},
|
||||||
|
"roles": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["user"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
23
backend/lib/access/ssl_passthrough_hosts-update.json
Normal file
23
backend/lib/access/ssl_passthrough_hosts-update.json
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "roles#/definitions/admin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": ["permission_ssl_passthrough_hosts", "roles"],
|
||||||
|
"properties": {
|
||||||
|
"permission_ssl_passthrough_hosts": {
|
||||||
|
"$ref": "perms#/definitions/manage"
|
||||||
|
},
|
||||||
|
"roles": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["user"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
40
backend/migrations/20210423103500_stream_domain.js
Normal file
40
backend/migrations/20210423103500_stream_domain.js
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
const migrate_name = 'stream_domain';
|
||||||
|
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('stream', (table) => {
|
||||||
|
table.renameColumn('forward_ip', 'forwarding_host');
|
||||||
|
})
|
||||||
|
.then(function () {
|
||||||
|
logger.info('[' + migrate_name + '] stream 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.table('stream', (table) => {
|
||||||
|
table.renameColumn('forwarding_host', 'forward_ip');
|
||||||
|
})
|
||||||
|
.then(function () {
|
||||||
|
logger.info('[' + migrate_name + '] stream Table altered');
|
||||||
|
});
|
||||||
|
};
|
85
backend/migrations/20211010141200_ssl_passthrough_host.js
Normal file
85
backend/migrations/20211010141200_ssl_passthrough_host.js
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
const migrate_name = 'ssl_passthrough_host';
|
||||||
|
const logger = require('../logger').migrate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Migrate
|
||||||
|
*
|
||||||
|
* @see http://knexjs.org/#Schema
|
||||||
|
*
|
||||||
|
* @param {Object} knex
|
||||||
|
* @param {Promise} Promise
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
exports.up = async function (knex/*, Promise*/) {
|
||||||
|
logger.info('[' + migrate_name + '] Migrating Up...');
|
||||||
|
|
||||||
|
await knex.schema.createTable('ssl_passthrough_host', (table) => {
|
||||||
|
table.increments().primary();
|
||||||
|
table.dateTime('created_on').notNull();
|
||||||
|
table.dateTime('modified_on').notNull();
|
||||||
|
table.integer('owner_user_id').notNull().unsigned();
|
||||||
|
table.integer('is_deleted').notNull().unsigned().defaultTo(0);
|
||||||
|
table.string('domain_name').notNull();
|
||||||
|
table.string('forwarding_host').notNull();
|
||||||
|
table.integer('forwarding_port').notNull().unsigned();
|
||||||
|
table.integer('enabled').notNull().unsigned().defaultTo(1);
|
||||||
|
table.json('meta').notNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.info('[' + migrate_name + '] Table created');
|
||||||
|
|
||||||
|
// Remove unique constraint so name can be used for new table
|
||||||
|
await knex.schema.alterTable('user_permission', (table) => {
|
||||||
|
table.dropUnique('user_id');
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex.schema.renameTable('user_permission', 'user_permission_old');
|
||||||
|
|
||||||
|
// We need to recreate the table since sqlite does not support altering columns
|
||||||
|
await knex.schema.createTable('user_permission', (table) => {
|
||||||
|
table.increments().primary();
|
||||||
|
table.dateTime('created_on').notNull();
|
||||||
|
table.dateTime('modified_on').notNull();
|
||||||
|
table.integer('user_id').notNull().unsigned();
|
||||||
|
table.string('visibility').notNull();
|
||||||
|
table.string('proxy_hosts').notNull();
|
||||||
|
table.string('redirection_hosts').notNull();
|
||||||
|
table.string('dead_hosts').notNull();
|
||||||
|
table.string('streams').notNull();
|
||||||
|
table.string('ssl_passthrough_hosts').notNull();
|
||||||
|
table.string('access_lists').notNull();
|
||||||
|
table.string('certificates').notNull();
|
||||||
|
table.unique('user_id');
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex('user_permission_old').select('*', 'streams as ssl_passthrough_hosts').then((data) => {
|
||||||
|
if (data.length) {
|
||||||
|
return knex('user_permission').insert(data);
|
||||||
|
}
|
||||||
|
return Promise.resolve();
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex.schema.dropTableIfExists('user_permission_old');
|
||||||
|
|
||||||
|
logger.info('[' + migrate_name + '] permissions updated');
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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('stream').then(() => {
|
||||||
|
return knex.schema.table('user_permission', (table) => {
|
||||||
|
table.dropColumn('ssl_passthrough_hosts');
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(function () {
|
||||||
|
logger.info('[' + migrate_name + '] Table altered and permissions updated');
|
||||||
|
});
|
||||||
|
};
|
56
backend/models/ssl_passthrough_host.js
Normal file
56
backend/models/ssl_passthrough_host.js
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
// Objection Docs:
|
||||||
|
// http://vincit.github.io/objection.js/
|
||||||
|
|
||||||
|
const db = require('../db');
|
||||||
|
const Model = require('objection').Model;
|
||||||
|
const User = require('./user');
|
||||||
|
const now = require('./now_helper');
|
||||||
|
|
||||||
|
Model.knex(db);
|
||||||
|
|
||||||
|
class SslPassthrougHost 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 'SslPassthrougHost';
|
||||||
|
}
|
||||||
|
|
||||||
|
static get tableName () {
|
||||||
|
return 'ssl_passthrough_host';
|
||||||
|
}
|
||||||
|
|
||||||
|
static get jsonAttributes () {
|
||||||
|
return ['meta'];
|
||||||
|
}
|
||||||
|
|
||||||
|
static get relationMappings () {
|
||||||
|
return {
|
||||||
|
owner: {
|
||||||
|
relation: Model.HasOneRelation,
|
||||||
|
modelClass: User,
|
||||||
|
join: {
|
||||||
|
from: 'ssl_passthrough_host.owner_user_id',
|
||||||
|
to: 'user.id'
|
||||||
|
},
|
||||||
|
modify: function (qb) {
|
||||||
|
qb.where('user.is_deleted', 0);
|
||||||
|
qb.omit(['id', 'created_on', 'modified_on', 'is_deleted', 'email', 'roles']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = SslPassthrougHost;
|
@ -5,6 +5,7 @@
|
|||||||
"main": "js/index.js",
|
"main": "js/index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ajv": "^6.12.0",
|
"ajv": "^6.12.0",
|
||||||
|
"archiver": "^5.3.0",
|
||||||
"batchflow": "^0.4.0",
|
"batchflow": "^0.4.0",
|
||||||
"bcrypt": "^5.0.0",
|
"bcrypt": "^5.0.0",
|
||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
@ -19,12 +20,12 @@
|
|||||||
"jsonwebtoken": "^8.5.1",
|
"jsonwebtoken": "^8.5.1",
|
||||||
"knex": "^0.20.13",
|
"knex": "^0.20.13",
|
||||||
"liquidjs": "^9.11.10",
|
"liquidjs": "^9.11.10",
|
||||||
"lodash": "^4.17.19",
|
"lodash": "^4.17.21",
|
||||||
"moment": "^2.24.0",
|
"moment": "^2.24.0",
|
||||||
"mysql": "^2.18.1",
|
"mysql": "^2.18.1",
|
||||||
"node-rsa": "^1.0.8",
|
"node-rsa": "^1.0.8",
|
||||||
"nodemon": "^2.0.2",
|
"nodemon": "^2.0.2",
|
||||||
"objection": "^2.1.3",
|
"objection": "^2.2.16",
|
||||||
"path": "^0.12.7",
|
"path": "^0.12.7",
|
||||||
"pg": "^7.12.1",
|
"pg": "^7.12.1",
|
||||||
"restler": "^3.4.0",
|
"restler": "^3.4.0",
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
const express = require('express');
|
const express = require('express');
|
||||||
const pjson = require('../../package.json');
|
const pjson = require('../../package.json');
|
||||||
const error = require('../../lib/error');
|
const error = require('../../lib/error');
|
||||||
|
const internalNginx = require('../../internal/nginx');
|
||||||
|
|
||||||
let router = express.Router({
|
let router = express.Router({
|
||||||
caseSensitive: true,
|
caseSensitive: true,
|
||||||
@ -34,10 +35,18 @@ router.use('/settings', require('./settings'));
|
|||||||
router.use('/nginx/proxy-hosts', require('./nginx/proxy_hosts'));
|
router.use('/nginx/proxy-hosts', require('./nginx/proxy_hosts'));
|
||||||
router.use('/nginx/redirection-hosts', require('./nginx/redirection_hosts'));
|
router.use('/nginx/redirection-hosts', require('./nginx/redirection_hosts'));
|
||||||
router.use('/nginx/dead-hosts', require('./nginx/dead_hosts'));
|
router.use('/nginx/dead-hosts', require('./nginx/dead_hosts'));
|
||||||
|
router.use('/nginx/ssl-passthrough-hosts', require('./nginx/ssl_passthrough_hosts'));
|
||||||
router.use('/nginx/streams', require('./nginx/streams'));
|
router.use('/nginx/streams', require('./nginx/streams'));
|
||||||
router.use('/nginx/access-lists', require('./nginx/access_lists'));
|
router.use('/nginx/access-lists', require('./nginx/access_lists'));
|
||||||
router.use('/nginx/certificates', require('./nginx/certificates'));
|
router.use('/nginx/certificates', require('./nginx/certificates'));
|
||||||
|
|
||||||
|
router.get('/ssl-passthrough-enabled', (req, res/*, next*/) => {
|
||||||
|
res.status(200).send({
|
||||||
|
status: 'OK',
|
||||||
|
ssl_passthrough_enabled: internalNginx.sslPassthroughEnabled()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* API 404 for all other routes
|
* API 404 for all other routes
|
||||||
*
|
*
|
||||||
|
@ -209,6 +209,35 @@ router
|
|||||||
.catch(next);
|
.catch(next);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Download LE Certs
|
||||||
|
*
|
||||||
|
* /api/nginx/certificates/123/download
|
||||||
|
*/
|
||||||
|
router
|
||||||
|
.route('/:certificate_id/download')
|
||||||
|
.options((req, res) => {
|
||||||
|
res.sendStatus(204);
|
||||||
|
})
|
||||||
|
.all(jwtdecode())
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET /api/nginx/certificates/123/download
|
||||||
|
*
|
||||||
|
* Renew certificate
|
||||||
|
*/
|
||||||
|
.get((req, res, next) => {
|
||||||
|
internalCertificate.download(res.locals.access, {
|
||||||
|
id: parseInt(req.params.certificate_id, 10)
|
||||||
|
})
|
||||||
|
.then((result) => {
|
||||||
|
res.status(200)
|
||||||
|
.download(result.fileName);
|
||||||
|
})
|
||||||
|
.catch(next);
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate Certs before saving
|
* Validate Certs before saving
|
||||||
*
|
*
|
||||||
|
196
backend/routes/api/nginx/ssl_passthrough_hosts.js
Normal file
196
backend/routes/api/nginx/ssl_passthrough_hosts.js
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const validator = require('../../../lib/validator');
|
||||||
|
const jwtdecode = require('../../../lib/express/jwt-decode');
|
||||||
|
const internalSslPassthrough = require('../../../internal/ssl-passthrough-host');
|
||||||
|
const apiValidator = require('../../../lib/validator/api');
|
||||||
|
|
||||||
|
let router = express.Router({
|
||||||
|
caseSensitive: true,
|
||||||
|
strict: true,
|
||||||
|
mergeParams: true
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* /api/nginx/ssl-passthrough-hosts
|
||||||
|
*/
|
||||||
|
router
|
||||||
|
.route('/')
|
||||||
|
.options((req, res) => {
|
||||||
|
res.sendStatus(204);
|
||||||
|
})
|
||||||
|
.all(jwtdecode()) // preferred so it doesn't apply to nonexistent routes
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET /api/nginx/ssl-passthrough-hosts
|
||||||
|
*
|
||||||
|
* Retrieve all ssl passthrough hosts
|
||||||
|
*/
|
||||||
|
.get((req, res, next) => {
|
||||||
|
validator({
|
||||||
|
additionalProperties: false,
|
||||||
|
properties: {
|
||||||
|
expand: {
|
||||||
|
$ref: 'definitions#/definitions/expand'
|
||||||
|
},
|
||||||
|
query: {
|
||||||
|
$ref: 'definitions#/definitions/query'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
expand: (typeof req.query.expand === 'string' ? req.query.expand.split(',') : null),
|
||||||
|
query: (typeof req.query.query === 'string' ? req.query.query : null)
|
||||||
|
})
|
||||||
|
.then((data) => {
|
||||||
|
return internalSslPassthrough.getAll(res.locals.access, data.expand, data.query);
|
||||||
|
})
|
||||||
|
.then((rows) => {
|
||||||
|
res.status(200)
|
||||||
|
.send(rows);
|
||||||
|
})
|
||||||
|
.catch(next);
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POST /api/nginx/ssl-passthrough-hosts
|
||||||
|
*
|
||||||
|
* Create a new ssl passthrough host
|
||||||
|
*/
|
||||||
|
.post((req, res, next) => {
|
||||||
|
apiValidator({$ref: 'endpoints/ssl-passthrough-hosts#/links/1/schema'}, req.body)
|
||||||
|
.then((payload) => {
|
||||||
|
return internalSslPassthrough.create(res.locals.access, payload);
|
||||||
|
})
|
||||||
|
.then((result) => {
|
||||||
|
res.status(201)
|
||||||
|
.send(result);
|
||||||
|
})
|
||||||
|
.catch(next);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specific ssl passthrough host
|
||||||
|
*
|
||||||
|
* /api/nginx/ssl-passthrough-hosts/123
|
||||||
|
*/
|
||||||
|
router
|
||||||
|
.route('/:host_id')
|
||||||
|
.options((req, res) => {
|
||||||
|
res.sendStatus(204);
|
||||||
|
})
|
||||||
|
.all(jwtdecode()) // preferred so it doesn't apply to nonexistent routes
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET /api/nginx/ssl-passthrough-hosts/123
|
||||||
|
*
|
||||||
|
* Retrieve a specific ssl passthrough host
|
||||||
|
*/
|
||||||
|
.get((req, res, next) => {
|
||||||
|
validator({
|
||||||
|
required: ['host_id'],
|
||||||
|
additionalProperties: false,
|
||||||
|
properties: {
|
||||||
|
host_id: {
|
||||||
|
$ref: 'definitions#/definitions/id'
|
||||||
|
},
|
||||||
|
expand: {
|
||||||
|
$ref: 'definitions#/definitions/expand'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
host_id: req.params.host_id,
|
||||||
|
expand: (typeof req.query.expand === 'string' ? req.query.expand.split(',') : null)
|
||||||
|
})
|
||||||
|
.then((data) => {
|
||||||
|
return internalSslPassthrough.get(res.locals.access, {
|
||||||
|
id: parseInt(data.host_id, 10),
|
||||||
|
expand: data.expand
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then((row) => {
|
||||||
|
res.status(200)
|
||||||
|
.send(row);
|
||||||
|
})
|
||||||
|
.catch(next);
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PUT /api/nginx/ssl-passthrough-hosts/123
|
||||||
|
*
|
||||||
|
* Update an existing ssl passthrough host
|
||||||
|
*/
|
||||||
|
.put((req, res, next) => {
|
||||||
|
apiValidator({$ref: 'endpoints/ssl-passthrough-hosts#/links/2/schema'}, req.body)
|
||||||
|
.then((payload) => {
|
||||||
|
payload.id = parseInt(req.params.host_id, 10);
|
||||||
|
return internalSslPassthrough.update(res.locals.access, payload);
|
||||||
|
})
|
||||||
|
.then((result) => {
|
||||||
|
res.status(200)
|
||||||
|
.send(result);
|
||||||
|
})
|
||||||
|
.catch(next);
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DELETE /api/nginx/ssl-passthrough-hosts/123
|
||||||
|
*
|
||||||
|
* Delete an ssl passthrough host
|
||||||
|
*/
|
||||||
|
.delete((req, res, next) => {
|
||||||
|
internalSslPassthrough.delete(res.locals.access, {id: parseInt(req.params.host_id, 10)})
|
||||||
|
.then((result) => {
|
||||||
|
res.status(200)
|
||||||
|
.send(result);
|
||||||
|
})
|
||||||
|
.catch(next);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable ssl passthrough host
|
||||||
|
*
|
||||||
|
* /api/nginx/ssl-passthrough-hosts/123/enable
|
||||||
|
*/
|
||||||
|
router
|
||||||
|
.route('/:host_id/enable')
|
||||||
|
.options((req, res) => {
|
||||||
|
res.sendStatus(204);
|
||||||
|
})
|
||||||
|
.all(jwtdecode())
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POST /api/nginx/ssl-passthrough-hosts/123/enable
|
||||||
|
*/
|
||||||
|
.post((req, res, next) => {
|
||||||
|
internalSslPassthrough.enable(res.locals.access, {id: parseInt(req.params.host_id, 10)})
|
||||||
|
.then((result) => {
|
||||||
|
res.status(200)
|
||||||
|
.send(result);
|
||||||
|
})
|
||||||
|
.catch(next);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable ssl passthrough host
|
||||||
|
*
|
||||||
|
* /api/nginx/ssl-passthrough-hosts/123/disable
|
||||||
|
*/
|
||||||
|
router
|
||||||
|
.route('/:host_id/disable')
|
||||||
|
.options((req, res) => {
|
||||||
|
res.sendStatus(204);
|
||||||
|
})
|
||||||
|
.all(jwtdecode())
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POST /api/nginx/ssl-passthrough-hosts/123/disable
|
||||||
|
*/
|
||||||
|
.post((req, res, next) => {
|
||||||
|
internalSslPassthrough.disable(res.locals.access, {id: parseInt(req.params.host_id, 10)})
|
||||||
|
.then((result) => {
|
||||||
|
res.status(200)
|
||||||
|
.send(result);
|
||||||
|
})
|
||||||
|
.catch(next);
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
@ -153,7 +153,7 @@
|
|||||||
"example": "john@example.com",
|
"example": "john@example.com",
|
||||||
"format": "email",
|
"format": "email",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"minLength": 8,
|
"minLength": 6,
|
||||||
"maxLength": 100
|
"maxLength": 100
|
||||||
},
|
},
|
||||||
"password": {
|
"password": {
|
||||||
|
208
backend/schema/endpoints/ssl-passthrough-hosts.json
Normal file
208
backend/schema/endpoints/ssl-passthrough-hosts.json
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"$id": "endpoints/ssl-passthrough-hosts",
|
||||||
|
"title": "SSL Passthrough Hosts",
|
||||||
|
"description": "Endpoints relating to SSL Passthrough Hosts",
|
||||||
|
"stability": "stable",
|
||||||
|
"type": "object",
|
||||||
|
"definitions": {
|
||||||
|
"id": {
|
||||||
|
"$ref": "../definitions.json#/definitions/id"
|
||||||
|
},
|
||||||
|
"created_on": {
|
||||||
|
"$ref": "../definitions.json#/definitions/created_on"
|
||||||
|
},
|
||||||
|
"modified_on": {
|
||||||
|
"$ref": "../definitions.json#/definitions/modified_on"
|
||||||
|
},
|
||||||
|
"domain_name": {
|
||||||
|
"$ref": "../definitions.json#/definitions/domain_name"
|
||||||
|
},
|
||||||
|
"forwarding_host": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "../definitions.json#/definitions/domain_name"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"format": "ipv4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"format": "ipv6"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"forwarding_port": {
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 1,
|
||||||
|
"maximum": 65535
|
||||||
|
},
|
||||||
|
"enabled": {
|
||||||
|
"$ref": "../definitions.json#/definitions/enabled"
|
||||||
|
},
|
||||||
|
"meta": {
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"$ref": "#/definitions/id"
|
||||||
|
},
|
||||||
|
"created_on": {
|
||||||
|
"$ref": "#/definitions/created_on"
|
||||||
|
},
|
||||||
|
"modified_on": {
|
||||||
|
"$ref": "#/definitions/modified_on"
|
||||||
|
},
|
||||||
|
"domain_name": {
|
||||||
|
"$ref": "#/definitions/domain_name"
|
||||||
|
},
|
||||||
|
"forwarding_host": {
|
||||||
|
"$ref": "#/definitions/forwarding_host"
|
||||||
|
},
|
||||||
|
"forwarding_port": {
|
||||||
|
"$ref": "#/definitions/forwarding_port"
|
||||||
|
},
|
||||||
|
"enabled": {
|
||||||
|
"$ref": "#/definitions/enabled"
|
||||||
|
},
|
||||||
|
"meta": {
|
||||||
|
"$ref": "#/definitions/meta"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"links": [
|
||||||
|
{
|
||||||
|
"title": "List",
|
||||||
|
"description": "Returns a list of SSL Passthrough Hosts",
|
||||||
|
"href": "/nginx/ssl-passthrough-hosts",
|
||||||
|
"access": "private",
|
||||||
|
"method": "GET",
|
||||||
|
"rel": "self",
|
||||||
|
"http_header": {
|
||||||
|
"$ref": "../examples.json#/definitions/auth_header"
|
||||||
|
},
|
||||||
|
"targetSchema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/properties"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Create",
|
||||||
|
"description": "Creates a new SSL Passthrough Host",
|
||||||
|
"href": "/nginx/ssl-passthrough-hosts",
|
||||||
|
"access": "private",
|
||||||
|
"method": "POST",
|
||||||
|
"rel": "create",
|
||||||
|
"http_header": {
|
||||||
|
"$ref": "../examples.json#/definitions/auth_header"
|
||||||
|
},
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": [
|
||||||
|
"domain_name",
|
||||||
|
"forwarding_host",
|
||||||
|
"forwarding_port"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"domain_name": {
|
||||||
|
"$ref": "#/definitions/domain_name"
|
||||||
|
},
|
||||||
|
"forwarding_host": {
|
||||||
|
"$ref": "#/definitions/forwarding_host"
|
||||||
|
},
|
||||||
|
"forwarding_port": {
|
||||||
|
"$ref": "#/definitions/forwarding_port"
|
||||||
|
},
|
||||||
|
"meta": {
|
||||||
|
"$ref": "#/definitions/meta"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"targetSchema": {
|
||||||
|
"properties": {
|
||||||
|
"$ref": "#/properties"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Update",
|
||||||
|
"description": "Updates a existing SSL Passthrough Host",
|
||||||
|
"href": "/nginx/ssl-passthrough-hosts/{definitions.identity.example}",
|
||||||
|
"access": "private",
|
||||||
|
"method": "PUT",
|
||||||
|
"rel": "update",
|
||||||
|
"http_header": {
|
||||||
|
"$ref": "../examples.json#/definitions/auth_header"
|
||||||
|
},
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"domain_name": {
|
||||||
|
"$ref": "#/definitions/domain_name"
|
||||||
|
},
|
||||||
|
"forwarding_host": {
|
||||||
|
"$ref": "#/definitions/forwarding_host"
|
||||||
|
},
|
||||||
|
"forwarding_port": {
|
||||||
|
"$ref": "#/definitions/forwarding_port"
|
||||||
|
},
|
||||||
|
"meta": {
|
||||||
|
"$ref": "#/definitions/meta"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"targetSchema": {
|
||||||
|
"properties": {
|
||||||
|
"$ref": "#/properties"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Delete",
|
||||||
|
"description": "Deletes a existing SSL Passthrough Host",
|
||||||
|
"href": "/nginx/ssl-passthrough-hosts/{definitions.identity.example}",
|
||||||
|
"access": "private",
|
||||||
|
"method": "DELETE",
|
||||||
|
"rel": "delete",
|
||||||
|
"http_header": {
|
||||||
|
"$ref": "../examples.json#/definitions/auth_header"
|
||||||
|
},
|
||||||
|
"targetSchema": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Enable",
|
||||||
|
"description": "Enables a existing SSL Passthrough Host",
|
||||||
|
"href": "/nginx/ssl-passthrough-hosts/{definitions.identity.example}/enable",
|
||||||
|
"access": "private",
|
||||||
|
"method": "POST",
|
||||||
|
"rel": "update",
|
||||||
|
"http_header": {
|
||||||
|
"$ref": "../examples.json#/definitions/auth_header"
|
||||||
|
},
|
||||||
|
"targetSchema": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Disable",
|
||||||
|
"description": "Disables a existing SSL Passthrough Host",
|
||||||
|
"href": "/nginx/ssl-passthrough-hosts/{definitions.identity.example}/disable",
|
||||||
|
"access": "private",
|
||||||
|
"method": "POST",
|
||||||
|
"rel": "update",
|
||||||
|
"http_header": {
|
||||||
|
"$ref": "../examples.json#/definitions/auth_header"
|
||||||
|
},
|
||||||
|
"targetSchema": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -20,10 +20,21 @@
|
|||||||
"minimum": 1,
|
"minimum": 1,
|
||||||
"maximum": 65535
|
"maximum": 65535
|
||||||
},
|
},
|
||||||
"forward_ip": {
|
"forwarding_host": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "../definitions.json#/definitions/domain_name"
|
||||||
|
},
|
||||||
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"format": "ipv4"
|
"format": "ipv4"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"format": "ipv6"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"forwarding_port": {
|
"forwarding_port": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"minimum": 1,
|
"minimum": 1,
|
||||||
@ -55,8 +66,8 @@
|
|||||||
"incoming_port": {
|
"incoming_port": {
|
||||||
"$ref": "#/definitions/incoming_port"
|
"$ref": "#/definitions/incoming_port"
|
||||||
},
|
},
|
||||||
"forward_ip": {
|
"forwarding_host": {
|
||||||
"$ref": "#/definitions/forward_ip"
|
"$ref": "#/definitions/forwarding_host"
|
||||||
},
|
},
|
||||||
"forwarding_port": {
|
"forwarding_port": {
|
||||||
"$ref": "#/definitions/forwarding_port"
|
"$ref": "#/definitions/forwarding_port"
|
||||||
@ -107,15 +118,15 @@
|
|||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"required": [
|
"required": [
|
||||||
"incoming_port",
|
"incoming_port",
|
||||||
"forward_ip",
|
"forwarding_host",
|
||||||
"forwarding_port"
|
"forwarding_port"
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"incoming_port": {
|
"incoming_port": {
|
||||||
"$ref": "#/definitions/incoming_port"
|
"$ref": "#/definitions/incoming_port"
|
||||||
},
|
},
|
||||||
"forward_ip": {
|
"forwarding_host": {
|
||||||
"$ref": "#/definitions/forward_ip"
|
"$ref": "#/definitions/forwarding_host"
|
||||||
},
|
},
|
||||||
"forwarding_port": {
|
"forwarding_port": {
|
||||||
"$ref": "#/definitions/forwarding_port"
|
"$ref": "#/definitions/forwarding_port"
|
||||||
@ -154,8 +165,8 @@
|
|||||||
"incoming_port": {
|
"incoming_port": {
|
||||||
"$ref": "#/definitions/incoming_port"
|
"$ref": "#/definitions/incoming_port"
|
||||||
},
|
},
|
||||||
"forward_ip": {
|
"forwarding_host": {
|
||||||
"$ref": "#/definitions/forward_ip"
|
"$ref": "#/definitions/forwarding_host"
|
||||||
},
|
},
|
||||||
"forwarding_port": {
|
"forwarding_port": {
|
||||||
"$ref": "#/definitions/forwarding_port"
|
"$ref": "#/definitions/forwarding_port"
|
||||||
|
@ -26,6 +26,9 @@
|
|||||||
"dead-hosts": {
|
"dead-hosts": {
|
||||||
"$ref": "endpoints/dead-hosts.json"
|
"$ref": "endpoints/dead-hosts.json"
|
||||||
},
|
},
|
||||||
|
"ssl-passthrough-hosts": {
|
||||||
|
"$ref": "endpoints/ssl-passthrough-hosts.json"
|
||||||
|
},
|
||||||
"streams": {
|
"streams": {
|
||||||
"$ref": "endpoints/streams.json"
|
"$ref": "endpoints/streams.json"
|
||||||
},
|
},
|
||||||
|
@ -8,7 +8,9 @@ const userPermissionModel = require('./models/user_permission');
|
|||||||
const utils = require('./lib/utils');
|
const utils = require('./lib/utils');
|
||||||
const authModel = require('./models/auth');
|
const authModel = require('./models/auth');
|
||||||
const settingModel = require('./models/setting');
|
const settingModel = require('./models/setting');
|
||||||
|
const passthroughHostModel = require('./models/ssl_passthrough_host');
|
||||||
const dns_plugins = require('./global/certbot-dns-plugins');
|
const dns_plugins = require('./global/certbot-dns-plugins');
|
||||||
|
const internalNginx = require('./internal/nginx');
|
||||||
const debug_mode = process.env.NODE_ENV !== 'production' || !!process.env.DEBUG;
|
const debug_mode = process.env.NODE_ENV !== 'production' || !!process.env.DEBUG;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -110,6 +112,7 @@ const setupDefaultUser = () => {
|
|||||||
proxy_hosts: 'manage',
|
proxy_hosts: 'manage',
|
||||||
redirection_hosts: 'manage',
|
redirection_hosts: 'manage',
|
||||||
dead_hosts: 'manage',
|
dead_hosts: 'manage',
|
||||||
|
ssl_passthrough_hosts: 'manage',
|
||||||
streams: 'manage',
|
streams: 'manage',
|
||||||
access_lists: 'manage',
|
access_lists: 'manage',
|
||||||
certificates: 'manage',
|
certificates: 'manage',
|
||||||
@ -175,7 +178,7 @@ const setupCertbotPlugins = () => {
|
|||||||
certificates.map(function (certificate) {
|
certificates.map(function (certificate) {
|
||||||
if (certificate.meta && certificate.meta.dns_challenge === true) {
|
if (certificate.meta && certificate.meta.dns_challenge === true) {
|
||||||
const dns_plugin = dns_plugins[certificate.meta.dns_provider];
|
const dns_plugin = dns_plugins[certificate.meta.dns_provider];
|
||||||
const packages_to_install = `${dns_plugin.package_name}==${dns_plugin.package_version} ${dns_plugin.dependencies}`;
|
const packages_to_install = `${dns_plugin.package_name}${dns_plugin.version_requirement || ''} ${dns_plugin.dependencies}`;
|
||||||
|
|
||||||
if (plugins.indexOf(packages_to_install) === -1) plugins.push(packages_to_install);
|
if (plugins.indexOf(packages_to_install) === -1) plugins.push(packages_to_install);
|
||||||
|
|
||||||
@ -187,7 +190,7 @@ const setupCertbotPlugins = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (plugins.length) {
|
if (plugins.length) {
|
||||||
const install_cmd = 'pip3 install ' + plugins.join(' ');
|
const install_cmd = 'pip install ' + plugins.join(' ');
|
||||||
promises.push(utils.exec(install_cmd));
|
promises.push(utils.exec(install_cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,9 +204,40 @@ const setupCertbotPlugins = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts a timer to call run the logrotation binary every two days
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
const setupLogrotation = () => {
|
||||||
|
const intervalTimeout = 1000 * 60 * 60 * 24 * 2; // 2 days
|
||||||
|
|
||||||
|
const runLogrotate = async () => {
|
||||||
|
try {
|
||||||
|
await utils.exec('logrotate /etc/logrotate.d/nginx-proxy-manager');
|
||||||
|
logger.info('Logrotate completed.');
|
||||||
|
} catch (e) { logger.warn(e); }
|
||||||
|
};
|
||||||
|
|
||||||
|
logger.info('Logrotate Timer initialized');
|
||||||
|
setInterval(runLogrotate, intervalTimeout);
|
||||||
|
// And do this now as well
|
||||||
|
return runLogrotate();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes sure the ssl passthrough option is reflected in the nginx config
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
const setupSslPassthrough = () => {
|
||||||
|
return internalNginx.configure(passthroughHostModel, 'ssl_passthrough_host', {});
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = function () {
|
module.exports = function () {
|
||||||
return setupJwt()
|
return setupJwt()
|
||||||
.then(setupDefaultUser)
|
.then(setupDefaultUser)
|
||||||
.then(setupDefaultSettings)
|
.then(setupDefaultSettings)
|
||||||
.then(setupCertbotPlugins);
|
.then(setupCertbotPlugins)
|
||||||
|
.then(setupLogrotation)
|
||||||
|
.then(setupSslPassthrough);
|
||||||
};
|
};
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
{% if certificate -%}
|
{% if certificate -%}
|
||||||
listen 443 ssl{% if http2_support %} http2{% endif %};
|
listen 443 ssl{% if http2_support %} http2{% endif %};
|
||||||
{% if ipv6 -%}
|
{% if ipv6 -%}
|
||||||
listen [::]:443;
|
listen [::]:443 ssl{% if http2_support %} http2{% endif %};
|
||||||
{% else -%}
|
{% else -%}
|
||||||
#listen [::]:443;
|
#listen [::]:443;
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -1,9 +1,46 @@
|
|||||||
location {{ path }} {
|
location {{ path }} {
|
||||||
|
set $upstream {{ forward_scheme }}://{{ forward_host }}:{{ forward_port }}{{ forward_path }};
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
proxy_set_header X-Forwarded-Scheme $scheme;
|
proxy_set_header X-Forwarded-Scheme $scheme;
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
proxy_set_header X-Forwarded-For $remote_addr;
|
proxy_set_header X-Forwarded-For $remote_addr;
|
||||||
proxy_pass {{ forward_scheme }}://{{ forward_host }}:{{ forward_port }}{{ forward_path }};
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_pass $upstream;
|
||||||
|
|
||||||
|
{% if access_list_id > 0 %}
|
||||||
|
{% if access_list.items.length > 0 %}
|
||||||
|
# Authorization
|
||||||
|
auth_basic "Authorization required";
|
||||||
|
auth_basic_user_file /data/access/{{ access_list_id }};
|
||||||
|
|
||||||
|
{{ access_list.passauth }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
# Access Rules
|
||||||
|
{% for client in access_list.clients %}
|
||||||
|
{{- client.rule -}};
|
||||||
|
{% endfor %}deny all;
|
||||||
|
|
||||||
|
# Access checks must...
|
||||||
|
{% if access_list.satisfy %}
|
||||||
|
{{ access_list.satisfy }};
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% include "_assets.conf" %}
|
||||||
|
{% include "_exploits.conf" %}
|
||||||
|
|
||||||
|
{% include "_forced_ssl.conf" %}
|
||||||
|
{% include "_hsts.conf" %}
|
||||||
|
|
||||||
|
{% if allow_websocket_upgrade == 1 or allow_websocket_upgrade == true %}
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection $http_connection;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
{{ advanced_config }}
|
{{ advanced_config }}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,14 +5,15 @@ server {
|
|||||||
{% include "_listen.conf" %}
|
{% include "_listen.conf" %}
|
||||||
{% include "_certificates.conf" %}
|
{% include "_certificates.conf" %}
|
||||||
{% include "_hsts.conf" %}
|
{% include "_hsts.conf" %}
|
||||||
|
{% include "_forced_ssl.conf" %}
|
||||||
|
|
||||||
access_log /data/logs/dead_host-{{ id }}.log standard;
|
access_log /data/logs/dead-host-{{ id }}_access.log standard;
|
||||||
|
error_log /data/logs/dead-host-{{ id }}_error.log warn;
|
||||||
|
|
||||||
{{ advanced_config }}
|
{{ advanced_config }}
|
||||||
|
|
||||||
{% if use_default_location %}
|
{% if use_default_location %}
|
||||||
location / {
|
location / {
|
||||||
{% include "_forced_ssl.conf" %}
|
|
||||||
{% include "_hsts.conf" %}
|
{% include "_hsts.conf" %}
|
||||||
return 404;
|
return 404;
|
||||||
}
|
}
|
||||||
|
@ -12,9 +12,12 @@ server {
|
|||||||
#listen [::]:80;
|
#listen [::]:80;
|
||||||
{% endif %}
|
{% endif %}
|
||||||
server_name default-host.localhost;
|
server_name default-host.localhost;
|
||||||
access_log /data/logs/default_host.log combined;
|
access_log /data/logs/default-host_access.log combined;
|
||||||
|
error_log /data/logs/default-host_error.log warn;
|
||||||
{% include "_exploits.conf" %}
|
{% include "_exploits.conf" %}
|
||||||
|
|
||||||
|
include conf.d/include/letsencrypt-acme-challenge.conf;
|
||||||
|
|
||||||
{%- if value == "404" %}
|
{%- if value == "404" %}
|
||||||
location / {
|
location / {
|
||||||
return 404;
|
return 404;
|
||||||
|
@ -8,7 +8,8 @@ server {
|
|||||||
|
|
||||||
server_name {{ domain_names | join: " " }};
|
server_name {{ domain_names | join: " " }};
|
||||||
|
|
||||||
access_log /data/logs/letsencrypt-requests.log standard;
|
access_log /data/logs/letsencrypt-requests_access.log standard;
|
||||||
|
error_log /data/logs/letsencrypt-requests_error.log warn;
|
||||||
|
|
||||||
include conf.d/include/letsencrypt-acme-challenge.conf;
|
include conf.d/include/letsencrypt-acme-challenge.conf;
|
||||||
|
|
||||||
|
@ -11,8 +11,16 @@ server {
|
|||||||
{% include "_assets.conf" %}
|
{% include "_assets.conf" %}
|
||||||
{% include "_exploits.conf" %}
|
{% include "_exploits.conf" %}
|
||||||
{% include "_hsts.conf" %}
|
{% include "_hsts.conf" %}
|
||||||
|
{% include "_forced_ssl.conf" %}
|
||||||
|
|
||||||
access_log /data/logs/proxy_host-{{ id }}.log proxy;
|
{% if allow_websocket_upgrade == 1 or allow_websocket_upgrade == true %}
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection $http_connection;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
access_log /data/logs/proxy-host-{{ id }}_access.log proxy;
|
||||||
|
error_log /data/logs/proxy-host-{{ id }}_error.log warn;
|
||||||
|
|
||||||
{{ advanced_config }}
|
{{ advanced_config }}
|
||||||
|
|
||||||
@ -43,7 +51,6 @@ server {
|
|||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% 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 %}
|
||||||
|
@ -7,14 +7,15 @@ server {
|
|||||||
{% include "_assets.conf" %}
|
{% include "_assets.conf" %}
|
||||||
{% include "_exploits.conf" %}
|
{% include "_exploits.conf" %}
|
||||||
{% include "_hsts.conf" %}
|
{% include "_hsts.conf" %}
|
||||||
|
{% include "_forced_ssl.conf" %}
|
||||||
|
|
||||||
access_log /data/logs/redirection_host-{{ id }}.log standard;
|
access_log /data/logs/redirection-host-{{ id }}_access.log standard;
|
||||||
|
error_log /data/logs/redirection-host-{{ id }}_error.log warn;
|
||||||
|
|
||||||
{{ advanced_config }}
|
{{ advanced_config }}
|
||||||
|
|
||||||
{% if use_default_location %}
|
{% if use_default_location %}
|
||||||
location / {
|
location / {
|
||||||
{% include "_forced_ssl.conf" %}
|
|
||||||
{% include "_hsts.conf" %}
|
{% include "_hsts.conf" %}
|
||||||
|
|
||||||
{% if preserve_path == 1 or preserve_path == true %}
|
{% if preserve_path == 1 or preserve_path == true %}
|
||||||
|
41
backend/templates/ssl_passthrough_host.conf
Normal file
41
backend/templates/ssl_passthrough_host.conf
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
# ------------------------------------------------------------
|
||||||
|
# SSL Passthrough hosts
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
|
||||||
|
map $ssl_preread_server_name $name {
|
||||||
|
{% for host in all_passthrough_hosts %}
|
||||||
|
{% if host.enabled %}
|
||||||
|
{{ host.domain_name }} ssl_passthrough_{{ host.domain_name }};
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
default https_default_backend;
|
||||||
|
}
|
||||||
|
|
||||||
|
{% for host in all_passthrough_hosts %}
|
||||||
|
{% if host.enabled %}
|
||||||
|
upstream ssl_passthrough_{{ host.domain_name }} {
|
||||||
|
server {{host.forwarding_host}}:{{host.forwarding_port}};
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
upstream https_default_backend {
|
||||||
|
server 127.0.0.1:443;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 444;
|
||||||
|
{% if ipv6 -%}
|
||||||
|
listen [::]:444;
|
||||||
|
{% else -%}
|
||||||
|
#listen [::]:444;
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
proxy_pass $name;
|
||||||
|
ssl_preread on;
|
||||||
|
|
||||||
|
error_log /data/logs/ssl-passthrough-hosts_error.log warn;
|
||||||
|
|
||||||
|
# Custom
|
||||||
|
include /data/nginx/custom/server_ssl_passthrough[.]conf;
|
||||||
|
}
|
@ -12,7 +12,7 @@ server {
|
|||||||
#listen [::]:{{ incoming_port }};
|
#listen [::]:{{ incoming_port }};
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
proxy_pass {{ forward_ip }}:{{ forwarding_port }};
|
proxy_pass {{ forwarding_host }}:{{ forwarding_port }};
|
||||||
|
|
||||||
# Custom
|
# Custom
|
||||||
include /data/nginx/custom/server_stream[.]conf;
|
include /data/nginx/custom/server_stream[.]conf;
|
||||||
@ -27,7 +27,7 @@ server {
|
|||||||
{% else -%}
|
{% else -%}
|
||||||
#listen [::]:{{ incoming_port }} udp;
|
#listen [::]:{{ incoming_port }} udp;
|
||||||
{% endif %}
|
{% endif %}
|
||||||
proxy_pass {{ forward_ip }}:{{ forwarding_port }};
|
proxy_pass {{ forwarding_host }}:{{ forwarding_port }};
|
||||||
|
|
||||||
# Custom
|
# Custom
|
||||||
include /data/nginx/custom/server_stream[.]conf;
|
include /data/nginx/custom/server_stream[.]conf;
|
||||||
|
@ -77,10 +77,10 @@ acorn@^7.1.1:
|
|||||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.0.tgz#e1ad486e6c54501634c6c397c5c121daa383607c"
|
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.0.tgz#e1ad486e6c54501634c6c397c5c121daa383607c"
|
||||||
integrity sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w==
|
integrity sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w==
|
||||||
|
|
||||||
ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.0:
|
ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.0, ajv@^6.12.6:
|
||||||
version "6.12.3"
|
version "6.12.6"
|
||||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.3.tgz#18c5af38a111ddeb4f2697bd78d68abc1cabd706"
|
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
|
||||||
integrity sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==
|
integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
|
||||||
dependencies:
|
dependencies:
|
||||||
fast-deep-equal "^3.1.1"
|
fast-deep-equal "^3.1.1"
|
||||||
fast-json-stable-stringify "^2.0.0"
|
fast-json-stable-stringify "^2.0.0"
|
||||||
@ -154,6 +154,35 @@ aproba@^1.0.3:
|
|||||||
resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a"
|
resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a"
|
||||||
integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==
|
integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==
|
||||||
|
|
||||||
|
archiver-utils@^2.1.0:
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/archiver-utils/-/archiver-utils-2.1.0.tgz#e8a460e94b693c3e3da182a098ca6285ba9249e2"
|
||||||
|
integrity sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==
|
||||||
|
dependencies:
|
||||||
|
glob "^7.1.4"
|
||||||
|
graceful-fs "^4.2.0"
|
||||||
|
lazystream "^1.0.0"
|
||||||
|
lodash.defaults "^4.2.0"
|
||||||
|
lodash.difference "^4.5.0"
|
||||||
|
lodash.flatten "^4.4.0"
|
||||||
|
lodash.isplainobject "^4.0.6"
|
||||||
|
lodash.union "^4.6.0"
|
||||||
|
normalize-path "^3.0.0"
|
||||||
|
readable-stream "^2.0.0"
|
||||||
|
|
||||||
|
archiver@^5.3.0:
|
||||||
|
version "5.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/archiver/-/archiver-5.3.0.tgz#dd3e097624481741df626267564f7dd8640a45ba"
|
||||||
|
integrity sha512-iUw+oDwK0fgNpvveEsdQ0Ase6IIKztBJU2U0E9MzszMfmVVUyv1QJhS2ITW9ZCqx8dktAxVAjWWkKehuZE8OPg==
|
||||||
|
dependencies:
|
||||||
|
archiver-utils "^2.1.0"
|
||||||
|
async "^3.2.0"
|
||||||
|
buffer-crc32 "^0.2.1"
|
||||||
|
readable-stream "^3.6.0"
|
||||||
|
readdir-glob "^1.0.0"
|
||||||
|
tar-stream "^2.2.0"
|
||||||
|
zip-stream "^4.1.0"
|
||||||
|
|
||||||
are-we-there-yet@~1.1.2:
|
are-we-there-yet@~1.1.2:
|
||||||
version "1.1.5"
|
version "1.1.5"
|
||||||
resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21"
|
resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21"
|
||||||
@ -221,6 +250,11 @@ astral-regex@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9"
|
resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9"
|
||||||
integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==
|
integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==
|
||||||
|
|
||||||
|
async@^3.2.0:
|
||||||
|
version "3.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/async/-/async-3.2.1.tgz#d3274ec66d107a47476a4c49136aacdb00665fc8"
|
||||||
|
integrity sha512-XdD5lRO/87udXCMC9meWdYiR+Nq6ZjUfXidViUZGu2F1MO4T3XwZ1et0hb2++BgLfhyJwy44BGB/yx80ABx8hg==
|
||||||
|
|
||||||
atob@^2.1.2:
|
atob@^2.1.2:
|
||||||
version "2.1.2"
|
version "2.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
|
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
|
||||||
@ -231,6 +265,11 @@ balanced-match@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||||
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
|
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
|
||||||
|
|
||||||
|
base64-js@^1.3.1:
|
||||||
|
version "1.5.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
|
||||||
|
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
|
||||||
|
|
||||||
base@^0.11.1:
|
base@^0.11.1:
|
||||||
version "0.11.2"
|
version "0.11.2"
|
||||||
resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f"
|
resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f"
|
||||||
@ -267,6 +306,15 @@ binary-extensions@^2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.1.0.tgz#30fa40c9e7fe07dbc895678cd287024dea241dd9"
|
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.1.0.tgz#30fa40c9e7fe07dbc895678cd287024dea241dd9"
|
||||||
integrity sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==
|
integrity sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==
|
||||||
|
|
||||||
|
bl@^4.0.3:
|
||||||
|
version "4.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a"
|
||||||
|
integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==
|
||||||
|
dependencies:
|
||||||
|
buffer "^5.5.0"
|
||||||
|
inherits "^2.0.4"
|
||||||
|
readable-stream "^3.4.0"
|
||||||
|
|
||||||
blueimp-md5@^2.16.0:
|
blueimp-md5@^2.16.0:
|
||||||
version "2.17.0"
|
version "2.17.0"
|
||||||
resolved "https://registry.yarnpkg.com/blueimp-md5/-/blueimp-md5-2.17.0.tgz#f4fcac088b115f7b4045f19f5da59e9d01b1bb96"
|
resolved "https://registry.yarnpkg.com/blueimp-md5/-/blueimp-md5-2.17.0.tgz#f4fcac088b115f7b4045f19f5da59e9d01b1bb96"
|
||||||
@ -333,6 +381,11 @@ braces@~3.0.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
fill-range "^7.0.1"
|
fill-range "^7.0.1"
|
||||||
|
|
||||||
|
buffer-crc32@^0.2.1, buffer-crc32@^0.2.13:
|
||||||
|
version "0.2.13"
|
||||||
|
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
|
||||||
|
integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=
|
||||||
|
|
||||||
buffer-equal-constant-time@1.0.1:
|
buffer-equal-constant-time@1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
|
resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
|
||||||
@ -343,6 +396,14 @@ buffer-writer@2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/buffer-writer/-/buffer-writer-2.0.0.tgz#ce7eb81a38f7829db09c873f2fbb792c0c98ec04"
|
resolved "https://registry.yarnpkg.com/buffer-writer/-/buffer-writer-2.0.0.tgz#ce7eb81a38f7829db09c873f2fbb792c0c98ec04"
|
||||||
integrity sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==
|
integrity sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==
|
||||||
|
|
||||||
|
buffer@^5.5.0:
|
||||||
|
version "5.7.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
|
||||||
|
integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==
|
||||||
|
dependencies:
|
||||||
|
base64-js "^1.3.1"
|
||||||
|
ieee754 "^1.1.13"
|
||||||
|
|
||||||
busboy@^0.3.1:
|
busboy@^0.3.1:
|
||||||
version "0.3.1"
|
version "0.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/busboy/-/busboy-0.3.1.tgz#170899274c5bf38aae27d5c62b71268cd585fd1b"
|
resolved "https://registry.yarnpkg.com/busboy/-/busboy-0.3.1.tgz#170899274c5bf38aae27d5c62b71268cd585fd1b"
|
||||||
@ -457,7 +518,7 @@ chokidar@^3.2.2:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
fsevents "~2.1.2"
|
fsevents "~2.1.2"
|
||||||
|
|
||||||
chownr@^1.1.1:
|
chownr@^1.1.4:
|
||||||
version "1.1.4"
|
version "1.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b"
|
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b"
|
||||||
integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==
|
integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==
|
||||||
@ -562,6 +623,16 @@ component-emitter@^1.2.1:
|
|||||||
resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"
|
resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"
|
||||||
integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==
|
integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==
|
||||||
|
|
||||||
|
compress-commons@^4.1.0:
|
||||||
|
version "4.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/compress-commons/-/compress-commons-4.1.1.tgz#df2a09a7ed17447642bad10a85cc9a19e5c42a7d"
|
||||||
|
integrity sha512-QLdDLCKNV2dtoTorqgxngQCMA+gWXkM/Nwu7FpeBhk/RdkzimqC3jueb/FDmaZeXh+uby1jkBqE3xArsLBE5wQ==
|
||||||
|
dependencies:
|
||||||
|
buffer-crc32 "^0.2.13"
|
||||||
|
crc32-stream "^4.0.2"
|
||||||
|
normalize-path "^3.0.0"
|
||||||
|
readable-stream "^3.6.0"
|
||||||
|
|
||||||
compressible@~2.0.16:
|
compressible@~2.0.16:
|
||||||
version "2.0.18"
|
version "2.0.18"
|
||||||
resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba"
|
resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba"
|
||||||
@ -643,6 +714,22 @@ core-util-is@~1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
|
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
|
||||||
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
|
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
|
||||||
|
|
||||||
|
crc-32@^1.2.0:
|
||||||
|
version "1.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.0.tgz#cb2db6e29b88508e32d9dd0ec1693e7b41a18208"
|
||||||
|
integrity sha512-1uBwHxF+Y/4yF5G48fwnKq6QsIXheor3ZLPT80yGBV1oEUwpPojlEhQbWKVw1VwcTQyMGHK1/XMmTjmlsmTTGA==
|
||||||
|
dependencies:
|
||||||
|
exit-on-epipe "~1.0.1"
|
||||||
|
printj "~1.1.0"
|
||||||
|
|
||||||
|
crc32-stream@^4.0.2:
|
||||||
|
version "4.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/crc32-stream/-/crc32-stream-4.0.2.tgz#c922ad22b38395abe9d3870f02fa8134ed709007"
|
||||||
|
integrity sha512-DxFZ/Hk473b/muq1VJ///PMNLj0ZMnzye9thBpmjpJKCc5eMgB95aK8zCGrGfQ90cWo561Te6HK9D+j4KPdM6w==
|
||||||
|
dependencies:
|
||||||
|
crc-32 "^1.2.0"
|
||||||
|
readable-stream "^3.4.0"
|
||||||
|
|
||||||
cross-spawn@^6.0.5:
|
cross-spawn@^6.0.5:
|
||||||
version "6.0.5"
|
version "6.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
|
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
|
||||||
@ -831,7 +918,7 @@ encodeurl@~1.0.2:
|
|||||||
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
|
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
|
||||||
integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
|
integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
|
||||||
|
|
||||||
end-of-stream@^1.1.0:
|
end-of-stream@^1.1.0, end-of-stream@^1.4.1:
|
||||||
version "1.4.4"
|
version "1.4.4"
|
||||||
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
|
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
|
||||||
integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
|
integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
|
||||||
@ -981,6 +1068,11 @@ etag@~1.8.1:
|
|||||||
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
|
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
|
||||||
integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
|
integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
|
||||||
|
|
||||||
|
exit-on-epipe@~1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz#0bdd92e87d5285d267daa8171d0eb06159689692"
|
||||||
|
integrity sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw==
|
||||||
|
|
||||||
expand-brackets@^2.1.4:
|
expand-brackets@^2.1.4:
|
||||||
version "2.1.4"
|
version "2.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622"
|
resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622"
|
||||||
@ -1237,7 +1329,12 @@ fresh@0.5.2:
|
|||||||
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
|
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
|
||||||
integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=
|
integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=
|
||||||
|
|
||||||
fs-minipass@^1.2.5:
|
fs-constants@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
|
||||||
|
integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==
|
||||||
|
|
||||||
|
fs-minipass@^1.2.7:
|
||||||
version "1.2.7"
|
version "1.2.7"
|
||||||
resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7"
|
resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7"
|
||||||
integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==
|
integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==
|
||||||
@ -1321,6 +1418,18 @@ glob@^7.1.3:
|
|||||||
once "^1.3.0"
|
once "^1.3.0"
|
||||||
path-is-absolute "^1.0.0"
|
path-is-absolute "^1.0.0"
|
||||||
|
|
||||||
|
glob@^7.1.4:
|
||||||
|
version "7.1.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90"
|
||||||
|
integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==
|
||||||
|
dependencies:
|
||||||
|
fs.realpath "^1.0.0"
|
||||||
|
inflight "^1.0.4"
|
||||||
|
inherits "2"
|
||||||
|
minimatch "^3.0.4"
|
||||||
|
once "^1.3.0"
|
||||||
|
path-is-absolute "^1.0.0"
|
||||||
|
|
||||||
global-dirs@^2.0.1:
|
global-dirs@^2.0.1:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-2.0.1.tgz#acdf3bb6685bcd55cb35e8a052266569e9469201"
|
resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-2.0.1.tgz#acdf3bb6685bcd55cb35e8a052266569e9469201"
|
||||||
@ -1377,6 +1486,11 @@ graceful-fs@^4.1.15, graceful-fs@^4.1.2:
|
|||||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb"
|
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb"
|
||||||
integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==
|
integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==
|
||||||
|
|
||||||
|
graceful-fs@^4.2.0:
|
||||||
|
version "4.2.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a"
|
||||||
|
integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==
|
||||||
|
|
||||||
gravatar@^1.8.0:
|
gravatar@^1.8.0:
|
||||||
version "1.8.1"
|
version "1.8.1"
|
||||||
resolved "https://registry.yarnpkg.com/gravatar/-/gravatar-1.8.1.tgz#743bbdf3185c3433172e00e0e6ff5f6b30c58997"
|
resolved "https://registry.yarnpkg.com/gravatar/-/gravatar-1.8.1.tgz#743bbdf3185c3433172e00e0e6ff5f6b30c58997"
|
||||||
@ -1494,6 +1608,11 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4:
|
|||||||
dependencies:
|
dependencies:
|
||||||
safer-buffer ">= 2.1.2 < 3"
|
safer-buffer ">= 2.1.2 < 3"
|
||||||
|
|
||||||
|
ieee754@^1.1.13:
|
||||||
|
version "1.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
|
||||||
|
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
|
||||||
|
|
||||||
ignore-by-default@^1.0.1:
|
ignore-by-default@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09"
|
resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09"
|
||||||
@ -1537,7 +1656,7 @@ inflight@^1.0.4:
|
|||||||
once "^1.3.0"
|
once "^1.3.0"
|
||||||
wrappy "1"
|
wrappy "1"
|
||||||
|
|
||||||
inherits@2, inherits@2.0.4, inherits@~2.0.3, inherits@~2.0.4:
|
inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3, inherits@~2.0.4:
|
||||||
version "2.0.4"
|
version "2.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||||
@ -1937,6 +2056,13 @@ latest-version@^5.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
package-json "^6.3.0"
|
package-json "^6.3.0"
|
||||||
|
|
||||||
|
lazystream@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.0.tgz#f6995fe0f820392f61396be89462407bb77168e4"
|
||||||
|
integrity sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=
|
||||||
|
dependencies:
|
||||||
|
readable-stream "^2.0.5"
|
||||||
|
|
||||||
levn@^0.3.0, levn@~0.3.0:
|
levn@^0.3.0, levn@~0.3.0:
|
||||||
version "0.3.0"
|
version "0.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
|
resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
|
||||||
@ -1989,6 +2115,21 @@ locate-path@^5.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
p-locate "^4.1.0"
|
p-locate "^4.1.0"
|
||||||
|
|
||||||
|
lodash.defaults@^4.2.0:
|
||||||
|
version "4.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c"
|
||||||
|
integrity sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=
|
||||||
|
|
||||||
|
lodash.difference@^4.5.0:
|
||||||
|
version "4.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash.difference/-/lodash.difference-4.5.0.tgz#9ccb4e505d486b91651345772885a2df27fd017c"
|
||||||
|
integrity sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw=
|
||||||
|
|
||||||
|
lodash.flatten@^4.4.0:
|
||||||
|
version "4.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f"
|
||||||
|
integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=
|
||||||
|
|
||||||
lodash.includes@^4.3.0:
|
lodash.includes@^4.3.0:
|
||||||
version "4.3.0"
|
version "4.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f"
|
resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f"
|
||||||
@ -2024,10 +2165,15 @@ lodash.once@^4.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
|
resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
|
||||||
integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=
|
integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=
|
||||||
|
|
||||||
lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19:
|
lodash.union@^4.6.0:
|
||||||
version "4.17.19"
|
version "4.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b"
|
resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88"
|
||||||
integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==
|
integrity sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=
|
||||||
|
|
||||||
|
lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21:
|
||||||
|
version "4.17.21"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||||
|
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||||
|
|
||||||
lowercase-keys@^1.0.0, lowercase-keys@^1.0.1:
|
lowercase-keys@^1.0.0, lowercase-keys@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
@ -2143,7 +2289,7 @@ minimist@^1.2.0, minimist@^1.2.5:
|
|||||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
|
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
|
||||||
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
|
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
|
||||||
|
|
||||||
minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0:
|
minipass@^2.6.0, minipass@^2.9.0:
|
||||||
version "2.9.0"
|
version "2.9.0"
|
||||||
resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6"
|
resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6"
|
||||||
integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==
|
integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==
|
||||||
@ -2151,7 +2297,7 @@ minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0:
|
|||||||
safe-buffer "^5.1.2"
|
safe-buffer "^5.1.2"
|
||||||
yallist "^3.0.0"
|
yallist "^3.0.0"
|
||||||
|
|
||||||
minizlib@^1.2.1:
|
minizlib@^1.3.3:
|
||||||
version "1.3.3"
|
version "1.3.3"
|
||||||
resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d"
|
resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d"
|
||||||
integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==
|
integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==
|
||||||
@ -2166,7 +2312,7 @@ mixin-deep@^1.2.0:
|
|||||||
for-in "^1.0.2"
|
for-in "^1.0.2"
|
||||||
is-extendable "^1.0.1"
|
is-extendable "^1.0.1"
|
||||||
|
|
||||||
mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3:
|
mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5:
|
||||||
version "0.5.5"
|
version "0.5.5"
|
||||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
|
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
|
||||||
integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
|
integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
|
||||||
@ -2340,9 +2486,9 @@ normalize-path@^3.0.0, normalize-path@~3.0.0:
|
|||||||
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
|
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
|
||||||
|
|
||||||
normalize-url@^4.1.0:
|
normalize-url@^4.1.0:
|
||||||
version "4.5.0"
|
version "4.5.1"
|
||||||
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129"
|
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a"
|
||||||
integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==
|
integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==
|
||||||
|
|
||||||
npm-bundled@^1.0.1:
|
npm-bundled@^1.0.1:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
@ -2426,12 +2572,12 @@ object.pick@^1.2.0, object.pick@^1.3.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
isobject "^3.0.1"
|
isobject "^3.0.1"
|
||||||
|
|
||||||
objection@^2.1.3:
|
objection@^2.2.16:
|
||||||
version "2.2.2"
|
version "2.2.16"
|
||||||
resolved "https://registry.yarnpkg.com/objection/-/objection-2.2.2.tgz#1a3c9010270e3677940d2bc91aeaeb3c0f103800"
|
resolved "https://registry.yarnpkg.com/objection/-/objection-2.2.16.tgz#552ec6d625a7f80d6e204fc63732cbd3fc56f31c"
|
||||||
integrity sha512-+1Ap7u9NQRochzDW5/BggUlKi94JfZGTJwQJuNXo8DwmAb8czEirvxcWBcX91/MmQq0BQUJjM4RPSiZhnkkWQw==
|
integrity sha512-sq8erZdxW5ruPUK6tVvwDxyO16U49XAn/BmOm2zaNhNA2phOPCe2/7+R70nDEF1SFrgJOrwDu/PtoxybuJxnjQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
ajv "^6.12.0"
|
ajv "^6.12.6"
|
||||||
db-errors "^0.2.3"
|
db-errors "^0.2.3"
|
||||||
|
|
||||||
on-finished@~2.3.0:
|
on-finished@~2.3.0:
|
||||||
@ -2608,9 +2754,9 @@ path-key@^2.0.1:
|
|||||||
integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=
|
integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=
|
||||||
|
|
||||||
path-parse@^1.0.6:
|
path-parse@^1.0.6:
|
||||||
version "1.0.6"
|
version "1.0.7"
|
||||||
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
|
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
|
||||||
integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==
|
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
|
||||||
|
|
||||||
path-root-regex@^0.1.0:
|
path-root-regex@^0.1.0:
|
||||||
version "0.1.2"
|
version "0.1.2"
|
||||||
@ -2754,6 +2900,11 @@ prettier@^2.0.4:
|
|||||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.0.5.tgz#d6d56282455243f2f92cc1716692c08aa31522d4"
|
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.0.5.tgz#d6d56282455243f2f92cc1716692c08aa31522d4"
|
||||||
integrity sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==
|
integrity sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==
|
||||||
|
|
||||||
|
printj@~1.1.0:
|
||||||
|
version "1.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/printj/-/printj-1.1.2.tgz#d90deb2975a8b9f600fb3a1c94e3f4c53c78a222"
|
||||||
|
integrity sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ==
|
||||||
|
|
||||||
process-nextick-args@~2.0.0:
|
process-nextick-args@~2.0.0:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
|
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
|
||||||
@ -2842,7 +2993,7 @@ rc@^1.2.7, rc@^1.2.8:
|
|||||||
minimist "^1.2.0"
|
minimist "^1.2.0"
|
||||||
strip-json-comments "~2.0.1"
|
strip-json-comments "~2.0.1"
|
||||||
|
|
||||||
readable-stream@2.3.7, readable-stream@^2.0.6:
|
readable-stream@2.3.7, readable-stream@^2.0.0, readable-stream@^2.0.5, readable-stream@^2.0.6:
|
||||||
version "2.3.7"
|
version "2.3.7"
|
||||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
|
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
|
||||||
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
|
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
|
||||||
@ -2855,6 +3006,22 @@ readable-stream@2.3.7, readable-stream@^2.0.6:
|
|||||||
string_decoder "~1.1.1"
|
string_decoder "~1.1.1"
|
||||||
util-deprecate "~1.0.1"
|
util-deprecate "~1.0.1"
|
||||||
|
|
||||||
|
readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0:
|
||||||
|
version "3.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
|
||||||
|
integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
|
||||||
|
dependencies:
|
||||||
|
inherits "^2.0.3"
|
||||||
|
string_decoder "^1.1.1"
|
||||||
|
util-deprecate "^1.0.1"
|
||||||
|
|
||||||
|
readdir-glob@^1.0.0:
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/readdir-glob/-/readdir-glob-1.1.1.tgz#f0e10bb7bf7bfa7e0add8baffdc54c3f7dbee6c4"
|
||||||
|
integrity sha512-91/k1EzZwDx6HbERR+zucygRFfiPl2zkIYZtv3Jjr6Mn7SkKcVct8aVO+sSRiGMc6fLf72du3d92/uY63YPdEA==
|
||||||
|
dependencies:
|
||||||
|
minimatch "^3.0.4"
|
||||||
|
|
||||||
readdirp@~3.4.0:
|
readdirp@~3.4.0:
|
||||||
version "3.4.0"
|
version "3.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.4.0.tgz#9fdccdf9e9155805449221ac645e8303ab5b9ada"
|
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.4.0.tgz#9fdccdf9e9155805449221ac645e8303ab5b9ada"
|
||||||
@ -3002,7 +3169,7 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
|
|||||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
||||||
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
|
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
|
||||||
|
|
||||||
safe-buffer@^5.0.1, safe-buffer@^5.1.2:
|
safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@^5.2.1, safe-buffer@~5.2.0:
|
||||||
version "5.2.1"
|
version "5.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
|
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
|
||||||
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
||||||
@ -3271,6 +3438,13 @@ string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0:
|
|||||||
is-fullwidth-code-point "^3.0.0"
|
is-fullwidth-code-point "^3.0.0"
|
||||||
strip-ansi "^6.0.0"
|
strip-ansi "^6.0.0"
|
||||||
|
|
||||||
|
string_decoder@^1.1.1:
|
||||||
|
version "1.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
|
||||||
|
integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
|
||||||
|
dependencies:
|
||||||
|
safe-buffer "~5.2.0"
|
||||||
|
|
||||||
string_decoder@~1.1.1:
|
string_decoder@~1.1.1:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
|
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
|
||||||
@ -3350,18 +3524,29 @@ table@^5.2.3:
|
|||||||
slice-ansi "^2.1.0"
|
slice-ansi "^2.1.0"
|
||||||
string-width "^3.0.0"
|
string-width "^3.0.0"
|
||||||
|
|
||||||
tar@^4, tar@^4.4.2:
|
tar-stream@^2.2.0:
|
||||||
version "4.4.13"
|
version "2.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525"
|
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287"
|
||||||
integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==
|
integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
chownr "^1.1.1"
|
bl "^4.0.3"
|
||||||
fs-minipass "^1.2.5"
|
end-of-stream "^1.4.1"
|
||||||
minipass "^2.8.6"
|
fs-constants "^1.0.0"
|
||||||
minizlib "^1.2.1"
|
inherits "^2.0.3"
|
||||||
mkdirp "^0.5.0"
|
readable-stream "^3.1.1"
|
||||||
safe-buffer "^5.1.2"
|
|
||||||
yallist "^3.0.3"
|
tar@^4, tar@^4.4.2:
|
||||||
|
version "4.4.19"
|
||||||
|
resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.19.tgz#2e4d7263df26f2b914dee10c825ab132123742f3"
|
||||||
|
integrity sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==
|
||||||
|
dependencies:
|
||||||
|
chownr "^1.1.4"
|
||||||
|
fs-minipass "^1.2.7"
|
||||||
|
minipass "^2.9.0"
|
||||||
|
minizlib "^1.3.3"
|
||||||
|
mkdirp "^0.5.5"
|
||||||
|
safe-buffer "^5.2.1"
|
||||||
|
yallist "^3.1.1"
|
||||||
|
|
||||||
tarn@^2.0.0:
|
tarn@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
@ -3587,7 +3772,7 @@ use@^3.1.0:
|
|||||||
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
|
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
|
||||||
integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==
|
integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==
|
||||||
|
|
||||||
util-deprecate@~1.0.1:
|
util-deprecate@^1.0.1, util-deprecate@~1.0.1:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||||
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
|
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
|
||||||
@ -3717,11 +3902,11 @@ xtend@^4.0.0:
|
|||||||
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
|
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
|
||||||
|
|
||||||
y18n@^4.0.0:
|
y18n@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b"
|
resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.1.tgz#8db2b83c31c5d75099bb890b23f3094891e247d4"
|
||||||
integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==
|
integrity sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==
|
||||||
|
|
||||||
yallist@^3.0.0, yallist@^3.0.3:
|
yallist@^3.0.0, yallist@^3.1.1:
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
|
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
|
||||||
integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
|
integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
|
||||||
@ -3755,3 +3940,12 @@ yargs@^15.4.1:
|
|||||||
which-module "^2.0.0"
|
which-module "^2.0.0"
|
||||||
y18n "^4.0.0"
|
y18n "^4.0.0"
|
||||||
yargs-parser "^18.1.2"
|
yargs-parser "^18.1.2"
|
||||||
|
|
||||||
|
zip-stream@^4.1.0:
|
||||||
|
version "4.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-4.1.0.tgz#51dd326571544e36aa3f756430b313576dc8fc79"
|
||||||
|
integrity sha512-zshzwQW7gG7hjpBlgeQP9RuyPGNxvJdzR8SUM3QhxCnLjWN2E7j3dOvpeDcQoETfHx0urRS7EtmVToql7YpU4A==
|
||||||
|
dependencies:
|
||||||
|
archiver-utils "^2.1.0"
|
||||||
|
compress-commons "^4.1.0"
|
||||||
|
readable-stream "^3.6.0"
|
||||||
|
14
docker/.dive-ci
Normal file
14
docker/.dive-ci
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
rules:
|
||||||
|
# If the efficiency is measured below X%, mark as failed.
|
||||||
|
# Expressed as a ratio between 0-1.
|
||||||
|
lowestEfficiency: 0.99
|
||||||
|
|
||||||
|
# If the amount of wasted space is at least X or larger than X, mark as failed.
|
||||||
|
# Expressed in B, KB, MB, and GB.
|
||||||
|
highestWastedBytes: 15MB
|
||||||
|
|
||||||
|
# If the amount of wasted space makes up for X% or more of the image, mark as failed.
|
||||||
|
# Note: the base image layer is NOT included in the total image size.
|
||||||
|
# Expressed as a ratio between 0-1; fails if the threshold is met or crossed.
|
||||||
|
highestUserWastedPercent: 0.02
|
||||||
|
|
@ -3,37 +3,35 @@
|
|||||||
|
|
||||||
# This file assumes that the frontend has been built using ./scripts/frontend-build
|
# This file assumes that the frontend has been built using ./scripts/frontend-build
|
||||||
|
|
||||||
FROM --platform=${TARGETPLATFORM:-linux/amd64} jc21/alpine-nginx-full:node
|
FROM nginxproxymanager/nginx-full:node
|
||||||
|
|
||||||
ARG TARGETPLATFORM
|
ARG TARGETPLATFORM
|
||||||
ARG BUILDPLATFORM
|
|
||||||
ARG BUILD_VERSION
|
ARG BUILD_VERSION
|
||||||
ARG BUILD_COMMIT
|
ARG BUILD_COMMIT
|
||||||
ARG BUILD_DATE
|
ARG BUILD_DATE
|
||||||
|
|
||||||
ENV SUPPRESS_NO_CONFIG_WARNING=1
|
ENV SUPPRESS_NO_CONFIG_WARNING=1 \
|
||||||
ENV S6_FIX_ATTRS_HIDDEN=1
|
S6_FIX_ATTRS_HIDDEN=1 \
|
||||||
ENV S6_BEHAVIOUR_IF_STAGE2_FAILS=1
|
S6_BEHAVIOUR_IF_STAGE2_FAILS=1 \
|
||||||
ENV NODE_ENV=production
|
NODE_ENV=production \
|
||||||
|
NPM_BUILD_VERSION="${BUILD_VERSION}" \
|
||||||
|
NPM_BUILD_COMMIT="${BUILD_COMMIT}" \
|
||||||
|
NPM_BUILD_DATE="${BUILD_DATE}"
|
||||||
|
|
||||||
RUN echo "fs.file-max = 65535" > /etc/sysctl.conf \
|
RUN echo "fs.file-max = 65535" > /etc/sysctl.conf \
|
||||||
&& apk update \
|
&& apt-get update \
|
||||||
&& apk add python3 certbot jq \
|
&& apt-get install -y --no-install-recommends jq logrotate \
|
||||||
&& python3 -m ensurepip \
|
&& apt-get clean \
|
||||||
&& rm -rf /var/cache/apk/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
ENV NPM_BUILD_VERSION="${BUILD_VERSION}" NPM_BUILD_COMMIT="${BUILD_COMMIT}" NPM_BUILD_DATE="${BUILD_DATE}"
|
|
||||||
|
|
||||||
# s6 overlay
|
# s6 overlay
|
||||||
COPY scripts/install-s6 /tmp/install-s6
|
COPY scripts/install-s6 /tmp/install-s6
|
||||||
RUN /tmp/install-s6 "${TARGETPLATFORM}" && rm -f /tmp/install-s6
|
RUN /tmp/install-s6 "${TARGETPLATFORM}" && rm -f /tmp/install-s6
|
||||||
|
|
||||||
EXPOSE 80
|
EXPOSE 80 81 443
|
||||||
EXPOSE 81
|
|
||||||
EXPOSE 443
|
|
||||||
|
|
||||||
ADD backend /app
|
COPY backend /app
|
||||||
ADD frontend/dist /app/frontend
|
COPY frontend/dist /app/frontend
|
||||||
COPY global /app/global
|
COPY global /app/global
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
@ -43,9 +41,18 @@ RUN yarn install
|
|||||||
COPY docker/rootfs /
|
COPY docker/rootfs /
|
||||||
|
|
||||||
# Remove frontend service not required for prod, dev nginx config as well
|
# Remove frontend service not required for prod, dev nginx config as well
|
||||||
RUN rm -rf /etc/services.d/frontend RUN rm -f /etc/nginx/conf.d/dev.conf
|
RUN rm -rf /etc/services.d/frontend /etc/nginx/conf.d/dev.conf
|
||||||
|
|
||||||
|
# Change permission of logrotate config file
|
||||||
|
RUN chmod 644 /etc/logrotate.d/nginx-proxy-manager
|
||||||
|
|
||||||
VOLUME [ "/data", "/etc/letsencrypt" ]
|
VOLUME [ "/data", "/etc/letsencrypt" ]
|
||||||
ENTRYPOINT [ "/init" ]
|
ENTRYPOINT [ "/init" ]
|
||||||
|
|
||||||
HEALTHCHECK --interval=5s --timeout=3s CMD /bin/check-health
|
LABEL org.label-schema.schema-version="1.0" \
|
||||||
|
org.label-schema.license="MIT" \
|
||||||
|
org.label-schema.name="nginx-proxy-manager" \
|
||||||
|
org.label-schema.description="Docker container for managing Nginx proxy hosts with a simple, powerful interface " \
|
||||||
|
org.label-schema.url="https://github.com/jc21/nginx-proxy-manager" \
|
||||||
|
org.label-schema.vcs-url="https://github.com/jc21/nginx-proxy-manager.git" \
|
||||||
|
org.label-schema.cmd="docker run --rm -ti jc21/nginx-proxy-manager:latest"
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
FROM jc21/alpine-nginx-full:node
|
FROM nginxproxymanager/nginx-full:node
|
||||||
LABEL maintainer="Jamie Curnow <jc@jc21.com>"
|
LABEL maintainer="Jamie Curnow <jc@jc21.com>"
|
||||||
|
|
||||||
ENV S6_LOGGING=0
|
ENV S6_LOGGING=0 \
|
||||||
ENV SUPPRESS_NO_CONFIG_WARNING=1
|
SUPPRESS_NO_CONFIG_WARNING=1 \
|
||||||
ENV S6_FIX_ATTRS_HIDDEN=1
|
S6_FIX_ATTRS_HIDDEN=1
|
||||||
|
|
||||||
RUN echo "fs.file-max = 65535" > /etc/sysctl.conf \
|
RUN echo "fs.file-max = 65535" > /etc/sysctl.conf \
|
||||||
&& apk update \
|
&& apt-get update \
|
||||||
&& apk add python3 certbot jq \
|
&& apt-get install -y certbot jq python3-pip logrotate \
|
||||||
&& python3 -m ensurepip \
|
&& apt-get clean \
|
||||||
&& rm -rf /var/cache/apk/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Task
|
# Task
|
||||||
RUN cd /usr \
|
RUN cd /usr \
|
||||||
@ -18,15 +18,12 @@ RUN cd /usr \
|
|||||||
|
|
||||||
COPY rootfs /
|
COPY rootfs /
|
||||||
RUN rm -f /etc/nginx/conf.d/production.conf
|
RUN rm -f /etc/nginx/conf.d/production.conf
|
||||||
|
RUN chmod 644 /etc/logrotate.d/nginx-proxy-manager
|
||||||
|
|
||||||
# s6 overlay
|
# s6 overlay
|
||||||
RUN curl -L -o /tmp/s6-overlay-amd64.tar.gz "https://github.com/just-containers/s6-overlay/releases/download/v1.22.1.0/s6-overlay-amd64.tar.gz" \
|
RUN curl -L -o /tmp/s6-overlay-amd64.tar.gz "https://github.com/just-containers/s6-overlay/releases/download/v1.22.1.0/s6-overlay-amd64.tar.gz" \
|
||||||
&& tar -xzf /tmp/s6-overlay-amd64.tar.gz -C /
|
&& tar -xzf /tmp/s6-overlay-amd64.tar.gz -C /
|
||||||
|
|
||||||
EXPOSE 80
|
EXPOSE 80 81 443
|
||||||
EXPOSE 81
|
|
||||||
EXPOSE 443
|
|
||||||
|
|
||||||
ENTRYPOINT [ "/init" ]
|
ENTRYPOINT [ "/init" ]
|
||||||
|
|
||||||
HEALTHCHECK --interval=5s --timeout=3s CMD /bin/check-health
|
|
@ -20,6 +20,10 @@ services:
|
|||||||
- 443
|
- 443
|
||||||
depends_on:
|
depends_on:
|
||||||
- db
|
- db
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "/bin/check-health"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 3s
|
||||||
|
|
||||||
fullstack-sqlite:
|
fullstack-sqlite:
|
||||||
image: ${IMAGE}:ci-${BUILD_NUMBER}
|
image: ${IMAGE}:ci-${BUILD_NUMBER}
|
||||||
@ -33,6 +37,10 @@ services:
|
|||||||
- 81
|
- 81
|
||||||
- 80
|
- 80
|
||||||
- 443
|
- 443
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "/bin/check-health"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 3s
|
||||||
|
|
||||||
db:
|
db:
|
||||||
image: jc21/mariadb-aria
|
image: jc21/mariadb-aria
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
# WARNING: This is a DEVELOPMENT docker-compose file, it should not be used for production.
|
# WARNING: This is a DEVELOPMENT docker-compose file, it should not be used for production.
|
||||||
version: "3"
|
version: "3.5"
|
||||||
services:
|
services:
|
||||||
|
|
||||||
npm:
|
npm:
|
||||||
image: nginxproxymanager:dev
|
image: nginxproxymanager:dev
|
||||||
|
container_name: npm_core
|
||||||
build:
|
build:
|
||||||
context: ./
|
context: ./
|
||||||
dockerfile: ./dev/Dockerfile
|
dockerfile: ./dev/Dockerfile
|
||||||
ports:
|
ports:
|
||||||
- 3080:80
|
- 3080:80
|
||||||
- 3081:81
|
- 3081:81
|
||||||
- 3443:443
|
- 3443:443 # Ususally you would only have this one
|
||||||
|
- 3444:444 # This is to test ssl passthrough
|
||||||
networks:
|
networks:
|
||||||
- nginx_proxy_manager
|
- nginx_proxy_manager
|
||||||
environment:
|
environment:
|
||||||
@ -22,6 +23,7 @@ services:
|
|||||||
DB_MYSQL_USER: "npm"
|
DB_MYSQL_USER: "npm"
|
||||||
DB_MYSQL_PASSWORD: "npm"
|
DB_MYSQL_PASSWORD: "npm"
|
||||||
DB_MYSQL_NAME: "npm"
|
DB_MYSQL_NAME: "npm"
|
||||||
|
# ENABLE_SSL_PASSTHROUGH: "true"
|
||||||
# DB_SQLITE_FILE: "/data/database.sqlite"
|
# DB_SQLITE_FILE: "/data/database.sqlite"
|
||||||
# DISABLE_IPV6: "true"
|
# DISABLE_IPV6: "true"
|
||||||
volumes:
|
volumes:
|
||||||
@ -36,8 +38,11 @@ services:
|
|||||||
|
|
||||||
db:
|
db:
|
||||||
image: jc21/mariadb-aria
|
image: jc21/mariadb-aria
|
||||||
|
container_name: npm_db
|
||||||
networks:
|
networks:
|
||||||
- nginx_proxy_manager
|
- nginx_proxy_manager
|
||||||
|
ports:
|
||||||
|
- 33306:3306
|
||||||
environment:
|
environment:
|
||||||
MYSQL_ROOT_PASSWORD: "npm"
|
MYSQL_ROOT_PASSWORD: "npm"
|
||||||
MYSQL_DATABASE: "npm"
|
MYSQL_DATABASE: "npm"
|
||||||
@ -47,21 +52,26 @@ services:
|
|||||||
- db_data:/var/lib/mysql
|
- db_data:/var/lib/mysql
|
||||||
|
|
||||||
swagger:
|
swagger:
|
||||||
image: 'swaggerapi/swagger-ui:latest'
|
image: "swaggerapi/swagger-ui:latest"
|
||||||
|
container_name: npm_swagger
|
||||||
ports:
|
ports:
|
||||||
- 3001:80
|
- 3001:80
|
||||||
networks:
|
networks:
|
||||||
- nginx_proxy_manager
|
- nginx_proxy_manager
|
||||||
environment:
|
environment:
|
||||||
URL: "http://127.0.0.1:3081/api/schema"
|
URL: "http://127.0.0.1:3081/api/schema"
|
||||||
PORT: '80'
|
PORT: "80"
|
||||||
depends_on:
|
depends_on:
|
||||||
- npm
|
- npm
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
npm_data:
|
npm_data:
|
||||||
|
name: npm_core_data
|
||||||
le_data:
|
le_data:
|
||||||
|
name: npm_le_data
|
||||||
db_data:
|
db_data:
|
||||||
|
name: npm_db_data
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
nginx_proxy_manager:
|
nginx_proxy_manager:
|
||||||
|
name: npm_network
|
||||||
|
7
docker/rootfs/etc/cont-init.d/01_perms.sh
Executable file
7
docker/rootfs/etc/cont-init.d/01_perms.sh
Executable file
@ -0,0 +1,7 @@
|
|||||||
|
#!/usr/bin/with-contenv bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
mkdir -p /data/logs
|
||||||
|
echo "Changing ownership of /data/logs to $(id -u):$(id -g)"
|
||||||
|
chown -R "$(id -u):$(id -g)" /data/logs
|
||||||
|
|
@ -1,4 +1,5 @@
|
|||||||
text = True
|
text = True
|
||||||
non-interactive = True
|
non-interactive = True
|
||||||
authenticator = webroot
|
|
||||||
webroot-path = /data/letsencrypt-acme-challenge
|
webroot-path = /data/letsencrypt-acme-challenge
|
||||||
|
key-type = ecdsa
|
||||||
|
elliptic-curve = secp384r1
|
||||||
|
25
docker/rootfs/etc/logrotate.d/nginx-proxy-manager
Normal file
25
docker/rootfs/etc/logrotate.d/nginx-proxy-manager
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/data/logs/*_access.log /data/logs/*/access.log {
|
||||||
|
create 0644 root root
|
||||||
|
weekly
|
||||||
|
rotate 4
|
||||||
|
missingok
|
||||||
|
notifempty
|
||||||
|
compress
|
||||||
|
sharedscripts
|
||||||
|
postrotate
|
||||||
|
/bin/kill -USR1 `cat /run/nginx.pid 2>/dev/null` 2>/dev/null || true
|
||||||
|
endscript
|
||||||
|
}
|
||||||
|
|
||||||
|
/data/logs/*_error.log /data/logs/*/error.log {
|
||||||
|
create 0644 root root
|
||||||
|
weekly
|
||||||
|
rotate 10
|
||||||
|
missingok
|
||||||
|
notifempty
|
||||||
|
compress
|
||||||
|
sharedscripts
|
||||||
|
postrotate
|
||||||
|
/bin/kill -USR1 `cat /run/nginx.pid 2>/dev/null` 2>/dev/null || true
|
||||||
|
endscript
|
||||||
|
}
|
@ -8,10 +8,11 @@ server {
|
|||||||
set $port "80";
|
set $port "80";
|
||||||
|
|
||||||
server_name localhost-nginx-proxy-manager;
|
server_name localhost-nginx-proxy-manager;
|
||||||
access_log /data/logs/default.log standard;
|
access_log /data/logs/fallback_access.log standard;
|
||||||
error_log /dev/null crit;
|
error_log /data/logs/fallback_error.log warn;
|
||||||
include conf.d/include/assets.conf;
|
include conf.d/include/assets.conf;
|
||||||
include conf.d/include/block-exploits.conf;
|
include conf.d/include/block-exploits.conf;
|
||||||
|
include conf.d/include/letsencrypt-acme-challenge.conf;
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
index index.html;
|
index index.html;
|
||||||
@ -29,7 +30,7 @@ server {
|
|||||||
set $port "443";
|
set $port "443";
|
||||||
|
|
||||||
server_name localhost;
|
server_name localhost;
|
||||||
access_log /data/logs/default.log standard;
|
access_log /data/logs/fallback-access.log standard;
|
||||||
error_log /dev/null crit;
|
error_log /dev/null crit;
|
||||||
ssl_certificate /data/nginx/dummycert.pem;
|
ssl_certificate /data/nginx/dummycert.pem;
|
||||||
ssl_certificate_key /data/nginx/dummykey.pem;
|
ssl_certificate_key /data/nginx/dummykey.pem;
|
||||||
|
@ -5,6 +5,7 @@ location ^~ /.well-known/acme-challenge/ {
|
|||||||
# Since this is for letsencrypt authentication of a domain and they do not give IP ranges of their infrastructure
|
# Since this is for letsencrypt authentication of a domain and they do not give IP ranges of their infrastructure
|
||||||
# we need to open up access by turning off auth and IP ACL for this location.
|
# we need to open up access by turning off auth and IP ACL for this location.
|
||||||
auth_basic off;
|
auth_basic off;
|
||||||
|
auth_request off;
|
||||||
allow all;
|
allow all;
|
||||||
|
|
||||||
# Set correct content type. According to this:
|
# Set correct content type. According to this:
|
||||||
|
@ -3,7 +3,5 @@ ssl_session_cache shared:SSL:50m;
|
|||||||
|
|
||||||
# intermediate configuration. tweak to your needs.
|
# intermediate configuration. tweak to your needs.
|
||||||
ssl_protocols TLSv1.2 TLSv1.3;
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
ssl_ciphers 'EECDH+AESGCM:AES256+EECDH:AES256+EDH:EDH+AESGCM:ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-
|
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';
|
||||||
ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AE
|
ssl_prefer_server_ciphers off;
|
||||||
S128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES';
|
|
||||||
ssl_prefer_server_ciphers on;
|
|
||||||
|
@ -9,7 +9,7 @@ worker_processes auto;
|
|||||||
# Enables the use of JIT for regular expressions to speed-up their processing.
|
# Enables the use of JIT for regular expressions to speed-up their processing.
|
||||||
pcre_jit on;
|
pcre_jit on;
|
||||||
|
|
||||||
error_log /data/logs/error.log warn;
|
error_log /data/logs/fallback_error.log warn;
|
||||||
|
|
||||||
# Includes files with directives to load dynamic modules.
|
# Includes files with directives to load dynamic modules.
|
||||||
include /etc/nginx/modules/*.conf;
|
include /etc/nginx/modules/*.conf;
|
||||||
@ -46,8 +46,7 @@ http {
|
|||||||
log_format proxy '[$time_local] $upstream_cache_status $upstream_status $status - $request_method $scheme $host "$request_uri" [Client $remote_addr] [Length $body_bytes_sent] [Gzip $gzip_ratio] [Sent-to $server] "$http_user_agent" "$http_referer"';
|
log_format proxy '[$time_local] $upstream_cache_status $upstream_status $status - $request_method $scheme $host "$request_uri" [Client $remote_addr] [Length $body_bytes_sent] [Gzip $gzip_ratio] [Sent-to $server] "$http_user_agent" "$http_referer"';
|
||||||
log_format standard '[$time_local] $status - $request_method $scheme $host "$request_uri" [Client $remote_addr] [Length $body_bytes_sent] [Gzip $gzip_ratio] "$http_user_agent" "$http_referer"';
|
log_format standard '[$time_local] $status - $request_method $scheme $host "$request_uri" [Client $remote_addr] [Length $body_bytes_sent] [Gzip $gzip_ratio] "$http_user_agent" "$http_referer"';
|
||||||
|
|
||||||
|
access_log /data/logs/fallback_access.log proxy;
|
||||||
access_log /data/logs/default.log proxy;
|
|
||||||
|
|
||||||
# Dynamically generated resolvers file
|
# Dynamically generated resolvers file
|
||||||
include /etc/nginx/conf.d/include/resolvers.conf;
|
include /etc/nginx/conf.d/include/resolvers.conf;
|
||||||
@ -58,11 +57,11 @@ http {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Real IP Determination
|
# Real IP Determination
|
||||||
# Docker subnet:
|
|
||||||
set_real_ip_from 172.0.0.0/8;
|
|
||||||
# Local subnets:
|
# Local subnets:
|
||||||
set_real_ip_from 10.0.0.0/8;
|
set_real_ip_from 10.0.0.0/8;
|
||||||
set_real_ip_from 192.0.0.0/8;
|
set_real_ip_from 172.16.0.0/12; # Includes Docker subnet
|
||||||
|
set_real_ip_from 192.168.0.0/16;
|
||||||
# NPM generated CDN ip ranges:
|
# NPM generated CDN ip ranges:
|
||||||
include conf.d/include/ip_ranges.conf;
|
include conf.d/include/ip_ranges.conf;
|
||||||
# always put the following 2 lines after ip subnets:
|
# always put the following 2 lines after ip subnets:
|
||||||
@ -86,6 +85,7 @@ http {
|
|||||||
|
|
||||||
stream {
|
stream {
|
||||||
# Files generated by NPM
|
# Files generated by NPM
|
||||||
|
include /data/nginx/ssl_passthrough_host/hosts[.]conf;
|
||||||
include /data/nginx/stream/*.conf;
|
include /data/nginx/stream/*.conf;
|
||||||
|
|
||||||
# Custom
|
# Custom
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
if [ "$DEVELOPMENT" == "true" ]; then
|
if [ "$DEVELOPMENT" == "true" ]; then
|
||||||
cd /app/frontend || exit 1
|
cd /app/frontend || exit 1
|
||||||
|
# If yarn install fails: add --verbose --network-concurrency 1
|
||||||
yarn install
|
yarn install
|
||||||
yarn watch
|
yarn watch
|
||||||
else
|
else
|
||||||
|
@ -6,6 +6,7 @@ cd /app || echo
|
|||||||
|
|
||||||
if [ "$DEVELOPMENT" == "true" ]; then
|
if [ "$DEVELOPMENT" == "true" ]; then
|
||||||
cd /app || exit 1
|
cd /app || exit 1
|
||||||
|
# If yarn install fails: add --verbose --network-concurrency 1
|
||||||
yarn install
|
yarn install
|
||||||
node --max_old_space_size=250 --abort_on_uncaught_exception node_modules/nodemon/bin/nodemon.js
|
node --max_old_space_size=250 --abort_on_uncaught_exception node_modules/nodemon/bin/nodemon.js
|
||||||
else
|
else
|
||||||
|
@ -12,6 +12,7 @@ mkdir -p /tmp/nginx/body \
|
|||||||
/data/nginx/default_www \
|
/data/nginx/default_www \
|
||||||
/data/nginx/proxy_host \
|
/data/nginx/proxy_host \
|
||||||
/data/nginx/redirection_host \
|
/data/nginx/redirection_host \
|
||||||
|
/data/nginx/ssl_passthrough_host \
|
||||||
/data/nginx/stream \
|
/data/nginx/stream \
|
||||||
/data/nginx/dead_host \
|
/data/nginx/dead_host \
|
||||||
/data/nginx/temp \
|
/data/nginx/temp \
|
||||||
@ -36,7 +37,7 @@ then
|
|||||||
-days 3650 \
|
-days 3650 \
|
||||||
-nodes \
|
-nodes \
|
||||||
-x509 \
|
-x509 \
|
||||||
-subj '/O=Nginx Proxy Manager/OU=Dummy Certificate/CN=localhost' \
|
-subj '/O=localhost/OU=localhost/CN=localhost' \
|
||||||
-keyout /data/nginx/dummykey.pem \
|
-keyout /data/nginx/dummykey.pem \
|
||||||
-out /data/nginx/dummycert.pem
|
-out /data/nginx/dummycert.pem
|
||||||
echo "Complete"
|
echo "Complete"
|
||||||
|
@ -16,5 +16,7 @@ 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} (${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 -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, ${ID:-debian} \E[1;32m${VERSION:-unknown}\E[1;36m, Certbot \E[1;32m$(certbot --version)\E[0m"
|
||||||
echo
|
echo -e -n '\E[1;34m'
|
||||||
|
cat /built-for-arch
|
||||||
|
echo -e '\E[0m'
|
||||||
|
@ -37,75 +37,3 @@ footer: MIT Licensed | Copyright © 2016-present jc21.com
|
|||||||
<p>Configure other users to either view or manage their own hosts. Full access permissions are available.</p>
|
<p>Configure other users to either view or manage their own hosts. Full access permissions are available.</p>
|
||||||
</div>
|
</div>
|
||||||
</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 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'
|
|
||||||
environment:
|
|
||||||
DB_MYSQL_HOST: "db"
|
|
||||||
DB_MYSQL_PORT: 3306
|
|
||||||
DB_MYSQL_USER: "npm"
|
|
||||||
DB_MYSQL_PASSWORD: "npm"
|
|
||||||
DB_MYSQL_NAME: "npm"
|
|
||||||
volumes:
|
|
||||||
- ./data:/data
|
|
||||||
- ./letsencrypt:/etc/letsencrypt
|
|
||||||
db:
|
|
||||||
image: 'jc21/mariadb-aria:latest'
|
|
||||||
environment:
|
|
||||||
MYSQL_ROOT_PASSWORD: 'npm'
|
|
||||||
MYSQL_DATABASE: 'npm'
|
|
||||||
MYSQL_USER: 'npm'
|
|
||||||
MYSQL_PASSWORD: 'npm'
|
|
||||||
volumes:
|
|
||||||
- ./data/mysql:/var/lib/mysql
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Bring up your stack
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker-compose up -d
|
|
||||||
```
|
|
||||||
|
|
||||||
4. 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.
|
|
||||||
|
|
||||||
5. Upgrading to new versions
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker-compose pull
|
|
||||||
docker-compose up -d
|
|
||||||
```
|
|
||||||
|
|
||||||
This project will automatically update any databases or other requirements so you don't have to follow
|
|
||||||
any crazy instructions. These steps above will pull the latest updates and recreate the docker
|
|
||||||
containers.
|
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
# Advanced Configuration
|
# Advanced Configuration
|
||||||
|
|
||||||
## Best Practice: Use a docker network
|
## Best Practice: Use a Docker network
|
||||||
|
|
||||||
For those who have a few of their upstream services running in docker on the same docker
|
For those who have a few of their upstream services running in Docker on the same Docker
|
||||||
host as NPM, here's a trick to secure things a bit better. By creating a custom docker network,
|
host as NPM, here's a trick to secure things a bit better. By creating a custom Docker network,
|
||||||
you don't need to publish ports for your upstream services to all of the docker host's interfaces.
|
you don't need to publish ports for your upstream services to all of the Docker host's interfaces.
|
||||||
|
|
||||||
Create a network, ie "scoobydoo":
|
Create a network, ie "scoobydoo":
|
||||||
|
|
||||||
@ -13,7 +13,7 @@ docker network create scoobydoo
|
|||||||
```
|
```
|
||||||
|
|
||||||
Then add the following to the `docker-compose.yml` file for both NPM and any other
|
Then add the following to the `docker-compose.yml` file for both NPM and any other
|
||||||
services running on this docker host:
|
services running on this Docker host:
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
networks:
|
networks:
|
||||||
@ -34,7 +34,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- './data:/data'
|
- './data:/data'
|
||||||
- '/var/run/docker.sock:/var/run/docker.sock'
|
- '/var/run/docker.sock:/var/run/docker.sock'
|
||||||
restart: always
|
restart: unless-stopped
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
default:
|
default:
|
||||||
@ -44,10 +44,22 @@ networks:
|
|||||||
|
|
||||||
Now in the NPM UI you can create a proxy host with `portainer` as the hostname,
|
Now in the NPM UI you can create a proxy host with `portainer` as the hostname,
|
||||||
and port `9000` as the port. Even though this port isn't listed in the docker-compose
|
and port `9000` as the port. Even though this port isn't listed in the docker-compose
|
||||||
file, it's "exposed" by the portainer docker image for you and not available on
|
file, it's "exposed" by the Portainer Docker image for you and not available on
|
||||||
the docker host outside of this docker network. The service name is used as the
|
the Docker host outside of this Docker network. The service name is used as the
|
||||||
hostname, so make sure your service names are unique when using the same network.
|
hostname, so make sure your service names are unique when using the same network.
|
||||||
|
|
||||||
|
## Docker Healthcheck
|
||||||
|
|
||||||
|
The `Dockerfile` that builds this project does not include a `HEALTHCHECK` but you can opt in to this
|
||||||
|
feature by adding the following to the service in your `docker-compose.yml` file:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "/bin/check-health"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 3s
|
||||||
|
```
|
||||||
|
|
||||||
## Docker Secrets
|
## Docker Secrets
|
||||||
|
|
||||||
This image supports the use of Docker secrets to import from file and keep sensitive usernames or passwords from being passed or preserved in plaintext.
|
This image supports the use of Docker secrets to import from file and keep sensitive usernames or passwords from being passed or preserved in plaintext.
|
||||||
@ -68,7 +80,7 @@ secrets:
|
|||||||
services:
|
services:
|
||||||
app:
|
app:
|
||||||
image: 'jc21/nginx-proxy-manager:latest'
|
image: 'jc21/nginx-proxy-manager:latest'
|
||||||
restart: always
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
# Public HTTP Port:
|
# Public HTTP Port:
|
||||||
- '80:80'
|
- '80:80'
|
||||||
@ -98,7 +110,7 @@ services:
|
|||||||
- db
|
- db
|
||||||
db:
|
db:
|
||||||
image: jc21/mariadb-aria
|
image: jc21/mariadb-aria
|
||||||
restart: always
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
# MYSQL_ROOT_PASSWORD: "npm" # use secret instead
|
# MYSQL_ROOT_PASSWORD: "npm" # use secret instead
|
||||||
MYSQL_ROOT_PASSWORD__FILE: /run/secrets/DB_ROOT_PWD
|
MYSQL_ROOT_PASSWORD__FILE: /run/secrets/DB_ROOT_PWD
|
||||||
@ -116,7 +128,7 @@ services:
|
|||||||
|
|
||||||
## Disabling IPv6
|
## Disabling IPv6
|
||||||
|
|
||||||
On some docker hosts IPv6 may not be enabled. In these cases, the following message may be seen in the log:
|
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
|
> Address family not supported by protocol
|
||||||
|
|
||||||
@ -160,3 +172,28 @@ value by specifying it as a Docker environment variable. The default if not spec
|
|||||||
X_FRAME_OPTIONS: "sameorigin"
|
X_FRAME_OPTIONS: "sameorigin"
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## SSL Passthrough
|
||||||
|
|
||||||
|
SSL Passthrough will allow you to proxy a server without [SSL termination](https://en.wikipedia.org/wiki/TLS_termination_proxy). This means the SSL encryption of the server will be passed right through the proxy, retaining the original certificate.
|
||||||
|
|
||||||
|
Because of the SSL encryption the proxy does not know anything about the traffic and it just relies on an SSL feature called [Server Name Indication](https://en.wikipedia.org/wiki/Server_Name_Indication) to know where to send this network packet. This also means if the client does not provide this additional information, accessing the site through the proxy won't be possible. But most modern browsers include this information a HTTPS requests.
|
||||||
|
|
||||||
|
Due to nginx constraints using SSL Passthrough comes with **a performance penalty for other hosts**, since all hosts (including normal proxy hosts) now have to pass through this additional step and basically being proxied twice. If you want to retain the upstream SSL certificate but do not need your service to be available on port 443, it is recommended to use a stream host instead.
|
||||||
|
|
||||||
|
To enable SSL Passthrough on your npm instance you need to do two things: add the environment variable `ENABLE_SSL_PASSTHROUGH` with the value `"true"`, and expose port 444 instead of 443 to the outside as port 443.
|
||||||
|
|
||||||
|
```yml
|
||||||
|
version: '3'
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
...
|
||||||
|
ports:
|
||||||
|
- '80:80'
|
||||||
|
- '81:81'
|
||||||
|
- '443:444' # Expose internal port 444 instead of 443 as SSL port
|
||||||
|
environment:
|
||||||
|
...
|
||||||
|
ENABLE_SSL_PASSTHROUGH: "true" # Enable SSL Passthrough
|
||||||
|
...
|
||||||
|
```
|
@ -143,7 +143,7 @@
|
|||||||
"css-select-base-adapter": "^0.1.1",
|
"css-select-base-adapter": "^0.1.1",
|
||||||
"css-tree": "^1.0.0-alpha.39",
|
"css-tree": "^1.0.0-alpha.39",
|
||||||
"css-unit-converter": "^1.1.2",
|
"css-unit-converter": "^1.1.2",
|
||||||
"css-what": "^3.3.0",
|
"css-what": "^5.0.1",
|
||||||
"cssesc": "^3.0.0",
|
"cssesc": "^3.0.0",
|
||||||
"cssnano": "^4.1.10",
|
"cssnano": "^4.1.10",
|
||||||
"cssnano-preset-default": "^4.0.7",
|
"cssnano-preset-default": "^4.0.7",
|
||||||
@ -443,7 +443,7 @@
|
|||||||
"normalize-url": "^5.1.0",
|
"normalize-url": "^5.1.0",
|
||||||
"npm-run-path": "^4.0.1",
|
"npm-run-path": "^4.0.1",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"nth-check": "^1.0.2",
|
"nth-check": "^2.0.1",
|
||||||
"num2fraction": "^1.2.2",
|
"num2fraction": "^1.2.2",
|
||||||
"number-is-nan": "^2.0.0",
|
"number-is-nan": "^2.0.0",
|
||||||
"oauth-sign": "^0.9.0",
|
"oauth-sign": "^0.9.0",
|
||||||
@ -500,7 +500,7 @@
|
|||||||
"pkg-up": "^3.1.0",
|
"pkg-up": "^3.1.0",
|
||||||
"portfinder": "^1.0.28",
|
"portfinder": "^1.0.28",
|
||||||
"posix-character-classes": "^1.0.0",
|
"posix-character-classes": "^1.0.0",
|
||||||
"postcss": "^7.0.32",
|
"postcss": "^8.2.10",
|
||||||
"postcss-calc": "^7.0.2",
|
"postcss-calc": "^7.0.2",
|
||||||
"postcss-colormin": "^4.0.3",
|
"postcss-colormin": "^4.0.3",
|
||||||
"postcss-convert-values": "^4.0.1",
|
"postcss-convert-values": "^4.0.1",
|
||||||
@ -612,7 +612,7 @@
|
|||||||
"serve-index": "^1.9.1",
|
"serve-index": "^1.9.1",
|
||||||
"serve-static": "^1.14.1",
|
"serve-static": "^1.14.1",
|
||||||
"set-blocking": "^2.0.0",
|
"set-blocking": "^2.0.0",
|
||||||
"set-value": "^3.0.2",
|
"set-value": "^4.0.1",
|
||||||
"setimmediate": "^1.0.5",
|
"setimmediate": "^1.0.5",
|
||||||
"setprototypeof": "^1.2.0",
|
"setprototypeof": "^1.2.0",
|
||||||
"sha.js": "^2.4.11",
|
"sha.js": "^2.4.11",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# Full Setup Instructions
|
# Full Setup Instructions
|
||||||
|
|
||||||
### MySQL Database
|
## MySQL Database
|
||||||
|
|
||||||
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:
|
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:
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ When using a `mariadb` database, the NPM configuration file should still use the
|
|||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
### Running the App
|
## Running the App
|
||||||
|
|
||||||
Via `docker-compose`:
|
Via `docker-compose`:
|
||||||
|
|
||||||
@ -25,7 +25,7 @@ version: "3"
|
|||||||
services:
|
services:
|
||||||
app:
|
app:
|
||||||
image: 'jc21/nginx-proxy-manager:latest'
|
image: 'jc21/nginx-proxy-manager:latest'
|
||||||
restart: always
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
# Public HTTP Port:
|
# Public HTTP Port:
|
||||||
- '80:80'
|
- '80:80'
|
||||||
@ -33,6 +33,8 @@ services:
|
|||||||
- '443:443'
|
- '443:443'
|
||||||
# Admin Web Port:
|
# Admin Web Port:
|
||||||
- '81:81'
|
- '81:81'
|
||||||
|
# Add any other Stream port you want to expose
|
||||||
|
# - '21:21' # FTP
|
||||||
environment:
|
environment:
|
||||||
# These are the settings to access your db
|
# These are the settings to access your db
|
||||||
DB_MYSQL_HOST: "db"
|
DB_MYSQL_HOST: "db"
|
||||||
@ -52,7 +54,7 @@ services:
|
|||||||
- db
|
- db
|
||||||
db:
|
db:
|
||||||
image: 'jc21/mariadb-aria:latest'
|
image: 'jc21/mariadb-aria:latest'
|
||||||
restart: always
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
MYSQL_ROOT_PASSWORD: 'npm'
|
MYSQL_ROOT_PASSWORD: 'npm'
|
||||||
MYSQL_DATABASE: 'npm'
|
MYSQL_DATABASE: 'npm'
|
||||||
@ -70,7 +72,7 @@ Then:
|
|||||||
docker-compose up -d
|
docker-compose up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
### Running on Raspberry PI / ARM devices
|
## Running on Raspberry PI / ARM devices
|
||||||
|
|
||||||
The docker images support the following architectures:
|
The docker images support the following architectures:
|
||||||
- amd64
|
- amd64
|
||||||
@ -87,8 +89,59 @@ for a list of supported architectures and if you want one that doesn't exist,
|
|||||||
Also, if you don't know how to already, follow [this guide to install docker and docker-compose](https://manre-universe.net/how-to-run-docker-and-docker-compose-on-raspbian/)
|
Also, if you don't know how to already, follow [this guide to install docker and docker-compose](https://manre-universe.net/how-to-run-docker-and-docker-compose-on-raspbian/)
|
||||||
on Raspbian.
|
on Raspbian.
|
||||||
|
|
||||||
|
Via `docker-compose`:
|
||||||
|
|
||||||
### Initial Run
|
```yml
|
||||||
|
version: "3"
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
image: 'jc21/nginx-proxy-manager:latest'
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
# Public HTTP Port:
|
||||||
|
- '80:80'
|
||||||
|
# Public HTTPS Port:
|
||||||
|
- '443:443'
|
||||||
|
# Admin Web Port:
|
||||||
|
- '81:81'
|
||||||
|
environment:
|
||||||
|
# These are the settings to access your db
|
||||||
|
DB_MYSQL_HOST: "db"
|
||||||
|
DB_MYSQL_PORT: 3306
|
||||||
|
DB_MYSQL_USER: "changeuser"
|
||||||
|
DB_MYSQL_PASSWORD: "changepass"
|
||||||
|
DB_MYSQL_NAME: "npm"
|
||||||
|
# If you would rather use Sqlite uncomment this
|
||||||
|
# and remove all DB_MYSQL_* lines above
|
||||||
|
# DB_SQLITE_FILE: "/data/database.sqlite"
|
||||||
|
# Uncomment this if IPv6 is not enabled on your host
|
||||||
|
# DISABLE_IPV6: 'true'
|
||||||
|
volumes:
|
||||||
|
- ./data/nginx-proxy-manager:/data
|
||||||
|
- ./letsencrypt:/etc/letsencrypt
|
||||||
|
depends_on:
|
||||||
|
- db
|
||||||
|
db:
|
||||||
|
image: yobasystems/alpine-mariadb:latest
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
MYSQL_ROOT_PASSWORD: "changeme"
|
||||||
|
MYSQL_DATABASE: "npm"
|
||||||
|
MYSQL_USER: "changeuser"
|
||||||
|
MYSQL_PASSWORD: "changepass"
|
||||||
|
volumes:
|
||||||
|
- ./data/mariadb:/var/lib/mysql
|
||||||
|
```
|
||||||
|
|
||||||
|
_Please note, that `DB_MYSQL_*` environment variables will take precedent over `DB_SQLITE_*` var>
|
||||||
|
|
||||||
|
Then:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker-compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
## Initial Run
|
||||||
|
|
||||||
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:
|
||||||
|
|
||||||
@ -99,7 +152,7 @@ After the app is running for the first time, the following will happen:
|
|||||||
This process can take a couple of minutes depending on your machine.
|
This process can take a couple of minutes depending on your machine.
|
||||||
|
|
||||||
|
|
||||||
### Default Administrator User
|
## Default Administrator User
|
||||||
|
|
||||||
```
|
```
|
||||||
Email: admin@example.com
|
Email: admin@example.com
|
||||||
@ -108,7 +161,7 @@ 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.
|
||||||
|
|
||||||
### Configuration File
|
## Configuration File
|
||||||
|
|
||||||
::: warning
|
::: warning
|
||||||
|
|
||||||
@ -129,7 +182,8 @@ Here's an example for `sqlite` configuration as it is generated from the environ
|
|||||||
"client": "sqlite3",
|
"client": "sqlite3",
|
||||||
"connection": {
|
"connection": {
|
||||||
"filename": "/data/database.sqlite"
|
"filename": "/data/database.sqlite"
|
||||||
}
|
},
|
||||||
|
"useNullAsDefault": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
196
docs/yarn.lock
196
docs/yarn.lock
@ -1624,9 +1624,9 @@ ansi-regex@^4.1.0:
|
|||||||
integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==
|
integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==
|
||||||
|
|
||||||
ansi-regex@^5.0.0:
|
ansi-regex@^5.0.0:
|
||||||
version "5.0.0"
|
version "5.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75"
|
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
|
||||||
integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==
|
integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
|
||||||
|
|
||||||
ansi-styles@^2.2.1:
|
ansi-styles@^2.2.1:
|
||||||
version "2.2.1"
|
version "2.2.1"
|
||||||
@ -2155,14 +2155,15 @@ browserify-zlib@^0.2.0:
|
|||||||
pako "~1.0.5"
|
pako "~1.0.5"
|
||||||
|
|
||||||
browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.13.0, browserslist@^4.8.5:
|
browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.13.0, browserslist@^4.8.5:
|
||||||
version "4.13.0"
|
version "4.16.5"
|
||||||
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.13.0.tgz#42556cba011e1b0a2775b611cba6a8eca18e940d"
|
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.5.tgz#952825440bca8913c62d0021334cbe928ef062ae"
|
||||||
integrity sha512-MINatJ5ZNrLnQ6blGvePd/QOz9Xtu+Ne+x29iQSCHfkU5BugKVJwZKn/iiL8UbpIpa3JhviKjz+XxMo0m2caFQ==
|
integrity sha512-C2HAjrM1AI/djrpAUU/tr4pml1DqLIzJKSLDBXBrNErl9ZCCTXdhwxdJjYc16953+mBWf7Lw+uUJgpgb8cN71A==
|
||||||
dependencies:
|
dependencies:
|
||||||
caniuse-lite "^1.0.30001093"
|
caniuse-lite "^1.0.30001214"
|
||||||
electron-to-chromium "^1.3.488"
|
colorette "^1.2.2"
|
||||||
escalade "^3.0.1"
|
electron-to-chromium "^1.3.719"
|
||||||
node-releases "^1.1.58"
|
escalade "^3.1.1"
|
||||||
|
node-releases "^1.1.71"
|
||||||
|
|
||||||
buffer-from@^1.0.0, buffer-from@^1.1.1:
|
buffer-from@^1.0.0, buffer-from@^1.1.1:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
@ -2417,10 +2418,10 @@ caniuse-api@^3.0.0:
|
|||||||
lodash.memoize "^4.1.2"
|
lodash.memoize "^4.1.2"
|
||||||
lodash.uniq "^4.5.0"
|
lodash.uniq "^4.5.0"
|
||||||
|
|
||||||
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001093, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001111:
|
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001111, caniuse-lite@^1.0.30001214:
|
||||||
version "1.0.30001111"
|
version "1.0.30001230"
|
||||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001111.tgz#dd0ce822c70eb6c7c068e4a55c22e19ec1501298"
|
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001230.tgz#8135c57459854b2240b57a4a6786044bdc5a9f71"
|
||||||
integrity sha512-xnDje2wchd/8mlJu8sXvWxOGvMgv+uT3iZ3bkIAynKOzToCssWCmkz/ZIkQBs/2pUB4uwnJKVORWQ31UkbVjOg==
|
integrity sha512-5yBd5nWCBS+jWKTcHOzXwo5xzcj4ePE/yjtkZyUV1BTUmrBaA9MRGC+e7mxnqXSA90CmCA8L3eKLaSUkt099IQ==
|
||||||
|
|
||||||
caseless@^0.12.0, caseless@~0.12.0:
|
caseless@^0.12.0, caseless@~0.12.0:
|
||||||
version "0.12.0"
|
version "0.12.0"
|
||||||
@ -2559,7 +2560,7 @@ cli-boxes@^2.2.0:
|
|||||||
resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.0.tgz#538ecae8f9c6ca508e3c3c95b453fe93cb4c168d"
|
resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.0.tgz#538ecae8f9c6ca508e3c3c95b453fe93cb4c168d"
|
||||||
integrity sha512-gpaBrMAizVEANOpfZp/EEUixTXDyGt7DFzdK5hU+UbWt/J0lB0w20ncZj59Z9a93xHb9u12zF5BS6i9RKbtg4w==
|
integrity sha512-gpaBrMAizVEANOpfZp/EEUixTXDyGt7DFzdK5hU+UbWt/J0lB0w20ncZj59Z9a93xHb9u12zF5BS6i9RKbtg4w==
|
||||||
|
|
||||||
clipboard@^2.0.0, clipboard@^2.0.6:
|
clipboard@^2.0.6:
|
||||||
version "2.0.6"
|
version "2.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-2.0.6.tgz#52921296eec0fdf77ead1749421b21c968647376"
|
resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-2.0.6.tgz#52921296eec0fdf77ead1749421b21c968647376"
|
||||||
integrity sha512-g5zbiixBRk/wyKakSwCKd7vQXDjFnAMGHoEyBogG/bw9kTD9GvdAvaoRR1ALcEzt3pVKxZR0pViekPMIS0QyGg==
|
integrity sha512-g5zbiixBRk/wyKakSwCKd7vQXDjFnAMGHoEyBogG/bw9kTD9GvdAvaoRR1ALcEzt3pVKxZR0pViekPMIS0QyGg==
|
||||||
@ -2649,9 +2650,9 @@ color-name@^1.0.0, color-name@^1.1.4, color-name@~1.1.4:
|
|||||||
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
|
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
|
||||||
|
|
||||||
color-string@^1.5.2, color-string@^1.5.3:
|
color-string@^1.5.2, color-string@^1.5.3:
|
||||||
version "1.5.3"
|
version "1.5.5"
|
||||||
resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.3.tgz#c9bbc5f01b58b5492f3d6857459cb6590ce204cc"
|
resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.5.tgz#65474a8f0e7439625f3d27a6a19d89fc45223014"
|
||||||
integrity sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==
|
integrity sha512-jgIoum0OfQfq9Whcfc2z/VhCNcmQjWbey6qBX0vqt7YICflUmBCh9E9CiQD5GSJ+Uehixm3NUwHVhqUAWRivZg==
|
||||||
dependencies:
|
dependencies:
|
||||||
color-name "^1.0.0"
|
color-name "^1.0.0"
|
||||||
simple-swizzle "^0.2.2"
|
simple-swizzle "^0.2.2"
|
||||||
@ -2664,10 +2665,10 @@ color@^3.0.0, color@^3.1.2:
|
|||||||
color-convert "^1.9.1"
|
color-convert "^1.9.1"
|
||||||
color-string "^1.5.2"
|
color-string "^1.5.2"
|
||||||
|
|
||||||
colorette@^1.2.1:
|
colorette@^1.2.1, colorette@^1.2.2:
|
||||||
version "1.2.1"
|
version "1.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.1.tgz#4d0b921325c14faf92633086a536db6e89564b1b"
|
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94"
|
||||||
integrity sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==
|
integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==
|
||||||
|
|
||||||
combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6:
|
combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6:
|
||||||
version "1.0.8"
|
version "1.0.8"
|
||||||
@ -3113,11 +3114,16 @@ css-what@2.1:
|
|||||||
resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2"
|
resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2"
|
||||||
integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==
|
integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==
|
||||||
|
|
||||||
css-what@^3.2.1, css-what@^3.3.0:
|
css-what@^3.2.1:
|
||||||
version "3.3.0"
|
version "3.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.3.0.tgz#10fec696a9ece2e591ac772d759aacabac38cd39"
|
resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.3.0.tgz#10fec696a9ece2e591ac772d759aacabac38cd39"
|
||||||
integrity sha512-pv9JPyatiPaQ6pf4OvD/dbfm0o5LviWmwxNWzblYf/1u9QZd0ihV+PMwy5jdQWQ3349kZmKEx9WXuSka2dM4cg==
|
integrity sha512-pv9JPyatiPaQ6pf4OvD/dbfm0o5LviWmwxNWzblYf/1u9QZd0ihV+PMwy5jdQWQ3349kZmKEx9WXuSka2dM4cg==
|
||||||
|
|
||||||
|
css-what@^5.0.1:
|
||||||
|
version "5.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.0.1.tgz#3efa820131f4669a8ac2408f9c32e7c7de9f4cad"
|
||||||
|
integrity sha512-FYDTSHb/7KXsWICVsxdmiExPjCfRC4qRFBdVwv7Ax9hMnvMmEjP9RfxTEZ3qPZGmADDn2vAKSo9UcN1jKVYscg==
|
||||||
|
|
||||||
css@^2.0.0:
|
css@^2.0.0:
|
||||||
version "2.2.4"
|
version "2.2.4"
|
||||||
resolved "https://registry.yarnpkg.com/css/-/css-2.2.4.tgz#c646755c73971f2bba6a601e2cf2fd71b1298929"
|
resolved "https://registry.yarnpkg.com/css/-/css-2.2.4.tgz#c646755c73971f2bba6a601e2cf2fd71b1298929"
|
||||||
@ -3495,9 +3501,9 @@ dns-packet@^4.0.0:
|
|||||||
safe-buffer "^5.1.1"
|
safe-buffer "^5.1.1"
|
||||||
|
|
||||||
dns-packet@^5.2.1:
|
dns-packet@^5.2.1:
|
||||||
version "5.2.1"
|
version "5.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-5.2.1.tgz#26cec0be92252a1b97ed106482921192a7e08f72"
|
resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-5.2.2.tgz#e4c7d12974cc320b0c0d4b9bbbf68ac151cfe81e"
|
||||||
integrity sha512-JHj2yJeKOqlxzeuYpN1d56GfhzivAxavNwHj9co3qptECel27B1rLY5PifJAvubsInX5pGLDjAHuCfCUc2Zv/w==
|
integrity sha512-sQN+vLwC3PvOXiCH/oHcdzML2opFeIdVh8gjjMZrM45n4dR80QF6o3AzInQy6F9Eoc0VJYog4JpQTilt4RFLYQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
ip "^1.1.5"
|
ip "^1.1.5"
|
||||||
|
|
||||||
@ -3669,10 +3675,10 @@ ee-first@1.1.1, ee-first@^1.1.1:
|
|||||||
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
|
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
|
||||||
integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
|
integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
|
||||||
|
|
||||||
electron-to-chromium@^1.3.488, electron-to-chromium@^1.3.522:
|
electron-to-chromium@^1.3.522, electron-to-chromium@^1.3.719:
|
||||||
version "1.3.522"
|
version "1.3.739"
|
||||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.522.tgz#4a6485ad187ffd31913bba0747d0e36405f405d6"
|
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.739.tgz#f07756aa92cabd5a6eec6f491525a64fe62f98b9"
|
||||||
integrity sha512-67V62Z4CFOiAtox+o+tosGfVk0QX4DJgH609tjT8QymbJZVAI/jWnAthnr8c5hnRNziIRwkc9EMQYejiVz3/9Q==
|
integrity sha512-+LPJVRsN7hGZ9EIUUiWCpO7l4E3qBYHNadazlucBfsXBbccDFNKUBAgzE68FnkWGJPwD/AfKhSzL+G+Iqb8A4A==
|
||||||
|
|
||||||
elliptic@^6.5.3:
|
elliptic@^6.5.3:
|
||||||
version "6.5.4"
|
version "6.5.4"
|
||||||
@ -3819,10 +3825,10 @@ es6-promise@^4.1.0, es6-promise@^4.2.8:
|
|||||||
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a"
|
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a"
|
||||||
integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==
|
integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==
|
||||||
|
|
||||||
escalade@^3.0.1:
|
escalade@^3.1.1:
|
||||||
version "3.0.2"
|
version "3.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.0.2.tgz#6a580d70edb87880f22b4c91d0d56078df6962c4"
|
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
|
||||||
integrity sha512-gPYAU37hYCUhW5euPeR+Y74F7BL+IBsV93j5cvGriSaD1aG6MGsqsV1yamRdrWrb2j3aiZvb0X+UBOWpx3JWtQ==
|
integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
|
||||||
|
|
||||||
escape-goat@^2.0.0:
|
escape-goat@^2.0.0:
|
||||||
version "2.1.1"
|
version "2.1.1"
|
||||||
@ -4421,9 +4427,9 @@ glob-parent@^3.1.0:
|
|||||||
path-dirname "^1.0.0"
|
path-dirname "^1.0.0"
|
||||||
|
|
||||||
glob-parent@^5.1.0, glob-parent@^5.1.1, glob-parent@~5.1.0:
|
glob-parent@^5.1.0, glob-parent@^5.1.1, glob-parent@~5.1.0:
|
||||||
version "5.1.1"
|
version "5.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229"
|
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
|
||||||
integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==
|
integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
|
||||||
dependencies:
|
dependencies:
|
||||||
is-glob "^4.0.1"
|
is-glob "^4.0.1"
|
||||||
|
|
||||||
@ -5524,9 +5530,9 @@ is-svg@^3.0.0:
|
|||||||
html-comment-regex "^1.1.0"
|
html-comment-regex "^1.1.0"
|
||||||
|
|
||||||
is-svg@^4.2.1:
|
is-svg@^4.2.1:
|
||||||
version "4.2.1"
|
version "4.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-4.2.1.tgz#095b496e345fec9211c2a7d5d021003e040d6f81"
|
resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-4.2.2.tgz#a4ea0f3f78dada7085db88f1e85b6f845626cfae"
|
||||||
integrity sha512-PHx3ANecKsKNl5y5+Jvt53Y4J7MfMpbNZkv384QNiswMKAWIbvcqbPz+sYbFKJI8Xv3be01GSFniPmoaP+Ai5A==
|
integrity sha512-JlA7Mc7mfWjdxxTkJ094oUK9amGD7gQaj5xA/NCY0vlVvZ1stmj4VX+bRuwOMN93IHRZ2ctpPH/0FO6DqvQ5Rw==
|
||||||
dependencies:
|
dependencies:
|
||||||
html-comment-regex "^1.1.2"
|
html-comment-regex "^1.1.2"
|
||||||
|
|
||||||
@ -5993,9 +5999,9 @@ lodash.uniq@^4.5.0:
|
|||||||
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
|
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
|
||||||
|
|
||||||
lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.3, lodash@^4.17.5:
|
lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.3, lodash@^4.17.5:
|
||||||
version "4.17.19"
|
version "4.17.21"
|
||||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b"
|
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||||
integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==
|
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||||
|
|
||||||
loglevel@^1.6.8:
|
loglevel@^1.6.8:
|
||||||
version "1.6.8"
|
version "1.6.8"
|
||||||
@ -6399,10 +6405,10 @@ minipass@^3.0.0, minipass@^3.1.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
yallist "^4.0.0"
|
yallist "^4.0.0"
|
||||||
|
|
||||||
minizlib@^2.1.0:
|
minizlib@^2.1.1:
|
||||||
version "2.1.0"
|
version "2.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.0.tgz#fd52c645301ef09a63a2c209697c294c6ce02cf3"
|
resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931"
|
||||||
integrity sha512-EzTZN/fjSvifSX0SlqUERCN39o6T40AMarPbv0MrarSFtIITCBh7bi+dU8nxGFHuqs9jdIAeoYoKuQAAASsPPA==
|
integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==
|
||||||
dependencies:
|
dependencies:
|
||||||
minipass "^3.0.0"
|
minipass "^3.0.0"
|
||||||
yallist "^4.0.0"
|
yallist "^4.0.0"
|
||||||
@ -6527,6 +6533,11 @@ nanoid@^2.0.3:
|
|||||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-2.1.11.tgz#ec24b8a758d591561531b4176a01e3ab4f0f0280"
|
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-2.1.11.tgz#ec24b8a758d591561531b4176a01e3ab4f0f0280"
|
||||||
integrity sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA==
|
integrity sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA==
|
||||||
|
|
||||||
|
nanoid@^3.1.22:
|
||||||
|
version "3.1.23"
|
||||||
|
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.23.tgz#f744086ce7c2bc47ee0a8472574d5c78e4183a81"
|
||||||
|
integrity sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==
|
||||||
|
|
||||||
nanomatch@^1.2.13, nanomatch@^1.2.9:
|
nanomatch@^1.2.13, nanomatch@^1.2.9:
|
||||||
version "1.2.13"
|
version "1.2.13"
|
||||||
resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119"
|
resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119"
|
||||||
@ -6618,10 +6629,10 @@ node-libs-browser@^2.2.1:
|
|||||||
util "^0.11.0"
|
util "^0.11.0"
|
||||||
vm-browserify "^1.0.1"
|
vm-browserify "^1.0.1"
|
||||||
|
|
||||||
node-releases@^1.1.58, node-releases@^1.1.60:
|
node-releases@^1.1.60, node-releases@^1.1.71:
|
||||||
version "1.1.60"
|
version "1.1.72"
|
||||||
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.60.tgz#6948bdfce8286f0b5d0e5a88e8384e954dfe7084"
|
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.72.tgz#14802ab6b1039a79a0c7d662b610a5bbd76eacbe"
|
||||||
integrity sha512-gsO4vjEdQaTusZAEebUWp2a5d7dF5DYoIpDG7WySnk7BuZDW+GPpHXoXXuYawRBr/9t5q54tirPz79kFIWg4dA==
|
integrity sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw==
|
||||||
|
|
||||||
nopt@1.0.10:
|
nopt@1.0.10:
|
||||||
version "1.0.10"
|
version "1.0.10"
|
||||||
@ -6685,9 +6696,9 @@ normalize-url@^4.1.0:
|
|||||||
integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==
|
integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==
|
||||||
|
|
||||||
normalize-url@^5.1.0:
|
normalize-url@^5.1.0:
|
||||||
version "5.1.0"
|
version "5.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-5.1.0.tgz#04b8f1b34ea49ff713fc20b2218eba41fb9974a3"
|
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-5.3.1.tgz#c8485c0f5ba2f9c17a6d2907b56117ae5967f882"
|
||||||
integrity sha512-UxHuSWsSAmzSqN+DSjasaZWQ3QPtEisHdlr4y9MJ5zg0RcImv5fQt8QM0izJSCdsdmhJGK+ubcTpJXwVDmwSVQ==
|
integrity sha512-K1c7+vaAP+Yh5bOGmA10PGPpp+6h7WZrl7GwqKhUflBc9flU9pzG27DDeB9+iuhZkE3BJZOcgN1P/2sS5pqrWw==
|
||||||
|
|
||||||
npm-run-path@^2.0.0:
|
npm-run-path@^2.0.0:
|
||||||
version "2.0.2"
|
version "2.0.2"
|
||||||
@ -6715,6 +6726,13 @@ nth-check@^1.0.2, nth-check@~1.0.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
boolbase "~1.0.0"
|
boolbase "~1.0.0"
|
||||||
|
|
||||||
|
nth-check@^2.0.1:
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.1.tgz#2efe162f5c3da06a28959fbd3db75dbeea9f0fc2"
|
||||||
|
integrity sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==
|
||||||
|
dependencies:
|
||||||
|
boolbase "^1.0.0"
|
||||||
|
|
||||||
num2fraction@^1.2.2:
|
num2fraction@^1.2.2:
|
||||||
version "1.2.2"
|
version "1.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede"
|
resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede"
|
||||||
@ -7162,9 +7180,9 @@ path-key@^3.0.0, path-key@^3.1.0, path-key@^3.1.1:
|
|||||||
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
|
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
|
||||||
|
|
||||||
path-parse@^1.0.6:
|
path-parse@^1.0.6:
|
||||||
version "1.0.6"
|
version "1.0.7"
|
||||||
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
|
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
|
||||||
integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==
|
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
|
||||||
|
|
||||||
path-to-regexp@0.1.7:
|
path-to-regexp@0.1.7:
|
||||||
version "0.1.7"
|
version "0.1.7"
|
||||||
@ -7640,6 +7658,15 @@ postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.26, postcss@^7.0.2
|
|||||||
source-map "^0.6.1"
|
source-map "^0.6.1"
|
||||||
supports-color "^6.1.0"
|
supports-color "^6.1.0"
|
||||||
|
|
||||||
|
postcss@^8.2.10:
|
||||||
|
version "8.2.10"
|
||||||
|
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.10.tgz#ca7a042aa8aff494b334d0ff3e9e77079f6f702b"
|
||||||
|
integrity sha512-b/h7CPV7QEdrqIxtAf2j31U5ef05uBDuvoXv6L51Q4rcS1jdlXAVKJv+atCFdUXYl9dyTHGyoMzIepwowRJjFw==
|
||||||
|
dependencies:
|
||||||
|
colorette "^1.2.2"
|
||||||
|
nanoid "^3.1.22"
|
||||||
|
source-map "^0.6.1"
|
||||||
|
|
||||||
prepend-http@^1.0.0:
|
prepend-http@^1.0.0:
|
||||||
version "1.0.4"
|
version "1.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc"
|
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc"
|
||||||
@ -7679,11 +7706,9 @@ pretty-time@^1.1.0:
|
|||||||
integrity sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==
|
integrity sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==
|
||||||
|
|
||||||
prismjs@^1.13.0, prismjs@^1.20.0:
|
prismjs@^1.13.0, prismjs@^1.20.0:
|
||||||
version "1.23.0"
|
version "1.25.0"
|
||||||
resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.23.0.tgz#d3b3967f7d72440690497652a9d40ff046067f33"
|
resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.25.0.tgz#6f822df1bdad965734b310b315a23315cf999756"
|
||||||
integrity sha512-c29LVsqOaLbBHuIbsTxaKENh1N2EQBOHaWv7gkHN4dgRbxSREqDnDbtFJYdpPauS4YCplMSNCABQ6Eeor69bAA==
|
integrity sha512-WCjJHl1KEWbnkQom1+SzftbtXMKQoezOCYs5rECqMN+jP+apI7ftoflyqigqzopSO3hMhTEb0mFClA8lkolgEg==
|
||||||
optionalDependencies:
|
|
||||||
clipboard "^2.0.0"
|
|
||||||
|
|
||||||
private@^0.1.8:
|
private@^0.1.8:
|
||||||
version "0.1.8"
|
version "0.1.8"
|
||||||
@ -8402,9 +8427,9 @@ set-blocking@^2.0.0:
|
|||||||
integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
|
integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
|
||||||
|
|
||||||
set-getter@^0.1.0:
|
set-getter@^0.1.0:
|
||||||
version "0.1.0"
|
version "0.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/set-getter/-/set-getter-0.1.0.tgz#d769c182c9d5a51f409145f2fba82e5e86e80376"
|
resolved "https://registry.yarnpkg.com/set-getter/-/set-getter-0.1.1.tgz#a3110e1b461d31a9cfc8c5c9ee2e9737ad447102"
|
||||||
integrity sha1-12nBgsnVpR9AkUXy+6guXoboA3Y=
|
integrity sha512-9sVWOy+gthr+0G9DzqqLaYNA7+5OKkSmcqjL9cBpDEaZrr3ShQlyX2cZ/O/ozE41oxn/Tt0LGEM/w4Rub3A3gw==
|
||||||
dependencies:
|
dependencies:
|
||||||
to-object-path "^0.3.0"
|
to-object-path "^0.3.0"
|
||||||
|
|
||||||
@ -8418,13 +8443,20 @@ set-value@^2.0.0, set-value@^2.0.1:
|
|||||||
is-plain-object "^2.0.3"
|
is-plain-object "^2.0.3"
|
||||||
split-string "^3.0.1"
|
split-string "^3.0.1"
|
||||||
|
|
||||||
set-value@^3.0.0, set-value@^3.0.2:
|
set-value@^3.0.0:
|
||||||
version "3.0.2"
|
version "3.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/set-value/-/set-value-3.0.2.tgz#74e8ecd023c33d0f77199d415409a40f21e61b90"
|
resolved "https://registry.yarnpkg.com/set-value/-/set-value-3.0.2.tgz#74e8ecd023c33d0f77199d415409a40f21e61b90"
|
||||||
integrity sha512-npjkVoz+ank0zjlV9F47Fdbjfj/PfXyVhZvGALWsyIYU/qrMzpi6avjKW3/7KeSU2Df3I46BrN1xOI1+6vW0hA==
|
integrity sha512-npjkVoz+ank0zjlV9F47Fdbjfj/PfXyVhZvGALWsyIYU/qrMzpi6avjKW3/7KeSU2Df3I46BrN1xOI1+6vW0hA==
|
||||||
dependencies:
|
dependencies:
|
||||||
is-plain-object "^2.0.4"
|
is-plain-object "^2.0.4"
|
||||||
|
|
||||||
|
set-value@^4.0.1:
|
||||||
|
version "4.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/set-value/-/set-value-4.0.1.tgz#bc23522ade2d52314ec3b5d6fb140f5cd3a88acf"
|
||||||
|
integrity sha512-ayATicCYPVnlNpFmjq2/VmVwhoCQA9+13j8qWp044fmFE3IFphosPtRM+0CJ5xoIx5Uy52fCcwg3XeH2pHbbPQ==
|
||||||
|
dependencies:
|
||||||
|
is-plain-object "^2.0.4"
|
||||||
|
|
||||||
setimmediate@^1.0.4, setimmediate@^1.0.5:
|
setimmediate@^1.0.4, setimmediate@^1.0.5:
|
||||||
version "1.0.5"
|
version "1.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
|
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
|
||||||
@ -8828,9 +8860,9 @@ ssri@^6.0.1:
|
|||||||
figgy-pudding "^3.5.1"
|
figgy-pudding "^3.5.1"
|
||||||
|
|
||||||
ssri@^8.0.0:
|
ssri@^8.0.0:
|
||||||
version "8.0.0"
|
version "8.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.0.tgz#79ca74e21f8ceaeddfcb4b90143c458b8d988808"
|
resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af"
|
||||||
integrity sha512-aq/pz989nxVYwn16Tsbj1TqFpD5LLrQxHf5zaHuieFV+R0Bbr4y8qUsOA45hXT/N4/9UNXTarBjnjVmjSOVaAA==
|
integrity sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
minipass "^3.1.1"
|
minipass "^3.1.1"
|
||||||
|
|
||||||
@ -9136,14 +9168,14 @@ tapable@^1.0.0, tapable@^1.1.3:
|
|||||||
integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==
|
integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==
|
||||||
|
|
||||||
tar@^6.0.2:
|
tar@^6.0.2:
|
||||||
version "6.0.2"
|
version "6.1.11"
|
||||||
resolved "https://registry.yarnpkg.com/tar/-/tar-6.0.2.tgz#5df17813468a6264ff14f766886c622b84ae2f39"
|
resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621"
|
||||||
integrity sha512-Glo3jkRtPcvpDlAs/0+hozav78yoXKFr+c4wgw62NNMO3oo4AaJdCo21Uu7lcwr55h39W2XD1LMERc64wtbItg==
|
integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==
|
||||||
dependencies:
|
dependencies:
|
||||||
chownr "^2.0.0"
|
chownr "^2.0.0"
|
||||||
fs-minipass "^2.0.0"
|
fs-minipass "^2.0.0"
|
||||||
minipass "^3.0.0"
|
minipass "^3.0.0"
|
||||||
minizlib "^2.1.0"
|
minizlib "^2.1.1"
|
||||||
mkdirp "^1.0.3"
|
mkdirp "^1.0.3"
|
||||||
yallist "^4.0.0"
|
yallist "^4.0.0"
|
||||||
|
|
||||||
@ -9632,9 +9664,9 @@ url-parse-lax@^3.0.0:
|
|||||||
prepend-http "^2.0.0"
|
prepend-http "^2.0.0"
|
||||||
|
|
||||||
url-parse@^1.4.3, url-parse@^1.4.7:
|
url-parse@^1.4.3, url-parse@^1.4.7:
|
||||||
version "1.4.7"
|
version "1.5.2"
|
||||||
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278"
|
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.2.tgz#a4eff6fd5ff9fe6ab98ac1f79641819d13247cda"
|
||||||
integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==
|
integrity sha512-6bTUPERy1muxxYClbzoRo5qtQuyoGEbzbQvi0SW4/8U8UyVkAQhWFBlnigqJkRm4su4x1zDQfNbEzWkt+vchcg==
|
||||||
dependencies:
|
dependencies:
|
||||||
querystringify "^2.1.1"
|
querystringify "^2.1.1"
|
||||||
requires-port "^1.0.0"
|
requires-port "^1.0.0"
|
||||||
@ -10239,9 +10271,9 @@ ws@^6.2.1:
|
|||||||
async-limiter "~1.0.0"
|
async-limiter "~1.0.0"
|
||||||
|
|
||||||
ws@^7.3.1:
|
ws@^7.3.1:
|
||||||
version "7.3.1"
|
version "7.4.6"
|
||||||
resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.1.tgz#d0547bf67f7ce4f12a72dfe31262c68d7dc551c8"
|
resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c"
|
||||||
integrity sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==
|
integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==
|
||||||
|
|
||||||
xdg-basedir@^4.0.0:
|
xdg-basedir@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
@ -10264,9 +10296,9 @@ xtend@^4.0.0, xtend@^4.0.2, xtend@~4.0.1:
|
|||||||
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
|
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
|
||||||
|
|
||||||
y18n@^4.0.0:
|
y18n@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b"
|
resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.1.tgz#8db2b83c31c5d75099bb890b23f3094891e247d4"
|
||||||
integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==
|
integrity sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==
|
||||||
|
|
||||||
yallist@^2.1.2:
|
yallist@^2.1.2:
|
||||||
version "2.1.2"
|
version "2.1.2"
|
||||||
|
@ -1 +0,0 @@
|
|||||||
./node_modules/tabler-ui/dist/assets/fonts
|
|
1
frontend/fonts/feather
Symbolic link
1
frontend/fonts/feather
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../node_modules/tabler-ui/dist/assets/fonts/feather
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -20,7 +20,6 @@
|
|||||||
<meta name="msapplication-TileColor" content="#333333">
|
<meta name="msapplication-TileColor" content="#333333">
|
||||||
<meta name="msapplication-config" content="/images/favicons/browserconfig.xml">
|
<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 href="/css/main.css?v=<%= version %>" rel="stylesheet">
|
<link href="/css/main.css?v=<%= version %>" rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
@ -152,6 +152,51 @@ function FileUpload(path, fd) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//ref : https://codepen.io/chrisdpratt/pen/RKxJNo
|
||||||
|
function DownloadFile(verb, path, filename) {
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
let api_url = '/api/';
|
||||||
|
let url = api_url + path;
|
||||||
|
let token = Tokens.getTopToken();
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: url,
|
||||||
|
type: verb,
|
||||||
|
crossDomain: true,
|
||||||
|
xhrFields: {
|
||||||
|
withCredentials: true,
|
||||||
|
responseType: 'blob'
|
||||||
|
},
|
||||||
|
|
||||||
|
beforeSend: function (xhr) {
|
||||||
|
xhr.setRequestHeader('Authorization', 'Bearer ' + (token ? token.t : null));
|
||||||
|
},
|
||||||
|
|
||||||
|
success: function (data) {
|
||||||
|
var a = document.createElement('a');
|
||||||
|
var url = window.URL.createObjectURL(data);
|
||||||
|
a.href = url;
|
||||||
|
a.download = filename;
|
||||||
|
document.body.append(a);
|
||||||
|
a.click();
|
||||||
|
a.remove();
|
||||||
|
window.URL.revokeObjectURL(url);
|
||||||
|
},
|
||||||
|
|
||||||
|
error: function (xhr, status, error_thrown) {
|
||||||
|
let code = 400;
|
||||||
|
|
||||||
|
if (typeof xhr.responseJSON !== 'undefined' && typeof xhr.responseJSON.error !== 'undefined' && typeof xhr.responseJSON.error.message !== 'undefined') {
|
||||||
|
error_thrown = xhr.responseJSON.error.message;
|
||||||
|
code = xhr.responseJSON.error.code || 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
reject(new ApiError(error_thrown, xhr.responseText, code));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
status: function () {
|
status: function () {
|
||||||
return fetch('get', '');
|
return fetch('get', '');
|
||||||
@ -470,6 +515,76 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
SslPassthroughHosts: {
|
||||||
|
/**
|
||||||
|
* @param {Array} [expand]
|
||||||
|
* @param {String} [query]
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
getFeatureEnabled: function () {
|
||||||
|
return fetch('get', 'ssl-passthrough-enabled');
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Array} [expand]
|
||||||
|
* @param {String} [query]
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
getAll: function (expand, query) {
|
||||||
|
return getAllObjects('nginx/ssl-passthrough-hosts', expand, query);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Object} data
|
||||||
|
*/
|
||||||
|
create: function (data) {
|
||||||
|
return fetch('post', 'nginx/ssl-passthrough-hosts', data);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Object} data
|
||||||
|
* @param {Number} data.id
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
update: function (data) {
|
||||||
|
let id = data.id;
|
||||||
|
delete data.id;
|
||||||
|
return fetch('put', 'nginx/ssl-passthrough-hosts/' + id, data);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Number} id
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
delete: function (id) {
|
||||||
|
return fetch('delete', 'nginx/ssl-passthrough-hosts/' + id);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Number} id
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
get: function (id) {
|
||||||
|
return fetch('get', 'nginx/ssl-passthrough-hosts/' + id);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Number} id
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
enable: function (id) {
|
||||||
|
return fetch('post', 'nginx/ssl-passthrough-hosts/' + id + '/enable');
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Number} id
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
disable: function (id) {
|
||||||
|
return fetch('post', 'nginx/ssl-passthrough-hosts/' + id + '/disable');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
DeadHosts: {
|
DeadHosts: {
|
||||||
/**
|
/**
|
||||||
* @param {Array} [expand]
|
* @param {Array} [expand]
|
||||||
@ -638,6 +753,14 @@ module.exports = {
|
|||||||
*/
|
*/
|
||||||
renew: function (id, timeout = 180000) {
|
renew: function (id, timeout = 180000) {
|
||||||
return fetch('post', 'nginx/certificates/' + id + '/renew', undefined, {timeout});
|
return fetch('post', 'nginx/certificates/' + id + '/renew', undefined, {timeout});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Number} id
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
download: function (id) {
|
||||||
|
return DownloadFile('get', "nginx/certificates/" + id + "/download", "certificate.zip")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -221,6 +221,46 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Nginx SSL Passthrough Hosts
|
||||||
|
*/
|
||||||
|
showNginxSslPassthrough: function () {
|
||||||
|
if (Cache.User.isAdmin() || Cache.User.canView('ssl_passthrough_hosts')) {
|
||||||
|
let controller = this;
|
||||||
|
|
||||||
|
require(['./main', './nginx/ssl-passthrough/main'], (App, View) => {
|
||||||
|
controller.navigate('/nginx/ssl-passthrough');
|
||||||
|
App.UI.showAppContent(new View());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SSL Passthrough Hosts Form
|
||||||
|
*
|
||||||
|
* @param [model]
|
||||||
|
*/
|
||||||
|
showNginxSslPassthroughForm: function (model) {
|
||||||
|
if (Cache.User.isAdmin() || Cache.User.canManage('ssl_passthrough_hosts')) {
|
||||||
|
require(['./main', './nginx/ssl-passthrough/form'], function (App, View) {
|
||||||
|
App.UI.showModalDialog(new View({model: model}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SSL Passthrough Hosts Delete Confirm
|
||||||
|
*
|
||||||
|
* @param model
|
||||||
|
*/
|
||||||
|
showNginxSslPassthroughDeleteConfirm: function (model) {
|
||||||
|
if (Cache.User.isAdmin() || Cache.User.canManage('ssl_passthrough_hosts')) {
|
||||||
|
require(['./main', './nginx/ssl-passthrough/delete'], function (App, View) {
|
||||||
|
App.UI.showModalDialog(new View({model: model}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Nginx Dead Hosts
|
* Nginx Dead Hosts
|
||||||
*/
|
*/
|
||||||
|
@ -46,6 +46,12 @@
|
|||||||
|
|
||||||
<!-- Authorization -->
|
<!-- Authorization -->
|
||||||
<div class="tab-pane" id="auth">
|
<div class="tab-pane" id="auth">
|
||||||
|
<p>
|
||||||
|
Basic Authorization via
|
||||||
|
<a target="_blank" href="https://nginx.org/en/docs/http/ngx_http_auth_basic_module.html">
|
||||||
|
Nginx HTTP Basic Authentication
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
<div class="row">
|
<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">
|
||||||
@ -60,10 +66,19 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="items"><!-- items --></div>
|
<div class="items"><!-- items --></div>
|
||||||
|
<div class="btn-list justify-content-end">
|
||||||
|
<button type="button" class="btn btn-teal auth_add"><%- i18n('access-lists', 'auth-add') %></button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Access -->
|
<!-- Access -->
|
||||||
<div class="tab-pane" id="access">
|
<div class="tab-pane" id="access">
|
||||||
|
<p>
|
||||||
|
IP Address Whitelist/Blacklist via
|
||||||
|
<a target="_blank" href="https://nginx.org/en/docs/http/ngx_http_access_module.html">
|
||||||
|
Nginx HTTP Access
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
<div class="clients"><!-- clients --></div>
|
<div class="clients"><!-- clients --></div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-3 col-md-3">
|
<div class="col-sm-3 col-md-3">
|
||||||
@ -78,6 +93,9 @@
|
|||||||
</div>
|
</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 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 class="btn-list justify-content-end">
|
||||||
|
<button type="button" class="btn btn-teal access_add"><%- i18n('access-lists', 'access-add') %></button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -25,7 +25,9 @@ module.exports = Mn.View.extend({
|
|||||||
form: 'form',
|
form: 'form',
|
||||||
buttons: '.modal-footer button',
|
buttons: '.modal-footer button',
|
||||||
cancel: 'button.cancel',
|
cancel: 'button.cancel',
|
||||||
save: 'button.save'
|
save: 'button.save',
|
||||||
|
access_add: 'button.access_add',
|
||||||
|
auth_add: 'button.auth_add'
|
||||||
},
|
},
|
||||||
|
|
||||||
regions: {
|
regions: {
|
||||||
@ -105,6 +107,24 @@ module.exports = Mn.View.extend({
|
|||||||
alert(err.message);
|
alert(err.message);
|
||||||
this.ui.buttons.prop('disabled', false).removeClass('btn-disabled');
|
this.ui.buttons.prop('disabled', false).removeClass('btn-disabled');
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
'click @ui.access_add': function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
let clients = this.model.get('clients');
|
||||||
|
clients.push({});
|
||||||
|
this.showChildView('clients_region', new ClientsView({
|
||||||
|
collection: new Backbone.Collection(clients)
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
'click @ui.auth_add': function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
let items = this.model.get('items');
|
||||||
|
items.push({});
|
||||||
|
this.showChildView('items_region', new ItemsView({
|
||||||
|
collection: new Backbone.Collection(items)
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -112,20 +132,9 @@ module.exports = Mn.View.extend({
|
|||||||
let items = this.model.get('items');
|
let items = this.model.get('items');
|
||||||
let clients = this.model.get('clients');
|
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
|
// Ensure at least one field is shown initally
|
||||||
let items_to_add = 5 - items.length;
|
if (!items.length) items.push({});
|
||||||
if (items_to_add) {
|
if (!clients.length) clients.push({});
|
||||||
for (let i = 0; i < items_to_add; i++) {
|
|
||||||
items.push({});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
||||||
|
@ -8,6 +8,6 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-sm-9 col-md-9">
|
<div class="col-sm-9 col-md-9">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input type="text" name="address[]" class="form-control" value="<%- typeof address !== 'undefined' ? address : '' %>" value="">
|
<input type="text" name="address[]" placeholder="IP / Subnet" class="form-control" value="<%- typeof address !== 'undefined' ? address : '' %>" value="">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
<div class="item-action dropdown">
|
<div class="item-action dropdown">
|
||||||
<a href="#" data-toggle="dropdown" class="icon"><i class="fe fe-more-vertical"></i></a>
|
<a href="#" data-toggle="dropdown" class="icon"><i class="fe fe-more-vertical"></i></a>
|
||||||
<div class="dropdown-menu dropdown-menu-right">
|
<div class="dropdown-menu dropdown-menu-right">
|
||||||
|
<span class="dropdown-header"><%- i18n('audit-log', 'access-list') %> #<%- id %></span>
|
||||||
<a href="#" class="edit dropdown-item"><i class="dropdown-icon fe fe-edit"></i> <%- i18n('str', 'edit') %></a>
|
<a href="#" class="edit dropdown-item"><i class="dropdown-icon fe fe-edit"></i> <%- i18n('str', 'edit') %></a>
|
||||||
<div class="dropdown-divider"></div>
|
<div class="dropdown-divider"></div>
|
||||||
<a href="#" class="delete dropdown-item"><i class="dropdown-icon fe fe-trash-2"></i> <%- i18n('str', 'delete') %></a>
|
<a href="#" class="delete dropdown-item"><i class="dropdown-icon fe fe-trash-2"></i> <%- i18n('str', 'delete') %></a>
|
||||||
|
@ -4,11 +4,20 @@ const App = require('../../main');
|
|||||||
const CertificateModel = require('../../../models/certificate');
|
const CertificateModel = require('../../../models/certificate');
|
||||||
const template = require('./form.ejs');
|
const template = require('./form.ejs');
|
||||||
const i18n = require('../../i18n');
|
const i18n = require('../../i18n');
|
||||||
const dns_providers = require('../../../../../global/certbot-dns-plugins');
|
const dns_providers = sortProvidersAlphabetically(require('../../../../../global/certbot-dns-plugins'));
|
||||||
|
|
||||||
require('jquery-serializejson');
|
require('jquery-serializejson');
|
||||||
require('selectize');
|
require('selectize');
|
||||||
|
|
||||||
|
function sortProvidersAlphabetically(obj) {
|
||||||
|
return Object.entries(obj)
|
||||||
|
.sort((a,b) => a[1].display_name.toLowerCase() > b[1].display_name.toLowerCase())
|
||||||
|
.reduce((result, entry) => {
|
||||||
|
result[entry[0]] = entry[1];
|
||||||
|
return result;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = Mn.View.extend({
|
module.exports = Mn.View.extend({
|
||||||
template: template,
|
template: template,
|
||||||
className: 'modal-dialog',
|
className: 'modal-dialog',
|
||||||
@ -242,7 +251,7 @@ module.exports = Mn.View.extend({
|
|||||||
text: input
|
text: input
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
createFilter: /^(?:[^.]+\.?)+[^.]$/
|
createFilter: /^(?:\*\.)?(?:[^.*]+\.?)+[^.]$/
|
||||||
});
|
});
|
||||||
this.ui.dns_challenge_content.hide();
|
this.ui.dns_challenge_content.hide();
|
||||||
this.ui.credentials_file_content.hide();
|
this.ui.credentials_file_content.hide();
|
||||||
|
@ -38,8 +38,10 @@
|
|||||||
<div class="item-action dropdown">
|
<div class="item-action dropdown">
|
||||||
<a href="#" data-toggle="dropdown" class="icon"><i class="fe fe-more-vertical"></i></a>
|
<a href="#" data-toggle="dropdown" class="icon"><i class="fe fe-more-vertical"></i></a>
|
||||||
<div class="dropdown-menu dropdown-menu-right">
|
<div class="dropdown-menu dropdown-menu-right">
|
||||||
|
<span class="dropdown-header"><%- i18n('audit-log', 'certificate') %> #<%- id %></span>
|
||||||
<% if (provider === 'letsencrypt') { %>
|
<% if (provider === 'letsencrypt') { %>
|
||||||
<a href="#" class="renew dropdown-item"><i class="dropdown-icon fe fe-refresh-cw"></i> <%- i18n('certificates', 'force-renew') %></a>
|
<a href="#" class="renew dropdown-item"><i class="dropdown-icon fe fe-refresh-cw"></i> <%- i18n('certificates', 'force-renew') %></a>
|
||||||
|
<a href="#" class="download dropdown-item"><i class="dropdown-icon fe fe-download"></i> <%- i18n('certificates', 'download') %></a>
|
||||||
<div class="dropdown-divider"></div>
|
<div class="dropdown-divider"></div>
|
||||||
<% } %>
|
<% } %>
|
||||||
<a href="#" class="delete dropdown-item"><i class="dropdown-icon fe fe-trash-2"></i> <%- i18n('str', 'delete') %></a>
|
<a href="#" class="delete dropdown-item"><i class="dropdown-icon fe fe-trash-2"></i> <%- i18n('str', 'delete') %></a>
|
||||||
|
@ -11,7 +11,8 @@ module.exports = Mn.View.extend({
|
|||||||
ui: {
|
ui: {
|
||||||
host_link: '.host-link',
|
host_link: '.host-link',
|
||||||
renew: 'a.renew',
|
renew: 'a.renew',
|
||||||
delete: 'a.delete'
|
delete: 'a.delete',
|
||||||
|
download: 'a.download'
|
||||||
},
|
},
|
||||||
|
|
||||||
events: {
|
events: {
|
||||||
@ -29,6 +30,11 @@ module.exports = Mn.View.extend({
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
let win = window.open($(e.currentTarget).attr('rel'), '_blank');
|
let win = window.open($(e.currentTarget).attr('rel'), '_blank');
|
||||||
win.focus();
|
win.focus();
|
||||||
|
},
|
||||||
|
|
||||||
|
'click @ui.download': function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
App.Api.Nginx.Certificates.download(this.model.get('id'))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -43,6 +43,7 @@
|
|||||||
<div class="item-action dropdown">
|
<div class="item-action dropdown">
|
||||||
<a href="#" data-toggle="dropdown" class="icon"><i class="fe fe-more-vertical"></i></a>
|
<a href="#" data-toggle="dropdown" class="icon"><i class="fe fe-more-vertical"></i></a>
|
||||||
<div class="dropdown-menu dropdown-menu-right">
|
<div class="dropdown-menu dropdown-menu-right">
|
||||||
|
<span class="dropdown-header"><%- i18n('audit-log', 'dead-host') %> #<%- id %></span>
|
||||||
<a href="#" class="edit dropdown-item"><i class="dropdown-icon fe fe-edit"></i> <%- i18n('str', 'edit') %></a>
|
<a href="#" class="edit dropdown-item"><i class="dropdown-icon fe fe-edit"></i> <%- i18n('str', 'edit') %></a>
|
||||||
<a href="#" class="able dropdown-item"><i class="dropdown-icon fe fe-power"></i> <%- i18n('str', enabled ? 'disable' : 'enable') %></a>
|
<a href="#" class="able dropdown-item"><i class="dropdown-icon fe fe-power"></i> <%- i18n('str', enabled ? 'disable' : 'enable') %></a>
|
||||||
<div class="dropdown-divider"></div>
|
<div class="dropdown-divider"></div>
|
||||||
|
@ -278,7 +278,7 @@ module.exports = Mn.View.extend({
|
|||||||
text: input
|
text: input
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
createFilter: /^(?:\.)?(?:[^.*]+\.?)+[^.]$/
|
createFilter: /^(?:\*\.)?(?:[^.*]+\.?)+[^.]$/
|
||||||
});
|
});
|
||||||
|
|
||||||
// Access Lists
|
// Access Lists
|
||||||
|
@ -49,6 +49,7 @@
|
|||||||
<div class="item-action dropdown">
|
<div class="item-action dropdown">
|
||||||
<a href="#" data-toggle="dropdown" class="icon"><i class="fe fe-more-vertical"></i></a>
|
<a href="#" data-toggle="dropdown" class="icon"><i class="fe fe-more-vertical"></i></a>
|
||||||
<div class="dropdown-menu dropdown-menu-right">
|
<div class="dropdown-menu dropdown-menu-right">
|
||||||
|
<span class="dropdown-header"><%- i18n('audit-log', 'proxy-host') %> #<%- id %></span>
|
||||||
<a href="#" class="edit dropdown-item"><i class="dropdown-icon fe fe-edit"></i> <%- i18n('str', 'edit') %></a>
|
<a href="#" class="edit dropdown-item"><i class="dropdown-icon fe fe-edit"></i> <%- i18n('str', 'edit') %></a>
|
||||||
<a href="#" class="able dropdown-item"><i class="dropdown-icon fe fe-power"></i> <%- i18n('str', enabled ? 'disable' : 'enable') %></a>
|
<a href="#" class="able dropdown-item"><i class="dropdown-icon fe fe-power"></i> <%- i18n('str', enabled ? 'disable' : 'enable') %></a>
|
||||||
<div class="dropdown-divider"></div>
|
<div class="dropdown-divider"></div>
|
||||||
|
@ -52,6 +52,7 @@
|
|||||||
<div class="item-action dropdown">
|
<div class="item-action dropdown">
|
||||||
<a href="#" data-toggle="dropdown" class="icon"><i class="fe fe-more-vertical"></i></a>
|
<a href="#" data-toggle="dropdown" class="icon"><i class="fe fe-more-vertical"></i></a>
|
||||||
<div class="dropdown-menu dropdown-menu-right">
|
<div class="dropdown-menu dropdown-menu-right">
|
||||||
|
<span class="dropdown-header"><%- i18n('audit-log', 'redirection-host') %> #<%- id %></span>
|
||||||
<a href="#" class="edit dropdown-item"><i class="dropdown-icon fe fe-edit"></i> <%- i18n('str', 'edit') %></a>
|
<a href="#" class="edit dropdown-item"><i class="dropdown-icon fe fe-edit"></i> <%- i18n('str', 'edit') %></a>
|
||||||
<a href="#" class="able dropdown-item"><i class="dropdown-icon fe fe-power"></i> <%- i18n('str', enabled ? 'disable' : 'enable') %></a>
|
<a href="#" class="able dropdown-item"><i class="dropdown-icon fe fe-power"></i> <%- i18n('str', enabled ? 'disable' : 'enable') %></a>
|
||||||
<div class="dropdown-divider"></div>
|
<div class="dropdown-divider"></div>
|
||||||
|
19
frontend/js/app/nginx/ssl-passthrough/delete.ejs
Normal file
19
frontend/js/app/nginx/ssl-passthrough/delete.ejs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title"><%- i18n('ssl-passthrough-hosts', 'delete') %></h5>
|
||||||
|
<button type="button" class="close cancel" aria-label="Close" data-dismiss="modal"> </button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-12 col-md-12">
|
||||||
|
<%= i18n('ssl-passthrough-hosts', 'delete-confirm') %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary cancel" data-dismiss="modal"><%- i18n('str', 'cancel') %></button>
|
||||||
|
<button type="button" class="btn btn-danger save"><%- i18n('str', 'sure') %></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
32
frontend/js/app/nginx/ssl-passthrough/delete.js
Normal file
32
frontend/js/app/nginx/ssl-passthrough/delete.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
const Mn = require('backbone.marionette');
|
||||||
|
const App = require('../../main');
|
||||||
|
const template = require('./delete.ejs');
|
||||||
|
|
||||||
|
module.exports = Mn.View.extend({
|
||||||
|
template: template,
|
||||||
|
className: 'modal-dialog',
|
||||||
|
|
||||||
|
ui: {
|
||||||
|
form: 'form',
|
||||||
|
buttons: '.modal-footer button',
|
||||||
|
cancel: 'button.cancel',
|
||||||
|
save: 'button.save'
|
||||||
|
},
|
||||||
|
|
||||||
|
events: {
|
||||||
|
|
||||||
|
'click @ui.save': function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
App.Api.Nginx.SslPassthroughHosts.delete(this.model.get('id'))
|
||||||
|
.then(() => {
|
||||||
|
App.Controller.showNginxSslPassthrough();
|
||||||
|
App.UI.closeModal();
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
alert(err.message);
|
||||||
|
this.ui.buttons.prop('disabled', false).removeClass('btn-disabled');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
34
frontend/js/app/nginx/ssl-passthrough/form.ejs
Normal file
34
frontend/js/app/nginx/ssl-passthrough/form.ejs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title"><%- i18n('ssl-passthrough-hosts', 'form-title', {id: id}) %></h5>
|
||||||
|
<button type="button" class="close cancel" aria-label="Close" data-dismiss="modal"> </button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-12 col-md-12">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label"><%- i18n('all-hosts', 'domain-name') %> <span class="form-required">*</span></label>
|
||||||
|
<input type="text" name="domain_name" class="form-control" id="input-domain" placeholder="example.com" value="<%- domain_name %>" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-8 col-md-8">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label"><%- i18n('ssl-passthrough-hosts', 'forwarding-host') %><span class="form-required">*</span></label>
|
||||||
|
<input type="text" name="forwarding_host" class="form-control text-monospace" placeholder="example.com or 10.0.0.1 or 2001:db8:3333:4444:5555:6666:7777:8888" value="<%- forwarding_host %>" autocomplete="off" maxlength="255" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-4 col-md-4">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label"><%- i18n('ssl-passthrough-hosts', 'forwarding-port') %> <span class="form-required">*</span></label>
|
||||||
|
<input name="forwarding_port" type="number" class="form-control text-monospace" placeholder="eg: 443" value="<%- forwarding_port %>" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary cancel" data-dismiss="modal"><%- i18n('str', 'cancel') %></button>
|
||||||
|
<button type="button" class="btn btn-teal save"><%- i18n('str', 'save') %></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
74
frontend/js/app/nginx/ssl-passthrough/form.js
Normal file
74
frontend/js/app/nginx/ssl-passthrough/form.js
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
const Mn = require('backbone.marionette');
|
||||||
|
const App = require('../../main');
|
||||||
|
const SslPassthroughModel = require('../../../models/ssl-passthrough-host');
|
||||||
|
const template = require('./form.ejs');
|
||||||
|
|
||||||
|
require('jquery-serializejson');
|
||||||
|
require('jquery-mask-plugin');
|
||||||
|
require('selectize');
|
||||||
|
|
||||||
|
module.exports = Mn.View.extend({
|
||||||
|
template: template,
|
||||||
|
className: 'modal-dialog',
|
||||||
|
|
||||||
|
ui: {
|
||||||
|
form: 'form',
|
||||||
|
forwarding_host: 'input[name="forwarding_host"]',
|
||||||
|
buttons: '.modal-footer button',
|
||||||
|
cancel: 'button.cancel',
|
||||||
|
save: 'button.save'
|
||||||
|
},
|
||||||
|
|
||||||
|
events: {
|
||||||
|
'change @ui.switches': function () {
|
||||||
|
this.ui.type_error.hide();
|
||||||
|
},
|
||||||
|
|
||||||
|
'click @ui.save': function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
if (!this.ui.form[0].checkValidity()) {
|
||||||
|
$('<input type="submit">').hide().appendTo(this.ui.form).click().remove();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let view = this;
|
||||||
|
let data = this.ui.form.serializeJSON();
|
||||||
|
|
||||||
|
// Manipulate
|
||||||
|
data.forwarding_port = parseInt(data.forwarding_port, 10);
|
||||||
|
|
||||||
|
let method = App.Api.Nginx.SslPassthroughHosts.create;
|
||||||
|
let is_new = true;
|
||||||
|
|
||||||
|
if (this.model.get('id')) {
|
||||||
|
// edit
|
||||||
|
is_new = false;
|
||||||
|
method = App.Api.Nginx.SslPassthroughHosts.update;
|
||||||
|
data.id = this.model.get('id');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.ui.buttons.prop('disabled', true).addClass('btn-disabled');
|
||||||
|
method(data)
|
||||||
|
.then(result => {
|
||||||
|
view.model.set(result);
|
||||||
|
|
||||||
|
App.UI.closeModal(function () {
|
||||||
|
if (is_new) {
|
||||||
|
App.Controller.showNginxSslPassthrough();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
alert(err.message);
|
||||||
|
this.ui.buttons.prop('disabled', false).removeClass('btn-disabled');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
initialize: function (options) {
|
||||||
|
if (typeof options.model === 'undefined' || !options.model) {
|
||||||
|
this.model = new SslPassthroughModel.Model();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
43
frontend/js/app/nginx/ssl-passthrough/list/item.ejs
Normal file
43
frontend/js/app/nginx/ssl-passthrough/list/item.ejs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<td class="text-center">
|
||||||
|
<div class="avatar d-block" style="background-image: url(<%- owner.avatar || '/images/default-avatar.jpg' %>)" title="Owned by <%- owner.name %>">
|
||||||
|
<span class="avatar-status <%- owner.is_disabled ? 'bg-red' : 'bg-green' %>"></span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div class="text-monospace">
|
||||||
|
<%- domain_name %>
|
||||||
|
</div>
|
||||||
|
<div class="small text-muted">
|
||||||
|
<%- i18n('str', 'created-on', {date: formatDbDate(created_on, 'Do MMMM YYYY')}) %>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div class="text-monospace"><%- forwarding_host %>:<%- forwarding_port %></div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<%
|
||||||
|
var o = isOnline();
|
||||||
|
if (!enabled) { %>
|
||||||
|
<span class="status-icon bg-warning"></span> <%- i18n('str', 'disabled') %>
|
||||||
|
<% } else if (o === true) { %>
|
||||||
|
<span class="status-icon bg-success"></span> <%- i18n('str', 'online') %>
|
||||||
|
<% } else if (o === false) { %>
|
||||||
|
<span title="<%- getOfflineError() %>"><span class="status-icon bg-danger"></span> <%- i18n('str', 'offline') %></span>
|
||||||
|
<% } else { %>
|
||||||
|
<span class="status-icon bg-warning"></span> <%- i18n('str', 'unknown') %>
|
||||||
|
<% } %>
|
||||||
|
</td>
|
||||||
|
<% if (canManage) { %>
|
||||||
|
<td class="text-right">
|
||||||
|
<div class="item-action dropdown">
|
||||||
|
<a href="#" data-toggle="dropdown" class="icon"><i class="fe fe-more-vertical"></i></a>
|
||||||
|
<div class="dropdown-menu dropdown-menu-right">
|
||||||
|
<span class="dropdown-header"><%- i18n('audit-log', 'ssl-passthrough-host') %> #<%- id %></span>
|
||||||
|
<a href="#" class="edit dropdown-item"><i class="dropdown-icon fe fe-edit"></i> <%- i18n('str', 'edit') %></a>
|
||||||
|
<a href="#" class="able dropdown-item"><i class="dropdown-icon fe fe-power"></i> <%- i18n('str', enabled ? 'disable' : 'enable') %></a>
|
||||||
|
<div class="dropdown-divider"></div>
|
||||||
|
<a href="#" class="delete dropdown-item"><i class="dropdown-icon fe fe-trash-2"></i> <%- i18n('str', 'delete') %></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<% } %>
|
54
frontend/js/app/nginx/ssl-passthrough/list/item.js
Normal file
54
frontend/js/app/nginx/ssl-passthrough/list/item.js
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
const Mn = require('backbone.marionette');
|
||||||
|
const App = require('../../../main');
|
||||||
|
const template = require('./item.ejs');
|
||||||
|
|
||||||
|
module.exports = Mn.View.extend({
|
||||||
|
template: template,
|
||||||
|
tagName: 'tr',
|
||||||
|
|
||||||
|
ui: {
|
||||||
|
able: 'a.able',
|
||||||
|
edit: 'a.edit',
|
||||||
|
delete: 'a.delete'
|
||||||
|
},
|
||||||
|
|
||||||
|
events: {
|
||||||
|
'click @ui.able': function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
let id = this.model.get('id');
|
||||||
|
App.Api.Nginx.SslPassthroughHosts[this.model.get('enabled') ? 'disable' : 'enable'](id)
|
||||||
|
.then(() => {
|
||||||
|
return App.Api.Nginx.SslPassthroughHosts.get(id)
|
||||||
|
.then(row => {
|
||||||
|
this.model.set(row);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
'click @ui.edit': function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
App.Controller.showNginxSslPassthroughForm(this.model);
|
||||||
|
},
|
||||||
|
|
||||||
|
'click @ui.delete': function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
App.Controller.showNginxSslPassthroughDeleteConfirm(this.model);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
templateContext: {
|
||||||
|
canManage: App.Cache.User.canManage('ssl_passthrough_hosts'),
|
||||||
|
|
||||||
|
isOnline: function () {
|
||||||
|
return typeof this.meta.nginx_online === 'undefined' ? null : this.meta.nginx_online;
|
||||||
|
},
|
||||||
|
|
||||||
|
getOfflineError: function () {
|
||||||
|
return this.meta.nginx_err || '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
initialize: function () {
|
||||||
|
this.listenTo(this.model, 'change', this.render);
|
||||||
|
}
|
||||||
|
});
|
12
frontend/js/app/nginx/ssl-passthrough/list/main.ejs
Normal file
12
frontend/js/app/nginx/ssl-passthrough/list/main.ejs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<thead>
|
||||||
|
<th width="30"> </th>
|
||||||
|
<th><%- i18n('all-hosts', 'domain-name') %></th>
|
||||||
|
<th><%- i18n('str', 'destination') %></th>
|
||||||
|
<th><%- i18n('str', 'status') %></th>
|
||||||
|
<% if (canManage) { %>
|
||||||
|
<th> </th>
|
||||||
|
<% } %>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<!-- items -->
|
||||||
|
</tbody>
|
32
frontend/js/app/nginx/ssl-passthrough/list/main.js
Normal file
32
frontend/js/app/nginx/ssl-passthrough/list/main.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
const Mn = require('backbone.marionette');
|
||||||
|
const App = require('../../../main');
|
||||||
|
const ItemView = require('./item');
|
||||||
|
const template = require('./main.ejs');
|
||||||
|
|
||||||
|
const TableBody = Mn.CollectionView.extend({
|
||||||
|
tagName: 'tbody',
|
||||||
|
childView: ItemView
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = Mn.View.extend({
|
||||||
|
tagName: 'table',
|
||||||
|
className: 'table table-hover table-outline table-vcenter card-table',
|
||||||
|
template: template,
|
||||||
|
|
||||||
|
regions: {
|
||||||
|
body: {
|
||||||
|
el: 'tbody',
|
||||||
|
replaceElement: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
templateContext: {
|
||||||
|
canManage: App.Cache.User.canManage('ssl_passthrough_hosts')
|
||||||
|
},
|
||||||
|
|
||||||
|
onRender: function () {
|
||||||
|
this.showChildView('body', new TableBody({
|
||||||
|
collection: this.collection
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
23
frontend/js/app/nginx/ssl-passthrough/main.ejs
Normal file
23
frontend/js/app/nginx/ssl-passthrough/main.ejs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<div class="card">
|
||||||
|
<div class="card-status bg-dark"></div>
|
||||||
|
<div class="card-header">
|
||||||
|
<h3 class="card-title"><%- i18n('ssl-passthrough-hosts', 'title') %></h3>
|
||||||
|
<div class="card-options">
|
||||||
|
<a href="#" class="btn btn-outline-secondary btn-sm ml-2 help"><i class="fe fe-help-circle"></i></a>
|
||||||
|
<% if (showAddButton) { %>
|
||||||
|
<a href="#" class="btn btn-outline-dark btn-sm ml-2 add-item"><%- i18n('ssl-passthrough-hosts', 'add') %></a>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-body no-padding min-100">
|
||||||
|
<div id="ssl-passthrough-disabled-info" class="alert alert-danger rounded-0 mb-0">
|
||||||
|
<%= i18n('ssl-passthrough-hosts', 'is-disabled-warning', {url: 'https://nginxproxymanager.com/advanced-config/#ssl-passthrough'}) %>
|
||||||
|
</div>
|
||||||
|
<div class="dimmer active">
|
||||||
|
<div class="loader"></div>
|
||||||
|
<div class="dimmer-content list-region">
|
||||||
|
<!-- List Region -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
91
frontend/js/app/nginx/ssl-passthrough/main.js
Normal file
91
frontend/js/app/nginx/ssl-passthrough/main.js
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
const Mn = require('backbone.marionette');
|
||||||
|
const App = require('../../main');
|
||||||
|
const SslPassthroughModel = require('../../../models/ssl-passthrough-host');
|
||||||
|
const ListView = require('./list/main');
|
||||||
|
const ErrorView = require('../../error/main');
|
||||||
|
const EmptyView = require('../../empty/main');
|
||||||
|
const template = require('./main.ejs');
|
||||||
|
|
||||||
|
module.exports = Mn.View.extend({
|
||||||
|
id: 'nginx-ssl-passthrough',
|
||||||
|
template: template,
|
||||||
|
|
||||||
|
ui: {
|
||||||
|
list_region: '.list-region',
|
||||||
|
add: '.add-item',
|
||||||
|
help: '.help',
|
||||||
|
dimmer: '.dimmer',
|
||||||
|
disabled_info: '#ssl-passthrough-disabled-info'
|
||||||
|
},
|
||||||
|
|
||||||
|
regions: {
|
||||||
|
list_region: '@ui.list_region'
|
||||||
|
},
|
||||||
|
|
||||||
|
events: {
|
||||||
|
'click @ui.add': function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
App.Controller.showNginxSslPassthroughForm();
|
||||||
|
},
|
||||||
|
|
||||||
|
'click @ui.help': function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
App.Controller.showHelp(App.i18n('ssl-passthrough-hosts', 'help-title'), App.i18n('ssl-passthrough-hosts', 'help-content'));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
templateContext: {
|
||||||
|
showAddButton: App.Cache.User.canManage('ssl_passthrough_hosts')
|
||||||
|
},
|
||||||
|
|
||||||
|
onRender: function () {
|
||||||
|
let view = this;
|
||||||
|
view.ui.disabled_info.hide();
|
||||||
|
|
||||||
|
App.Api.Nginx.SslPassthroughHosts.getFeatureEnabled().then((response) => {
|
||||||
|
if (response.ssl_passthrough_enabled === false) {
|
||||||
|
view.ui.disabled_info.show();
|
||||||
|
} else {
|
||||||
|
view.ui.disabled_info.hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
App.Api.Nginx.SslPassthroughHosts.getAll(['owner'])
|
||||||
|
.then(response => {
|
||||||
|
if (!view.isDestroyed()) {
|
||||||
|
if (response && response.length) {
|
||||||
|
view.showChildView('list_region', new ListView({
|
||||||
|
collection: new SslPassthroughModel.Collection(response)
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
let manage = App.Cache.User.canManage('ssl_passthrough_hosts');
|
||||||
|
|
||||||
|
view.showChildView('list_region', new EmptyView({
|
||||||
|
title: App.i18n('ssl-passthrough-hosts', 'empty'),
|
||||||
|
subtitle: App.i18n('all-hosts', 'empty-subtitle', {manage: manage}),
|
||||||
|
link: manage ? App.i18n('ssl-passthrough-hosts', 'add') : null,
|
||||||
|
btn_color: 'dark',
|
||||||
|
permission: 'ssl-passthrough-hosts',
|
||||||
|
action: function () {
|
||||||
|
App.Controller.showNginxSslPassthroughForm();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
view.showChildView('list_region', new ErrorView({
|
||||||
|
code: err.code,
|
||||||
|
message: err.message,
|
||||||
|
retry: function () {
|
||||||
|
App.Controller.showNginxSslPassthrough();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
console.error(err);
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
view.ui.dimmer.removeClass('active');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
@ -14,8 +14,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-sm-8 col-md-8">
|
<div class="col-sm-8 col-md-8">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label"><%- i18n('streams', 'forward-ip') %><span class="form-required">*</span></label>
|
<label class="form-label"><%- i18n('streams', 'forwarding-host') %><span class="form-required">*</span></label>
|
||||||
<input type="text" name="forward_ip" class="form-control text-monospace" placeholder="000.000.000.000" value="<%- forward_ip %>" autocomplete="off" maxlength="15" required>
|
<input type="text" name="forwarding_host" class="form-control text-monospace" placeholder="example.com or 10.0.0.1 or 2001:db8:3333:4444:5555:6666:7777:8888" value="<%- forwarding_host %>" autocomplete="off" maxlength="255" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-4 col-md-4">
|
<div class="col-sm-4 col-md-4">
|
||||||
|
@ -13,7 +13,7 @@ module.exports = Mn.View.extend({
|
|||||||
|
|
||||||
ui: {
|
ui: {
|
||||||
form: 'form',
|
form: 'form',
|
||||||
forward_ip: 'input[name="forward_ip"]',
|
forwarding_host: 'input[name="forwarding_host"]',
|
||||||
type_error: '.forward-type-error',
|
type_error: '.forward-type-error',
|
||||||
buttons: '.modal-footer button',
|
buttons: '.modal-footer button',
|
||||||
switches: '.custom-switch-input',
|
switches: '.custom-switch-input',
|
||||||
@ -76,13 +76,6 @@ module.exports = Mn.View.extend({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onRender: function () {
|
|
||||||
this.ui.forward_ip.mask('099.099.099.099', {
|
|
||||||
clearIfNotMatch: true,
|
|
||||||
placeholder: '000.000.000.000'
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
initialize: function (options) {
|
initialize: function (options) {
|
||||||
if (typeof options.model === 'undefined' || !options.model) {
|
if (typeof options.model === 'undefined' || !options.model) {
|
||||||
this.model = new StreamModel.Model();
|
this.model = new StreamModel.Model();
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="text-monospace"><%- forward_ip %>:<%- forwarding_port %></div>
|
<div class="text-monospace"><%- forwarding_host %>:<%- forwarding_port %></div>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div>
|
<div>
|
||||||
@ -42,6 +42,7 @@
|
|||||||
<div class="item-action dropdown">
|
<div class="item-action dropdown">
|
||||||
<a href="#" data-toggle="dropdown" class="icon"><i class="fe fe-more-vertical"></i></a>
|
<a href="#" data-toggle="dropdown" class="icon"><i class="fe fe-more-vertical"></i></a>
|
||||||
<div class="dropdown-menu dropdown-menu-right">
|
<div class="dropdown-menu dropdown-menu-right">
|
||||||
|
<span class="dropdown-header"><%- i18n('audit-log', 'stream') %> #<%- id %></span>
|
||||||
<a href="#" class="edit dropdown-item"><i class="dropdown-icon fe fe-edit"></i> <%- i18n('str', 'edit') %></a>
|
<a href="#" class="edit dropdown-item"><i class="dropdown-icon fe fe-edit"></i> <%- i18n('str', 'edit') %></a>
|
||||||
<a href="#" class="able dropdown-item"><i class="dropdown-icon fe fe-power"></i> <%- i18n('str', enabled ? 'disable' : 'enable') %></a>
|
<a href="#" class="able dropdown-item"><i class="dropdown-icon fe fe-power"></i> <%- i18n('str', enabled ? 'disable' : 'enable') %></a>
|
||||||
<div class="dropdown-divider"></div>
|
<div class="dropdown-divider"></div>
|
||||||
|
@ -9,6 +9,7 @@ module.exports = AppRouter.default.extend({
|
|||||||
'nginx/proxy': 'showNginxProxy',
|
'nginx/proxy': 'showNginxProxy',
|
||||||
'nginx/redirection': 'showNginxRedirection',
|
'nginx/redirection': 'showNginxRedirection',
|
||||||
'nginx/404': 'showNginxDead',
|
'nginx/404': 'showNginxDead',
|
||||||
|
'nginx/ssl-passthrough': 'showNginxSslPassthrough',
|
||||||
'nginx/stream': 'showNginxStream',
|
'nginx/stream': 'showNginxStream',
|
||||||
'nginx/access': 'showNginxAccess',
|
'nginx/access': 'showNginxAccess',
|
||||||
'nginx/certificates': 'showNginxCertificates',
|
'nginx/certificates': 'showNginxCertificates',
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user