Compare commits

..

7 Commits

Author SHA1 Message Date
Julian Reinhardt
70163a66fb Fixes migration 2021-10-25 13:08:35 +02:00
chaptergy
6e82161987 Adds comments to docker compose dev 2021-10-12 15:55:28 +02:00
chaptergy
f650137c84 Fixes eslint errors 2021-10-12 15:42:22 +02:00
chaptergy
02d3093d88 Finalizes SSL Passthrough hosts 2021-10-12 15:25:46 +02:00
chaptergy
ab026e5e18 Merge branch 'develop' 2021-10-12 13:44:50 +02:00
chaptergy
5a2548c89d WIP: complete control of new passthrough host type 2021-10-10 23:49:57 +02:00
chaptergy
5b1f0cead1 WIP: started adding new host type ssl passthrough 2021-10-10 23:49:07 +02:00
103 changed files with 3439 additions and 1569 deletions

View File

@ -1 +1 @@
2.9.19 2.9.9

5
Jenkinsfile vendored
View File

@ -62,13 +62,12 @@ pipeline {
stage('Backend') { stage('Backend') {
steps { steps {
echo 'Checking Syntax ...' echo 'Checking Syntax ...'
sh 'docker pull nginxproxymanager/nginx-full:certbot-node'
// See: https://github.com/yarnpkg/yarn/issues/3254 // See: https://github.com/yarnpkg/yarn/issues/3254
sh '''docker run --rm \\ sh '''docker run --rm \\
-v "$(pwd)/backend:/app" \\ -v "$(pwd)/backend:/app" \\
-v "$(pwd)/global:/app/global" \\ -v "$(pwd)/global:/app/global" \\
-w /app \\ -w /app \\
nginxproxymanager/nginx-full:certbot-node \\ node:latest \\
sh -c "yarn install && yarn eslint . && rm -rf node_modules" sh -c "yarn install && yarn eslint . && rm -rf node_modules"
''' '''
@ -221,7 +220,7 @@ pipeline {
} }
post { post {
always { always {
sh 'docker-compose down --remove-orphans --volumes -t 30' sh 'docker-compose down --rmi all --remove-orphans --volumes -t 30'
sh 'echo Reverting ownership' sh 'echo Reverting ownership'
sh 'docker run --rm -v $(pwd):/data jc21/ci-tools chown -R $(id -u):$(id -g) /data' sh 'docker run --rm -v $(pwd):/data jc21/ci-tools chown -R $(id -u):$(id -g) /data'
} }

432
README.md
View File

@ -1,13 +1,22 @@
<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.9.19-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>
<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/pulls/jc21/nginx-proxy-manager.svg?style=for-the-badge"> <img src="https://img.shields.io/docker/pulls/jc21/nginx-proxy-manager.svg?style=for-the-badge">
</a> </a>
<a href="https://ci.nginxproxymanager.com/blue/organizations/jenkins/nginx-proxy-manager/branches/">
<img src="https://img.shields.io/jenkins/build?jobUrl=https%3A%2F%2Fci.nginxproxymanager.com%2Fjob%2Fnginx-proxy-manager%2Fjob%2Fmaster&style=for-the-badge">
</a>
<a href="https://gitter.im/nginx-proxy-manager/community">
<img alt="Gitter" src="https://img.shields.io/gitter/room/nginx-proxy-manager/community?style=for-the-badge">
</a>
<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
@ -65,19 +74,31 @@ services:
- '80:80' - '80:80'
- '81:81' - '81:81'
- '443:443' - '443:443'
environment:
DB_MYSQL_HOST: "db"
DB_MYSQL_PORT: 3306
DB_MYSQL_USER: "npm"
DB_MYSQL_PASSWORD: "npm"
DB_MYSQL_NAME: "npm"
volumes: volumes:
- ./data:/data - ./data:/data
- ./letsencrypt:/etc/letsencrypt - ./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 by running 3. Bring up your stack
```bash ```bash
docker-compose up -d docker-compose up -d
# If using docker-compose-plugin
docker compose up -d
``` ```
4. Log in to the Admin UI 4. Log in to the Admin UI
@ -98,12 +119,395 @@ Immediately after logging in with this default user you will be asked to modify
## Contributors ## Contributors
Special thanks to [all of our contributors](https://github.com/NginxProxyManager/nginx-proxy-manager/graphs/contributors). Special thanks to the following contributors:
<!-- prettier-ignore-start -->
## Getting Support <!-- markdownlint-disable -->
<table>
1. [Found a bug?](https://github.com/NginxProxyManager/nginx-proxy-manager/issues) <tr>
2. [Discussions](https://github.com/NginxProxyManager/nginx-proxy-manager/discussions) <td align="center">
3. [Development Gitter](https://gitter.im/nginx-proxy-manager/community) <a href="https://github.com/Subv">
4. [Reddit](https://reddit.com/r/nginxproxymanager) <img src="https://avatars1.githubusercontent.com/u/357072?s=460&u=d8adcdc91d749ae53e177973ed9b6bb6c4c894a3&v=4" width="80" alt=""/>
<br /><sub><b>Sebastian Valle</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/Indemnity83">
<img src="https://avatars3.githubusercontent.com/u/35218?s=460&u=7082004ff35138157c868d7d9c683ccebfce5968&v=4" width="80" alt=""/>
<br /><sub><b>Kyle Klaus</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/theraw">
<img src="https://avatars1.githubusercontent.com/u/32969774?s=460&u=6b359971e15685fb0359e6a8c065a399b40dc228&v=4" width="80" alt=""/>
<br /><sub><b>ƬHE ЯAW</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/spalger">
<img src="https://avatars2.githubusercontent.com/u/1329312?s=400&u=565223e38f1c052afb4c5dcca3fcf1c63ba17ae7&v=4" width="80" alt=""/>
<br /><sub><b>Spencer</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/Xantios">
<img src="https://avatars3.githubusercontent.com/u/1507836?s=460&v=4" width="80" alt=""/>
<br /><sub><b>Xantios Krugor</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/dpanesso">
<img src="https://avatars2.githubusercontent.com/u/2687121?s=460&v=4" width="80" alt=""/>
<br /><sub><b>David Panesso</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/IronTooch">
<img src="https://avatars3.githubusercontent.com/u/27360514?s=460&u=69bf854a6647c55725f62ecb8d39249c6c0b2602&v=4" width="80" alt=""/>
<br /><sub><b>IronTooch</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center">
<a href="https://github.com/damianog">
<img src="https://avatars1.githubusercontent.com/u/2786682?s=460&u=76c6136fae797abb76b951cd8a246dcaecaf21af&v=4" width="80" alt=""/>
<br /><sub><b>Damiano</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/tfmm">
<img src="https://avatars3.githubusercontent.com/u/6880538?s=460&u=ce0160821cc4aa802df8395200f2d4956a5bc541&v=4" width="80" alt=""/>
<br /><sub><b>Russ</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/margaale">
<img src="https://avatars3.githubusercontent.com/u/20794934?s=460&v=4" width="80" alt=""/>
<br /><sub><b>Marcelo Castagna</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/Steven-Harris">
<img src="https://avatars2.githubusercontent.com/u/7720242?s=460&v=4" width="80" alt=""/>
<br /><sub><b>Steven Harris</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/jlesage">
<img src="https://avatars0.githubusercontent.com/u/1791123?s=460&v=4" width="80" alt=""/>
<br /><sub><b>Jocelyn Le Sage</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/cmer">
<img src="https://avatars0.githubusercontent.com/u/412?s=460&u=67dd8b2e3661bfd6f68ec1eaa5b9821bd8a321cd&v=4" width="80" alt=""/>
<br /><sub><b>Carl Mercier</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/the1ts">
<img src="https://avatars1.githubusercontent.com/u/84956?s=460&v=4" width="80" alt=""/>
<br /><sub><b>Paul Mansfield</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center">
<a href="https://github.com/OhHeyAlan">
<img src="https://avatars0.githubusercontent.com/u/11955126?s=460&u=fbaa5a1a4f73ef8960132c703349bfd037fe2630&v=4" width="80" alt=""/>
<br /><sub><b>OhHeyAlan</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/dogmatic69">
<img src="https://avatars2.githubusercontent.com/u/94674?s=460&u=ca7647de53145c6283b6373ade5dc94ba99347db&v=4" width="80" alt=""/>
<br /><sub><b>Carl Sutton</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/tg44">
<img src="https://avatars0.githubusercontent.com/u/31839?s=460&u=ad32f4cadfef5e5fb09cdfa4b7b7b36a99ba6811&v=4" width="80" alt=""/>
<br /><sub><b>Gergő Törcsvári</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/vrenjith">
<img src="https://avatars3.githubusercontent.com/u/2093241?s=460&u=96ce93a9bebabdd0a60a2dc96cd093a41d5edaba&v=4" width="80" alt=""/>
<br /><sub><b>vrenjith</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/duhruh">
<img src="https://avatars2.githubusercontent.com/u/1133969?s=460&u=c0691e6131ec6d516416c1c6fcedb5034f877bbe&v=4" width="80" alt=""/>
<br /><sub><b>David Rivera</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/jipjan">
<img src="https://avatars2.githubusercontent.com/u/1384618?s=460&v=4" width="80" alt=""/>
<br /><sub><b>Jaap-Jan de Wit</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/jmwebslave">
<img src="https://avatars2.githubusercontent.com/u/6118262?s=460&u=7db409c47135b1e141c366bbb03ed9fae6ac2638&v=4" width="80" alt=""/>
<br /><sub><b>James Morgan</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center">
<a href="https://github.com/chaptergy">
<img src="https://avatars2.githubusercontent.com/u/26956711?s=460&u=7d9adebabb6b4e7af7cb05d98d751087a372304b&v=4" width="80" alt=""/>
<br /><sub><b>chaptergy</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/Philip-Mooney">
<img src="https://avatars0.githubusercontent.com/u/48624631?s=460&v=4" width="80" alt=""/>
<br /><sub><b>Philip Mooney</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/WaterCalm">
<img src="https://avatars1.githubusercontent.com/u/23502129?s=400&v=4" width="80" alt=""/>
<br /><sub><b>WaterCalm</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/lebrou34">
<img src="https://avatars1.githubusercontent.com/u/16373103?s=460&v=4" width="80" alt=""/>
<br /><sub><b>lebrou34</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/lightglitch">
<img src="https://avatars0.githubusercontent.com/u/196953?s=460&v=4" width="80" alt=""/>
<br /><sub><b>Mário Franco</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/klutchell">
<img src="https://avatars3.githubusercontent.com/u/20458272?s=460&v=4" width="80" alt=""/>
<br /><sub><b>Kyle Harding</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/ahgraber">
<img src="https://avatars.githubusercontent.com/u/24922003?s=460&u=8376c9f00af9b6057ba4d2fb03b4f1b20a75277f&v=4" width="80" alt=""/>
<br /><sub><b>Alex Graber</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center">
<a href="https://github.com/MooBaloo">
<img src="https://avatars.githubusercontent.com/u/9493496?s=460&v=4" width="80" alt=""/>
<br /><sub><b>MooBaloo</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/Shuro">
<img src="https://avatars.githubusercontent.com/u/944030?s=460&v=4" width="80" alt=""/>
<br /><sub><b>Shuro</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/lorisbergeron">
<img src="https://avatars.githubusercontent.com/u/51918567?s=460&u=778e4ff284b7d7304450f98421c99f79298371fb&v=4" width="80" alt=""/>
<br /><sub><b>Loris Bergeron</b></sub>
</a>
</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>
</table>
<!-- markdownlint-enable -->
<!-- prettier-ignore-end -->

View File

@ -40,12 +40,13 @@ app.use(function (req, res, next) {
} }
res.set({ res.set({
'X-XSS-Protection': '1; mode=block', 'Strict-Transport-Security': 'includeSubDomains; max-age=631138519; preload',
'X-Content-Type-Options': 'nosniff', 'X-XSS-Protection': '1; mode=block',
'X-Frame-Options': x_frame_options, 'X-Content-Type-Options': 'nosniff',
'Cache-Control': 'no-cache, no-store, max-age=0, must-revalidate', 'X-Frame-Options': x_frame_options,
Pragma: 'no-cache', 'Cache-Control': 'no-cache, no-store, max-age=0, must-revalidate',
Expires: 0 Pragma: 'no-cache',
Expires: 0
}); });
next(); next();
}); });
@ -73,12 +74,10 @@ 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' || process.env.DEBUG) { 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

View File

@ -44,85 +44,84 @@ async function appStart () {
async function createDbConfigFromEnvironment() { async function createDbConfigFromEnvironment() {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const envMysqlHost = process.env.DB_MYSQL_HOST || null; const envMysqlHost = process.env.DB_MYSQL_HOST || null;
const envMysqlPort = process.env.DB_MYSQL_PORT || null; const envMysqlPort = process.env.DB_MYSQL_PORT || null;
const envMysqlUser = process.env.DB_MYSQL_USER || null; const envMysqlUser = process.env.DB_MYSQL_USER || null;
const envMysqlName = process.env.DB_MYSQL_NAME || null; const envMysqlName = process.env.DB_MYSQL_NAME || null;
let envSqliteFile = process.env.DB_SQLITE_FILE || null; const envSqliteFile = process.env.DB_SQLITE_FILE || null;
const fs = require('fs'); if ((envMysqlHost && envMysqlPort && envMysqlUser && envMysqlName) || envSqliteFile) {
const filename = (process.env.NODE_CONFIG_DIR || './config') + '/' + (process.env.NODE_ENV || 'default') + '.json'; const fs = require('fs');
let configData = {}; const filename = (process.env.NODE_CONFIG_DIR || './config') + '/' + (process.env.NODE_ENV || 'default') + '.json';
let configData = {};
try { try {
configData = require(filename); configData = require(filename);
} catch (err) { } catch (err) {
// do nothing // do nothing
} }
if (configData.database && configData.database.engine && !configData.database.fromEnv) { if (configData.database && configData.database.engine && !configData.database.fromEnv) {
logger.info('Manual db configuration already exists, skipping config creation from environment variables'); logger.info('Manual db configuration already exists, skipping config creation from environment variables');
resolve();
return;
}
if ((!envMysqlHost || !envMysqlPort || !envMysqlUser || !envMysqlName) && !envSqliteFile){
envSqliteFile = '/data/database.sqlite';
logger.info(`No valid environment variables for database provided, using default SQLite file '${envSqliteFile}'`);
}
if (envMysqlHost && envMysqlPort && envMysqlUser && envMysqlName) {
const newConfig = {
fromEnv: true,
engine: 'mysql',
host: envMysqlHost,
port: envMysqlPort,
user: envMysqlUser,
password: process.env.DB_MYSQL_PASSWORD,
name: envMysqlName,
};
if (JSON.stringify(configData.database) === JSON.stringify(newConfig)) {
// Config is unchanged, skip overwrite
resolve(); resolve();
return; return;
} }
logger.info('Generating MySQL knex configuration from environment variables'); if (envMysqlHost && envMysqlPort && envMysqlUser && envMysqlName) {
configData.database = newConfig; const newConfig = {
fromEnv: true,
engine: 'mysql',
host: envMysqlHost,
port: envMysqlPort,
user: envMysqlUser,
password: process.env.DB_MYSQL_PASSWORD,
name: envMysqlName,
};
} else { if (JSON.stringify(configData.database) === JSON.stringify(newConfig)) {
const newConfig = { // Config is unchanged, skip overwrite
fromEnv: true, resolve();
engine: 'knex-native', return;
knex: {
client: 'sqlite3',
connection: {
filename: envSqliteFile
},
useNullAsDefault: true
} }
};
if (JSON.stringify(configData.database) === JSON.stringify(newConfig)) {
// Config is unchanged, skip overwrite
resolve();
return;
}
logger.info('Generating SQLite knex configuration'); logger.info('Generating MySQL db configuration from environment variables');
configData.database = newConfig; configData.database = newConfig;
}
// Write config
fs.writeFile(filename, JSON.stringify(configData, null, 2), (err) => {
if (err) {
logger.error('Could not write db config to config file: ' + filename);
reject(err);
} else { } else {
logger.debug('Wrote db configuration to config file: ' + filename); const newConfig = {
resolve(); fromEnv: true,
engine: 'knex-native',
knex: {
client: 'sqlite3',
connection: {
filename: envSqliteFile
},
useNullAsDefault: true
}
};
if (JSON.stringify(configData.database) === JSON.stringify(newConfig)) {
// Config is unchanged, skip overwrite
resolve();
return;
}
logger.info('Generating Sqlite db configuration from environment variables');
configData.database = newConfig;
} }
});
// Write config
fs.writeFile(filename, JSON.stringify(configData, null, 2), (err) => {
if (err) {
logger.error('Could not write db config to config file: ' + filename);
reject(err);
} else {
logger.info('Wrote db configuration to config file: ' + filename);
resolve();
}
});
} else {
resolve();
}
}); });
} }

View File

@ -1,6 +1,5 @@
const _ = require('lodash'); const _ = require('lodash');
const fs = require('fs'); const fs = require('fs');
const https = require('https');
const tempWrite = require('temp-write'); const tempWrite = require('temp-write');
const moment = require('moment'); const moment = require('moment');
const logger = require('../logger').ssl; const logger = require('../logger').ssl;
@ -16,7 +15,6 @@ const letsencryptConfig = '/etc/letsencrypt.ini';
const certbotCommand = 'certbot'; const certbotCommand = 'certbot';
const archiver = require('archiver'); const archiver = require('archiver');
const path = require('path'); const path = require('path');
const { isArray } = require('lodash');
function omissions() { function omissions() {
return ['is_deleted']; return ['is_deleted'];
@ -116,7 +114,7 @@ const internalCertificate = {
data.owner_user_id = access.token.getUserId(1); data.owner_user_id = access.token.getUserId(1);
if (data.provider === 'letsencrypt') { if (data.provider === 'letsencrypt') {
data.nice_name = data.domain_names.join(', '); data.nice_name = data.domain_names.sort().join(', ');
} }
return certificateModel return certificateModel
@ -171,7 +169,6 @@ const internalCertificate = {
// 3. Generate the LE config // 3. Generate the LE config
return internalNginx.generateLetsEncryptRequestConfig(certificate) return internalNginx.generateLetsEncryptRequestConfig(certificate)
.then(internalNginx.reload) .then(internalNginx.reload)
.then(async() => await new Promise((r) => setTimeout(r, 5000)))
.then(() => { .then(() => {
// 4. Request cert // 4. Request cert
return internalCertificate.requestLetsEncryptSsl(certificate); return internalCertificate.requestLetsEncryptSsl(certificate);
@ -388,7 +385,7 @@ const internalCertificate = {
zipFiles(source, out) { zipFiles(source, out) {
const archive = archiver('zip', { zlib: { level: 9 } }); const archive = archiver('zip', { zlib: { level: 9 } });
const stream = fs.createWriteStream(out); const stream = fs.createWriteStream(out);
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
source source
.map((fl) => { .map((fl) => {
@ -399,7 +396,7 @@ const internalCertificate = {
archive archive
.on('error', (err) => reject(err)) .on('error', (err) => reject(err))
.pipe(stream); .pipe(stream);
stream.on('close', () => resolve()); stream.on('close', () => resolve());
archive.finalize(); archive.finalize();
}); });
@ -477,7 +474,7 @@ const internalCertificate = {
// Query is used for searching // Query is used for searching
if (typeof search_query === 'string') { if (typeof search_query === 'string') {
query.where(function () { query.where(function () {
this.where('nice_name', 'like', '%' + search_query + '%'); this.where('name', 'like', '%' + search_query + '%');
}); });
} }
@ -835,7 +832,7 @@ const internalCertificate = {
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(', '));
const cmd = certbotCommand + ' certonly ' + const cmd = certbotCommand + ' certonly --non-interactive ' +
'--config "' + letsencryptConfig + '" ' + '--config "' + letsencryptConfig + '" ' +
'--cert-name "npm-' + certificate.id + '" ' + '--cert-name "npm-' + certificate.id + '" ' +
'--agree-tos ' + '--agree-tos ' +
@ -871,21 +868,13 @@ 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 credentialsLocation = '/etc/letsencrypt/credentials/credentials-' + certificate.id; const credentialsLocation = '/etc/letsencrypt/credentials/credentials-' + certificate.id;
// Escape single quotes and backslashes const credentialsCmd = 'mkdir -p /etc/letsencrypt/credentials 2> /dev/null; echo \'' + certificate.meta.dns_provider_credentials.replace('\'', '\\\'') + '\' > \'' + credentialsLocation + '\' && chmod 600 \'' + credentialsLocation + '\'';
const escapedCredentials = certificate.meta.dns_provider_credentials.replaceAll('\'', '\\\'').replaceAll('\\', '\\\\'); const prepareCmd = 'pip install ' + dns_plugin.package_name + (dns_plugin.version_requirement || '') + ' ' + dns_plugin.dependencies;
const credentialsCmd = 'mkdir -p /etc/letsencrypt/credentials 2> /dev/null; echo \'' + escapedCredentials + '\' > \'' + credentialsLocation + '\' && chmod 600 \'' + credentialsLocation + '\'';
let prepareCmd = 'pip install ' + dns_plugin.package_name + (dns_plugin.version_requirement || '') + ' ' + dns_plugin.dependencies;
// Special case for cloudflare
if (dns_plugin.package_name === 'certbot-dns-cloudflare') {
prepareCmd = 'pip install certbot-dns-cloudflare --index-url https://www.piwheels.org/simple --prefer-binary';
}
// Whether the plugin has a --<name>-credentials argument // Whether the plugin has a --<name>-credentials argument
const hasConfigArg = certificate.meta.dns_provider !== 'route53'; const hasConfigArg = certificate.meta.dns_provider !== 'route53';
let mainCmd = certbotCommand + ' certonly ' + let mainCmd = certbotCommand + ' certonly --non-interactive ' +
'--config "' + letsencryptConfig + '" ' +
'--cert-name "npm-' + certificate.id + '" ' + '--cert-name "npm-' + certificate.id + '" ' +
'--agree-tos ' + '--agree-tos ' +
'--email "' + certificate.meta.letsencrypt_email + '" ' + '--email "' + certificate.meta.letsencrypt_email + '" ' +
@ -980,11 +969,10 @@ 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(', '));
const cmd = certbotCommand + ' renew --force-renewal ' + const cmd = certbotCommand + ' renew --force-renewal --non-interactive ' +
'--config "' + letsencryptConfig + '" ' + '--config "' + letsencryptConfig + '" ' +
'--cert-name "npm-' + certificate.id + '" ' + '--cert-name "npm-' + certificate.id + '" ' +
'--preferred-challenges "dns,http" ' + '--preferred-challenges "dns,http" ' +
'--no-random-sleep-on-renew ' +
'--disable-hook-validation ' + '--disable-hook-validation ' +
(letsencryptStaging ? '--staging' : ''); (letsencryptStaging ? '--staging' : '');
@ -1010,11 +998,9 @@ 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 mainCmd = certbotCommand + ' renew ' + let mainCmd = certbotCommand + ' renew --non-interactive ' +
'--config "' + letsencryptConfig + '" ' +
'--cert-name "npm-' + certificate.id + '" ' + '--cert-name "npm-' + certificate.id + '" ' +
'--disable-hook-validation ' + '--disable-hook-validation' +
'--no-random-sleep-on-renew ' +
(letsencryptStaging ? ' --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
@ -1040,8 +1026,7 @@ 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 mainCmd = certbotCommand + ' revoke ' + const mainCmd = certbotCommand + ' revoke --non-interactive ' +
'--config "' + letsencryptConfig + '" ' +
'--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 ' +
(letsencryptStaging ? '--staging' : ''); (letsencryptStaging ? '--staging' : '');
@ -1134,94 +1119,6 @@ const internalCertificate = {
} else { } else {
return Promise.resolve(); return Promise.resolve();
} }
},
testHttpsChallenge: async (access, domains) => {
await access.can('certificates:list');
if (!isArray(domains)) {
throw new error.InternalValidationError('Domains must be an array of strings');
}
if (domains.length === 0) {
throw new error.InternalValidationError('No domains provided');
}
// Create a test challenge file
const testChallengeDir = '/data/letsencrypt-acme-challenge/.well-known/acme-challenge';
const testChallengeFile = testChallengeDir + '/test-challenge';
fs.mkdirSync(testChallengeDir, {recursive: true});
fs.writeFileSync(testChallengeFile, 'Success', {encoding: 'utf8'});
async function performTestForDomain (domain) {
logger.info('Testing http challenge for ' + domain);
const url = `http://${domain}/.well-known/acme-challenge/test-challenge`;
const formBody = `method=G&url=${encodeURI(url)}&bodytype=T&requestbody=&headername=User-Agent&headervalue=None&locationid=1&ch=false&cc=false`;
const options = {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(formBody)
}
};
const result = await new Promise((resolve) => {
const req = https.request('https://www.site24x7.com/tools/restapi-tester', options, function (res) {
let responseBody = '';
res.on('data', (chunk) => responseBody = responseBody + chunk);
res.on('end', function () {
const parsedBody = JSON.parse(responseBody + '');
if (res.statusCode !== 200) {
logger.warn(`Failed to test HTTP challenge for domain ${domain}`, res);
resolve(undefined);
}
resolve(parsedBody);
});
});
// Make sure to write the request body.
req.write(formBody);
req.end();
req.on('error', function (e) { logger.warn(`Failed to test HTTP challenge for domain ${domain}`, e);
resolve(undefined); });
});
if (!result) {
// Some error occurred while trying to get the data
return 'failed';
} else if (`${result.responsecode}` === '200' && result.htmlresponse === 'Success') {
// Server exists and has responded with the correct data
return 'ok';
} else if (`${result.responsecode}` === '200') {
// Server exists but has responded with wrong data
logger.info(`HTTP challenge test failed for domain ${domain} because of invalid returned data:`, result.htmlresponse);
return 'wrong-data';
} else if (`${result.responsecode}` === '404') {
// Server exists but responded with a 404
logger.info(`HTTP challenge test failed for domain ${domain} because code 404 was returned`);
return '404';
} else if (`${result.responsecode}` === '0' || (typeof result.reason === 'string' && result.reason.toLowerCase() === 'host unavailable')) {
// Server does not exist at domain
logger.info(`HTTP challenge test failed for domain ${domain} the host was not found`);
return 'no-host';
} else {
// Other errors
logger.info(`HTTP challenge test failed for domain ${domain} because code ${result.responsecode} was returned`);
return `other:${result.responsecode}`;
}
}
const results = {};
for (const domain of domains){
results[domain] = await performTestForDomain(domain);
}
// Remove the test challenge file
fs.unlinkSync(testChallengeFile);
return results;
} }
}; };

View File

@ -1,7 +1,8 @@
const _ = require('lodash'); 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);
}
}); });
} }

View File

@ -9,9 +9,6 @@ const CLOUDFRONT_URL = 'https://ip-ranges.amazonaws.com/ip-ranges.json';
const CLOUDFARE_V4_URL = 'https://www.cloudflare.com/ips-v4'; const CLOUDFARE_V4_URL = 'https://www.cloudflare.com/ips-v4';
const CLOUDFARE_V6_URL = 'https://www.cloudflare.com/ips-v6'; const CLOUDFARE_V6_URL = 'https://www.cloudflare.com/ips-v6';
const regIpV4 = /^(\d+\.?){4}\/\d+/;
const regIpV6 = /^(([\da-fA-F]+)?:)+\/\d+/;
const internalIpRanges = { const internalIpRanges = {
interval_timeout: 1000 * 60 * 60 * 6, // 6 hours interval_timeout: 1000 * 60 * 60 * 6, // 6 hours
@ -77,14 +74,14 @@ const internalIpRanges = {
return internalIpRanges.fetchUrl(CLOUDFARE_V4_URL); return internalIpRanges.fetchUrl(CLOUDFARE_V4_URL);
}) })
.then((cloudfare_data) => { .then((cloudfare_data) => {
let items = cloudfare_data.split('\n').filter((line) => regIpV4.test(line)); let items = cloudfare_data.split('\n');
ip_ranges = [... ip_ranges, ... items]; ip_ranges = [... ip_ranges, ... items];
}) })
.then(() => { .then(() => {
return internalIpRanges.fetchUrl(CLOUDFARE_V6_URL); return internalIpRanges.fetchUrl(CLOUDFARE_V6_URL);
}) })
.then((cloudfare_data) => { .then((cloudfare_data) => {
let items = cloudfare_data.split('\n').filter((line) => regIpV6.test(line)); let items = cloudfare_data.split('\n');
ip_ranges = [... ip_ranges, ... items]; ip_ranges = [... ip_ranges, ... items];
}) })
.then(() => { .then(() => {

View File

@ -1,10 +1,11 @@
const _ = require('lodash'); const _ = require('lodash');
const fs = require('fs'); const fs = require('fs');
const logger = require('../logger').nginx; 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 debug_mode = process.env.NODE_ENV !== 'production' || !!process.env.DEBUG; const passthroughHostModel = require('../models/ssl_passthrough_host');
const debug_mode = process.env.NODE_ENV !== 'production' || !!process.env.DEBUG;
const internalNginx = { const internalNginx = {
@ -23,7 +24,8 @@ const internalNginx = {
* @returns {Promise} * @returns {Promise}
*/ */
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(() => {
return internalNginx.generateConfig(host_type, host); 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);
}
}) })
.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';
@ -186,7 +235,7 @@ 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) {
@ -199,72 +248,87 @@ const internalNginx = {
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
if (host_type !== 'default') {
host.use_default_location = true;
if (typeof host.advanced_config !== 'undefined' && host.advanced_config) {
host.use_default_location = !internalNginx.advancedConfigHasDefaultLocation(host.advanced_config);
}
}
if (host.locations) {
//logger.info ('host.locations = ' + JSON.stringify(host.locations, null, 2));
origLocations = [].concat(host.locations);
locationsPromise = internalNginx.renderLocations(host).then((renderedLocations) => {
host.locations = renderedLocations;
});
// Allow someone who is using / custom location path to use it, and skip the default / location
_.map(host.locations, (location) => {
if (location.path === '/') {
host.use_default_location = false;
}
});
// Manipulate the data a bit before sending it to the template
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 { } else {
locationsPromise = Promise.resolve(); internalNginx.deleteConfig(host_type, host, false);
} }
} else if (host_type !== 'default') {
host.use_default_location = true;
if (typeof host.advanced_config !== 'undefined' && host.advanced_config) {
host.use_default_location = !internalNginx.advancedConfigHasDefaultLocation(host.advanced_config);
}
}
// Set the IPv6 setting for the host if (host.locations) {
host.ipv6 = internalNginx.ipv6Enabled(); //logger.info ('host.locations = ' + JSON.stringify(host.locations, null, 2));
origLocations = [].concat(host.locations);
locationsPromise.then(() => { locationsPromise = internalNginx.renderLocations(host).then((renderedLocations) => {
renderEngine host.locations = renderedLocations;
.parseAndRender(template, host)
.then((config_text) => {
fs.writeFileSync(filename, config_text, {encoding: 'utf8'});
if (debug_mode) {
logger.success('Wrote config:', filename, config_text);
}
// Restore locations array
host.locations = origLocations;
resolve(true);
})
.catch((err) => {
if (debug_mode) {
logger.warn('Could not write ' + filename + ':', err.message);
}
reject(new error.ConfigurationError(err.message));
});
}); });
// Allow someone who is using / custom location path to use it, and skip the default / location
_.map(host.locations, (location) => {
if (location.path === '/') {
host.use_default_location = false;
}
});
} else {
locationsPromise = Promise.resolve();
}
// Set the IPv6 setting for the host
host.ipv6 = internalNginx.ipv6Enabled();
return locationsPromise.then(() => {
renderEngine
.parseAndRender(template, host)
.then((config_text) => {
fs.writeFileSync(filename, config_text, {encoding: 'utf8'});
if (debug_mode) {
logger.success('Wrote config:', filename, config_text);
}
// Restore locations array
host.locations = origLocations;
return true;
})
.catch((err) => {
if (debug_mode) {
logger.warn('Could not write ' + filename + ':', err.message);
}
throw new error.ConfigurationError(err.message);
});
}); });
}, },
@ -429,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;
} }
}; };

View 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;

View File

@ -62,14 +62,15 @@ const internalUser = {
return userPermissionModel return userPermissionModel
.query() .query()
.insert({ .insert({
user_id: user.id, user_id: user.id,
visibility: is_admin ? 'all' : 'user', visibility: is_admin ? 'all' : 'user',
proxy_hosts: 'manage', proxy_hosts: 'manage',
redirection_hosts: 'manage', redirection_hosts: 'manage',
dead_hosts: 'manage', dead_hosts: 'manage',
streams: 'manage', ssl_passthrough_hosts: 'manage',
access_lists: 'manage', streams: 'manage',
certificates: 'manage' access_lists: 'manage',
certificates: 'manage'
}) })
.then(() => { .then(() => {
return internalUser.get(access, {id: user.id, expand: ['permissions']}); return internalUser.get(access, {id: user.id, expand: ['permissions']});

View 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"]
}
}
}
}
]
}

View 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"]
}
}
}
}
]
}

View 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"]
}
}
}
}
]
}

View 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"]
}
}
}
}
]
}

View 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"]
}
}
}
}
]
}

View 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');
});
};

View File

@ -1,50 +0,0 @@
const migrate_name = 'stream_domain';
const logger = require('../logger').migrate;
const internalNginx = require('../internal/nginx');
async function regenerateDefaultHost(knex) {
const row = await knex('setting').select('*').where('id', 'default-site').first();
if (!row) {
return Promise.resolve();
}
return internalNginx.deleteConfig('default')
.then(() => {
return internalNginx.generateConfig('default', row);
})
.then(() => {
return internalNginx.test();
})
.then(() => {
return internalNginx.reload();
});
}
/**
* Migrate
*
* @see http://knexjs.org/#Schema
*
* @param {Object} knex
* @param {Promise} Promise
* @returns {Promise}
*/
exports.up = function (knex) {
logger.info('[' + migrate_name + '] Migrating Up...');
return regenerateDefaultHost(knex);
};
/**
* Undo Migrate
*
* @param {Object} knex
* @param {Promise} Promise
* @returns {Promise}
*/
exports.down = function (knex) {
logger.info('[' + migrate_name + '] Migrating Down...');
return regenerateDefaultHost(knex);
};

View 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;

View File

@ -11,23 +11,28 @@
"body-parser": "^1.19.0", "body-parser": "^1.19.0",
"compression": "^1.7.4", "compression": "^1.7.4",
"config": "^3.3.1", "config": "^3.3.1",
"diskdb": "^0.1.17",
"express": "^4.17.1", "express": "^4.17.1",
"express-fileupload": "^1.1.9", "express-fileupload": "^1.1.9",
"gravatar": "^1.8.0", "gravatar": "^1.8.0",
"html-entities": "^1.2.1",
"json-schema-ref-parser": "^8.0.0", "json-schema-ref-parser": "^8.0.0",
"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.21", "lodash": "^4.17.21",
"moment": "^2.29.4", "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.2.16", "objection": "^2.2.16",
"path": "^0.12.7", "path": "^0.12.7",
"pg": "^7.12.1",
"restler": "^3.4.0",
"signale": "^1.4.0", "signale": "^1.4.0",
"sqlite3": "^4.1.1", "sqlite3": "^4.1.1",
"temp-write": "^4.0.0" "temp-write": "^4.0.0",
"unix-timestamp": "^0.2.0"
}, },
"signale": { "signale": {
"displayDate": true, "displayDate": true,

View File

@ -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
* *

View File

@ -68,32 +68,6 @@ router
.catch(next); .catch(next);
}); });
/**
* Test HTTP challenge for domains
*
* /api/nginx/certificates/test-http
*/
router
.route('/test-http')
.options((req, res) => {
res.sendStatus(204);
})
.all(jwtdecode())
/**
* GET /api/nginx/certificates/test-http
*
* Test HTTP challenge for domains
*/
.get((req, res, next) => {
internalCertificate.testHttpsChallenge(res.locals.access, JSON.parse(req.query.domains))
.then((result) => {
res.status(200)
.send(result);
})
.catch(next);
});
/** /**
* Specific certificate * Specific certificate
* *
@ -235,6 +209,7 @@ router
.catch(next); .catch(next);
}); });
/** /**
* Download LE Certs * Download LE Certs
* *

View 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;

View File

@ -157,17 +157,6 @@
"targetSchema": { "targetSchema": {
"type": "boolean" "type": "boolean"
} }
},
{
"title": "Test HTTP Challenge",
"description": "Tests whether the HTTP challenge should work",
"href": "/nginx/certificates/{definitions.identity.example}/test-http",
"access": "private",
"method": "GET",
"rel": "info",
"http_header": {
"$ref": "../examples.json#/definitions/auth_header"
}
} }
] ]
} }

View 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"
}
}
]
}

View File

@ -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"
}, },

View File

@ -1,15 +1,17 @@
const fs = require('fs'); const fs = require('fs');
const NodeRSA = require('node-rsa'); const NodeRSA = require('node-rsa');
const config = require('config'); const config = require('config');
const logger = require('./logger').setup; const logger = require('./logger').setup;
const certificateModel = require('./models/certificate'); const certificateModel = require('./models/certificate');
const userModel = require('./models/user'); const userModel = require('./models/user');
const userPermissionModel = require('./models/user_permission'); 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 dns_plugins = require('./global/certbot-dns-plugins'); const passthroughHostModel = require('./models/ssl_passthrough_host');
const debug_mode = process.env.NODE_ENV !== 'production' || !!process.env.DEBUG; const dns_plugins = require('./global/certbot-dns-plugins');
const internalNginx = require('./internal/nginx');
const debug_mode = process.env.NODE_ENV !== 'production' || !!process.env.DEBUG;
/** /**
* Creates a new JWT RSA Keypair if not alread set on the config * Creates a new JWT RSA Keypair if not alread set on the config
@ -105,14 +107,15 @@ const setupDefaultUser = () => {
}) })
.then(() => { .then(() => {
return userPermissionModel.query().insert({ return userPermissionModel.query().insert({
user_id: user.id, user_id: user.id,
visibility: 'all', visibility: 'all',
proxy_hosts: 'manage', proxy_hosts: 'manage',
redirection_hosts: 'manage', redirection_hosts: 'manage',
dead_hosts: 'manage', dead_hosts: 'manage',
streams: 'manage', ssl_passthrough_hosts: 'manage',
access_lists: 'manage', streams: 'manage',
certificates: 'manage', access_lists: 'manage',
certificates: 'manage',
}); });
}); });
}) })
@ -169,26 +172,19 @@ const setupCertbotPlugins = () => {
.andWhere('provider', 'letsencrypt') .andWhere('provider', 'letsencrypt')
.then((certificates) => { .then((certificates) => {
if (certificates && certificates.length) { if (certificates && certificates.length) {
let plugins = []; let plugins = [];
let promises = []; let promises = [];
let install_cloudflare_plugin = false;
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.version_requirement || ''} ${dns_plugin.dependencies}`;
if (dns_plugin.package_name === 'certbot-dns-cloudflare') { if (plugins.indexOf(packages_to_install) === -1) plugins.push(packages_to_install);
install_cloudflare_plugin = true;
} else {
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);
}
// Make sure credentials file exists // Make sure credentials file exists
const credentials_loc = '/etc/letsencrypt/credentials/credentials-' + certificate.id; const credentials_loc = '/etc/letsencrypt/credentials/credentials-' + certificate.id;
// Escape single quotes and backslashes const credentials_cmd = '[ -f \'' + credentials_loc + '\' ] || { mkdir -p /etc/letsencrypt/credentials 2> /dev/null; echo \'' + certificate.meta.dns_provider_credentials.replace('\'', '\\\'') + '\' > \'' + credentials_loc + '\' && chmod 600 \'' + credentials_loc + '\'; }';
const escapedCredentials = certificate.meta.dns_provider_credentials.replaceAll('\'', '\\\'').replaceAll('\\', '\\\\');
const credentials_cmd = '[ -f \'' + credentials_loc + '\' ] || { mkdir -p /etc/letsencrypt/credentials 2> /dev/null; echo \'' + escapedCredentials + '\' > \'' + credentials_loc + '\' && chmod 600 \'' + credentials_loc + '\'; }';
promises.push(utils.exec(credentials_cmd)); promises.push(utils.exec(credentials_cmd));
} }
}); });
@ -198,14 +194,10 @@ const setupCertbotPlugins = () => {
promises.push(utils.exec(install_cmd)); promises.push(utils.exec(install_cmd));
} }
if (install_cloudflare_plugin) {
promises.push(utils.exec('pip install certbot-dns-cloudflare --index-url https://www.piwheels.org/simple --prefer-binary'));
}
if (promises.length) { if (promises.length) {
return Promise.all(promises) return Promise.all(promises)
.then(() => { .then(() => {
logger.info('Added Certbot plugins ' + plugins.join(', ')); logger.info('Added Certbot plugins ' + plugins.join(', '));
}); });
} }
} }
@ -233,10 +225,19 @@ const setupLogrotation = () => {
return runLogrotate(); 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(setupLogrotation)
.then(setupSslPassthrough);
}; };

View File

@ -1,10 +1,11 @@
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_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_pass {{ forward_scheme }}://{{ forward_host }}:{{ forward_port }}{{ forward_path }}; proxy_pass $upstream;
{% if access_list_id > 0 %} {% if access_list_id > 0 %}
{% if access_list.items.length > 0 %} {% if access_list.items.length > 0 %}

View File

@ -7,9 +7,9 @@
server { server {
listen 80 default; listen 80 default;
{% if ipv6 -%} {% if ipv6 -%}
listen [::]:80 default; listen [::]:80;
{% else -%} {% else -%}
#listen [::]:80 default; #listen [::]:80;
{% endif %} {% endif %}
server_name default-host.localhost; server_name default-host.localhost;
access_log /data/logs/default-host_access.log combined; access_log /data/logs/default-host_access.log combined;

View 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;
}

View File

@ -136,6 +136,11 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0:
"@types/color-name" "^1.1.1" "@types/color-name" "^1.1.1"
color-convert "^2.0.1" color-convert "^2.0.1"
ansi-styles@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-1.0.0.tgz#cb102df1c56f5123eab8b67cd7b98027a0279178"
integrity sha1-yxAt8cVvUSPquLZ817mAJ6AnkXg=
anymatch@~3.1.1: anymatch@~3.1.1:
version "3.1.1" version "3.1.1"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142"
@ -246,9 +251,9 @@ astral-regex@^1.0.0:
integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==
async@^3.2.0: async@^3.2.0:
version "3.2.4" version "3.2.1"
resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" resolved "https://registry.yarnpkg.com/async/-/async-3.2.1.tgz#d3274ec66d107a47476a4c49136aacdb00665fc8"
integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== integrity sha512-XdD5lRO/87udXCMC9meWdYiR+Nq6ZjUfXidViUZGu2F1MO4T3XwZ1et0hb2++BgLfhyJwy44BGB/yx80ABx8hg==
atob@^2.1.2: atob@^2.1.2:
version "2.1.2" version "2.1.2"
@ -386,6 +391,11 @@ buffer-equal-constant-time@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"
integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk= integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=
buffer-writer@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/buffer-writer/-/buffer-writer-2.0.0.tgz#ce7eb81a38f7829db09c873f2fbb792c0c98ec04"
integrity sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==
buffer@^5.5.0: buffer@^5.5.0:
version "5.7.1" version "5.7.1"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
@ -454,6 +464,15 @@ camelcase@^5.0.0, camelcase@^5.3.1:
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
chalk@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-0.4.0.tgz#5199a3ddcd0c1efe23bc08c1b027b06176e0c64f"
integrity sha1-UZmj3c0MHv4jvAjBsCewYXbgxk8=
dependencies:
ansi-styles "~1.0.0"
has-color "~0.1.0"
strip-ansi "~0.1.0"
chalk@^2.0.0, chalk@^2.1.0, chalk@^2.3.2: chalk@^2.0.0, chalk@^2.1.0, chalk@^2.3.2:
version "2.4.2" version "2.4.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
@ -839,6 +858,15 @@ dicer@0.3.0:
dependencies: dependencies:
streamsearch "0.1.2" streamsearch "0.1.2"
diskdb@^0.1.17:
version "0.1.17"
resolved "https://registry.yarnpkg.com/diskdb/-/diskdb-0.1.17.tgz#8abd095196b33b406791f1494b6b13b4422240c4"
integrity sha1-ir0JUZazO0BnkfFJS2sTtEIiQMQ=
dependencies:
chalk "^0.4.0"
merge "^1.1.3"
node-uuid "^1.4.1"
doctrine@^3.0.0: doctrine@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961"
@ -1473,6 +1501,11 @@ gravatar@^1.8.0:
querystring "0.2.0" querystring "0.2.0"
yargs "^15.4.1" yargs "^15.4.1"
has-color@~0.1.0:
version "0.1.7"
resolved "https://registry.yarnpkg.com/has-color/-/has-color-0.1.7.tgz#67144a5260c34fc3cca677d041daf52fe7b78b2f"
integrity sha1-ZxRKUmDDT8PMpnfQQdr1L+e3iy8=
has-flag@^3.0.0: has-flag@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
@ -1531,6 +1564,11 @@ homedir-polyfill@^1.0.1:
dependencies: dependencies:
parse-passwd "^1.0.0" parse-passwd "^1.0.0"
html-entities@^1.2.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.3.1.tgz#fb9a1a4b5b14c5daba82d3e34c6ae4fe701a0e44"
integrity sha512-rhE/4Z3hIhzHAUKbW8jVcCyuT5oJCXXqhN/6mXXVCpzTmvJnoH2HL/bt3EZ6p55jbFJBeAe1ZNpL5BugLujxNA==
http-cache-semantics@^4.0.0: http-cache-semantics@^4.0.0:
version "4.1.0" version "4.1.0"
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390"
@ -1558,6 +1596,11 @@ http-errors@~1.7.2:
statuses ">= 1.5.0 < 2" statuses ">= 1.5.0 < 2"
toidentifier "1.0.0" toidentifier "1.0.0"
iconv-lite@0.2.11:
version "0.2.11"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.2.11.tgz#1ce60a3a57864a292d1321ff4609ca4bb965adc8"
integrity sha1-HOYKOleGSiktEyH/RgnKS7llrcg=
iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4: iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4:
version "0.4.24" version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
@ -2178,6 +2221,11 @@ merge-descriptors@1.0.1:
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=
merge@^1.1.3:
version "1.2.1"
resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.1.tgz#38bebf80c3220a8a487b6fcfb3941bb11720c145"
integrity sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ==
methods@~1.1.2: methods@~1.1.2:
version "1.1.2" version "1.1.2"
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
@ -2271,10 +2319,10 @@ mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5:
dependencies: dependencies:
minimist "^1.2.5" minimist "^1.2.5"
moment@^2.29.4: moment@^2.24.0:
version "2.29.4" version "2.27.0"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108" resolved "https://registry.yarnpkg.com/moment/-/moment-2.27.0.tgz#8bff4e3e26a236220dfe3e36de756b6ebaa0105d"
integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w== integrity sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ==
ms@2.0.0: ms@2.0.0:
version "2.0.0" version "2.0.0"
@ -2396,6 +2444,11 @@ node-rsa@^1.0.8:
dependencies: dependencies:
asn1 "^0.2.4" asn1 "^0.2.4"
node-uuid@^1.4.1:
version "1.4.8"
resolved "https://registry.yarnpkg.com/node-uuid/-/node-uuid-1.4.8.tgz#b040eb0923968afabf8d32fb1f17f1167fdab907"
integrity sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=
nodemon@^2.0.2: nodemon@^2.0.2:
version "2.0.4" version "2.0.4"
resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.4.tgz#55b09319eb488d6394aa9818148c0c2d1c04c416" resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.4.tgz#55b09319eb488d6394aa9818148c0c2d1c04c416"
@ -2636,6 +2689,11 @@ package-json@^6.3.0:
registry-url "^5.0.0" registry-url "^5.0.0"
semver "^6.2.0" semver "^6.2.0"
packet-reader@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/packet-reader/-/packet-reader-1.0.0.tgz#9238e5480dedabacfe1fe3f2771063f164157d74"
integrity sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==
parent-module@^1.0.0: parent-module@^1.0.0:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
@ -2725,11 +2783,63 @@ path@^0.12.7:
process "^0.11.1" process "^0.11.1"
util "^0.10.3" util "^0.10.3"
pg-connection-string@0.1.3:
version "0.1.3"
resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-0.1.3.tgz#da1847b20940e42ee1492beaf65d49d91b245df7"
integrity sha1-2hhHsglA5C7hSSvq9l1J2RskXfc=
pg-connection-string@2.1.0: pg-connection-string@2.1.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.1.0.tgz#e07258f280476540b24818ebb5dca29e101ca502" resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.1.0.tgz#e07258f280476540b24818ebb5dca29e101ca502"
integrity sha512-bhlV7Eq09JrRIvo1eKngpwuqKtJnNhZdpdOlvrPrA4dxqXPjxSrbNrfnIDmTpwMyRszrcV4kU5ZA4mMsQUrjdg== integrity sha512-bhlV7Eq09JrRIvo1eKngpwuqKtJnNhZdpdOlvrPrA4dxqXPjxSrbNrfnIDmTpwMyRszrcV4kU5ZA4mMsQUrjdg==
pg-int8@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/pg-int8/-/pg-int8-1.0.1.tgz#943bd463bf5b71b4170115f80f8efc9a0c0eb78c"
integrity sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==
pg-packet-stream@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/pg-packet-stream/-/pg-packet-stream-1.1.0.tgz#e45c3ae678b901a2873af1e17b92d787962ef914"
integrity sha512-kRBH0tDIW/8lfnnOyTwKD23ygJ/kexQVXZs7gEyBljw4FYqimZFxnMMx50ndZ8In77QgfGuItS5LLclC2TtjYg==
pg-pool@^2.0.10:
version "2.0.10"
resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-2.0.10.tgz#842ee23b04e86824ce9d786430f8365082d81c4a"
integrity sha512-qdwzY92bHf3nwzIUcj+zJ0Qo5lpG/YxchahxIN8+ZVmXqkahKXsnl2aiJPHLYN9o5mB/leG+Xh6XKxtP7e0sjg==
pg-types@^2.1.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/pg-types/-/pg-types-2.2.0.tgz#2d0250d636454f7cfa3b6ae0382fdfa8063254a3"
integrity sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==
dependencies:
pg-int8 "1.0.1"
postgres-array "~2.0.0"
postgres-bytea "~1.0.0"
postgres-date "~1.0.4"
postgres-interval "^1.1.0"
pg@^7.12.1:
version "7.18.2"
resolved "https://registry.yarnpkg.com/pg/-/pg-7.18.2.tgz#4e219f05a00aff4db6aab1ba02f28ffa4513b0bb"
integrity sha512-Mvt0dGYMwvEADNKy5PMQGlzPudKcKKzJds/VbOeZJpb6f/pI3mmoXX0JksPgI3l3JPP/2Apq7F36O63J7mgveA==
dependencies:
buffer-writer "2.0.0"
packet-reader "1.0.0"
pg-connection-string "0.1.3"
pg-packet-stream "^1.1.0"
pg-pool "^2.0.10"
pg-types "^2.1.0"
pgpass "1.x"
semver "4.3.2"
pgpass@1.x:
version "1.0.2"
resolved "https://registry.yarnpkg.com/pgpass/-/pgpass-1.0.2.tgz#2a7bb41b6065b67907e91da1b07c1847c877b306"
integrity sha1-Knu0G2BltnkH6R2hsHwYR8h3swY=
dependencies:
split "^1.0.0"
picomatch@^2.0.4, picomatch@^2.2.1: picomatch@^2.0.4, picomatch@^2.2.1:
version "2.2.2" version "2.2.2"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad"
@ -2753,6 +2863,28 @@ posix-character-classes@^0.1.0:
resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=
postgres-array@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/postgres-array/-/postgres-array-2.0.0.tgz#48f8fce054fbc69671999329b8834b772652d82e"
integrity sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==
postgres-bytea@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/postgres-bytea/-/postgres-bytea-1.0.0.tgz#027b533c0aa890e26d172d47cf9ccecc521acd35"
integrity sha1-AntTPAqokOJtFy1Hz5zOzFIazTU=
postgres-date@~1.0.4:
version "1.0.6"
resolved "https://registry.yarnpkg.com/postgres-date/-/postgres-date-1.0.6.tgz#4925e8085b30c2ba1a06ac91b9a3473954a2ce2d"
integrity sha512-o2a4gxeFcox+CgB3Ig/kNHBP23PiEXHCXx7pcIIsvzoNz4qv+lKTyiSkjOXIMNUl12MO/mOYl2K6wR9X5K6Plg==
postgres-interval@^1.1.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/postgres-interval/-/postgres-interval-1.2.0.tgz#b460c82cb1587507788819a06aa0fffdb3544695"
integrity sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==
dependencies:
xtend "^4.0.0"
prelude-ls@~1.1.2: prelude-ls@~1.1.2:
version "1.1.2" version "1.1.2"
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
@ -2821,6 +2953,11 @@ pupa@^2.0.1:
dependencies: dependencies:
escape-goat "^2.0.0" escape-goat "^2.0.0"
qs@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-1.2.0.tgz#ed079be28682147e6fd9a34cc2b0c1e0ec6453ee"
integrity sha1-7Qeb4oaCFH5v2aNMwrDB4OxkU+4=
qs@6.7.0: qs@6.7.0:
version "6.7.0" version "6.7.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
@ -2978,6 +3115,16 @@ responselike@^1.0.2:
dependencies: dependencies:
lowercase-keys "^1.0.0" lowercase-keys "^1.0.0"
restler@^3.4.0:
version "3.4.0"
resolved "https://registry.yarnpkg.com/restler/-/restler-3.4.0.tgz#741ec0b3d16b949feea2813d0c3c68529e888d9b"
integrity sha1-dB7As9FrlJ/uooE9DDxoUp6IjZs=
dependencies:
iconv-lite "0.2.11"
qs "1.2.0"
xml2js "0.4.0"
yaml "0.2.3"
restore-cursor@^3.1.0: restore-cursor@^3.1.0:
version "3.1.0" version "3.1.0"
resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e"
@ -3039,6 +3186,11 @@ safe-regex@^1.1.0:
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
sax@0.5.x:
version "0.5.8"
resolved "https://registry.yarnpkg.com/sax/-/sax-0.5.8.tgz#d472db228eb331c2506b0e8c15524adb939d12c1"
integrity sha1-1HLbIo6zMcJQaw6MFVJK25OdEsE=
sax@^1.2.4: sax@^1.2.4:
version "1.2.4" version "1.2.4"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
@ -3051,6 +3203,11 @@ semver-diff@^3.1.1:
dependencies: dependencies:
semver "^6.3.0" semver "^6.3.0"
semver@4.3.2:
version "4.3.2"
resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.2.tgz#c7a07158a80bedd052355b770d82d6640f803be7"
integrity sha1-x6BxWKgL7dBSNVt3DYLWZA+AO+c=
semver@^5.3.0, semver@^5.5.0, semver@^5.6.0, semver@^5.7.1: semver@^5.3.0, semver@^5.5.0, semver@^5.6.0, semver@^5.7.1:
version "5.7.1" version "5.7.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
@ -3203,6 +3360,13 @@ split-string@^3.0.1, split-string@^3.0.2:
dependencies: dependencies:
extend-shallow "^3.0.0" extend-shallow "^3.0.0"
split@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9"
integrity sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==
dependencies:
through "2"
sprintf-js@~1.0.2: sprintf-js@~1.0.2:
version "1.0.3" version "1.0.3"
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
@ -3316,6 +3480,11 @@ strip-ansi@^6.0.0:
dependencies: dependencies:
ansi-regex "^5.0.0" ansi-regex "^5.0.0"
strip-ansi@~0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-0.1.1.tgz#39e8a98d044d150660abe4a6808acf70bb7bc991"
integrity sha1-OeipjQRNFQZgq+SmgIrPcLt7yZE=
strip-bom@^3.0.0: strip-bom@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
@ -3410,7 +3579,7 @@ text-table@^0.2.0:
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=
through@^2.3.6: through@2, through@^2.3.6:
version "2.3.8" version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
@ -3542,6 +3711,11 @@ unique-string@^2.0.0:
dependencies: dependencies:
crypto-random-string "^2.0.0" crypto-random-string "^2.0.0"
unix-timestamp@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/unix-timestamp/-/unix-timestamp-0.2.0.tgz#e1cdc2808df6327d27e635d9351e72815288733e"
integrity sha1-4c3CgI32Mn0n5jXZNR5ygVKIcz4=
unpipe@1.0.0, unpipe@~1.0.0: unpipe@1.0.0, unpipe@~1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
@ -3709,6 +3883,24 @@ xdg-basedir@^4.0.0:
resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13"
integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q== integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==
xml2js@0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.0.tgz#124fc4114b4129c810800ecb2ac86cf25462cb9a"
integrity sha1-Ek/EEUtBKcgQgA7LKshs8lRiy5o=
dependencies:
sax "0.5.x"
xmlbuilder ">=0.4.2"
xmlbuilder@>=0.4.2:
version "15.1.1"
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-15.1.1.tgz#9dcdce49eea66d8d10b42cae94a79c3c8d0c2ec5"
integrity sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==
xtend@^4.0.0:
version "4.0.2"
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
y18n@^4.0.0: y18n@^4.0.0:
version "4.0.1" version "4.0.1"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.1.tgz#8db2b83c31c5d75099bb890b23f3094891e247d4" resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.1.tgz#8db2b83c31c5d75099bb890b23f3094891e247d4"
@ -3719,6 +3911,11 @@ yallist@^3.0.0, yallist@^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==
yaml@0.2.3:
version "0.2.3"
resolved "https://registry.yarnpkg.com/yaml/-/yaml-0.2.3.tgz#b5450e92e76ef36b5dd24e3660091ebaeef3e5c7"
integrity sha1-tUUOkudu82td0k42YAkeuu7z5cc=
yargs-parser@^18.1.2: yargs-parser@^18.1.2:
version "18.1.3" version "18.1.3"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0"

View File

@ -3,7 +3,7 @@
# 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 nginxproxymanager/nginx-full:certbot-node FROM nginxproxymanager/nginx-full:node
ARG TARGETPLATFORM ARG TARGETPLATFORM
ARG BUILD_VERSION ARG BUILD_VERSION
@ -46,11 +46,6 @@ RUN rm -rf /etc/services.d/frontend /etc/nginx/conf.d/dev.conf
# Change permission of logrotate config file # Change permission of logrotate config file
RUN chmod 644 /etc/logrotate.d/nginx-proxy-manager RUN chmod 644 /etc/logrotate.d/nginx-proxy-manager
# fix for pip installs
# https://github.com/NginxProxyManager/nginx-proxy-manager/issues/1769
RUN pip uninstall --yes setuptools \
&& pip install "setuptools==58.0.0"
VOLUME [ "/data", "/etc/letsencrypt" ] VOLUME [ "/data", "/etc/letsencrypt" ]
ENTRYPOINT [ "/init" ] ENTRYPOINT [ "/init" ]

View File

@ -1,4 +1,4 @@
FROM nginxproxymanager/nginx-full:certbot-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 \

View File

@ -10,7 +10,8 @@ services:
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:
@ -37,10 +39,10 @@ services:
db: db:
image: jc21/mariadb-aria image: jc21/mariadb-aria
container_name: npm_db container_name: npm_db
ports:
- 33306:3306
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"
@ -49,6 +51,19 @@ services:
volumes: volumes:
- db_data:/var/lib/mysql - db_data:/var/lib/mysql
swagger:
image: "swaggerapi/swagger-ui:latest"
container_name: npm_swagger
ports:
- 3001:80
networks:
- nginx_proxy_manager
environment:
URL: "http://127.0.0.1:3081/api/schema"
PORT: "80"
depends_on:
- npm
volumes: volumes:
npm_data: npm_data:
name: npm_core_data name: npm_core_data

View File

@ -2,7 +2,7 @@
# ref: https://github.com/linuxserver/docker-baseimage-alpine/blob/master/root/etc/cont-init.d/01-envfile # ref: https://github.com/linuxserver/docker-baseimage-alpine/blob/master/root/etc/cont-init.d/01-envfile
# in s6, environmental variables are written as text files for s6 to monitor # in s6, environmental variables are written as text files for s6 to monitor
# search through full-path filenames for files ending in "__FILE" # seach through full-path filenames for files ending in "__FILE"
for FILENAME in $(find /var/run/s6/container_environment/ | grep "__FILE$"); do for FILENAME in $(find /var/run/s6/container_environment/ | grep "__FILE$"); do
echo "[secret-init] Evaluating ${FILENAME##*/} ..." echo "[secret-init] Evaluating ${FILENAME##*/} ..."

View File

@ -3,4 +3,3 @@ non-interactive = True
webroot-path = /data/letsencrypt-acme-challenge webroot-path = /data/letsencrypt-acme-challenge
key-type = ecdsa key-type = ecdsa
elliptic-curve = secp384r1 elliptic-curve = secp384r1
preferred-chain = ISRG Root X1

View File

@ -30,7 +30,7 @@ server {
set $port "443"; set $port "443";
server_name localhost; server_name localhost;
access_log /data/logs/fallback_access.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;

View File

@ -1,4 +1,4 @@
location ~* ^.*\.(css|js|jpe?g|gif|png|webp|woff|eot|ttf|svg|ico|css\.map|js\.map)$ { location ~* ^.*\.(css|js|jpe?g|gif|png|woff|eot|ttf|svg|ico|css\.map|js\.map)$ {
if_modified_since off; if_modified_since off;
# use the public cache # use the public cache

View File

@ -2,7 +2,7 @@ add_header X-Served-By $host;
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 $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_pass $forward_scheme://$server:$port$request_uri; proxy_pass $forward_scheme://$server:$port;

View File

@ -85,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

View File

@ -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 \
@ -24,12 +25,8 @@ chown root /tmp/nginx
# Dynamically generate resolvers file, if resolver is IPv6, enclose in `[]` # Dynamically generate resolvers file, if resolver is IPv6, enclose in `[]`
# thanks @tfmm # thanks @tfmm
if [ "$DISABLE_IPV6" == "true" ] || [ "$DISABLE_IPV6" == "on" ] || [ "$DISABLE_IPV6" == "1" ] || [ "$DISABLE_IPV6" == "yes" ]; echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" {print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf);" > /etc/nginx/conf.d/include/resolvers.conf
then
echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" { sub(/%.*$/,"",$2); print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf) ipv6=off valid=10s;" > /etc/nginx/conf.d/include/resolvers.conf
else
echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" { sub(/%.*$/,"",$2); print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf) valid=10s;" > /etc/nginx/conf.d/include/resolvers.conf
fi
# Generate dummy self-signed certificate. # Generate dummy self-signed certificate.
if [ ! -f /data/nginx/dummycert.pem ] || [ ! -f /data/nginx/dummykey.pem ] if [ ! -f /data/nginx/dummycert.pem ] || [ ! -f /data/nginx/dummykey.pem ]
then then

View File

@ -18,8 +18,8 @@ services running on this Docker host:
```yml ```yml
networks: networks:
default: default:
external: true external:
name: scoobydoo name: scoobydoo
``` ```
Let's look at a Portainer example: Let's look at a Portainer example:
@ -38,8 +38,8 @@ services:
networks: networks:
default: default:
external: true external:
name: scoobydoo name: scoobydoo
``` ```
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,
@ -172,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
...
```

View File

@ -21,6 +21,3 @@ Your best bet is to ask the [Reddit community for support](https://www.reddit.co
Gitter is best left for anyone contributing to the project to ask for help about internals, code reviews etc. Gitter is best left for anyone contributing to the project to ask for help about internals, code reviews etc.
## When adding username and password access control to a proxy host, I can no longer login into the app.
Having an Access Control List (ACL) with username and password requires the browser to always send this username and password in the `Authorization` header on each request. If your proxied app also requires authentication (like Nginx Proxy Manager itself), most likely the app will also use the `Authorization` header to transmit this information, as this is the standardized header meant for this kind of information. However having multiples of the same headers is not allowed in the [internet standard](https://www.rfc-editor.org/rfc/rfc7230#section-3.2.2) and almost all apps do not support multiple values in the `Authorization` header. Hence one of the two logins will be broken. This can only be fixed by either removing one of the logins or by changing the app to use other non-standard headers for authorization.

View File

@ -213,7 +213,7 @@
"etag": "^1.8.1", "etag": "^1.8.1",
"eventemitter3": "^4.0.4", "eventemitter3": "^4.0.4",
"events": "^3.2.0", "events": "^3.2.0",
"eventsource": "^2.0.2", "eventsource": "^1.0.7",
"evp_bytestokey": "^1.0.3", "evp_bytestokey": "^1.0.3",
"execa": "^4.0.3", "execa": "^4.0.3",
"expand-brackets": "^4.0.0", "expand-brackets": "^4.0.0",
@ -357,7 +357,7 @@
"jsbn": "^1.1.0", "jsbn": "^1.1.0",
"jsesc": "^3.0.1", "jsesc": "^3.0.1",
"json-parse-better-errors": "^1.0.2", "json-parse-better-errors": "^1.0.2",
"json-schema": "^0.4.0", "json-schema": "^0.2.5",
"json-schema-traverse": "^0.4.1", "json-schema-traverse": "^0.4.1",
"json-stringify-safe": "^5.0.1", "json-stringify-safe": "^5.0.1",
"json3": "^3.3.3", "json3": "^3.3.3",
@ -394,7 +394,7 @@
"map-age-cleaner": "^0.1.3", "map-age-cleaner": "^0.1.3",
"map-cache": "^0.2.2", "map-cache": "^0.2.2",
"map-visit": "^1.0.0", "map-visit": "^1.0.0",
"markdown-it": "^12.3.2", "markdown-it": "^11.0.0",
"markdown-it-anchor": "^5.3.0", "markdown-it-anchor": "^5.3.0",
"markdown-it-chain": "^1.3.0", "markdown-it-chain": "^1.3.0",
"markdown-it-container": "^3.0.0", "markdown-it-container": "^3.0.0",
@ -434,7 +434,7 @@
"neo-async": "^2.6.2", "neo-async": "^2.6.2",
"nice-try": "^2.0.1", "nice-try": "^2.0.1",
"no-case": "^3.0.3", "no-case": "^3.0.3",
"node-forge": "^1.0.0", "node-forge": "^0.10.0",
"node-libs-browser": "^2.2.1", "node-libs-browser": "^2.2.1",
"node-releases": "^1.1.60", "node-releases": "^1.1.60",
"nopt": "^4.0.3", "nopt": "^4.0.3",

View File

@ -1,44 +1,6 @@
# Full Setup Instructions # Full Setup Instructions
## Running the App ## MySQL Database
Create a `docker-compose.yml` file:
```yml
version: "3"
services:
app:
image: 'jc21/nginx-proxy-manager:latest'
restart: unless-stopped
ports:
# These ports are in format <host-port>:<container-port>
- '80:80' # Public HTTP Port
- '443:443' # Public HTTPS Port
- '81:81' # Admin Web Port
# Add any other Stream port you want to expose
# - '21:21' # FTP
# Uncomment the next line if you uncomment anything in the section
# environment:
# Uncomment this if you want to change the location of
# the SQLite DB file within the container
# DB_SQLITE_FILE: "/data/database.sqlite"
# Uncomment this if IPv6 is not enabled on your host
# DISABLE_IPV6: 'true'
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
```
Then:
```bash
docker-compose up -d
```
## Using MySQL / MariaDB 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:
@ -48,7 +10,15 @@ If you opt for the MySQL configuration you will have to provide the database ser
It's easy to use another docker container for your database also and link it as part of the docker stack, so that's what the following examples It's easy to use another docker container for your database also and link it as part of the docker stack, so that's what the following examples
are going to use. are going to use.
Here is an example of what your `docker-compose.yml` will look like when using a MariaDB container: ::: warning
When using a `mariadb` database, the NPM configuration file should still use the `mysql` engine!
:::
## Running the App
Via `docker-compose`:
```yml ```yml
version: "3" version: "3"
@ -57,18 +27,24 @@ services:
image: 'jc21/nginx-proxy-manager:latest' image: 'jc21/nginx-proxy-manager:latest'
restart: unless-stopped restart: unless-stopped
ports: ports:
# These ports are in format <host-port>:<container-port> # Public HTTP Port:
- '80:80' # Public HTTP Port - '80:80'
- '443:443' # Public HTTPS Port # Public HTTPS Port:
- '81:81' # Admin Web Port - '443:443'
# Admin Web Port:
- '81:81'
# Add any other Stream port you want to expose # Add any other Stream port you want to expose
# - '21:21' # FTP # - '21:21' # FTP
environment: environment:
# These are the settings to access your db
DB_MYSQL_HOST: "db" DB_MYSQL_HOST: "db"
DB_MYSQL_PORT: 3306 DB_MYSQL_PORT: 3306
DB_MYSQL_USER: "npm" DB_MYSQL_USER: "npm"
DB_MYSQL_PASSWORD: "npm" DB_MYSQL_PASSWORD: "npm"
DB_MYSQL_NAME: "npm" 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 # Uncomment this if IPv6 is not enabled on your host
# DISABLE_IPV6: 'true' # DISABLE_IPV6: 'true'
volumes: volumes:
@ -76,7 +52,6 @@ services:
- ./letsencrypt:/etc/letsencrypt - ./letsencrypt:/etc/letsencrypt
depends_on: depends_on:
- db - db
db: db:
image: 'jc21/mariadb-aria:latest' image: 'jc21/mariadb-aria:latest'
restart: unless-stopped restart: unless-stopped
@ -89,11 +64,13 @@ services:
- ./data/mysql:/var/lib/mysql - ./data/mysql:/var/lib/mysql
``` ```
::: warning _Please note, that `DB_MYSQL_*` environment variables will take precedent over `DB_SQLITE_*` variables. So if you keep the MySQL variables, you will not be able to use Sqlite._
Please note, that `DB_MYSQL_*` environment variables will take precedent over `DB_SQLITE_*` variables. So if you keep the MySQL variables, you will not be able to use SQLite. Then:
::: ```bash
docker-compose up -d
```
## Running on Raspberry PI / ARM devices ## Running on Raspberry PI / ARM devices
@ -107,12 +84,62 @@ you don't have to worry about doing anything special and you can follow the comm
Check out the [dockerhub tags](https://hub.docker.com/r/jc21/nginx-proxy-manager/tags) Check out the [dockerhub tags](https://hub.docker.com/r/jc21/nginx-proxy-manager/tags)
for a list of supported architectures and if you want one that doesn't exist, for a list of supported architectures and if you want one that doesn't exist,
[create a feature request](https://github.com/NginxProxyManager/nginx-proxy-manager/issues/new?assignees=&labels=enhancement&template=feature_request.md&title=). [create a feature request](https://github.com/jc21/nginx-proxy-manager/issues/new?assignees=&labels=enhancement&template=feature_request.md&title=).
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.
Please note that the `jc21/mariadb-aria:latest` image might have some problems on some ARM devices, if you want a separate database container, use the `yobasystems/alpine-mariadb:latest` image. Via `docker-compose`:
```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 ## Initial Run

View File

@ -1,6 +1,6 @@
# Third Party # Third Party
As this software gains popularity it's common to see it integrated with other platforms. Please be aware that unless specifically mentioned in the documentation of those As this software gains popularity it's common to see it integrated with other platforms. Please be aware that unless specifically mentioned in the documenation of those
integrations, they are *not supported* by me. integrations, they are *not supported* by me.
Known integrations: Known integrations:

View File

@ -967,46 +967,6 @@
lodash "^4.17.19" lodash "^4.17.19"
to-fast-properties "^2.0.0" to-fast-properties "^2.0.0"
"@jridgewell/gen-mapping@^0.3.0":
version "0.3.2"
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9"
integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==
dependencies:
"@jridgewell/set-array" "^1.0.1"
"@jridgewell/sourcemap-codec" "^1.4.10"
"@jridgewell/trace-mapping" "^0.3.9"
"@jridgewell/resolve-uri@^3.0.3":
version "3.1.0"
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78"
integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==
"@jridgewell/set-array@^1.0.1":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72"
integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
"@jridgewell/source-map@^0.3.2":
version "0.3.2"
resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb"
integrity sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==
dependencies:
"@jridgewell/gen-mapping" "^0.3.0"
"@jridgewell/trace-mapping" "^0.3.9"
"@jridgewell/sourcemap-codec@^1.4.10":
version "1.4.14"
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
"@jridgewell/trace-mapping@^0.3.9":
version "0.3.14"
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz#b231a081d8f66796e475ad588a1ef473112701ed"
integrity sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==
dependencies:
"@jridgewell/resolve-uri" "^3.0.3"
"@jridgewell/sourcemap-codec" "^1.4.10"
"@mrmlnc/readdir-enhanced@^2.2.1": "@mrmlnc/readdir-enhanced@^2.2.1":
version "2.2.1" version "2.2.1"
resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde"
@ -1536,11 +1496,6 @@ acorn@^7.4.0:
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==
acorn@^8.5.0:
version "8.7.1"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30"
integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==
agentkeepalive@^2.2.0: agentkeepalive@^2.2.0:
version "2.2.0" version "2.2.0"
resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-2.2.0.tgz#c5d1bd4b129008f1163f236f86e5faea2026e2ef" resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-2.2.0.tgz#c5d1bd4b129008f1163f236f86e5faea2026e2ef"
@ -1731,11 +1686,6 @@ argparse@^1.0.10, argparse@^1.0.7:
dependencies: dependencies:
sprintf-js "~1.0.2" sprintf-js "~1.0.2"
argparse@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
arr-diff@^4.0.0: arr-diff@^4.0.0:
version "4.0.0" version "4.0.0"
resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520"
@ -3794,10 +3744,10 @@ entities@^1.1.1, entities@~1.1.1:
resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56"
integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==
entities@^2.0.0, entities@^2.0.3, entities@~2.1.0: entities@^2.0.0, entities@^2.0.3, entities@~2.0.0:
version "2.1.0" version "2.0.3"
resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5" resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.3.tgz#5c487e5742ab93c15abb5da22759b8590ec03b7f"
integrity sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w== integrity sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==
envify@^4.0.0, envify@^4.1.0: envify@^4.0.0, envify@^4.1.0:
version "4.1.0" version "4.1.0"
@ -3975,11 +3925,6 @@ eventsource@^1.0.7:
dependencies: dependencies:
original "^1.0.0" original "^1.0.0"
eventsource@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-2.0.2.tgz#76dfcc02930fb2ff339520b6d290da573a9e8508"
integrity sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==
evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3:
version "1.0.3" version "1.0.3"
resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02"
@ -4167,13 +4112,6 @@ fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0:
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
fast-xml-parser@^3.19.0:
version "3.21.1"
resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-3.21.1.tgz#152a1d51d445380f7046b304672dd55d15c9e736"
integrity sha512-FTFVjYoBOZTJekiUsawGsSYV9QL0A+zDYCRj7y34IO6Jg+2IMYEtQa+bbictpdpV8dHxXywqU7C0gRDEOFtBFg==
dependencies:
strnum "^1.0.4"
fastq@^1.6.0: fastq@^1.6.0:
version "1.8.0" version "1.8.0"
resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.8.0.tgz#550e1f9f59bbc65fe185cb6a9b4d95357107f481" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.8.0.tgz#550e1f9f59bbc65fe185cb6a9b4d95357107f481"
@ -4316,9 +4254,9 @@ flush-write-stream@^2.0.0:
readable-stream "^3.1.1" readable-stream "^3.1.1"
follow-redirects@^1.0.0, follow-redirects@^1.12.1: follow-redirects@^1.0.0, follow-redirects@^1.12.1:
version "1.14.8" version "1.12.1"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.8.tgz#016996fb9a11a100566398b1c6839337d7bfa8fc" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.12.1.tgz#de54a6205311b93d60398ebc01cf7015682312b6"
integrity sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA== integrity sha512-tmRv0AVuR7ZyouUHLeNSiO6pqulF7dYa3s19c6t+wz9LD69/uSzdMxJ2S91nTI9U3rt/IldxpzMOFejp6f0hjg==
for-in@^1.0.2: for-in@^1.0.2:
version "1.0.2" version "1.0.2"
@ -5592,11 +5530,11 @@ 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.3.0" version "4.2.2"
resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-4.3.0.tgz#3e46a45dcdb2780e42a3c8538154d7f7bfc07216" resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-4.2.2.tgz#a4ea0f3f78dada7085db88f1e85b6f845626cfae"
integrity sha512-Np3TOGLVr0J27VDaS/gVE7bT45ZcSmX4pMmMTsPjqO8JY383fuPIcWmZr3QsHVWhqhZWxSdmW+tkkl3PWOB0Nw== integrity sha512-JlA7Mc7mfWjdxxTkJ094oUK9amGD7gQaj5xA/NCY0vlVvZ1stmj4VX+bRuwOMN93IHRZ2ctpPH/0FO6DqvQ5Rw==
dependencies: dependencies:
fast-xml-parser "^3.19.0" html-comment-regex "^1.1.2"
is-symbol@^1.0.2, is-symbol@^1.0.3: is-symbol@^1.0.2, is-symbol@^1.0.3:
version "1.0.3" version "1.0.3"
@ -5776,10 +5714,10 @@ json-schema@0.2.3:
resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=
json-schema@^0.4.0: json-schema@^0.2.5:
version "0.4.0" version "0.2.5"
resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.5.tgz#97997f50972dd0500214e208c407efa4b5d7063b"
integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== integrity sha512-gWJOWYFrhQ8j7pVm0EM8Slr+EPVq1Phf6lvzvD/WCeqkrx/f2xBI0xOsRRS9xCn3I4vKtP519dvs3TP09r24wQ==
json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1:
version "5.0.1" version "5.0.1"
@ -5977,9 +5915,9 @@ loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.3, loader-utils@^1.4
json5 "^1.0.1" json5 "^1.0.1"
loader-utils@^2.0.0: loader-utils@^2.0.0:
version "2.0.3" version "2.0.0"
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.3.tgz#d4b15b8504c63d1fc3f2ade52d41bc8459d6ede1" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.0.tgz#e4cace5b816d425a166b5f097e10cd12b36064b0"
integrity sha512-THWqIsn8QRnvLl0shHYVBN9syumU8pYWEHPTmkiVGd+7K5eFNVSY6AJhRvgGF70gg1Dz+l/k8WicvFCxdEs60A== integrity sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==
dependencies: dependencies:
big.js "^5.2.2" big.js "^5.2.2"
emojis-list "^3.0.0" emojis-list "^3.0.0"
@ -6197,13 +6135,13 @@ markdown-it-table-of-contents@^0.4.0, markdown-it-table-of-contents@^0.4.4:
resolved "https://registry.yarnpkg.com/markdown-it-table-of-contents/-/markdown-it-table-of-contents-0.4.4.tgz#3dc7ce8b8fc17e5981c77cc398d1782319f37fbc" resolved "https://registry.yarnpkg.com/markdown-it-table-of-contents/-/markdown-it-table-of-contents-0.4.4.tgz#3dc7ce8b8fc17e5981c77cc398d1782319f37fbc"
integrity sha512-TAIHTHPwa9+ltKvKPWulm/beozQU41Ab+FIefRaQV1NRnpzwcV9QOe6wXQS5WLivm5Q/nlo0rl6laGkMDZE7Gw== integrity sha512-TAIHTHPwa9+ltKvKPWulm/beozQU41Ab+FIefRaQV1NRnpzwcV9QOe6wXQS5WLivm5Q/nlo0rl6laGkMDZE7Gw==
markdown-it@^12.3.2: markdown-it@^11.0.0:
version "12.3.2" version "11.0.0"
resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-12.3.2.tgz#bf92ac92283fe983fe4de8ff8abfb5ad72cd0c90" resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-11.0.0.tgz#dbfc30363e43d756ebc52c38586b91b90046b876"
integrity sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg== integrity sha512-+CvOnmbSubmQFSA9dKz1BRiaSMV7rhexl3sngKqFyXSagoA3fBdJQ8oZWtRy2knXdpDXaBw44euz37DeJQ9asg==
dependencies: dependencies:
argparse "^2.0.1" argparse "^1.0.7"
entities "~2.1.0" entities "~2.0.0"
linkify-it "^3.0.1" linkify-it "^3.0.1"
mdurl "^1.0.1" mdurl "^1.0.1"
uc.micro "^1.0.5" uc.micro "^1.0.5"
@ -6657,10 +6595,10 @@ node-forge@0.9.0:
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.9.0.tgz#d624050edbb44874adca12bb9a52ec63cb782579" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.9.0.tgz#d624050edbb44874adca12bb9a52ec63cb782579"
integrity sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ== integrity sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ==
node-forge@^1.0.0: node-forge@^0.10.0:
version "1.0.0" version "0.10.0"
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.0.0.tgz#a025e3beeeb90d9cee37dae34d25b968ec3e6f15" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3"
integrity sha512-ShkiiAlzSsgH1IwGlA0jybk9vQTIOLyJ9nBd0JTuP+nzADJFLY0NoDijM2zvD/JaezooGu3G2p2FNxOAK6459g== integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==
node-libs-browser@^2.2.1: node-libs-browser@^2.2.1:
version "2.2.1" version "2.2.1"
@ -7768,9 +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.27.0" version "1.25.0"
resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.27.0.tgz#bb6ee3138a0b438a3653dd4d6ce0cc6510a45057" resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.25.0.tgz#6f822df1bdad965734b310b315a23315cf999756"
integrity sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA== integrity sha512-WCjJHl1KEWbnkQom1+SzftbtXMKQoezOCYs5rECqMN+jP+apI7ftoflyqigqzopSO3hMhTEb0mFClA8lkolgEg==
private@^0.1.8: private@^0.1.8:
version "0.1.8" version "0.1.8"
@ -8816,10 +8754,10 @@ source-map-resolve@^0.6.0:
atob "^2.1.2" atob "^2.1.2"
decode-uri-component "^0.2.0" decode-uri-component "^0.2.0"
source-map-support@^0.5.19, source-map-support@~0.5.12, source-map-support@~0.5.20: source-map-support@^0.5.19, source-map-support@~0.5.12:
version "0.5.21" version "0.5.19"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61"
integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==
dependencies: dependencies:
buffer-from "^1.0.0" buffer-from "^1.0.0"
source-map "^0.6.0" source-map "^0.6.0"
@ -9142,11 +9080,6 @@ strip-json-comments@~2.0.1:
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
strnum@^1.0.4:
version "1.0.5"
resolved "https://registry.yarnpkg.com/strnum/-/strnum-1.0.5.tgz#5c4e829fe15ad4ff0d20c3db5ac97b73c9b072db"
integrity sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==
stylehacks@^4.0.0, stylehacks@^4.0.3: stylehacks@^4.0.0, stylehacks@^4.0.3:
version "4.0.3" version "4.0.3"
resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-4.0.3.tgz#6718fcaf4d1e07d8a1318690881e8d96726a71d5" resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-4.0.3.tgz#6718fcaf4d1e07d8a1318690881e8d96726a71d5"
@ -9291,14 +9224,13 @@ terser@^4.1.2:
source-map-support "~0.5.12" source-map-support "~0.5.12"
terser@^5.0.0: terser@^5.0.0:
version "5.14.2" version "5.0.0"
resolved "https://registry.yarnpkg.com/terser/-/terser-5.14.2.tgz#9ac9f22b06994d736174f4091aa368db896f1c10" resolved "https://registry.yarnpkg.com/terser/-/terser-5.0.0.tgz#269640e4e92f15d628de1e5f01c4c61e1ba3d765"
integrity sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA== integrity sha512-olH2DwGINoSuEpSGd+BsPuAQaA3OrHnHnFL/rDB2TVNc3srUbz/rq/j2BlF4zDXI+JqAvGr86bIm1R2cJgZ3FA==
dependencies: dependencies:
"@jridgewell/source-map" "^0.3.2"
acorn "^8.5.0"
commander "^2.20.0" commander "^2.20.0"
source-map-support "~0.5.20" source-map "~0.6.1"
source-map-support "~0.5.12"
text-table@^0.2.0: text-table@^0.2.0:
version "0.2.0" version "0.2.0"
@ -9732,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.5.9" version "1.5.2"
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.9.tgz#05ff26484a0b5e4040ac64dcee4177223d74675e" resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.2.tgz#a4eff6fd5ff9fe6ab98ac1f79641819d13247cda"
integrity sha512-HpOvhKBvre8wYez+QhHcYiVvVmeF6DVnuSOOPhe3cTum3BnqHhvKaZm8FU5yTiOu/Jut2ZpB2rA/SbBA1JIGlQ== 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"

View File

@ -515,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]
@ -685,16 +755,6 @@ module.exports = {
return fetch('post', 'nginx/certificates/' + id + '/renew', undefined, {timeout}); return fetch('post', 'nginx/certificates/' + id + '/renew', undefined, {timeout});
}, },
/**
* @param {Number} id
* @returns {Promise}
*/
testHttpChallenge: function (domains) {
return fetch('get', 'nginx/certificates/test-http?' + new URLSearchParams({
domains: JSON.stringify(domains),
}));
},
/** /**
* @param {Number} id * @param {Number} id
* @returns {Promise} * @returns {Promise}

View File

@ -2,16 +2,6 @@
<div class="card-status bg-teal"></div> <div class="card-status bg-teal"></div>
<div class="card-header"> <div class="card-header">
<h3 class="card-title"><%- i18n('audit-log', 'title') %></h3> <h3 class="card-title"><%- i18n('audit-log', 'title') %></h3>
<div class="card-options">
<form class="search-form" role="search">
<div class="input-icon">
<span class="input-icon-addon">
<i class="fe fe-search"></i>
</span>
<input name="source-query" type="text" value="" class="form-control form-control-sm" placeholder="<%- i18n('audit-log', 'search') %>" aria-label="<%- i18n('audit-log', 'search') %>">
</div>
</form>
</div>
</div> </div>
<div class="card-body no-padding min-100"> <div class="card-body no-padding min-100">
<div class="dimmer active"> <div class="dimmer active">

View File

@ -12,68 +12,39 @@ module.exports = Mn.View.extend({
ui: { ui: {
list_region: '.list-region', list_region: '.list-region',
dimmer: '.dimmer', dimmer: '.dimmer'
search: '.search-form',
query: 'input[name="source-query"]'
},
fetch: App.Api.AuditLog.getAll,
showData: function(response) {
this.showChildView('list_region', new ListView({
collection: new AuditLogModel.Collection(response)
}));
},
showError: function(err) {
this.showChildView('list_region', new ErrorView({
code: err.code,
message: err.message,
retry: function () {
App.Controller.showAuditLog();
}
}));
console.error(err);
},
showEmpty: function() {
this.showChildView('list_region', new EmptyView({
title: App.i18n('audit-log', 'empty'),
subtitle: App.i18n('audit-log', 'empty-subtitle')
}));
}, },
regions: { regions: {
list_region: '@ui.list_region' list_region: '@ui.list_region'
}, },
events: {
'submit @ui.search': function (e) {
e.preventDefault();
let query = this.ui.query.val();
this.fetch(['user'], query)
.then(response => this.showData(response))
.catch(err => {
this.showError(err);
});
}
},
onRender: function () { onRender: function () {
let view = this; let view = this;
view.fetch(['user']) App.Api.AuditLog.getAll(['user'])
.then(response => { .then(response => {
if (!view.isDestroyed() && response && response.length) { if (!view.isDestroyed() && response && response.length) {
view.showData(response); view.showChildView('list_region', new ListView({
collection: new AuditLogModel.Collection(response)
}));
} else { } else {
view.showEmpty(); view.showChildView('list_region', new EmptyView({
title: App.i18n('audit-log', 'empty'),
subtitle: App.i18n('audit-log', 'empty-subtitle')
}));
} }
}) })
.catch(err => { .catch(err => {
view.showError(err); view.showChildView('list_region', new ErrorView({
code: err.code,
message: err.message,
retry: function () {
App.Controller.showAuditLog();
}
}));
console.error(err);
}) })
.then(() => { .then(() => {
view.ui.dimmer.removeClass('active'); view.ui.dimmer.removeClass('active');

View File

@ -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
*/ */
@ -366,19 +406,6 @@ module.exports = {
} }
}, },
/**
* Certificate Test Reachability
*
* @param model
*/
showNginxCertificateTestReachability: function (model) {
if (Cache.User.isAdmin() || Cache.User.canManage('certificates')) {
require(['./main', './nginx/certificates/test'], function (App, View) {
App.UI.showModalDialog(new View({model: model}));
});
}
},
/** /**
* Audit Log * Audit Log
*/ */

View File

@ -3,14 +3,6 @@
<div class="card-header"> <div class="card-header">
<h3 class="card-title"><%- i18n('access-lists', 'title') %></h3> <h3 class="card-title"><%- i18n('access-lists', 'title') %></h3>
<div class="card-options"> <div class="card-options">
<form class="search-form" role="search">
<div class="input-icon">
<span class="input-icon-addon">
<i class="fe fe-search"></i>
</span>
<input name="source-query" type="text" value="" class="form-control form-control-sm" placeholder="<%- i18n('access-lists', 'search') %>" aria-label="<%- i18n('access-lists', 'search') %>">
</div>
</form>
<a href="#" class="btn btn-outline-secondary btn-sm ml-2 help"><i class="fe fe-help-circle"></i></a> <a href="#" class="btn btn-outline-secondary btn-sm ml-2 help"><i class="fe fe-help-circle"></i></a>
<% if (showAddButton) { %> <% if (showAddButton) { %>
<a href="#" class="btn btn-outline-teal btn-sm ml-2 add-item"><%- i18n('access-lists', 'add') %></a> <a href="#" class="btn btn-outline-teal btn-sm ml-2 add-item"><%- i18n('access-lists', 'add') %></a>

View File

@ -14,44 +14,7 @@ module.exports = Mn.View.extend({
list_region: '.list-region', list_region: '.list-region',
add: '.add-item', add: '.add-item',
help: '.help', help: '.help',
dimmer: '.dimmer', dimmer: '.dimmer'
search: '.search-form',
query: 'input[name="source-query"]'
},
fetch: App.Api.Nginx.AccessLists.getAll,
showData: function(response) {
this.showChildView('list_region', new ListView({
collection: new AccessListModel.Collection(response)
}));
},
showError: function(err) {
this.showChildView('list_region', new ErrorView({
code: err.code,
message: err.message,
retry: function () {
App.Controller.showNginxAccess();
}
}));
console.error(err);
},
showEmpty: function() {
let manage = App.Cache.User.canManage('access_lists');
this.showChildView('list_region', new EmptyView({
title: App.i18n('access-lists', 'empty'),
subtitle: App.i18n('all-hosts', 'empty-subtitle', {manage: manage}),
link: manage ? App.i18n('access-lists', 'add') : null,
btn_color: 'teal',
permission: 'access_lists',
action: function () {
App.Controller.showNginxAccessListForm();
}
}));
}, },
regions: { regions: {
@ -67,17 +30,6 @@ module.exports = Mn.View.extend({
'click @ui.help': function (e) { 'click @ui.help': function (e) {
e.preventDefault(); e.preventDefault();
App.Controller.showHelp(App.i18n('access-lists', 'help-title'), App.i18n('access-lists', 'help-content')); App.Controller.showHelp(App.i18n('access-lists', 'help-title'), App.i18n('access-lists', 'help-content'));
},
'submit @ui.search': function (e) {
e.preventDefault();
let query = this.ui.query.val();
this.fetch(['owner', 'items', 'clients'], query)
.then(response => this.showData(response))
.catch(err => {
this.showError(err);
});
} }
}, },
@ -88,18 +40,39 @@ module.exports = Mn.View.extend({
onRender: function () { onRender: function () {
let view = this; let view = this;
view.fetch(['owner', 'items', 'clients']) App.Api.Nginx.AccessLists.getAll(['owner', 'items', 'clients'])
.then(response => { .then(response => {
if (!view.isDestroyed()) { if (!view.isDestroyed()) {
if (response && response.length) { if (response && response.length) {
view.showData(response); view.showChildView('list_region', new ListView({
collection: new AccessListModel.Collection(response)
}));
} else { } else {
view.showEmpty(); let manage = App.Cache.User.canManage('access_lists');
view.showChildView('list_region', new EmptyView({
title: App.i18n('access-lists', 'empty'),
subtitle: App.i18n('all-hosts', 'empty-subtitle', {manage: manage}),
link: manage ? App.i18n('access-lists', 'add') : null,
btn_color: 'teal',
permission: 'access_lists',
action: function () {
App.Controller.showNginxAccessListForm();
}
}));
} }
} }
}) })
.catch(err => { .catch(err => {
view.showError(err); view.showChildView('list_region', new ErrorView({
code: err.code,
message: err.message,
retry: function () {
App.Controller.showNginxAccess();
}
}));
console.error(err);
}) })
.then(() => { .then(() => {
view.ui.dimmer.removeClass('active'); view.ui.dimmer.removeClass('active');

View File

@ -18,14 +18,6 @@
<input type="text" name="domain_names" class="form-control" id="input-domains" value="<%- domain_names.join(',') %>" required> <input type="text" name="domain_names" class="form-control" id="input-domains" value="<%- domain_names.join(',') %>" required>
<div class="text-blue"><i class="fe fe-alert-triangle"></i> <%- i18n('ssl', 'hosts-warning') %></div> <div class="text-blue"><i class="fe fe-alert-triangle"></i> <%- i18n('ssl', 'hosts-warning') %></div>
</div> </div>
<div class="mb-3 test-domains-container">
<button type="button" class="btn btn-secondary test-domains col-sm-12"><%- i18n('certificates', 'test-reachability') %></button>
<div class="text-secondary small">
<i class="fe fe-info"></i>
<%- i18n('certificates', 'reachability-info') %>
</div>
</div>
</div> </div>
<div class="col-sm-12 col-md-12"> <div class="col-sm-12 col-md-12">
<div class="form-group"> <div class="form-group">

View File

@ -29,8 +29,6 @@ module.exports = Mn.View.extend({
non_loader_content: '.non-loader-content', non_loader_content: '.non-loader-content',
le_error_info: '#le-error-info', le_error_info: '#le-error-info',
domain_names: 'input[name="domain_names"]', domain_names: 'input[name="domain_names"]',
test_domains_container: '.test-domains-container',
test_domains_button: '.test-domains',
buttons: '.modal-footer button', buttons: '.modal-footer button',
cancel: 'button.cancel', cancel: 'button.cancel',
save: 'button.save', save: 'button.save',
@ -58,12 +56,10 @@ module.exports = Mn.View.extend({
this.ui.dns_provider_credentials.prop('required', 'required'); this.ui.dns_provider_credentials.prop('required', 'required');
} }
this.ui.dns_challenge_content.show(); this.ui.dns_challenge_content.show();
this.ui.test_domains_container.hide();
} else { } else {
this.ui.dns_provider.prop('required', false); this.ui.dns_provider.prop('required', false);
this.ui.dns_provider_credentials.prop('required', false); this.ui.dns_provider_credentials.prop('required', false);
this.ui.dns_challenge_content.hide(); this.ui.dns_challenge_content.hide();
this.ui.test_domains_container.show();
} }
}, },
@ -209,23 +205,6 @@ module.exports = Mn.View.extend({
this.ui.non_loader_content.show(); this.ui.non_loader_content.show();
}); });
}, },
'click @ui.test_domains_button': function (e) {
e.preventDefault();
const domainNames = this.ui.domain_names[0].value.split(',');
if (domainNames && domainNames.length > 0) {
this.model.set('domain_names', domainNames);
this.model.set('back_to_add', true);
App.Controller.showNginxCertificateTestReachability(this.model);
}
},
'change @ui.domain_names': function(e){
const domainNames = e.target.value.split(',');
if (domainNames && domainNames.length > 0) {
this.ui.test_domains_button.prop('disabled', false);
} else {
this.ui.test_domains_button.prop('disabled', true);
}
},
'change @ui.other_certificate_key': function(e){ 'change @ui.other_certificate_key': function(e){
this.setFileName("other_certificate_key_label", e) this.setFileName("other_certificate_key_label", e)
}, },
@ -278,12 +257,6 @@ module.exports = Mn.View.extend({
this.ui.credentials_file_content.hide(); this.ui.credentials_file_content.hide();
this.ui.loader_content.hide(); this.ui.loader_content.hide();
this.ui.le_error_info.hide(); this.ui.le_error_info.hide();
if (this.ui.domain_names[0]) {
const domainNames = this.ui.domain_names[0].value.split(',');
if (!domainNames || domainNames.length === 0 || (domainNames.length === 1 && domainNames[0] === "")) {
this.ui.test_domains_button.prop('disabled', true);
}
}
}, },
initialize: function (options) { initialize: function (options) {

View File

@ -42,9 +42,6 @@
<% 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> <a href="#" class="download dropdown-item"><i class="dropdown-icon fe fe-download"></i> <%- i18n('certificates', 'download') %></a>
<% if (meta.dns_challenge === false) { %>
<a href="#" class="test dropdown-item"><i class="dropdown-icon fe fe-globe"></i> <%- i18n('certificates', 'test-reachability') %></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>

View File

@ -2,7 +2,7 @@ const Mn = require('backbone.marionette');
const moment = require('moment'); const moment = require('moment');
const App = require('../../../main'); const App = require('../../../main');
const template = require('./item.ejs'); const template = require('./item.ejs');
const dns_providers = require('../../../../../../global/certbot-dns-plugins'); const dns_providers = require('../../../../../../global/certbot-dns-plugins')
module.exports = Mn.View.extend({ module.exports = Mn.View.extend({
template: template, template: template,
@ -12,8 +12,7 @@ module.exports = Mn.View.extend({
host_link: '.host-link', host_link: '.host-link',
renew: 'a.renew', renew: 'a.renew',
delete: 'a.delete', delete: 'a.delete',
download: 'a.download', download: 'a.download'
test: 'a.test'
}, },
events: { events: {
@ -32,16 +31,11 @@ module.exports = Mn.View.extend({
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) { 'click @ui.download': function (e) {
e.preventDefault(); e.preventDefault();
App.Api.Nginx.Certificates.download(this.model.get('id')); App.Api.Nginx.Certificates.download(this.model.get('id'))
}, }
'click @ui.test': function (e) {
e.preventDefault();
App.Controller.showNginxCertificateTestReachability(this.model);
},
}, },
templateContext: { templateContext: {

View File

@ -3,14 +3,6 @@
<div class="card-header"> <div class="card-header">
<h3 class="card-title"><%- i18n('certificates', 'title') %></h3> <h3 class="card-title"><%- i18n('certificates', 'title') %></h3>
<div class="card-options"> <div class="card-options">
<form class="search-form" role="search">
<div class="input-icon">
<span class="input-icon-addon">
<i class="fe fe-search"></i>
</span>
<input name="source-query" type="text" value="" class="form-control form-control-sm" placeholder="<%- i18n('certificates', 'search') %>" aria-label="<%- i18n('certificates', 'search') %>">
</div>
</form>
<a href="#" class="btn btn-outline-secondary btn-sm ml-2 help"><i class="fe fe-help-circle"></i></a> <a href="#" class="btn btn-outline-secondary btn-sm ml-2 help"><i class="fe fe-help-circle"></i></a>
<% if (showAddButton) { %> <% if (showAddButton) { %>
<div class="dropdown"> <div class="dropdown">

View File

@ -14,44 +14,7 @@ module.exports = Mn.View.extend({
list_region: '.list-region', list_region: '.list-region',
add: '.add-item', add: '.add-item',
help: '.help', help: '.help',
dimmer: '.dimmer', dimmer: '.dimmer'
search: '.search-form',
query: 'input[name="source-query"]'
},
fetch: App.Api.Nginx.Certificates.getAll,
showData: function(response) {
this.showChildView('list_region', new ListView({
collection: new CertificateModel.Collection(response)
}));
},
showError: function(err) {
this.showChildView('list_region', new ErrorView({
code: err.code,
message: err.message,
retry: function () {
App.Controller.showNginxCertificates();
}
}));
console.error(err);
},
showEmpty: function() {
let manage = App.Cache.User.canManage('certificates');
this.showChildView('list_region', new EmptyView({
title: App.i18n('certificates', 'empty'),
subtitle: App.i18n('all-hosts', 'empty-subtitle', {manage: manage}),
link: manage ? App.i18n('certificates', 'add') : null,
btn_color: 'pink',
permission: 'certificates',
action: function () {
App.Controller.showNginxCertificateForm();
}
}));
}, },
regions: { regions: {
@ -68,17 +31,6 @@ module.exports = Mn.View.extend({
'click @ui.help': function (e) { 'click @ui.help': function (e) {
e.preventDefault(); e.preventDefault();
App.Controller.showHelp(App.i18n('certificates', 'help-title'), App.i18n('certificates', 'help-content')); App.Controller.showHelp(App.i18n('certificates', 'help-title'), App.i18n('certificates', 'help-content'));
},
'submit @ui.search': function (e) {
e.preventDefault();
let query = this.ui.query.val();
this.fetch(['owner'], query)
.then(response => this.showData(response))
.catch(err => {
this.showError(err);
});
} }
}, },
@ -89,18 +41,39 @@ module.exports = Mn.View.extend({
onRender: function () { onRender: function () {
let view = this; let view = this;
view.fetch(['owner']) App.Api.Nginx.Certificates.getAll(['owner'])
.then(response => { .then(response => {
if (!view.isDestroyed()) { if (!view.isDestroyed()) {
if (response && response.length) { if (response && response.length) {
view.showData(response); view.showChildView('list_region', new ListView({
collection: new CertificateModel.Collection(response)
}));
} else { } else {
view.showEmpty(); let manage = App.Cache.User.canManage('certificates');
view.showChildView('list_region', new EmptyView({
title: App.i18n('certificates', 'empty'),
subtitle: App.i18n('all-hosts', 'empty-subtitle', {manage: manage}),
link: manage ? App.i18n('certificates', 'add') : null,
btn_color: 'pink',
permission: 'certificates',
action: function () {
App.Controller.showNginxCertificateForm();
}
}));
} }
} }
}) })
.catch(err => { .catch(err => {
view.showError(err); view.showChildView('list_region', new ErrorView({
code: err.code,
message: err.message,
retry: function () {
App.Controller.showNginxCertificates();
}
}));
console.error(err);
}) })
.then(() => { .then(() => {
view.ui.dimmer.removeClass('active'); view.ui.dimmer.removeClass('active');

View File

@ -1,15 +0,0 @@
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><%- i18n('certificates', 'reachability-title') %></h5>
</div>
<div class="modal-body">
<div class="waiting text-center">
<%= i18n('str', 'please-wait') %>
</div>
<div class="alert alert-danger error" role="alert"></div>
<div class="alert alert-success success" role="alert"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary cancel" disabled><%- i18n('str', 'close') %></button>
</div>
</div>

View File

@ -1,75 +0,0 @@
const Mn = require('backbone.marionette');
const App = require('../../main');
const template = require('./test.ejs');
module.exports = Mn.View.extend({
template: template,
className: 'modal-dialog',
ui: {
waiting: '.waiting',
error: '.error',
success: '.success',
close: 'button.cancel'
},
events: {
'click @ui.close': function (e) {
e.preventDefault();
if (this.model.get('back_to_add')) {
App.Controller.showNginxCertificateForm(this.model);
} else {
App.UI.closeModal();
}
},
},
onRender: function () {
this.ui.error.hide();
this.ui.success.hide();
App.Api.Nginx.Certificates.testHttpChallenge(this.model.get('domain_names'))
.then((result) => {
let allOk = true;
let text = '';
for (const domain in result) {
const status = result[domain];
if (status === 'ok') {
text += `<p><strong>${domain}:</strong> ${App.i18n('certificates', 'reachability-ok')}</p>`;
} else {
allOk = false;
if (status === 'no-host') {
text += `<p><strong>${domain}:</strong> ${App.i18n('certificates', 'reachability-not-resolved')}</p>`;
} else if (status === 'failed') {
text += `<p><strong>${domain}:</strong> ${App.i18n('certificates', 'reachability-failed-to-check')}</p>`;
} else if (status === '404') {
text += `<p><strong>${domain}:</strong> ${App.i18n('certificates', 'reachability-404')}</p>`;
} else if (status === 'wrong-data') {
text += `<p><strong>${domain}:</strong> ${App.i18n('certificates', 'reachability-wrong-data')}</p>`;
} else if (status.startsWith('other:')) {
const code = status.substring(6);
text += `<p><strong>${domain}:</strong> ${App.i18n('certificates', 'reachability-other', {code})}</p>`;
} else {
// This should never happen
text += `<p><strong>${domain}:</strong> ?</p>`;
}
}
}
this.ui.waiting.hide();
if (allOk) {
this.ui.success.html(text).show();
} else {
this.ui.error.html(text).show();
}
this.ui.close.prop('disabled', false);
})
.catch((e) => {
console.error(e);
this.ui.waiting.hide();
this.ui.error.text(App.i18n('certificates', 'reachability-failed-to-reach-api')).show();
this.ui.close.prop('disabled', false);
});
}
});

View File

@ -7,7 +7,7 @@
<form> <form>
<div class="row"> <div class="row">
<div class="col-sm-12 col-md-12"> <div class="col-sm-12 col-md-12">
<%= i18n('dead-hosts', 'delete-confirm', {domains: domain_names.join(', ').toHtmlEntities()}) %> <%= i18n('dead-hosts', 'delete-confirm', {domains: domain_names.join(', ')}) %>
<% if (certificate_id) { %> <% if (certificate_id) { %>
<br><br> <br><br>
<%- i18n('ssl', 'delete-ssl') %> <%- i18n('ssl', 'delete-ssl') %>

View File

@ -3,14 +3,6 @@
<div class="card-header"> <div class="card-header">
<h3 class="card-title"><%- i18n('dead-hosts', 'title') %></h3> <h3 class="card-title"><%- i18n('dead-hosts', 'title') %></h3>
<div class="card-options"> <div class="card-options">
<form class="search-form" role="search">
<div class="input-icon">
<span class="input-icon-addon">
<i class="fe fe-search"></i>
</span>
<input name="source-query" type="text" value="" class="form-control form-control-sm" placeholder="<%- i18n('dead-hosts', 'search') %>" aria-label="<%- i18n('dead-hosts', 'search') %>">
</div>
</form>
<a href="#" class="btn btn-outline-secondary btn-sm ml-2 help"><i class="fe fe-help-circle"></i></a> <a href="#" class="btn btn-outline-secondary btn-sm ml-2 help"><i class="fe fe-help-circle"></i></a>
<% if (showAddButton) { %> <% if (showAddButton) { %>
<a href="#" class="btn btn-outline-danger btn-sm ml-2 add-item"><%- i18n('dead-hosts', 'add') %></a> <a href="#" class="btn btn-outline-danger btn-sm ml-2 add-item"><%- i18n('dead-hosts', 'add') %></a>

View File

@ -14,44 +14,7 @@ module.exports = Mn.View.extend({
list_region: '.list-region', list_region: '.list-region',
add: '.add-item', add: '.add-item',
help: '.help', help: '.help',
dimmer: '.dimmer', dimmer: '.dimmer'
search: '.search-form',
query: 'input[name="source-query"]'
},
fetch: App.Api.Nginx.DeadHosts.getAll,
showData: function(response) {
this.showChildView('list_region', new ListView({
collection: new DeadHostModel.Collection(response)
}));
},
showError: function(err) {
this.showChildView('list_region', new ErrorView({
code: err.code,
message: err.message,
retry: function () {
App.Controller.showNginxDead();
}
}));
console.error(err);
},
showEmpty: function() {
let manage = App.Cache.User.canManage('dead_hosts');
this.showChildView('list_region', new EmptyView({
title: App.i18n('dead-hosts', 'empty'),
subtitle: App.i18n('all-hosts', 'empty-subtitle', {manage: manage}),
link: manage ? App.i18n('dead-hosts', 'add') : null,
btn_color: 'danger',
permission: 'dead_hosts',
action: function () {
App.Controller.showNginxDeadForm();
}
}));
}, },
regions: { regions: {
@ -67,17 +30,6 @@ module.exports = Mn.View.extend({
'click @ui.help': function (e) { 'click @ui.help': function (e) {
e.preventDefault(); e.preventDefault();
App.Controller.showHelp(App.i18n('dead-hosts', 'help-title'), App.i18n('dead-hosts', 'help-content')); App.Controller.showHelp(App.i18n('dead-hosts', 'help-title'), App.i18n('dead-hosts', 'help-content'));
},
'submit @ui.search': function (e) {
e.preventDefault();
let query = this.ui.query.val();
this.fetch(['owner', 'certificate'], query)
.then(response => this.showData(response))
.catch(err => {
this.showError(err);
});
} }
}, },
@ -88,18 +40,39 @@ module.exports = Mn.View.extend({
onRender: function () { onRender: function () {
let view = this; let view = this;
view.fetch(['owner', 'certificate']) App.Api.Nginx.DeadHosts.getAll(['owner', 'certificate'])
.then(response => { .then(response => {
if (!view.isDestroyed()) { if (!view.isDestroyed()) {
if (response && response.length) { if (response && response.length) {
view.showData(response); view.showChildView('list_region', new ListView({
collection: new DeadHostModel.Collection(response)
}));
} else { } else {
view.showEmpty(); let manage = App.Cache.User.canManage('dead_hosts');
view.showChildView('list_region', new EmptyView({
title: App.i18n('dead-hosts', 'empty'),
subtitle: App.i18n('all-hosts', 'empty-subtitle', {manage: manage}),
link: manage ? App.i18n('dead-hosts', 'add') : null,
btn_color: 'danger',
permission: 'dead_hosts',
action: function () {
App.Controller.showNginxDeadForm();
}
}));
} }
} }
}) })
.catch(err => { .catch(err => {
view.showError(err); view.showChildView('list_region', new ErrorView({
code: err.code,
message: err.message,
retry: function () {
App.Controller.showNginxDead();
}
}));
console.error(err);
}) })
.then(() => { .then(() => {
view.ui.dimmer.removeClass('active'); view.ui.dimmer.removeClass('active');

View File

@ -7,7 +7,7 @@
<form> <form>
<div class="row"> <div class="row">
<div class="col-sm-12 col-md-12"> <div class="col-sm-12 col-md-12">
<%= i18n('proxy-hosts', 'delete-confirm', {domains: domain_names.join(', ').toHtmlEntities()}) %> <%= i18n('proxy-hosts', 'delete-confirm', {domains: domain_names.join(', ')}) %>
<% if (certificate_id) { %> <% if (certificate_id) { %>
<br><br> <br><br>
<%- i18n('ssl', 'delete-ssl') %> <%- i18n('ssl', 'delete-ssl') %>

View File

@ -257,17 +257,16 @@
<div role="tabpanel" class="tab-pane" id="advanced"> <div role="tabpanel" class="tab-pane" id="advanced">
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<p><%- i18n('all-hosts', 'advanced-config-var-headline') %></p> <p>Nginx variables available to you are:</p>
<ul class="text-monospace"> <ul class="text-monospace">
<li><code>$server</code> <%- i18n('proxy-hosts', 'forward-host') %></li> <li>$server # Host/IP</li>
<li><code>$port</code> <%- i18n('proxy-hosts', 'forward-port') %></li> <li>$port # Port Number</li>
<li><code>$forward_scheme</code> <%- i18n('proxy-hosts', 'forward-scheme') %></li> <li>$forward_scheme # http or https</li>
</ul> </ul>
<div class="form-group mb-0"> <div class="form-group mb-0">
<label class="form-label"><%- i18n('all-hosts', 'advanced-config') %></label> <label class="form-label"><%- i18n('all-hosts', 'advanced-config') %></label>
<textarea name="advanced_config" rows="8" class="form-control text-monospace" placeholder="# <%- i18n('all-hosts', 'advanced-warning') %>"><%- advanced_config %></textarea> <textarea name="advanced_config" rows="8" class="form-control text-monospace" placeholder="# <%- i18n('all-hosts', 'advanced-warning') %>"><%- advanced_config %></textarea>
</div> </div>
<p class="small text-gray"><i class="fe fe-alert-triangle"></i> <%- i18n('all-hosts', 'advanced-config-header-info') %></p>
</div> </div>
</div> </div>
</div> </div>

View File

@ -3,14 +3,6 @@
<div class="card-header"> <div class="card-header">
<h3 class="card-title"><%- i18n('proxy-hosts', 'title') %></h3> <h3 class="card-title"><%- i18n('proxy-hosts', 'title') %></h3>
<div class="card-options"> <div class="card-options">
<form class="search-form" role="search">
<div class="input-icon">
<span class="input-icon-addon">
<i class="fe fe-search"></i>
</span>
<input name="source-query" type="text" value="" class="form-control form-control-sm" placeholder="<%- i18n('proxy-hosts', 'search') %>" aria-label="<%- i18n('proxy-hosts', 'search') %>">
</div>
</form>
<a href="#" class="btn btn-outline-secondary btn-sm ml-2 help"><i class="fe fe-help-circle"></i></a> <a href="#" class="btn btn-outline-secondary btn-sm ml-2 help"><i class="fe fe-help-circle"></i></a>
<% if (showAddButton) { %> <% if (showAddButton) { %>
<a href="#" class="btn btn-outline-success btn-sm ml-2 add-item"><%- i18n('proxy-hosts', 'add') %></a> <a href="#" class="btn btn-outline-success btn-sm ml-2 add-item"><%- i18n('proxy-hosts', 'add') %></a>

View File

@ -14,44 +14,7 @@ module.exports = Mn.View.extend({
list_region: '.list-region', list_region: '.list-region',
add: '.add-item', add: '.add-item',
help: '.help', help: '.help',
dimmer: '.dimmer', dimmer: '.dimmer'
search: '.search-form',
query: 'input[name="source-query"]'
},
fetch: App.Api.Nginx.ProxyHosts.getAll,
showData: function(response) {
this.showChildView('list_region', new ListView({
collection: new ProxyHostModel.Collection(response)
}));
},
showError: function(err) {
this.showChildView('list_region', new ErrorView({
code: err.code,
message: err.message,
retry: function () {
App.Controller.showNginxProxy();
}
}));
console.error(err);
},
showEmpty: function() {
let manage = App.Cache.User.canManage('proxy_hosts');
this.showChildView('list_region', new EmptyView({
title: App.i18n('proxy-hosts', 'empty'),
subtitle: App.i18n('all-hosts', 'empty-subtitle', {manage: manage}),
link: manage ? App.i18n('proxy-hosts', 'add') : null,
btn_color: 'success',
permission: 'proxy_hosts',
action: function () {
App.Controller.showNginxProxyForm();
}
}));
}, },
regions: { regions: {
@ -67,17 +30,6 @@ module.exports = Mn.View.extend({
'click @ui.help': function (e) { 'click @ui.help': function (e) {
e.preventDefault(); e.preventDefault();
App.Controller.showHelp(App.i18n('proxy-hosts', 'help-title'), App.i18n('proxy-hosts', 'help-content')); App.Controller.showHelp(App.i18n('proxy-hosts', 'help-title'), App.i18n('proxy-hosts', 'help-content'));
},
'submit @ui.search': function (e) {
e.preventDefault();
let query = this.ui.query.val();
this.fetch(['owner', 'access_list', 'certificate'], query)
.then(response => this.showData(response))
.catch(err => {
this.showError(err);
});
} }
}, },
@ -88,18 +40,39 @@ module.exports = Mn.View.extend({
onRender: function () { onRender: function () {
let view = this; let view = this;
view.fetch(['owner', 'access_list', 'certificate']) App.Api.Nginx.ProxyHosts.getAll(['owner', 'access_list', 'certificate'])
.then(response => { .then(response => {
if (!view.isDestroyed()) { if (!view.isDestroyed()) {
if (response && response.length) { if (response && response.length) {
view.showData(response); view.showChildView('list_region', new ListView({
collection: new ProxyHostModel.Collection(response)
}));
} else { } else {
view.showEmpty(); let manage = App.Cache.User.canManage('proxy_hosts');
view.showChildView('list_region', new EmptyView({
title: App.i18n('proxy-hosts', 'empty'),
subtitle: App.i18n('all-hosts', 'empty-subtitle', {manage: manage}),
link: manage ? App.i18n('proxy-hosts', 'add') : null,
btn_color: 'success',
permission: 'proxy_hosts',
action: function () {
App.Controller.showNginxProxyForm();
}
}));
} }
} }
}) })
.catch(err => { .catch(err => {
view.showError(err); view.showChildView('list_region', new ErrorView({
code: err.code,
message: err.message,
retry: function () {
App.Controller.showNginxProxy();
}
}));
console.error(err);
}) })
.then(() => { .then(() => {
view.ui.dimmer.removeClass('active'); view.ui.dimmer.removeClass('active');

View File

@ -7,7 +7,7 @@
<form> <form>
<div class="row"> <div class="row">
<div class="col-sm-12 col-md-12"> <div class="col-sm-12 col-md-12">
<%= i18n('redirection-hosts', 'delete-confirm', {domains: domain_names.join(', ').toHtmlEntities()}) %> <%= i18n('redirection-hosts', 'delete-confirm', {domains: domain_names.join(', ')}) %>
<% if (certificate_id) { %> <% if (certificate_id) { %>
<br><br> <br><br>
<%- i18n('ssl', 'delete-ssl') %> <%- i18n('ssl', 'delete-ssl') %>

View File

@ -1,19 +1,11 @@
<div class="card"> <div class="card">
<div class="card-status bg-yellow"></div> <div class="card-status bg-yellow"></div>
<div class="card-header"> <div class="card-header">
<h3 class="card-title"><%- i18n('redirection-hosts', 'title') %></h3> <h3 class="card-title">Redirection Hosts</h3>
<div class="card-options"> <div class="card-options">
<form class="search-form" role="search">
<div class="input-icon">
<span class="input-icon-addon">
<i class="fe fe-search"></i>
</span>
<input name="source-query" type="text" value="" class="form-control form-control-sm" placeholder="<%- i18n('redirection-hosts', 'search') %>" aria-label="<%- i18n('redirection-hosts', 'search') %>">
</div>
</form>
<a href="#" class="btn btn-outline-secondary btn-sm ml-2 help"><i class="fe fe-help-circle"></i></a> <a href="#" class="btn btn-outline-secondary btn-sm ml-2 help"><i class="fe fe-help-circle"></i></a>
<% if (showAddButton) { %> <% if (showAddButton) { %>
<a href="#" class="btn btn-outline-yellow btn-sm ml-2 add-item"><%- i18n('redirection-hosts', 'add') %></a> <a href="#" class="btn btn-outline-yellow btn-sm ml-2 add-item">Add Redirection Host</a>
<% } %> <% } %>
</div> </div>
</div> </div>

View File

@ -14,43 +14,7 @@ module.exports = Mn.View.extend({
list_region: '.list-region', list_region: '.list-region',
add: '.add-item', add: '.add-item',
help: '.help', help: '.help',
dimmer: '.dimmer', dimmer: '.dimmer'
search: '.search-form',
query: 'input[name="source-query"]'
},
fetch: App.Api.Nginx.RedirectionHosts.getAll,
showData: function(response) {
this.showChildView('list_region', new ListView({
collection: new RedirectionHostModel.Collection(response)
}));
},
showError: function(err) {
this.showChildView('list_region', new ErrorView({
code: err.code,
message: err.message,
retry: function () {
App.Controller.showNginxRedirection();
}
}));
console.error(err);
},
showEmpty: function() {
let manage = App.Cache.User.canManage('redirection_hosts');
this.showChildView('list_region', new EmptyView({
title: App.i18n('redirection-hosts', 'empty'),
subtitle: App.i18n('all-hosts', 'empty-subtitle', {manage: manage}),
link: manage ? App.i18n('redirection-hosts', 'add') : null,
btn_color: 'yellow',
permission: 'redirection_hosts',
action: function () {
App.Controller.showNginxRedirectionForm();
}
}));
}, },
regions: { regions: {
@ -66,17 +30,6 @@ module.exports = Mn.View.extend({
'click @ui.help': function (e) { 'click @ui.help': function (e) {
e.preventDefault(); e.preventDefault();
App.Controller.showHelp(App.i18n('redirection-hosts', 'help-title'), App.i18n('redirection-hosts', 'help-content')); App.Controller.showHelp(App.i18n('redirection-hosts', 'help-title'), App.i18n('redirection-hosts', 'help-content'));
},
'submit @ui.search': function (e) {
e.preventDefault();
let query = this.ui.query.val();
this.fetch(['owner', 'certificate'], query)
.then(response => this.showData(response))
.catch(err => {
this.showError(err);
});
} }
}, },
@ -87,18 +40,39 @@ module.exports = Mn.View.extend({
onRender: function () { onRender: function () {
let view = this; let view = this;
view.fetch(['owner', 'certificate']) App.Api.Nginx.RedirectionHosts.getAll(['owner', 'certificate'])
.then(response => { .then(response => {
if (!view.isDestroyed()) { if (!view.isDestroyed()) {
if (response && response.length) { if (response && response.length) {
view.showData(response); view.showChildView('list_region', new ListView({
collection: new RedirectionHostModel.Collection(response)
}));
} else { } else {
view.showEmpty(); let manage = App.Cache.User.canManage('redirection_hosts');
view.showChildView('list_region', new EmptyView({
title: App.i18n('redirection-hosts', 'empty'),
subtitle: App.i18n('all-hosts', 'empty-subtitle', {manage: manage}),
link: manage ? App.i18n('redirection-hosts', 'add') : null,
btn_color: 'yellow',
permission: 'redirection_hosts',
action: function () {
App.Controller.showNginxRedirectionForm();
}
}));
} }
} }
}) })
.catch(err => { .catch(err => {
view.showError(err); view.showChildView('list_region', new ErrorView({
code: err.code,
message: err.message,
retry: function () {
App.Controller.showNginxRedirection();
}
}));
console.error(err);
}) })
.then(() => { .then(() => {
view.ui.dimmer.removeClass('active'); view.ui.dimmer.removeClass('active');

View 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">&nbsp;</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>

View 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');
});
}
}
});

View 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">&nbsp;</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>

View 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();
}
}
});

View 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>
<% } %>

View 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);
}
});

View File

@ -0,0 +1,12 @@
<thead>
<th width="30">&nbsp;</th>
<th><%- i18n('all-hosts', 'domain-name') %></th>
<th><%- i18n('str', 'destination') %></th>
<th><%- i18n('str', 'status') %></th>
<% if (canManage) { %>
<th>&nbsp;</th>
<% } %>
</thead>
<tbody>
<!-- items -->
</tbody>

View 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
}));
}
});

View 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>

View 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');
});
}
});

View File

@ -3,14 +3,6 @@
<div class="card-header"> <div class="card-header">
<h3 class="card-title"><%- i18n('streams', 'title') %></h3> <h3 class="card-title"><%- i18n('streams', 'title') %></h3>
<div class="card-options"> <div class="card-options">
<form class="search-form" role="search">
<div class="input-icon">
<span class="input-icon-addon">
<i class="fe fe-search"></i>
</span>
<input name="source-query" type="text" value="" class="form-control form-control-sm" placeholder="<%- i18n('streams', 'search') %>" aria-label="<%- i18n('streams', 'search') %>">
</div>
</form>
<a href="#" class="btn btn-outline-secondary btn-sm ml-2 help"><i class="fe fe-help-circle"></i></a> <a href="#" class="btn btn-outline-secondary btn-sm ml-2 help"><i class="fe fe-help-circle"></i></a>
<% if (showAddButton) { %> <% if (showAddButton) { %>
<a href="#" class="btn btn-outline-blue btn-sm ml-2 add-item"><%- i18n('streams', 'add') %></a> <a href="#" class="btn btn-outline-blue btn-sm ml-2 add-item"><%- i18n('streams', 'add') %></a>

View File

@ -14,44 +14,7 @@ module.exports = Mn.View.extend({
list_region: '.list-region', list_region: '.list-region',
add: '.add-item', add: '.add-item',
help: '.help', help: '.help',
dimmer: '.dimmer', dimmer: '.dimmer'
search: '.search-form',
query: 'input[name="source-query"]'
},
fetch: App.Api.Nginx.Streams.getAll,
showData: function(response) {
this.showChildView('list_region', new ListView({
collection: new StreamModel.Collection(response)
}));
},
showError: function(err) {
this.showChildView('list_region', new ErrorView({
code: err.code,
message: err.message,
retry: function () {
App.Controller.showNginxStream();
}
}));
console.error(err);
},
showEmpty: function() {
let manage = App.Cache.User.canManage('streams');
this.showChildView('list_region', new EmptyView({
title: App.i18n('streams', 'empty'),
subtitle: App.i18n('all-hosts', 'empty-subtitle', {manage: manage}),
link: manage ? App.i18n('streams', 'add') : null,
btn_color: 'blue',
permission: 'streams',
action: function () {
App.Controller.showNginxStreamForm();
}
}));
}, },
regions: { regions: {
@ -67,17 +30,6 @@ module.exports = Mn.View.extend({
'click @ui.help': function (e) { 'click @ui.help': function (e) {
e.preventDefault(); e.preventDefault();
App.Controller.showHelp(App.i18n('streams', 'help-title'), App.i18n('streams', 'help-content')); App.Controller.showHelp(App.i18n('streams', 'help-title'), App.i18n('streams', 'help-content'));
},
'submit @ui.search': function (e) {
e.preventDefault();
let query = this.ui.query.val();
this.fetch(['owner'], query)
.then(response => this.showData(response))
.catch(err => {
this.showError(err);
});
} }
}, },
@ -88,18 +40,39 @@ module.exports = Mn.View.extend({
onRender: function () { onRender: function () {
let view = this; let view = this;
view.fetch(['owner']) App.Api.Nginx.Streams.getAll(['owner'])
.then(response => { .then(response => {
if (!view.isDestroyed()) { if (!view.isDestroyed()) {
if (response && response.length) { if (response && response.length) {
view.showData(response); view.showChildView('list_region', new ListView({
collection: new StreamModel.Collection(response)
}));
} else { } else {
view.showEmpty(); let manage = App.Cache.User.canManage('streams');
view.showChildView('list_region', new EmptyView({
title: App.i18n('streams', 'empty'),
subtitle: App.i18n('all-hosts', 'empty-subtitle', {manage: manage}),
link: manage ? App.i18n('streams', 'add') : null,
btn_color: 'blue',
permission: 'streams',
action: function () {
App.Controller.showNginxStreamForm();
}
}));
} }
} }
}) })
.catch(err => { .catch(err => {
view.showError(err); view.showChildView('list_region', new ErrorView({
code: err.code,
message: err.message,
retry: function () {
App.Controller.showNginxStream();
}
}));
console.error(err);
}) })
.then(() => { .then(() => {
view.ui.dimmer.removeClass('active'); view.ui.dimmer.removeClass('active');

View File

@ -4,16 +4,17 @@ const Controller = require('./controller');
module.exports = AppRouter.default.extend({ module.exports = AppRouter.default.extend({
controller: Controller, controller: Controller,
appRoutes: { appRoutes: {
users: 'showUsers', users: 'showUsers',
logout: 'logout', logout: 'logout',
'nginx/proxy': 'showNginxProxy', 'nginx/proxy': 'showNginxProxy',
'nginx/redirection': 'showNginxRedirection', 'nginx/redirection': 'showNginxRedirection',
'nginx/404': 'showNginxDead', 'nginx/404': 'showNginxDead',
'nginx/stream': 'showNginxStream', 'nginx/ssl-passthrough': 'showNginxSslPassthrough',
'nginx/access': 'showNginxAccess', 'nginx/stream': 'showNginxStream',
'nginx/certificates': 'showNginxCertificates', 'nginx/access': 'showNginxAccess',
'audit-log': 'showAuditLog', 'nginx/certificates': 'showNginxCertificates',
'settings': 'showSettings', 'audit-log': 'showAuditLog',
'*default': 'showDashboard' 'settings': 'showSettings',
'*default': 'showDashboard'
} }
}); });

View File

@ -3,7 +3,7 @@
<div class="row align-items-center"> <div class="row align-items-center">
<div class="col-auto"> <div class="col-auto">
<ul class="list-inline list-inline-dots mb-0"> <ul class="list-inline list-inline-dots mb-0">
<li class="list-inline-item"><a href="https://github.com/jc21/nginx-proxy-manager?utm_source=nginx-proxy-manager" target="_blank"><%- i18n('footer', 'fork-me') %></a></li> <li class="list-inline-item"><a href="https://github.com/jc21/nginx-proxy-manager?utm_source=nginx-proxy-manager"><%- i18n('footer', 'fork-me') %></a></li>
</ul> </ul>
</div> </div>
</div> </div>

View File

@ -20,6 +20,10 @@
<a href="/nginx/stream" class="dropdown-item "><%- i18n('streams', 'title') %></a> <a href="/nginx/stream" class="dropdown-item "><%- i18n('streams', 'title') %></a>
<% } %> <% } %>
<% if (canShow('ssl_passthrough_hosts')) { %>
<a href="/nginx/ssl-passthrough" class="dropdown-item "><%- i18n('ssl-passthrough-hosts', 'title') %></a>
<% } %>
<% if (canShow('dead_hosts')) { %> <% if (canShow('dead_hosts')) { %>
<a href="/nginx/404" class="dropdown-item "><%- i18n('dead-hosts', 'title') %></a> <a href="/nginx/404" class="dropdown-item "><%- i18n('dead-hosts', 'title') %></a>
<% } %> <% } %>

View File

@ -7,7 +7,7 @@
<form> <form>
<div class="row"> <div class="row">
<div class="col-sm-12 col-md-12"> <div class="col-sm-12 col-md-12">
<%= i18n('users', 'delete-confirm', {name: name.toHtmlEntities()}) %> <%= i18n('users', 'delete-confirm', {name: name}) %>
</div> </div>
</div> </div>
</form> </form>

View File

@ -31,9 +31,9 @@
</div> </div>
<% <%
var list = ['proxy-hosts', 'redirection-hosts', 'dead-hosts', 'streams', 'access-lists', 'certificates']; var list = ['proxy-hosts', 'redirection-hosts', 'dead-hosts', 'streams', 'ssl-passthrough-hosts', 'access-lists', 'certificates'];
list.map(function(item) { list.map(function(item) {
var perm = item.replace('-', '_'); var perm = item.replace(/-/g, '_');
%> %>
<div class="col-sm-12 col-md-12"> <div class="col-sm-12 col-md-12">
<div class="form-group"> <div class="form-group">

View File

@ -29,12 +29,13 @@ module.exports = Mn.View.extend({
if (view.model.isAdmin()) { if (view.model.isAdmin()) {
// Force some attributes for admin // Force some attributes for admin
data = _.assign({}, data, { data = _.assign({}, data, {
access_lists: 'manage', access_lists: 'manage',
dead_hosts: 'manage', dead_hosts: 'manage',
proxy_hosts: 'manage', proxy_hosts: 'manage',
redirection_hosts: 'manage', redirection_hosts: 'manage',
streams: 'manage', ssl_passthrough_hosts: 'manage',
certificates: 'manage' streams: 'manage',
certificates: 'manage'
}); });
} }

View File

@ -3,14 +3,6 @@
<div class="card-header"> <div class="card-header">
<h3 class="card-title"><%- i18n('users', 'title') %></h3> <h3 class="card-title"><%- i18n('users', 'title') %></h3>
<div class="card-options"> <div class="card-options">
<form class="search-form" role="search">
<div class="input-icon">
<span class="input-icon-addon">
<i class="fe fe-search"></i>
</span>
<input name="source-query" type="text" value="" class="form-control form-control-sm" placeholder="<%- i18n('users', 'search') %>" aria-label="<%- i18n('users', 'search') %>">
</div>
</form>
<a href="#" class="btn btn-outline-teal btn-sm ml-2 add-item"><%- i18n('users', 'add') %></a> <a href="#" class="btn btn-outline-teal btn-sm ml-2 add-item"><%- i18n('users', 'add') %></a>
</div> </div>
</div> </div>

View File

@ -12,29 +12,7 @@ module.exports = Mn.View.extend({
ui: { ui: {
list_region: '.list-region', list_region: '.list-region',
add: '.add-item', add: '.add-item',
dimmer: '.dimmer', dimmer: '.dimmer'
search: '.search-form',
query: 'input[name="source-query"]'
},
fetch: App.Api.Users.getAll,
showData: function(response) {
this.showChildView('list_region', new ListView({
collection: new UserModel.Collection(response)
}));
},
showError: function(err) {
this.showChildView('list_region', new ErrorView({
code: err.code,
message: err.message,
retry: function () {
App.Controller.showUsers();
}
}));
console.error(err);
}, },
regions: { regions: {
@ -45,31 +23,30 @@ module.exports = Mn.View.extend({
'click @ui.add': function (e) { 'click @ui.add': function (e) {
e.preventDefault(); e.preventDefault();
App.Controller.showUserForm(new UserModel.Model()); App.Controller.showUserForm(new UserModel.Model());
},
'submit @ui.search': function (e) {
e.preventDefault();
let query = this.ui.query.val();
this.fetch(['permissions'], query)
.then(response => this.showData(response))
.catch(err => {
this.showError(err);
});
} }
}, },
onRender: function () { onRender: function () {
let view = this; let view = this;
view.fetch(['permissions']) App.Api.Users.getAll(['permissions'])
.then(response => { .then(response => {
if (!view.isDestroyed() && response && response.length) { if (!view.isDestroyed() && response && response.length) {
view.showData(response); view.showChildView('list_region', new ListView({
collection: new UserModel.Collection(response)
}));
} }
}) })
.catch(err => { .catch(err => {
view.showError(err); view.showChildView('list_region', new ErrorView({
code: err.code,
message: err.message,
retry: function () {
App.Controller.showUsers();
}
}));
console.error(err);
}) })
.then(() => { .then(() => {
view.ui.dimmer.removeClass('active'); view.ui.dimmer.removeClass('active');

View File

@ -60,7 +60,7 @@
}, },
"footer": { "footer": {
"fork-me": "Fork me on Github", "fork-me": "Fork me on Github",
"copy": "&copy; 2022 <a href=\"{url}\" target=\"_blank\">jc21.com</a>.", "copy": "&copy; 2021 <a href=\"{url}\" target=\"_blank\">jc21.com</a>.",
"theme": "Theme by <a href=\"{url}\" target=\"_blank\">Tabler</a>" "theme": "Theme by <a href=\"{url}\" target=\"_blank\">Tabler</a>"
}, },
"dashboard": { "dashboard": {
@ -72,6 +72,7 @@
"enable-ssl": "Enable SSL", "enable-ssl": "Enable SSL",
"force-ssl": "Force SSL", "force-ssl": "Force SSL",
"http2-support": "HTTP/2 Support", "http2-support": "HTTP/2 Support",
"domain-name": "Domain Name",
"domain-names": "Domain Names", "domain-names": "Domain Names",
"cert-provider": "Certificate Provider", "cert-provider": "Certificate Provider",
"block-exploits": "Block Common Exploits", "block-exploits": "Block Common Exploits",
@ -84,8 +85,6 @@
"advanced": "Advanced", "advanced": "Advanced",
"advanced-warning": "Enter your custom Nginx configuration here at your own risk!", "advanced-warning": "Enter your custom Nginx configuration here at your own risk!",
"advanced-config": "Custom Nginx Configuration", "advanced-config": "Custom Nginx Configuration",
"advanced-config-var-headline": "These proxy details are available as nginx variables:",
"advanced-config-header-info": "Please note, that any add_header or set_header directives added here will not be used by nginx. You will have to add a custom location '/' and add the header in the custom config there.",
"hsts-enabled": "HSTS Enabled", "hsts-enabled": "HSTS Enabled",
"hsts-subdomains": "HSTS Subdomains", "hsts-subdomains": "HSTS Subdomains",
"locations": "Custom locations" "locations": "Custom locations"
@ -117,6 +116,19 @@
"processing-info": "Processing... This might take a few minutes.", "processing-info": "Processing... This might take a few minutes.",
"passphrase-protection-support-info": "Key files protected with a passphrase are not supported." "passphrase-protection-support-info": "Key files protected with a passphrase are not supported."
}, },
"ssl-passthrough-hosts": {
"title": "SSL Passthrough Hosts",
"empty": "There are no SSL Passthrough Hosts",
"add": "Add SSL Passthrough Hosts",
"form-title": "{id, select, undefined{New} other{Edit}} SSL Passthrough Host",
"forwarding-host": "Forward Host",
"forwarding-port": "Forward Port",
"delete": "Delete SSL Passthrough Host",
"delete-confirm": "Are you sure you want to delete this SSL Passthrough Host?",
"is-disabled-warning": "SSL Passthrough Hosts are not enabled in the environment. Please see <a href=\"{url}\" target=\"_blank\">the docs</a> for more information.",
"help-title": "What is an SSL Passthrough Host?",
"help-content": "An SSL Passthrough Host will allow you to proxy a server without SSL termination. This means the SSL encryption of the server will be passed right through the proxy, retaining the upstream certificate.\n 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 to know where to send this 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 in HTTP requests.\n\nDue 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."
},
"proxy-hosts": { "proxy-hosts": {
"title": "Proxy Hosts", "title": "Proxy Hosts",
"empty": "There are no Proxy Hosts", "empty": "There are no Proxy Hosts",
@ -132,8 +144,7 @@
"access-list": "Access List", "access-list": "Access List",
"allow-websocket-upgrade": "Websockets Support", "allow-websocket-upgrade": "Websockets Support",
"ignore-invalid-upstream-ssl": "Ignore Invalid SSL", "ignore-invalid-upstream-ssl": "Ignore Invalid SSL",
"custom-forward-host-help": "Add a path for sub-folder forwarding.\nExample: 203.0.113.25/path/", "custom-forward-host-help": "Add a path for sub-folder forwarding.\nExample: 203.0.113.25/path"
"search": "Search Host…"
}, },
"redirection-hosts": { "redirection-hosts": {
"title": "Redirection Hosts", "title": "Redirection Hosts",
@ -144,11 +155,10 @@
"forward-http-status-code": "HTTP Code", "forward-http-status-code": "HTTP Code",
"forward-domain": "Forward Domain", "forward-domain": "Forward Domain",
"preserve-path": "Preserve Path", "preserve-path": "Preserve Path",
"delete": "Delete Redirection Host", "delete": "Delete Proxy Host",
"delete-confirm": "Are you sure you want to delete the Redirection host for: <strong>{domains}</strong>?", "delete-confirm": "Are you sure you want to delete the Redirection host for: <strong>{domains}</strong>?",
"help-title": "What is a Redirection Host?", "help-title": "What is a Redirection Host?",
"help-content": "A Redirection Host will redirect requests from the incoming domain and push the viewer to another domain.\nThe most common reason to use this type of host is when your website changes domains but you still have search engine or referrer links pointing to the old domain.", "help-content": "A Redirection Host will redirect requests from the incoming domain and push the viewer to another domain.\nThe most common reason to use this type of host is when your website changes domains but you still have search engine or referrer links pointing to the old domain."
"search": "Search Host…"
}, },
"dead-hosts": { "dead-hosts": {
"title": "404 Hosts", "title": "404 Hosts",
@ -158,8 +168,7 @@
"delete": "Delete 404 Host", "delete": "Delete 404 Host",
"delete-confirm": "Are you sure you want to delete this 404 Host?", "delete-confirm": "Are you sure you want to delete this 404 Host?",
"help-title": "What is a 404 Host?", "help-title": "What is a 404 Host?",
"help-content": "A 404 Host is simply a host setup that shows a 404 page.\nThis can be useful when your domain is listed in search engines and you want to provide a nicer error page or specifically to tell the search indexers that the domain pages no longer exist.\nAnother benefit of having this host is to track the logs for hits to it and view the referrers.", "help-content": "A 404 Host is simply a host setup that shows a 404 page.\nThis can be useful when your domain is listed in search engines and you want to provide a nicer error page or specifically to tell the search indexers that the domain pages no longer exist.\nAnother benefit of having this host is to track the logs for hits to it and view the referrers."
"search": "Search Host…"
}, },
"streams": { "streams": {
"title": "Streams", "title": "Streams",
@ -178,8 +187,7 @@
"delete": "Delete Stream", "delete": "Delete Stream",
"delete-confirm": "Are you sure you want to delete this Stream?", "delete-confirm": "Are you sure you want to delete this Stream?",
"help-title": "What is a Stream?", "help-title": "What is a Stream?",
"help-content": "A relatively new feature for Nginx, a Stream will serve to forward TCP/UDP traffic directly to another computer on the network.\nIf you're running game servers, FTP or SSH servers this can come in handy.", "help-content": "A relatively new feature for Nginx, a Stream will serve to forward TCP/UDP traffic directly to another computer on the network.\nIf you're running game servers, FTP or SSH servers this can come in handy."
"search": "Search Incoming Port…"
}, },
"certificates": { "certificates": {
"title": "SSL Certificates", "title": "SSL Certificates",
@ -194,19 +202,8 @@
"other-certificate-key": "Certificate Key", "other-certificate-key": "Certificate Key",
"other-intermediate-certificate": "Intermediate Certificate", "other-intermediate-certificate": "Intermediate Certificate",
"force-renew": "Renew Now", "force-renew": "Renew Now",
"test-reachability": "Test Server Reachability",
"reachability-title": "Test Server Reachability",
"reachability-info": "Test whether the domains are reachable from the public internet using Site24x7. This is not necessary when using the DNS Challenge.",
"reachability-failed-to-reach-api": "Communication with the API failed, is NPM running correctly?",
"reachability-failed-to-check": "Failed to check the reachability due to a communication error with site24x7.com.",
"reachability-ok": "Your server is reachable and creating certificates should be possible.",
"reachability-404": "There is a server found at this domain but it does not seem to be Nginx Proxy Manager. Please make sure your domain points to the IP where your NPM instance is running.",
"reachability-not-resolved": "There is no server available at this domain. Please make sure your domain exists and points to the IP where your NPM instance is running and if necessary port 80 is forwarded in your router.",
"reachability-wrong-data": "There is a server found at this domain but it returned an unexpected data. Is it the NPM server? Please make sure your domain points to the IP where your NPM instance is running.",
"reachability-other": "There is a server found at this domain but it returned an unexpected status code {code}. Is it the NPM server? Please make sure your domain points to the IP where your NPM instance is running.",
"download": "Download", "download": "Download",
"renew-title": "Renew Let's Encrypt Certificate", "renew-title": "Renew Let'sEncrypt Certificate"
"search": "Search Certificate…"
}, },
"access-lists": { "access-lists": {
"title": "Access Lists", "title": "Access Lists",
@ -230,8 +227,7 @@
"satisfy-any": "Satisfy Any", "satisfy-any": "Satisfy Any",
"pass-auth": "Pass Auth to Host", "pass-auth": "Pass Auth to Host",
"access-add": "Add", "access-add": "Add",
"auth-add": "Add", "auth-add": "Add"
"search": "Search Access…"
}, },
"users": { "users": {
"title": "Users", "title": "Users",
@ -257,8 +253,7 @@
"perms-visibility-all": "All Items", "perms-visibility-all": "All Items",
"perm-manage": "Manage", "perm-manage": "Manage",
"perm-view": "View Only", "perm-view": "View Only",
"perm-hidden": "Hidden", "perm-hidden": "Hidden"
"search": "Search User…"
}, },
"audit-log": { "audit-log": {
"title": "Audit Log", "title": "Audit Log",
@ -267,6 +262,7 @@
"proxy-host": "Proxy Host", "proxy-host": "Proxy Host",
"redirection-host": "Redirection Host", "redirection-host": "Redirection Host",
"dead-host": "404 Host", "dead-host": "404 Host",
"ssl-passthrough-host": "SSL Passthrough Host",
"stream": "Stream", "stream": "Stream",
"user": "User", "user": "User",
"certificate": "Certificate", "certificate": "Certificate",
@ -279,8 +275,7 @@
"renewed": "Renewed {name}", "renewed": "Renewed {name}",
"meta-title": "Details for Event", "meta-title": "Details for Event",
"view-meta": "View Details", "view-meta": "View Details",
"date": "Date", "date": "Date"
"search": "Search Log…"
}, },
"settings": { "settings": {
"title": "Settings", "title": "Settings",

View File

@ -103,13 +103,6 @@ window.tabler = {
} }
}; };
String.prototype.toHtmlEntities = function() {
return this.replace(/./gm, function(s) {
// return "&#" + s.charCodeAt(0) + ";";
return (s.match(/[a-z0-9\s]+/i)) ? s : "&#" + s.charCodeAt(0) + ";";
});
};
require('tabler-core'); require('tabler-core');
const App = require('./app/main'); const App = require('./app/main');

View File

@ -17,7 +17,7 @@
<div class="card-title"><%- i18n('login', 'title') %></div> <div class="card-title"><%- i18n('login', 'title') %></div>
<div class="form-group"> <div class="form-group">
<label class="form-label"><%- i18n('str', 'email-address') %></label> <label class="form-label"><%- i18n('str', 'email-address') %></label>
<input name="identity" type="email" class="form-control" placeholder="<%- i18n('str', 'email-address') %>" required autofocus> <input name="identity" type="email" class="form-control" placeholder="<%- i18n('str', 'email-address') %>" required>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="form-label"><%- i18n('str', 'password') %></label> <label class="form-label"><%- i18n('str', 'password') %></label>

View File

@ -0,0 +1,27 @@
const Backbone = require('backbone');
const model = Backbone.Model.extend({
idAttribute: 'id',
defaults: function () {
return {
id: undefined,
created_on: null,
modified_on: null,
domain_name: null,
forwarding_host: null,
forwarding_port: null,
enabled: true,
meta: {},
// The following are expansions:
owner: null
};
}
});
module.exports = {
Model: model,
Collection: Backbone.Collection.extend({
model: model
})
};

View File

@ -7,6 +7,7 @@
"@babel/core": "^7.9.0", "@babel/core": "^7.9.0",
"babel-core": "^6.26.3", "babel-core": "^6.26.3",
"babel-loader": "^8.1.0", "babel-loader": "^8.1.0",
"babel-minify-webpack-plugin": "^0.3.1",
"babel-preset-env": "^1.7.0", "babel-preset-env": "^1.7.0",
"backbone": "^1.4.0", "backbone": "^1.4.0",
"backbone.marionette": "^4.1.2", "backbone.marionette": "^4.1.2",
@ -26,7 +27,7 @@
"messageformat": "^2.3.0", "messageformat": "^2.3.0",
"messageformat-loader": "^0.8.1", "messageformat-loader": "^0.8.1",
"mini-css-extract-plugin": "^0.9.0", "mini-css-extract-plugin": "^0.9.0",
"moment": "^2.29.4", "moment": "^2.24.0",
"node-sass": "^6.0.1", "node-sass": "^6.0.1",
"nodemon": "^2.0.2", "nodemon": "^2.0.2",
"numeral": "^2.0.6", "numeral": "^2.0.6",

View File

@ -12,6 +12,15 @@ a:hover {
color: darken($primary-color, 10%); color: darken($primary-color, 10%);
} }
.alert-danger a {
color: #6b1110;
text-decoration: underline;
}
a:hover {
color: darken(#6b1110, 10%);
}
.dropdown-header { .dropdown-header {
padding-left: 1rem; padding-left: 1rem;
} }

View File

@ -13,8 +13,8 @@ module.exports = {
}, },
output: { output: {
path: path.resolve(__dirname, 'dist'), path: path.resolve(__dirname, 'dist'),
filename: `js/[name].bundle.js?v=${PACKAGE.version}`, filename: 'js/[name].bundle.js',
chunkFilename: `js/[name].bundle.[id].js?v=${PACKAGE.version}`, chunkFilename: 'js/[name].bundle.[id].js',
publicPath: '/' publicPath: '/'
}, },
resolve: { resolve: {
@ -92,17 +92,17 @@ module.exports = {
] ]
}, },
{ {
test: /source-sans-pro.*\.(woff(2)?)(\?v=\d+\.\d+\.\d+)?$/, test: /source-sans-pro.*\.(woff(2)?)(\?v=\d+\.\d+\.\d+)?$/,
use: [ use: [
{ {
loader: 'file-loader', loader: 'file-loader',
options: { options: {
name: '[name].[ext]', name: '[name].[ext]',
outputPath: 'assets/' outputPath: 'assets/'
} }
} }
] ]
} }
] ]
}, },
plugins: [ plugins: [

View File

@ -823,6 +823,11 @@ babel-helper-define-map@^6.24.1:
babel-types "^6.26.0" babel-types "^6.26.0"
lodash "^4.17.4" lodash "^4.17.4"
babel-helper-evaluate-path@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/babel-helper-evaluate-path/-/babel-helper-evaluate-path-0.3.0.tgz#2439545e0b6eae5b7f49b790acbebd6b9a73df20"
integrity sha512-dRFlMTqUJRGzx5a2smKxmptDdNCXKSkPcXWzKLwAV72hvIZumrd/0z9RcewHkr7PmAEq+ETtpD1GK6wZ6ZUXzw==
babel-helper-explode-assignable-expression@^6.24.1: babel-helper-explode-assignable-expression@^6.24.1:
version "6.24.1" version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz#f25b82cf7dc10433c55f70592d5746400ac22caa" resolved "https://registry.yarnpkg.com/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz#f25b82cf7dc10433c55f70592d5746400ac22caa"
@ -832,6 +837,11 @@ babel-helper-explode-assignable-expression@^6.24.1:
babel-traverse "^6.24.1" babel-traverse "^6.24.1"
babel-types "^6.24.1" babel-types "^6.24.1"
babel-helper-flip-expressions@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/babel-helper-flip-expressions/-/babel-helper-flip-expressions-0.3.0.tgz#f5b6394bd5219b43cf8f7b201535ed540c6e7fa2"
integrity sha512-kNGohWmtAG3b7tN1xocRQ5rsKkH/hpvZsMiGOJ1VwGJKhnwzR5KlB3rvKBaBPl5/IGHcopB2JN+r1SUEX1iMAw==
babel-helper-function-name@^6.24.1: babel-helper-function-name@^6.24.1:
version "6.24.1" version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz#d3475b8c03ed98242a25b48351ab18399d3580a9" resolved "https://registry.yarnpkg.com/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz#d3475b8c03ed98242a25b48351ab18399d3580a9"
@ -859,6 +869,21 @@ babel-helper-hoist-variables@^6.24.1:
babel-runtime "^6.22.0" babel-runtime "^6.22.0"
babel-types "^6.24.1" babel-types "^6.24.1"
babel-helper-is-nodes-equiv@^0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/babel-helper-is-nodes-equiv/-/babel-helper-is-nodes-equiv-0.0.1.tgz#34e9b300b1479ddd98ec77ea0bbe9342dfe39684"
integrity sha1-NOmzALFHnd2Y7HfqC76TQt/jloQ=
babel-helper-is-void-0@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/babel-helper-is-void-0/-/babel-helper-is-void-0-0.3.0.tgz#95570d20bd27b2206f68083ae9980ee7003d8fe7"
integrity sha512-JVqdX8y7Rf/x4NwbqtUI7mdQjL9HWoDnoAEQ8Gv8oxzjvbJv+n75f7l36m9Y8C7sCUltX3V5edndrp7Hp1oSXQ==
babel-helper-mark-eval-scopes@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/babel-helper-mark-eval-scopes/-/babel-helper-mark-eval-scopes-0.3.0.tgz#b4731314fdd7a89091271a5213b4e12d236e29e8"
integrity sha512-nrho5Dg4vl0VUgURVpGpEGiwbst5JX7efIyDHFxmkCx/ocQFnrPt8ze9Kxl6TKjR29bJ7D/XKY1NMlSxOQJRbQ==
babel-helper-optimise-call-expression@^6.24.1: babel-helper-optimise-call-expression@^6.24.1:
version "6.24.1" version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz#f7a13427ba9f73f8f4fa993c54a97882d1244257" resolved "https://registry.yarnpkg.com/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz#f7a13427ba9f73f8f4fa993c54a97882d1244257"
@ -887,6 +912,11 @@ babel-helper-remap-async-to-generator@^6.24.1:
babel-traverse "^6.24.1" babel-traverse "^6.24.1"
babel-types "^6.24.1" babel-types "^6.24.1"
babel-helper-remove-or-void@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/babel-helper-remove-or-void/-/babel-helper-remove-or-void-0.3.0.tgz#f43c86147c8fcc395a9528cbb31e7ff49d7e16e3"
integrity sha512-D68W1M3ibCcbg0ysh3ww4/O0g10X1CXK720oOuR8kpfY7w0yP4tVcpK7zDmI1JecynycTQYAZ1rhLJo9aVtIKQ==
babel-helper-replace-supers@^6.24.1: babel-helper-replace-supers@^6.24.1:
version "6.24.1" version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz#bf6dbfe43938d17369a213ca8a8bf74b6a90ab1a" resolved "https://registry.yarnpkg.com/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz#bf6dbfe43938d17369a213ca8a8bf74b6a90ab1a"
@ -899,6 +929,11 @@ babel-helper-replace-supers@^6.24.1:
babel-traverse "^6.24.1" babel-traverse "^6.24.1"
babel-types "^6.24.1" babel-types "^6.24.1"
babel-helper-to-multiple-sequence-expressions@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/babel-helper-to-multiple-sequence-expressions/-/babel-helper-to-multiple-sequence-expressions-0.3.0.tgz#8da2275ccc26995566118f7213abfd9af7214427"
integrity sha512-1uCrBD+EAaMnAYh7hc944n8Ga19y3daEnoXWPYDvFVsxMCc1l8aDjksApaCEaNSSuewq8BEcff47Cy1PbLg2Gw==
babel-helpers@^6.24.1: babel-helpers@^6.24.1:
version "6.24.1" version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.24.1.tgz#3471de9caec388e5c850e597e58a26ddf37602b2" resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.24.1.tgz#3471de9caec388e5c850e597e58a26ddf37602b2"
@ -925,6 +960,15 @@ babel-messages@^6.23.0:
dependencies: dependencies:
babel-runtime "^6.22.0" babel-runtime "^6.22.0"
babel-minify-webpack-plugin@^0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/babel-minify-webpack-plugin/-/babel-minify-webpack-plugin-0.3.1.tgz#292aa240af190e2dcadf4f684d6d84d179b6d5a4"
integrity sha512-Johg6Ju0Gxevk2R55eutMqnyXwlyUzCtwunBpiyNzoxGnKum+x5nfNuYZYHGd5Bmc1gmhjwzb7GkxHWOtYWmtQ==
dependencies:
babel-core "^6.26.0"
babel-preset-minify "^0.3.0"
webpack-sources "^1.0.1"
babel-plugin-check-es2015-constants@^6.22.0: babel-plugin-check-es2015-constants@^6.22.0:
version "6.22.0" version "6.22.0"
resolved "https://registry.yarnpkg.com/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz#35157b101426fd2ffd3da3f75c7d1e91835bbf8a" resolved "https://registry.yarnpkg.com/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz#35157b101426fd2ffd3da3f75c7d1e91835bbf8a"
@ -932,6 +976,82 @@ babel-plugin-check-es2015-constants@^6.22.0:
dependencies: dependencies:
babel-runtime "^6.22.0" babel-runtime "^6.22.0"
babel-plugin-minify-builtins@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/babel-plugin-minify-builtins/-/babel-plugin-minify-builtins-0.3.0.tgz#4740117a6a784063aaf8f092989cf9e4bd484860"
integrity sha512-MqhSHlxkmgURqj3144qPksbZ/qof1JWdumcbucc4tysFcf3P3V3z3munTevQgKEFNMd8F5/ECGnwb63xogLjAg==
dependencies:
babel-helper-evaluate-path "^0.3.0"
babel-plugin-minify-constant-folding@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/babel-plugin-minify-constant-folding/-/babel-plugin-minify-constant-folding-0.3.0.tgz#687e40336bd4ddd921e0e197f0006235ac184bb9"
integrity sha512-1XeRpx+aY1BuNY6QU/cm6P+FtEi3ar3XceYbmC+4q4W+2Ewq5pL7V68oHg1hKXkBIE0Z4/FjSoHz6vosZLOe/A==
dependencies:
babel-helper-evaluate-path "^0.3.0"
babel-plugin-minify-dead-code-elimination@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/babel-plugin-minify-dead-code-elimination/-/babel-plugin-minify-dead-code-elimination-0.3.0.tgz#a323f686c404b824186ba5583cf7996cac81719e"
integrity sha512-SjM2Fzg85YZz+q/PNJ/HU4O3W98FKFOiP9K5z3sfonlamGOzvZw3Eup2OTiEBsbbqTeY8yzNCAv3qpJRYCgGmw==
dependencies:
babel-helper-evaluate-path "^0.3.0"
babel-helper-mark-eval-scopes "^0.3.0"
babel-helper-remove-or-void "^0.3.0"
lodash.some "^4.6.0"
babel-plugin-minify-flip-comparisons@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/babel-plugin-minify-flip-comparisons/-/babel-plugin-minify-flip-comparisons-0.3.0.tgz#6627893a409c9f30ef7f2c89e0c6eea7ee97ddc4"
integrity sha512-B8lK+ekcpSNVH7PZpWDe5nC5zxjRiiT4nTsa6h3QkF3Kk6y9qooIFLemdGlqBq6j0zALEnebvCpw8v7gAdpgnw==
dependencies:
babel-helper-is-void-0 "^0.3.0"
babel-plugin-minify-guarded-expressions@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/babel-plugin-minify-guarded-expressions/-/babel-plugin-minify-guarded-expressions-0.3.0.tgz#2552d96189ef45d9a463f1a6b5e4fa110703ac8d"
integrity sha512-O+6CvF5/Ttsth3LMg4/BhyvVZ82GImeKMXGdVRQGK/8jFiP15EjRpdgFlxv3cnqRjqdYxLCS6r28VfLpb9C/kA==
dependencies:
babel-helper-flip-expressions "^0.3.0"
babel-plugin-minify-infinity@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/babel-plugin-minify-infinity/-/babel-plugin-minify-infinity-0.3.0.tgz#c5ec0edd433517cf31b3af17077c202beb48bbe7"
integrity sha512-Sj8ia3/w9158DWieUxU6/VvnYVy59geeFEkVgLZYBE8EBP+sN48tHtBM/jSgz0ejEdBlcfqJ6TnvPmVXTzR2BQ==
babel-plugin-minify-mangle-names@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/babel-plugin-minify-mangle-names/-/babel-plugin-minify-mangle-names-0.3.0.tgz#f28561bad0dd2f0380816816bb946e219b3b6135"
integrity sha512-PYTonhFWURsfAN8achDwvR5Xgy6EeTClLz+fSgGRqjAIXb0OyFm3/xfccbQviVi1qDXmlSnt6oJhBg8KE4Fn7Q==
dependencies:
babel-helper-mark-eval-scopes "^0.3.0"
babel-plugin-minify-numeric-literals@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/babel-plugin-minify-numeric-literals/-/babel-plugin-minify-numeric-literals-0.3.0.tgz#b57734a612e8a592005407323c321119f27d4b40"
integrity sha512-TgZj6ay8zDw74AS3yiIfoQ8vRSNJisYO/Du60S8nPV7EW7JM6fDMx5Sar6yVHlVuuwNgvDUBh191K33bVrAhpg==
babel-plugin-minify-replace@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/babel-plugin-minify-replace/-/babel-plugin-minify-replace-0.3.0.tgz#980125bbf7cbb5a637439de9d0b1b030a4693893"
integrity sha512-VR6tTg2Lt0TicHIOw04fsUtpPw7RaRP8PC8YzSFwEixnzvguZjZJoL7TgG7ZyEWQD1cJ96UezswECmFNa815bg==
babel-plugin-minify-simplify@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/babel-plugin-minify-simplify/-/babel-plugin-minify-simplify-0.3.0.tgz#14574cc74d21c81d3060fafa041010028189f11b"
integrity sha512-2M16ytQOCqBi7bYMu4DCWn8e6KyFCA108F6+tVrBJxOmm5u2sOmTFEa8s94tR9RHRRNYmcUf+rgidfnzL3ik9Q==
dependencies:
babel-helper-flip-expressions "^0.3.0"
babel-helper-is-nodes-equiv "^0.0.1"
babel-helper-to-multiple-sequence-expressions "^0.3.0"
babel-plugin-minify-type-constructors@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/babel-plugin-minify-type-constructors/-/babel-plugin-minify-type-constructors-0.3.0.tgz#7f5a86ef322c4746364e3c591b8514eeafea6ad4"
integrity sha512-XRXpvsUCPeVw9YEUw+9vSiugcSZfow81oIJT0yR9s8H4W7yJ6FHbImi5DJHoL8KcDUjYnL9wYASXk/fOkbyR6Q==
dependencies:
babel-helper-is-void-0 "^0.3.0"
babel-plugin-syntax-async-functions@^6.8.0: babel-plugin-syntax-async-functions@^6.8.0:
version "6.13.0" version "6.13.0"
resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz#cad9cad1191b5ad634bf30ae0872391e0647be95" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz#cad9cad1191b5ad634bf30ae0872391e0647be95"
@ -1155,6 +1275,33 @@ babel-plugin-transform-exponentiation-operator@^6.22.0:
babel-plugin-syntax-exponentiation-operator "^6.8.0" babel-plugin-syntax-exponentiation-operator "^6.8.0"
babel-runtime "^6.22.0" babel-runtime "^6.22.0"
babel-plugin-transform-inline-consecutive-adds@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-inline-consecutive-adds/-/babel-plugin-transform-inline-consecutive-adds-0.3.0.tgz#f07d93689c0002ed2b2b62969bdd99f734e03f57"
integrity sha512-iZsYAIjYLLfLK0yN5WVT7Xf7Y3wQ9Z75j9A8q/0IglQSpUt2ppTdHlwl/GeaXnxdaSmsxBu861klbTBbv2n+RA==
babel-plugin-transform-member-expression-literals@^6.9.0:
version "6.9.4"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-member-expression-literals/-/babel-plugin-transform-member-expression-literals-6.9.4.tgz#37039c9a0c3313a39495faac2ff3a6b5b9d038bf"
integrity sha1-NwOcmgwzE6OUlfqsL/OmtbnQOL8=
babel-plugin-transform-merge-sibling-variables@^6.9.0:
version "6.9.4"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-merge-sibling-variables/-/babel-plugin-transform-merge-sibling-variables-6.9.4.tgz#85b422fc3377b449c9d1cde44087203532401dae"
integrity sha1-hbQi/DN3tEnJ0c3kQIcgNTJAHa4=
babel-plugin-transform-minify-booleans@^6.9.0:
version "6.9.4"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-minify-booleans/-/babel-plugin-transform-minify-booleans-6.9.4.tgz#acbb3e56a3555dd23928e4b582d285162dd2b198"
integrity sha1-rLs+VqNVXdI5KOS1gtKFFi3SsZg=
babel-plugin-transform-property-literals@^6.9.0:
version "6.9.4"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-property-literals/-/babel-plugin-transform-property-literals-6.9.4.tgz#98c1d21e255736573f93ece54459f6ce24985d39"
integrity sha1-mMHSHiVXNlc/k+zlRFn2ziSYXTk=
dependencies:
esutils "^2.0.2"
babel-plugin-transform-regenerator@^6.22.0: babel-plugin-transform-regenerator@^6.22.0:
version "6.26.0" version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz#e0703696fbde27f0a3efcacf8b4dca2f7b3a8f2f" resolved "https://registry.yarnpkg.com/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz#e0703696fbde27f0a3efcacf8b4dca2f7b3a8f2f"
@ -1162,6 +1309,33 @@ babel-plugin-transform-regenerator@^6.22.0:
dependencies: dependencies:
regenerator-transform "^0.10.0" regenerator-transform "^0.10.0"
babel-plugin-transform-regexp-constructors@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-regexp-constructors/-/babel-plugin-transform-regexp-constructors-0.3.0.tgz#9bb2c8dd082271a5cb1b3a441a7c52e8fd07e0f5"
integrity sha512-h92YHzyl042rb0naKO8frTHntpRFwRgKkfWD8602kFHoQingjJNtbvZzvxqHncJ6XmKVyYvfrBpDOSkCTDIIxw==
babel-plugin-transform-remove-console@^6.9.0:
version "6.9.4"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-remove-console/-/babel-plugin-transform-remove-console-6.9.4.tgz#b980360c067384e24b357a588d807d3c83527780"
integrity sha1-uYA2DAZzhOJLNXpYjYB9PINSd4A=
babel-plugin-transform-remove-debugger@^6.9.0:
version "6.9.4"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-remove-debugger/-/babel-plugin-transform-remove-debugger-6.9.4.tgz#42b727631c97978e1eb2d199a7aec84a18339ef2"
integrity sha1-QrcnYxyXl44estGZp67IShgznvI=
babel-plugin-transform-remove-undefined@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-remove-undefined/-/babel-plugin-transform-remove-undefined-0.3.0.tgz#03f5f0071867781e9beabbc7b77bf8095fd3f3ec"
integrity sha512-TYGQucc8iP3LJwN3kDZLEz5aa/2KuFrqpT+s8f8NnHsBU1sAgR3y8Opns0xhC+smyDYWscqFCKM1gbkWQOhhnw==
dependencies:
babel-helper-evaluate-path "^0.3.0"
babel-plugin-transform-simplify-comparison-operators@^6.9.0:
version "6.9.4"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-simplify-comparison-operators/-/babel-plugin-transform-simplify-comparison-operators-6.9.4.tgz#f62afe096cab0e1f68a2d753fdf283888471ceb9"
integrity sha1-9ir+CWyrDh9ootdT/fKDiIRxzrk=
babel-plugin-transform-strict-mode@^6.24.1: babel-plugin-transform-strict-mode@^6.24.1:
version "6.24.1" version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz#d5faf7aa578a65bbe591cf5edae04a0c67020758" resolved "https://registry.yarnpkg.com/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz#d5faf7aa578a65bbe591cf5edae04a0c67020758"
@ -1170,6 +1344,11 @@ babel-plugin-transform-strict-mode@^6.24.1:
babel-runtime "^6.22.0" babel-runtime "^6.22.0"
babel-types "^6.24.1" babel-types "^6.24.1"
babel-plugin-transform-undefined-to-void@^6.9.0:
version "6.9.4"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-undefined-to-void/-/babel-plugin-transform-undefined-to-void-6.9.4.tgz#be241ca81404030678b748717322b89d0c8fe280"
integrity sha1-viQcqBQEAwZ4t0hxcyK4nQyP4oA=
babel-preset-env@^1.7.0: babel-preset-env@^1.7.0:
version "1.7.0" version "1.7.0"
resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-1.7.0.tgz#dea79fa4ebeb883cd35dab07e260c1c9c04df77a" resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-1.7.0.tgz#dea79fa4ebeb883cd35dab07e260c1c9c04df77a"
@ -1206,6 +1385,35 @@ babel-preset-env@^1.7.0:
invariant "^2.2.2" invariant "^2.2.2"
semver "^5.3.0" semver "^5.3.0"
babel-preset-minify@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/babel-preset-minify/-/babel-preset-minify-0.3.0.tgz#7db64afa75f16f6e06c0aa5f25195f6f36784d77"
integrity sha512-+VV2GWEyak3eDOmzT1DDMuqHrw3VbE9nBNkx2LLVs4pH/Me32ND8DRpVDd8IRvk1xX5p75nygyRPtkMh6GIAbQ==
dependencies:
babel-plugin-minify-builtins "^0.3.0"
babel-plugin-minify-constant-folding "^0.3.0"
babel-plugin-minify-dead-code-elimination "^0.3.0"
babel-plugin-minify-flip-comparisons "^0.3.0"
babel-plugin-minify-guarded-expressions "^0.3.0"
babel-plugin-minify-infinity "^0.3.0"
babel-plugin-minify-mangle-names "^0.3.0"
babel-plugin-minify-numeric-literals "^0.3.0"
babel-plugin-minify-replace "^0.3.0"
babel-plugin-minify-simplify "^0.3.0"
babel-plugin-minify-type-constructors "^0.3.0"
babel-plugin-transform-inline-consecutive-adds "^0.3.0"
babel-plugin-transform-member-expression-literals "^6.9.0"
babel-plugin-transform-merge-sibling-variables "^6.9.0"
babel-plugin-transform-minify-booleans "^6.9.0"
babel-plugin-transform-property-literals "^6.9.0"
babel-plugin-transform-regexp-constructors "^0.3.0"
babel-plugin-transform-remove-console "^6.9.0"
babel-plugin-transform-remove-debugger "^6.9.0"
babel-plugin-transform-remove-undefined "^0.3.0"
babel-plugin-transform-simplify-comparison-operators "^6.9.0"
babel-plugin-transform-undefined-to-void "^6.9.0"
lodash.isplainobject "^4.0.6"
babel-register@^6.26.0: babel-register@^6.26.0:
version "6.26.0" version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.26.0.tgz#6ed021173e2fcb486d7acb45c6009a856f647071" resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.26.0.tgz#6ed021173e2fcb486d7acb45c6009a856f647071"
@ -1501,9 +1709,9 @@ browserslist@^3.2.6:
electron-to-chromium "^1.3.47" electron-to-chromium "^1.3.47"
buffer-from@^1.0.0: buffer-from@^1.0.0:
version "1.1.2" version "1.1.1"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
buffer-xor@^1.0.3: buffer-xor@^1.0.3:
version "1.0.3" version "1.0.3"
@ -4081,6 +4289,16 @@ locate-path@^5.0.0:
dependencies: dependencies:
p-locate "^4.1.0" p-locate "^4.1.0"
lodash.isplainobject@^4.0.6:
version "4.0.6"
resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=
lodash.some@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb68e4d"
integrity sha1-G7nzFO9ri63tE7VJFpsqlF62jk0=
lodash@^4.0.0, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.4, lodash@~4.17.10: lodash@^4.0.0, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.4, lodash@~4.17.10:
version "4.17.21" version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
@ -4433,10 +4651,10 @@ mkdirp@^1.0.3:
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
moment@^2.29.4: moment@^2.24.0:
version "2.29.4" version "2.27.0"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108" resolved "https://registry.yarnpkg.com/moment/-/moment-2.27.0.tgz#8bff4e3e26a236220dfe3e36de756b6ebaa0105d"
integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w== integrity sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ==
move-concurrently@^1.0.1: move-concurrently@^1.0.1:
version "1.0.1" version "1.0.1"
@ -5863,9 +6081,9 @@ source-map-support@^0.4.15:
source-map "^0.5.6" source-map "^0.5.6"
source-map-support@~0.5.12: source-map-support@~0.5.12:
version "0.5.21" version "0.5.19"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61"
integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==
dependencies: dependencies:
buffer-from "^1.0.0" buffer-from "^1.0.0"
source-map "^0.6.0" source-map "^0.6.0"
@ -6214,9 +6432,9 @@ terser-webpack-plugin@^1.4.3:
worker-farm "^1.7.0" worker-farm "^1.7.0"
terser@^4.1.2, terser@^4.6.3: terser@^4.1.2, terser@^4.6.3:
version "4.8.1" version "4.8.0"
resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.1.tgz#a00e5634562de2239fd404c649051bf6fc21144f" resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.0.tgz#63056343d7c70bb29f3af665865a46fe03a0df17"
integrity sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw== integrity sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==
dependencies: dependencies:
commander "^2.20.0" commander "^2.20.0"
source-map "~0.6.1" source-map "~0.6.1"
@ -6651,7 +6869,7 @@ webpack-log@^2.0.0:
ansi-colors "^3.0.0" ansi-colors "^3.0.0"
uuid "^3.3.2" uuid "^3.3.2"
webpack-sources@^1.1.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1: webpack-sources@^1.0.1, webpack-sources@^1.1.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1:
version "1.4.3" version "1.4.3"
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933"
integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==

View File

@ -12,7 +12,7 @@
* version_requirement: "Optional package version requirements (e.g. ==1.3 or >=1.2,<2.0, see https://www.python.org/dev/peps/pep-0440/#version-specifiers)", * version_requirement: "Optional package version requirements (e.g. ==1.3 or >=1.2,<2.0, see https://www.python.org/dev/peps/pep-0440/#version-specifiers)",
* dependencies: "Additional dependencies, space separated (as you would pass it to pip install)", * dependencies: "Additional dependencies, space separated (as you would pass it to pip install)",
* credentials: `Template of the credentials file`, * credentials: `Template of the credentials file`,
* full_plugin_name: "The full plugin name as used in the commandline with certbot, e.g. 'dns-njalla'", * full_plugin_name: "The full plugin name as used in the commandline with certbot, including prefixes, e.g. 'certbot-dns-njalla:dns-njalla'",
* }, * },
* ... * ...
* } * }
@ -26,18 +26,18 @@ module.exports = {
package_name: 'certbot-dns-acmedns', package_name: 'certbot-dns-acmedns',
version_requirement: '~=0.1.0', version_requirement: '~=0.1.0',
dependencies: '', dependencies: '',
credentials: `dns_acmedns_api_url = http://acmedns-server/ credentials: `certbot_dns_acmedns:dns_acmedns_api_url = http://acmedns-server/
dns_acmedns_registration_file = /data/acme-registration.json`, certbot_dns_acmedns:dns_acmedns_registration_file = /data/acme-registration.json`,
full_plugin_name: 'dns-acmedns', full_plugin_name: 'certbot-dns-acmedns:dns-acmedns',
}, },
aliyun: { aliyun: {
display_name: 'Aliyun', display_name: 'Aliyun',
package_name: 'certbot-dns-aliyun', package_name: 'certbot-dns-aliyun',
version_requirement: '~=0.38.1', version_requirement: '~=0.38.1',
dependencies: '', dependencies: '',
credentials: `dns_aliyun_access_key = 12345678 credentials: `certbot_dns_aliyun:dns_aliyun_access_key = 12345678
dns_aliyun_access_key_secret = 1234567890abcdef1234567890abcdef`, certbot_dns_aliyun:dns_aliyun_access_key_secret = 1234567890abcdef1234567890abcdef`,
full_plugin_name: 'dns-aliyun', full_plugin_name: 'certbot-dns-aliyun:dns-aliyun',
}, },
//####################################################// //####################################################//
azure: { azure: {
@ -67,11 +67,11 @@ dns_azure_zone2 = example.org:/subscriptions/99800903-fb14-4992-9aff-12eaf274462
}, },
//####################################################// //####################################################//
cloudflare: { cloudflare: {
display_name: 'Cloudflare', display_name: 'Cloudflare',
package_name: 'certbot-dns-cloudflare', package_name: 'certbot-dns-cloudflare',
version_requirement: '==$(certbot --version | grep -Eo \'[0-9](\\.[0-9]+)+\')', // official plugin, use certbot version // version_requirement: '', // Official plugin, no version requirement
dependencies: 'cloudflare', dependencies: 'cloudflare',
credentials: `# Cloudflare API token credentials: `# Cloudflare API token
dns_cloudflare_api_token = 0123456789abcdef0123456789abcdef01234567`, dns_cloudflare_api_token = 0123456789abcdef0123456789abcdef01234567`,
full_plugin_name: 'dns-cloudflare', full_plugin_name: 'dns-cloudflare',
}, },
@ -93,34 +93,23 @@ dns_cloudflare_api_token = 0123456789abcdef0123456789abcdef01234567`,
}, },
//####################################################// //####################################################//
cloudxns: { cloudxns: {
display_name: 'CloudXNS', display_name: 'CloudXNS',
package_name: 'certbot-dns-cloudxns', package_name: 'certbot-dns-cloudxns',
version_requirement: '==$(certbot --version | grep -Eo \'[0-9](\\.[0-9]+)+\')', // official plugin, use certbot version // version_requirement: '', // Official plugin, no version requirement
dependencies: '', dependencies: '',
credentials: `dns_cloudxns_api_key = 1234567890abcdef1234567890abcdef credentials: `dns_cloudxns_api_key = 1234567890abcdef1234567890abcdef
dns_cloudxns_secret_key = 1122334455667788`, dns_cloudxns_secret_key = 1122334455667788`,
full_plugin_name: 'dns-cloudxns', full_plugin_name: 'dns-cloudxns',
}, },
//####################################################// //####################################################//
constellix: {
display_name: 'Constellix',
package_name: 'certbot-dns-constellix',
version_requirement: '~=0.2.1',
dependencies: '',
credentials: `dns_constellix_apikey = 5fb4e76f-ac91-43e5-f982458bc595
dns_constellix_secretkey = 47d99fd0-32e7-4e07-85b46d08e70b
dns_constellix_endpoint = https://api.dns.constellix.com/v1`,
full_plugin_name: 'dns-constellix',
},
//####################################################//
corenetworks: { corenetworks: {
display_name: 'Core Networks', display_name: 'Core Networks',
package_name: 'certbot-dns-corenetworks', package_name: 'certbot-dns-corenetworks',
version_requirement: '~=0.1.4', version_requirement: '~=0.1.4',
dependencies: '', dependencies: '',
credentials: `dns_corenetworks_username = asaHB12r credentials: `certbot_dns_corenetworks:dns_corenetworks_username = asaHB12r
dns_corenetworks_password = secure_password`, certbot_dns_corenetworks:dns_corenetworks_password = secure_password`,
full_plugin_name: 'dns-corenetworks', full_plugin_name: 'certbot-dns-corenetworks:dns-corenetworks',
}, },
//####################################################// //####################################################//
cpanel: { cpanel: {
@ -128,10 +117,10 @@ dns_corenetworks_password = secure_password`,
package_name: 'certbot-dns-cpanel', package_name: 'certbot-dns-cpanel',
version_requirement: '~=0.2.2', version_requirement: '~=0.2.2',
dependencies: '', dependencies: '',
credentials: `cpanel_url = https://cpanel.example.com:2083 credentials: `certbot_dns_cpanel:cpanel_url = https://cpanel.example.com:2083
cpanel_username = user certbot_dns_cpanel:cpanel_username = user
cpanel_password = hunter2`, certbot_dns_cpanel:cpanel_password = hunter2`,
full_plugin_name: 'cpanel', full_plugin_name: 'certbot-dns-cpanel:cpanel',
}, },
//####################################################// //####################################################//
desec: { desec: {
@ -139,27 +128,27 @@ cpanel_password = hunter2`,
package_name: 'certbot-dns-desec', package_name: 'certbot-dns-desec',
version_requirement: '~=0.3.0', version_requirement: '~=0.3.0',
dependencies: '', dependencies: '',
credentials: `dns_desec_token = YOUR_DESEC_API_TOKEN credentials: `certbot_dns_desec:dns_desec_token = YOUR_DESEC_API_TOKEN
dns_desec_endpoint = https://desec.io/api/v1/`, certbot_dns_desec:dns_desec_endpoint = https://desec.io/api/v1/`,
full_plugin_name: 'dns-desec', full_plugin_name: 'certbot-dns-desec:dns-desec',
}, },
//####################################################// //####################################################//
duckdns: { duckdns: {
display_name: 'DuckDNS', display_name: 'DuckDNS',
package_name: 'certbot-dns-duckdns', package_name: 'certbot-dns-duckdns',
version_requirement: '~=0.9', version_requirement: '~=0.6',
dependencies: '', dependencies: '',
credentials: 'dns_duckdns_token=your-duckdns-token', credentials: 'dns_duckdns_token=your-duckdns-token',
full_plugin_name: 'dns-duckdns', full_plugin_name: 'dns-duckdns',
}, },
//####################################################// //####################################################//
digitalocean: { digitalocean: {
display_name: 'DigitalOcean', display_name: 'DigitalOcean',
package_name: 'certbot-dns-digitalocean', package_name: 'certbot-dns-digitalocean',
version_requirement: '==$(certbot --version | grep -Eo \'[0-9](\\.[0-9]+)+\')', // official plugin, use certbot version // version_requirement: '', // Official plugin, no version requirement
dependencies: '', dependencies: '',
credentials: 'dns_digitalocean_token = 0000111122223333444455556666777788889999aaaabbbbccccddddeeeeffff', credentials: 'dns_digitalocean_token = 0000111122223333444455556666777788889999aaaabbbbccccddddeeeeffff',
full_plugin_name: 'dns-digitalocean', full_plugin_name: 'dns-digitalocean',
}, },
//####################################################// //####################################################//
directadmin: { directadmin: {
@ -174,20 +163,20 @@ directadmin_password = aSuperStrongPassword`,
}, },
//####################################################// //####################################################//
dnsimple: { dnsimple: {
display_name: 'DNSimple', display_name: 'DNSimple',
package_name: 'certbot-dns-dnsimple', package_name: 'certbot-dns-dnsimple',
version_requirement: '==$(certbot --version | grep -Eo \'[0-9](\\.[0-9]+)+\')', // official plugin, use certbot version // version_requirement: '', // Official plugin, no version requirement
dependencies: '', dependencies: '',
credentials: 'dns_dnsimple_token = MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw', credentials: 'dns_dnsimple_token = MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw',
full_plugin_name: 'dns-dnsimple', full_plugin_name: 'dns-dnsimple',
}, },
//####################################################// //####################################################//
dnsmadeeasy: { dnsmadeeasy: {
display_name: 'DNS Made Easy', display_name: 'DNS Made Easy',
package_name: 'certbot-dns-dnsmadeeasy', package_name: 'certbot-dns-dnsmadeeasy',
version_requirement: '==$(certbot --version | grep -Eo \'[0-9](\\.[0-9]+)+\')', // official plugin, use certbot version // version_requirement: '', // Official plugin, no version requirement
dependencies: '', dependencies: '',
credentials: `dns_dnsmadeeasy_api_key = 1c1a3c91-4770-4ce7-96f4-54c0eb0e457a credentials: `dns_dnsmadeeasy_api_key = 1c1a3c91-4770-4ce7-96f4-54c0eb0e457a
dns_dnsmadeeasy_secret_key = c9b5625f-9834-4ff8-baba-4ed5f32cae55`, dns_dnsmadeeasy_secret_key = c9b5625f-9834-4ff8-baba-4ed5f32cae55`,
full_plugin_name: 'dns-dnsmadeeasy', full_plugin_name: 'dns-dnsmadeeasy',
}, },
@ -197,28 +186,9 @@ dns_dnsmadeeasy_secret_key = c9b5625f-9834-4ff8-baba-4ed5f32cae55`,
package_name: 'certbot-dns-dnspod', package_name: 'certbot-dns-dnspod',
version_requirement: '~=0.1.0', version_requirement: '~=0.1.0',
dependencies: '', dependencies: '',
credentials: `dns_dnspod_email = "email@example.com" credentials: `certbot_dns_dnspod:dns_dnspod_email = "DNSPOD-API-REQUIRES-A-VALID-EMAIL"
dns_dnspod_api_token = "id,key"`, certbot_dns_dnspod:dns_dnspod_api_token = "DNSPOD-API-TOKEN"`,
full_plugin_name: 'dns-dnspod', full_plugin_name: 'certbot-dns-dnspod:dns-dnspod',
},
//####################################################//
domainoffensive: {
display_name: 'DomainOffensive (do.de)',
package_name: 'certbot-dns-do',
version_requirement: '~=0.31.0',
dependencies: '',
credentials: 'dns_do_api_token = YOUR_DO_DE_AUTH_TOKEN',
full_plugin_name: 'dns-do',
},
//####################################################//
domeneshop: {
display_name: 'Domeneshop',
package_name: 'certbot-dns-domeneshop',
version_requirement: '~=0.2.8',
dependencies: '',
credentials: `dns_domeneshop_client_token=YOUR_DOMENESHOP_CLIENT_TOKEN
dns_domeneshop_client_secret=YOUR_DOMENESHOP_CLIENT_SECRET`,
full_plugin_name: 'dns-domeneshop',
}, },
//####################################################// //####################################################//
dynu: { dynu: {
@ -226,8 +196,8 @@ dns_domeneshop_client_secret=YOUR_DOMENESHOP_CLIENT_SECRET`,
package_name: 'certbot-dns-dynu', package_name: 'certbot-dns-dynu',
version_requirement: '~=0.0.1', version_requirement: '~=0.0.1',
dependencies: '', dependencies: '',
credentials: 'dns_dynu_auth_token = YOUR_DYNU_AUTH_TOKEN', credentials: 'certbot_dns_dynu:dns_dynu_auth_token = YOUR_DYNU_AUTH_TOKEN',
full_plugin_name: 'dns-dynu', full_plugin_name: 'certbot-dns-dynu:dns-dynu',
}, },
//####################################################// //####################################################//
eurodns: { eurodns: {
@ -238,20 +208,16 @@ dns_domeneshop_client_secret=YOUR_DOMENESHOP_CLIENT_SECRET`,
credentials: `dns_eurodns_applicationId = myuser credentials: `dns_eurodns_applicationId = myuser
dns_eurodns_apiKey = mysecretpassword dns_eurodns_apiKey = mysecretpassword
dns_eurodns_endpoint = https://rest-api.eurodns.com/user-api-gateway/proxy`, dns_eurodns_endpoint = https://rest-api.eurodns.com/user-api-gateway/proxy`,
full_plugin_name: 'dns-eurodns', full_plugin_name: 'certbot-dns-eurodns:dns-eurodns',
}, },
//####################################################// //####################################################//
gandi: { gandi: {
display_name: 'Gandi Live DNS', display_name: 'Gandi Live DNS',
package_name: 'certbot_plugin_gandi', package_name: 'certbot_plugin_gandi',
version_requirement: '~=1.3.2', version_requirement: '~=1.2.5',
dependencies: '', dependencies: '',
credentials: `# live dns v5 api key credentials: 'certbot_plugin_gandi:dns_api_key = APIKEY',
dns_gandi_api_key=APIKEY full_plugin_name: 'certbot-plugin-gandi:dns',
# optional organization id, remove it if not used
dns_gandi_sharing_id=SHARINGID`,
full_plugin_name: 'dns-gandi',
}, },
//####################################################// //####################################################//
godaddy: { godaddy: {
@ -265,11 +231,11 @@ dns_godaddy_key = abcdef0123456789abcdef01234567abcdef0123`,
}, },
//####################################################// //####################################################//
google: { google: {
display_name: 'Google', display_name: 'Google',
package_name: 'certbot-dns-google', package_name: 'certbot-dns-google',
version_requirement: '==$(certbot --version | grep -Eo \'[0-9](\\.[0-9]+)+\')', // official plugin, use certbot version // version_requirement: '', // Official plugin, no version requirement
dependencies: '', dependencies: '',
credentials: `{ credentials: `{
"type": "service_account", "type": "service_account",
... ...
}`, }`,
@ -281,8 +247,8 @@ dns_godaddy_key = abcdef0123456789abcdef01234567abcdef0123`,
package_name: 'certbot-dns-hetzner', package_name: 'certbot-dns-hetzner',
version_requirement: '~=1.0.4', version_requirement: '~=1.0.4',
dependencies: '', dependencies: '',
credentials: 'dns_hetzner_api_token = 0123456789abcdef0123456789abcdef', credentials: 'certbot_dns_hetzner:dns_hetzner_api_token = 0123456789abcdef0123456789abcdef',
full_plugin_name: 'dns-hetzner', full_plugin_name: 'certbot-dns-hetzner:dns-hetzner',
}, },
//####################################################// //####################################################//
infomaniak: { infomaniak: {
@ -290,8 +256,8 @@ dns_godaddy_key = abcdef0123456789abcdef01234567abcdef0123`,
package_name: 'certbot-dns-infomaniak', package_name: 'certbot-dns-infomaniak',
version_requirement: '~=0.1.12', version_requirement: '~=0.1.12',
dependencies: '', dependencies: '',
credentials: 'dns_infomaniak_token = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', credentials: 'certbot_dns_infomaniak:dns_infomaniak_token = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
full_plugin_name: 'dns-infomaniak', full_plugin_name: 'certbot-dns-infomaniak:dns-infomaniak',
}, },
//####################################################// //####################################################//
inwx: { inwx: {
@ -299,22 +265,22 @@ dns_godaddy_key = abcdef0123456789abcdef01234567abcdef0123`,
package_name: 'certbot-dns-inwx', package_name: 'certbot-dns-inwx',
version_requirement: '~=2.1.2', version_requirement: '~=2.1.2',
dependencies: '', dependencies: '',
credentials: `dns_inwx_url = https://api.domrobot.com/xmlrpc/ credentials: `certbot_dns_inwx:dns_inwx_url = https://api.domrobot.com/xmlrpc/
dns_inwx_username = your_username certbot_dns_inwx:dns_inwx_username = your_username
dns_inwx_password = your_password certbot_dns_inwx:dns_inwx_password = your_password
dns_inwx_shared_secret = your_shared_secret optional`, certbot_dns_inwx:dns_inwx_shared_secret = your_shared_secret optional`,
full_plugin_name: 'dns-inwx', full_plugin_name: 'certbot-dns-inwx:dns-inwx',
}, },
//####################################################// //####################################################//
ionos: { ionos: {
display_name: 'IONOS', display_name: 'IONOS',
package_name: 'certbot-dns-ionos', package_name: 'certbot-dns-ionos',
version_requirement: '==2021.9.20.post1', version_requirement: '~=0.0.7',
dependencies: '', dependencies: '',
credentials: `dns_ionos_prefix = myapikeyprefix credentials: `certbot_dns_ionos:dns_ionos_prefix = myapikeyprefix
dns_ionos_secret = verysecureapikeysecret certbot_dns_ionos:dns_ionos_secret = verysecureapikeysecret
dns_ionos_endpoint = https://api.hosting.ionos.com`, certbot_dns_ionos:dns_ionos_endpoint = https://api.hosting.ionos.com`,
full_plugin_name: 'dns-ionos', full_plugin_name: 'certbot-dns-ionos:dns-ionos',
}, },
//####################################################// //####################################################//
ispconfig: { ispconfig: {
@ -322,10 +288,10 @@ dns_ionos_endpoint = https://api.hosting.ionos.com`,
package_name: 'certbot-dns-ispconfig', package_name: 'certbot-dns-ispconfig',
version_requirement: '~=0.2.0', version_requirement: '~=0.2.0',
dependencies: '', dependencies: '',
credentials: `dns_ispconfig_username = myremoteuser credentials: `certbot_dns_ispconfig:dns_ispconfig_username = myremoteuser
dns_ispconfig_password = verysecureremoteuserpassword certbot_dns_ispconfig:dns_ispconfig_password = verysecureremoteuserpassword
dns_ispconfig_endpoint = https://localhost:8080`, certbot_dns_ispconfig:dns_ispconfig_endpoint = https://localhost:8080`,
full_plugin_name: 'dns-ispconfig', full_plugin_name: 'certbot-dns-ispconfig:dns-ispconfig',
}, },
//####################################################// //####################################################//
isset: { isset: {
@ -333,27 +299,27 @@ dns_ispconfig_endpoint = https://localhost:8080`,
package_name: 'certbot-dns-isset', package_name: 'certbot-dns-isset',
version_requirement: '~=0.0.3', version_requirement: '~=0.0.3',
dependencies: '', dependencies: '',
credentials: `dns_isset_endpoint="https://customer.isset.net/api" credentials: `certbot_dns_isset:dns_isset_endpoint="https://customer.isset.net/api"
dns_isset_token="<token>"`, certbot_dns_isset:dns_isset_token="<token>"`,
full_plugin_name: 'dns-isset', full_plugin_name: 'certbot-dns-isset:dns-isset',
}, },
joker: { joker: {
display_name: 'Joker', display_name: 'Joker',
package_name: 'certbot-dns-joker', package_name: 'certbot-dns-joker',
version_requirement: '~=1.1.0', version_requirement: '~=1.1.0',
dependencies: '', dependencies: '',
credentials: `dns_joker_username = <Dynamic DNS Authentication Username> credentials: `certbot_dns_joker:dns_joker_username = <Dynamic DNS Authentication Username>
dns_joker_password = <Dynamic DNS Authentication Password> certbot_dns_joker:dns_joker_password = <Dynamic DNS Authentication Password>
dns_joker_domain = <Dynamic DNS Domain>`, certbot_dns_joker:dns_joker_domain = <Dynamic DNS Domain>`,
full_plugin_name: 'dns-joker', full_plugin_name: 'certbot-dns-joker:dns-joker',
}, },
//####################################################// //####################################################//
linode: { linode: {
display_name: 'Linode', display_name: 'Linode',
package_name: 'certbot-dns-linode', package_name: 'certbot-dns-linode',
version_requirement: '==$(certbot --version | grep -Eo \'[0-9](\\.[0-9]+)+\')', // official plugin, use certbot version // version_requirement: '', // Official plugin, no version requirement
dependencies: '', dependencies: '',
credentials: `dns_linode_key = 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ64 credentials: `dns_linode_key = 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ64
dns_linode_version = [<blank>|3|4]`, dns_linode_version = [<blank>|3|4]`,
full_plugin_name: 'dns-linode', full_plugin_name: 'dns-linode',
}, },
@ -369,34 +335,24 @@ dns_loopia_password = abcdef0123456789abcdef01234567abcdef0123`,
}, },
//####################################################// //####################################################//
luadns: { luadns: {
display_name: 'LuaDNS', display_name: 'LuaDNS',
package_name: 'certbot-dns-luadns', package_name: 'certbot-dns-luadns',
version_requirement: '==$(certbot --version | grep -Eo \'[0-9](\\.[0-9]+)+\')', // official plugin, use certbot version // version_requirement: '', // Official plugin, no version requirement
dependencies: '', dependencies: '',
credentials: `dns_luadns_email = user@example.com credentials: `dns_luadns_email = user@example.com
dns_luadns_token = 0123456789abcdef0123456789abcdef`, dns_luadns_token = 0123456789abcdef0123456789abcdef`,
full_plugin_name: 'dns-luadns', full_plugin_name: 'dns-luadns',
}, },
//####################################################// //####################################################//
namecheap: {
display_name: 'Namecheap',
package_name: 'certbot-dns-namecheap',
version_requirement: '~=1.0.0',
dependencies: '',
credentials: `dns_namecheap_username = 123456
dns_namecheap_api_key = 0123456789abcdef0123456789abcdef01234567`,
full_plugin_name: 'dns-namecheap',
},
//####################################################//
netcup: { netcup: {
display_name: 'netcup', display_name: 'netcup',
package_name: 'certbot-dns-netcup', package_name: 'certbot-dns-netcup',
version_requirement: '~=1.0.0', version_requirement: '~=1.0.0',
dependencies: '', dependencies: '',
credentials: `dns_netcup_customer_id = 123456 credentials: `certbot_dns_netcup:dns_netcup_customer_id = 123456
dns_netcup_api_key = 0123456789abcdef0123456789abcdef01234567 certbot_dns_netcup:dns_netcup_api_key = 0123456789abcdef0123456789abcdef01234567
dns_netcup_api_password = abcdef0123456789abcdef01234567abcdef0123`, certbot_dns_netcup:dns_netcup_api_password = abcdef0123456789abcdef01234567abcdef0123`,
full_plugin_name: 'dns-netcup', full_plugin_name: 'certbot-dns-netcup:dns-netcup',
}, },
//####################################################// //####################################################//
njalla: { njalla: {
@ -404,17 +360,17 @@ dns_netcup_api_password = abcdef0123456789abcdef01234567abcdef0123`,
package_name: 'certbot-dns-njalla', package_name: 'certbot-dns-njalla',
version_requirement: '~=1.0.0', version_requirement: '~=1.0.0',
dependencies: '', dependencies: '',
credentials: 'dns_njalla_token = 0123456789abcdef0123456789abcdef01234567', credentials: 'certbot_dns_njalla:dns_njalla_token = 0123456789abcdef0123456789abcdef01234567',
full_plugin_name: 'dns-njalla', full_plugin_name: 'certbot-dns-njalla:dns-njalla',
}, },
//####################################################// //####################################################//
nsone: { nsone: {
display_name: 'NS1', display_name: 'NS1',
package_name: 'certbot-dns-nsone', package_name: 'certbot-dns-nsone',
version_requirement: '==$(certbot --version | grep -Eo \'[0-9](\\.[0-9]+)+\')', // official plugin, use certbot version // version_requirement: '', // Official plugin, no version requirement
dependencies: '', dependencies: '',
credentials: 'dns_nsone_api_key = MDAwMDAwMDAwMDAwMDAw', credentials: 'dns_nsone_api_key = MDAwMDAwMDAwMDAwMDAw',
full_plugin_name: 'dns-nsone', full_plugin_name: 'dns-nsone',
}, },
//####################################################// //####################################################//
oci: { oci: {
@ -432,11 +388,11 @@ key_file = ~/.oci/oci_api_key.pem`,
}, },
//####################################################// //####################################################//
ovh: { ovh: {
display_name: 'OVH', display_name: 'OVH',
package_name: 'certbot-dns-ovh', package_name: 'certbot-dns-ovh',
version_requirement: '==$(certbot --version | grep -Eo \'[0-9](\\.[0-9]+)+\')', // official plugin, use certbot version // version_requirement: '', // Official plugin, no version requirement
dependencies: '', dependencies: '',
credentials: `dns_ovh_endpoint = ovh-eu credentials: `dns_ovh_endpoint = ovh-eu
dns_ovh_application_key = MDAwMDAwMDAwMDAw dns_ovh_application_key = MDAwMDAwMDAwMDAw
dns_ovh_application_secret = MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw dns_ovh_application_secret = MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw
dns_ovh_consumer_key = MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw`, dns_ovh_consumer_key = MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw`,
@ -458,9 +414,9 @@ dns_porkbun_secret=your-porkbun-api-secret`,
package_name: 'certbot-dns-powerdns', package_name: 'certbot-dns-powerdns',
version_requirement: '~=0.2.0', version_requirement: '~=0.2.0',
dependencies: '', dependencies: '',
credentials: `dns_powerdns_api_url = https://api.mypowerdns.example.org credentials: `certbot_dns_powerdns:dns_powerdns_api_url = https://api.mypowerdns.example.org
dns_powerdns_api_key = AbCbASsd!@34`, certbot_dns_powerdns:dns_powerdns_api_key = AbCbASsd!@34`,
full_plugin_name: 'dns-powerdns', full_plugin_name: 'certbot-dns-powerdns:dns-powerdns',
}, },
//####################################################// //####################################################//
regru: { regru: {
@ -474,11 +430,11 @@ certbot_regru:dns_password=password`,
}, },
//####################################################// //####################################################//
rfc2136: { rfc2136: {
display_name: 'RFC 2136', display_name: 'RFC 2136',
package_name: 'certbot-dns-rfc2136', package_name: 'certbot-dns-rfc2136',
version_requirement: '==$(certbot --version | grep -Eo \'[0-9](\\.[0-9]+)+\')', // official plugin, use certbot version // version_requirement: '', // Official plugin, no version requirement
dependencies: '', dependencies: '',
credentials: `# Target DNS server credentials: `# Target DNS server
dns_rfc2136_server = 192.0.2.1 dns_rfc2136_server = 192.0.2.1
# Target DNS port # Target DNS port
dns_rfc2136_port = 53 dns_rfc2136_port = 53
@ -492,11 +448,11 @@ dns_rfc2136_algorithm = HMAC-SHA512`,
}, },
//####################################################// //####################################################//
route53: { route53: {
display_name: 'Route 53 (Amazon)', display_name: 'Route 53 (Amazon)',
package_name: 'certbot-dns-route53', package_name: 'certbot-dns-route53',
version_requirement: '==$(certbot --version | grep -Eo \'[0-9](\\.[0-9]+)+\')', // official plugin, use certbot version // version_requirement: '', // Official plugin, no version requirement
dependencies: '', dependencies: '',
credentials: `[default] credentials: `[default]
aws_access_key_id=AKIAIOSFODNN7EXAMPLE aws_access_key_id=AKIAIOSFODNN7EXAMPLE
aws_secret_access_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY`, aws_secret_access_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY`,
full_plugin_name: 'dns-route53', full_plugin_name: 'dns-route53',
@ -505,21 +461,11 @@ aws_secret_access_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY`,
transip: { transip: {
display_name: 'TransIP', display_name: 'TransIP',
package_name: 'certbot-dns-transip', package_name: 'certbot-dns-transip',
version_requirement: '~=0.4.3', version_requirement: '~=0.3.3',
dependencies: '', dependencies: '',
credentials: `dns_transip_username = my_username credentials: `certbot_dns_transip:dns_transip_username = my_username
dns_transip_key_file = /etc/letsencrypt/transip-rsa.key`, certbot_dns_transip:dns_transip_key_file = /etc/letsencrypt/transip-rsa.key`,
full_plugin_name: 'dns-transip', full_plugin_name: 'certbot-dns-transip:dns-transip',
},
//####################################################//
tencentcloud: {
display_name: 'Tencent Cloud',
package_name: 'certbot-dns-tencentcloud',
version_requirement: '~=2.0.0',
dependencies: '',
credentials: `dns_tencentcloud_secret_id = TENCENT_CLOUD_SECRET_ID
dns_tencentcloud_secret_key = TENCENT_CLOUD_SECRET_KEY`,
full_plugin_name: 'dns-tencentcloud',
}, },
//####################################################// //####################################################//
vultr: { vultr: {
@ -527,18 +473,7 @@ dns_tencentcloud_secret_key = TENCENT_CLOUD_SECRET_KEY`,
package_name: 'certbot-dns-vultr', package_name: 'certbot-dns-vultr',
version_requirement: '~=1.0.3', version_requirement: '~=1.0.3',
dependencies: '', dependencies: '',
credentials: 'dns_vultr_key = YOUR_VULTR_API_KEY', credentials: 'certbot_dns_vultr:dns_vultr_key = YOUR_VULTR_API_KEY',
full_plugin_name: 'dns-vultr', full_plugin_name: 'certbot-dns-vultr:dns-vultr',
},
//####################################################//
websupportsk: {
display_name: 'Websupport.sk',
package_name: 'certbot-dns-websupportsk',
version_requirement: '~=0.1.6',
dependencies: '',
credentials: `dns_websupportsk_api_key = <api_key>
dns_websupportsk_secret = <secret>
dns_websupportsk_domain = example.com`,
full_plugin_name: 'dns-websupportsk',
}, },
}; };

Some files were not shown because too many files have changed in this diff Show More