Compare commits

...

34 Commits

Author SHA1 Message Date
jc21
35d7a3a407
Merge pull request #3847 from NginxProxyManager/develop
v2.11.3
2024-07-01 21:37:42 +10:00
jc21
63d06da8a8
Merge branch 'master' into develop
All checks were successful
Close stale issues and PRs / stale (push) Successful in 4s
2024-07-01 16:12:21 +10:00
Jamie Curnow
b5a0d74654
Bump version 2024-07-01 16:09:33 +10:00
Jamie Curnow
99cce7e2b0
Fix command injection when passing bash commands into the dns provider configuration
- Use built in node functions to write the file
- And to delete the file
2024-07-01 16:08:01 +10:00
jc21
120d50e5c0
Merge pull request #3766 from kroegerama/kroegerama-patch-1
Add include for `root_top.conf` in the nginx.conf
2024-07-01 15:23:43 +10:00
jc21
5454fd61b3
Merge pull request #3781 from jinhei/patch-1
Remove spaces around Cloudflare API Credential
2024-07-01 15:22:43 +10:00
jc21
b33012705b
Merge pull request #3790 from DavidLievrouw/initial_admin
Read initial admin email and password from env vars
2024-07-01 15:22:15 +10:00
jc21
e948b60194
Merge pull request #3809 from NginxProxyManager/dependabot/npm_and_yarn/backend/braces-3.0.3
Bump braces from 3.0.2 to 3.0.3 in /backend
2024-07-01 15:20:48 +10:00
jc21
7913c9a07d
Merge pull request #3827 from Hadatko/feature/addWedosDns
Add wedos dns
2024-07-01 15:20:23 +10:00
jc21
d1c23b6286
Merge pull request #3833 from NginxProxyManager/dependabot/npm_and_yarn/backend/glob-parent-5.1.2
Bump glob-parent from 5.1.1 to 5.1.2 in /backend
2024-07-01 15:19:39 +10:00
jc21
c7e2946dbf
Merge pull request #3837 from Allesanddro/patch-1
Update README.md
2024-07-01 15:19:28 +10:00
jc21
8936402229
Merge pull request #3843 from jay-lab/feature/fix-syntax-cause-err
Fix syntax that causes errors (generateKeys log)
2024-07-01 15:19:00 +10:00
An Seongjin
001c77e686
Fix syntax that causes errors (generateKeys log) 2024-06-30 22:27:54 +09:00
Dusan Cervenka
5578e825b1 Update version
Signed-off-by: Dusan Cervenka <cervenka.dusan@gmail.com>
2024-06-29 21:30:27 +02:00
S.S
c93656a7a1
Update README.md
In 2020, the concept of a single compose specification was introduced, removing the need for versioning.
2024-06-28 20:04:31 +02:00
dependabot[bot]
50aeae234f
Bump glob-parent from 5.1.1 to 5.1.2 in /backend
Bumps [glob-parent](https://github.com/gulpjs/glob-parent) from 5.1.1 to 5.1.2.
- [Release notes](https://github.com/gulpjs/glob-parent/releases)
- [Changelog](https://github.com/gulpjs/glob-parent/blob/main/CHANGELOG.md)
- [Commits](https://github.com/gulpjs/glob-parent/compare/v5.1.1...v5.1.2)

---
updated-dependencies:
- dependency-name: glob-parent
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-27 18:24:09 +00:00
Dusan Cervenka
a5c06c1a34 Add wedos dns
Signed-off-by: Dusan Cervenka <cervenka.dusan@gmail.com>
2024-06-25 23:26:50 +02:00
jc21
51414ced3a
Merge pull request #3810 from Brendon-Mendicino/change_log_format_location
All checks were successful
Close stale issues and PRs / stale (push) Successful in 4s
Changing `log_format proxy` default location
2024-06-25 10:17:04 +10:00
jc21
5e35e538af
Merge pull request #3815 from alexjsp/alex/hover-dns-plugin
Add Hover.com DNS plugin
2024-06-25 10:16:03 +10:00
Alex Stevenson-Price
13fec42d1f
Add Hover.com DNS plugin 2024-06-20 11:47:50 +01:00
Brendon Mendicino
b4560d7dde feat: changing log_format proxy default location
This is useful when some user would want to change the default
log format for each of the service, without the need of creating a
new `log_format custom` and changing the `access_log` for each
service.
2024-06-16 15:44:52 +02:00
dependabot[bot]
6f9eed8a61
Bump braces from 3.0.2 to 3.0.3 in /backend
Bumps [braces](https://github.com/micromatch/braces) from 3.0.2 to 3.0.3.
- [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/braces/compare/3.0.2...3.0.3)

---
updated-dependencies:
- dependency-name: braces
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-16 10:41:32 +00:00
David Lievrouw
d66e4e03e6 #3790 Attempt to make ci happy. 2024-06-03 13:44:08 +02:00
David Lievrouw
1d19c29bb0 Read initial admin email and password from env vars. 2024-06-03 13:32:23 +02:00
Nicholas Fong
e20a11de4a
Remove spaces around cloudflare api credential 2024-05-28 23:32:03 -04:00
Jamie Curnow
d3a654b546
Fix flakey CI due to full stack network determination 2024-05-23 08:12:51 +10:00
Jamie Curnow
bed387ebd4 Small fix for CI cleanup 2024-05-21 13:16:53 +10:00
Jamie Curnow
6ac9a82279 Major update to cypress
- Updated cypress
- Ground work for testing DNS certs in CI
2024-05-21 12:53:07 +10:00
kroegerama
ef23e796ec
update advanced config documentation
describe the `root_top.conf` file and add a snippet for enabling the geoip2 module
2024-05-20 10:35:36 +02:00
jc21
3754a569ba
Merge pull request #3729 from clhey/custom_proxy
move advanced_config section of /app/templates/_location.conf to top of default config
2024-05-20 13:53:09 +10:00
jc21
b383f46656
Merge pull request #3764 from ransbachm/develop
Fix Cloudflare DNS Auth
2024-05-20 13:46:39 +10:00
kroegerama
3ce477d350
add include for root_top.conf in the nginx.conf
Allow custom configuration of the root config in the top of the file. This can be used to load modules, which is not possible at the end of the config file.
There is already a `http_top.conf`, so `root_top.conf` is a logical addition.
2024-05-19 15:53:02 +02:00
ransbachm
516b4d991c
Pin version as requested by dep 2024-05-18 22:49:48 +02:00
clhey
280bac8b43 advanced config move to top of default confg 2024-04-28 18:18:38 +08:00
43 changed files with 1460 additions and 1219 deletions

4
.gitignore vendored
View File

@ -3,3 +3,7 @@
._* ._*
.vscode .vscode
certbot-help.txt certbot-help.txt
test/node_modules
*/node_modules
docker/dev/dnsrouter-config.json.tmp
docker/dev/resolv.conf

View File

@ -1 +1 @@
2.11.2 2.11.3

123
Jenkinsfile vendored
View File

@ -18,10 +18,8 @@ pipeline {
BUILD_VERSION = getVersion() BUILD_VERSION = getVersion()
MAJOR_VERSION = '2' MAJOR_VERSION = '2'
BRANCH_LOWER = "${BRANCH_NAME.toLowerCase().replaceAll('\\\\', '-').replaceAll('/', '-').replaceAll('\\.', '-')}" BRANCH_LOWER = "${BRANCH_NAME.toLowerCase().replaceAll('\\\\', '-').replaceAll('/', '-').replaceAll('\\.', '-')}"
COMPOSE_PROJECT_NAME = "npm_${BRANCH_LOWER}_${BUILD_NUMBER}" BUILDX_NAME = "npm_${BRANCH_LOWER}_${BUILD_NUMBER}"
COMPOSE_FILE = 'docker/docker-compose.ci.yml'
COMPOSE_INTERACTIVE_NO_CLI = 1 COMPOSE_INTERACTIVE_NO_CLI = 1
BUILDX_NAME = "${COMPOSE_PROJECT_NAME}"
} }
stages { stages {
stage('Environment') { stage('Environment') {
@ -94,75 +92,61 @@ pipeline {
} }
} }
} }
stage('Cypress') { }
steps { }
// Creating will also create the network prior to stage('Test Sqlite') {
// using it in parallel stages below and mitigating environment {
// a race condition. COMPOSE_PROJECT_NAME = "npm_${BRANCH_LOWER}_${BUILD_NUMBER}_sqlite"
sh 'docker-compose build cypress-sqlite' COMPOSE_FILE = 'docker/docker-compose.ci.yml:docker/docker-compose.ci.sqlite.yml'
sh 'docker-compose build cypress-mysql' }
sh 'docker-compose create cypress-sqlite' when {
sh 'docker-compose create cypress-mysql' not {
} equals expected: 'UNSTABLE', actual: currentBuild.result
}
}
steps {
sh 'rm -rf ./test/results/junit/*'
sh './scripts/ci/fulltest-cypress'
}
post {
always {
// Dumps to analyze later
sh 'mkdir -p debug/sqlite'
sh 'docker logs $(docker-compose ps --all -q fullstack) > debug/sqlite/docker_fullstack.log 2>&1'
sh 'docker logs $(docker-compose ps --all -q stepca) > debug/sqlite/docker_stepca.log 2>&1'
sh 'docker logs $(docker-compose ps --all -q pdns) > debug/sqlite/docker_pdns.log 2>&1'
sh 'docker logs $(docker-compose ps --all -q pdns-db) > debug/sqlite/docker_pdns-db.log 2>&1'
sh 'docker logs $(docker-compose ps --all -q dnsrouter) > debug/sqlite/docker_dnsrouter.log 2>&1'
junit 'test/results/junit/*'
sh 'docker-compose down --remove-orphans --volumes -t 30 || true'
} }
} }
} }
stage('Integration Tests') { stage('Test Mysql') {
parallel { environment {
stage('Sqlite') { COMPOSE_PROJECT_NAME = "npm_${BRANCH_LOWER}_${BUILD_NUMBER}_mysql"
steps { COMPOSE_FILE = 'docker/docker-compose.ci.yml:docker/docker-compose.ci.mysql.yml'
// Bring up a stack }
sh 'docker-compose up -d fullstack-sqlite' when {
sh './scripts/wait-healthy $(docker-compose ps --all -q fullstack-sqlite) 120' not {
// Stop and Start it, as this will test it's ability to restart with existing data equals expected: 'UNSTABLE', actual: currentBuild.result
sh 'docker-compose stop fullstack-sqlite'
sh 'docker-compose start fullstack-sqlite'
sh './scripts/wait-healthy $(docker-compose ps --all -q fullstack-sqlite) 120'
// Run tests
sh 'rm -rf test/results-sqlite'
sh 'docker-compose up cypress-sqlite'
// Get results
sh 'docker cp -L "$(docker-compose ps --all -q cypress-sqlite):/test/results" test/results-sqlite'
}
post {
always {
// Dumps to analyze later
sh 'mkdir -p debug/sqlite'
sh 'docker-compose logs fullstack-sqlite > debug/sqlite/docker_fullstack_sqlite.log'
// Cypress videos and screenshot artifacts
dir(path: 'test/results-sqlite') {
archiveArtifacts allowEmptyArchive: true, artifacts: '**/*', excludes: '**/*.xml'
}
junit 'test/results-sqlite/junit/*'
}
}
} }
stage('Mysql') { }
steps { steps {
// Bring up a stack sh 'rm -rf ./test/results/junit/*'
sh 'docker-compose up -d fullstack-mysql' sh './scripts/ci/fulltest-cypress'
sh './scripts/wait-healthy $(docker-compose ps --all -q fullstack-mysql) 120' }
post {
// Run tests always {
sh 'rm -rf test/results-mysql' // Dumps to analyze later
sh 'docker-compose up cypress-mysql' sh 'mkdir -p debug/mysql'
// Get results sh 'docker logs $(docker-compose ps --all -q fullstack) > debug/mysql/docker_fullstack.log 2>&1'
sh 'docker cp -L "$(docker-compose ps --all -q cypress-mysql):/test/results" test/results-mysql' sh 'docker logs $(docker-compose ps --all -q stepca) > debug/mysql/docker_stepca.log 2>&1'
} sh 'docker logs $(docker-compose ps --all -q pdns) > debug/mysql/docker_pdns.log 2>&1'
post { sh 'docker logs $(docker-compose ps --all -q pdns-db) > debug/mysql/docker_pdns-db.log 2>&1'
always { sh 'docker logs $(docker-compose ps --all -q dnsrouter) > debug/mysql/docker_dnsrouter.log 2>&1'
// Dumps to analyze later junit 'test/results/junit/*'
sh 'mkdir -p debug/mysql' sh 'docker-compose down --remove-orphans --volumes -t 30 || true'
sh 'docker-compose logs fullstack-mysql > debug/mysql/docker_fullstack_mysql.log'
sh 'docker-compose logs db > debug/mysql/docker_db.log'
// Cypress videos and screenshot artifacts
dir(path: 'test/results-mysql') {
archiveArtifacts allowEmptyArchive: true, artifacts: '**/*', excludes: '**/*.xml'
}
junit 'test/results-mysql/junit/*'
}
}
} }
} }
} }
@ -214,9 +198,8 @@ pipeline {
} }
post { post {
always { always {
sh 'docker-compose down --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'
} }
success { success {
juxtapose event: 'success' juxtapose event: 'success'

View File

@ -1,7 +1,7 @@
<p align="center"> <p align="center">
<img src="https://nginxproxymanager.com/github.png"> <img src="https://nginxproxymanager.com/github.png">
<br><br> <br><br>
<img src="https://img.shields.io/badge/version-2.11.2-green.svg?style=for-the-badge"> <img src="https://img.shields.io/badge/version-2.11.3-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>
@ -56,7 +56,6 @@ I won't go in to too much detail here but here are the basics for someone new to
2. Create a docker-compose.yml file similar to this: 2. Create a docker-compose.yml file similar to this:
```yml ```yml
version: '3.8'
services: services:
app: app:
image: 'docker.io/jc21/nginx-proxy-manager:latest' image: 'docker.io/jc21/nginx-proxy-manager:latest'

View File

@ -861,9 +861,8 @@ const internalCertificate = {
logger.info(`Requesting Let'sEncrypt certificates via ${dnsPlugin.name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`); logger.info(`Requesting Let'sEncrypt certificates via ${dnsPlugin.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 fs.mkdirSync('/etc/letsencrypt/credentials', { recursive: true });
const escapedCredentials = certificate.meta.dns_provider_credentials.replaceAll('\'', '\\\'').replaceAll('\\', '\\\\'); fs.writeFileSync(credentialsLocation, certificate.meta.dns_provider_credentials, {mode: 0o600});
const credentialsCmd = 'mkdir -p /etc/letsencrypt/credentials 2> /dev/null; echo \'' + escapedCredentials + '\' > \'' + credentialsLocation + '\' && chmod 600 \'' + credentialsLocation + '\'';
// 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';
@ -898,17 +897,15 @@ const internalCertificate = {
mainCmd = mainCmd + ' --dns-duckdns-no-txt-restore'; mainCmd = mainCmd + ' --dns-duckdns-no-txt-restore';
} }
logger.info('Command:', `${credentialsCmd} && && ${mainCmd}`); logger.info('Command:', mainCmd);
try { try {
await utils.exec(credentialsCmd);
const result = await utils.exec(mainCmd); const result = await utils.exec(mainCmd);
logger.info(result); logger.info(result);
return result; return result;
} catch (err) { } catch (err) {
// Don't fail if file does not exist // Don't fail if file does not exist, so no need for action in the callback
const delete_credentialsCmd = `rm -f '${credentialsLocation}' || true`; fs.unlink(credentialsLocation, () => {});
await utils.exec(delete_credentialsCmd);
throw err; throw err;
} }
}, },

View File

@ -93,7 +93,7 @@ const generateKeys = () => {
try { try {
fs.writeFileSync(keysFile, JSON.stringify(keys, null, 2)); fs.writeFileSync(keysFile, JSON.stringify(keys, null, 2));
} catch (err) { } catch (err) {
logger.error('Could not write JWT key pair to config file: ' + keysFile + ': ' . err.message); logger.error('Could not write JWT key pair to config file: ' + keysFile + ': ' + err.message);
process.exit(1); process.exit(1);
} }
logger.info('Wrote JWT key pair to config file: ' + keysFile); logger.info('Wrote JWT key pair to config file: ' + keysFile);

View File

@ -21,11 +21,14 @@ const setupDefaultUser = () => {
.then((row) => { .then((row) => {
if (!row.count) { if (!row.count) {
// Create a new user and set password // Create a new user and set password
logger.info('Creating a new user: admin@example.com with password: changeme'); let email = process.env.INITIAL_ADMIN_EMAIL || 'admin@example.com';
let password = process.env.INITIAL_ADMIN_PASSWORD || 'changeme';
logger.info('Creating a new user: ' + email + ' with password: ' + password);
let data = { let data = {
is_deleted: 0, is_deleted: 0,
email: 'admin@example.com', email: email,
name: 'Administrator', name: 'Administrator',
nickname: 'Admin', nickname: 'Admin',
avatar: '', avatar: '',
@ -41,7 +44,7 @@ const setupDefaultUser = () => {
.insert({ .insert({
user_id: user.id, user_id: user.id,
type: 'password', type: 'password',
secret: 'changeme', secret: password,
meta: {}, meta: {},
}) })
.then(() => { .then(() => {

View File

@ -1,4 +1,6 @@
location {{ path }} { location {{ path }} {
{{ advanced_config }}
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;
@ -17,8 +19,5 @@
proxy_set_header Connection $http_connection; proxy_set_header Connection $http_connection;
proxy_http_version 1.1; proxy_http_version 1.1;
{% endif %} {% endif %}
{{ advanced_config }}
} }

View File

@ -448,11 +448,11 @@ brace-expansion@^1.1.7:
concat-map "0.0.1" concat-map "0.0.1"
braces@~3.0.2: braces@~3.0.2:
version "3.0.2" version "3.0.3"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789"
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==
dependencies: dependencies:
fill-range "^7.0.1" fill-range "^7.1.1"
buffer-crc32@^0.2.1, buffer-crc32@^0.2.13: buffer-crc32@^0.2.1, buffer-crc32@^0.2.13:
version "0.2.13" version "0.2.13"
@ -1206,10 +1206,10 @@ file-entry-cache@^6.0.1:
dependencies: dependencies:
flat-cache "^3.0.4" flat-cache "^3.0.4"
fill-range@^7.0.1: fill-range@^7.1.1:
version "7.0.1" version "7.1.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292"
integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==
dependencies: dependencies:
to-regex-range "^5.0.1" to-regex-range "^5.0.1"
@ -1402,9 +1402,9 @@ glob-parent@^6.0.2:
is-glob "^4.0.3" is-glob "^4.0.3"
glob-parent@~5.1.0: glob-parent@~5.1.0:
version "5.1.1" version "5.1.2"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
dependencies: dependencies:
is-glob "^4.0.1" is-glob "^4.0.1"

View File

@ -0,0 +1,28 @@
{
"log": {
"format": "nice",
"level": "debug"
},
"servers": [
{
"host": "0.0.0.0",
"port": 53,
"upstreams": [
{
"regex": "website[0-9]+.example\\.com",
"upstream": "127.0.0.11"
},
{
"regex": ".*\\.example\\.com",
"upstream": "1.1.1.1"
},
{
"regex": "local",
"nxdomain": true
}
],
"internal": null,
"default_upstream": "127.0.0.11"
}
]
}

View File

@ -0,0 +1,7 @@
text = True
non-interactive = True
webroot-path = /data/letsencrypt-acme-challenge
key-type = ecdsa
elliptic-curve = secp384r1
preferred-chain = ISRG Root X1
server =

255
docker/dev/pdns-db.sql Normal file
View File

@ -0,0 +1,255 @@
/*
How this was generated:
1. bring up an empty pdns stack
2. use api to create a zone ...
curl -X POST \
'http://npm.dev:8081/api/v1/servers/localhost/zones' \
--header 'X-API-Key: npm' \
--header 'Content-Type: application/json' \
--data-raw '{
"name": "example.com.",
"kind": "Native",
"masters": [],
"nameservers": [
"ns1.pdns.",
"ns2.pdns."
]
}'
3. Dump sql:
docker exec -ti npm.pdns.db mysqldump -u pdns -p pdns
*/
----------------------------------------------------------------------
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
--
-- Table structure for table `comments`
--
DROP TABLE IF EXISTS `comments`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `comments` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`domain_id` int(11) NOT NULL,
`name` varchar(255) NOT NULL,
`type` varchar(10) NOT NULL,
`modified_at` int(11) NOT NULL,
`account` varchar(40) CHARACTER SET utf8mb3 DEFAULT NULL,
`comment` text CHARACTER SET utf8mb3 NOT NULL,
PRIMARY KEY (`id`),
KEY `comments_name_type_idx` (`name`,`type`),
KEY `comments_order_idx` (`domain_id`,`modified_at`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `comments`
--
LOCK TABLES `comments` WRITE;
/*!40000 ALTER TABLE `comments` DISABLE KEYS */;
/*!40000 ALTER TABLE `comments` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `cryptokeys`
--
DROP TABLE IF EXISTS `cryptokeys`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `cryptokeys` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`domain_id` int(11) NOT NULL,
`flags` int(11) NOT NULL,
`active` tinyint(1) DEFAULT NULL,
`published` tinyint(1) DEFAULT 1,
`content` text DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `domainidindex` (`domain_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `cryptokeys`
--
LOCK TABLES `cryptokeys` WRITE;
/*!40000 ALTER TABLE `cryptokeys` DISABLE KEYS */;
/*!40000 ALTER TABLE `cryptokeys` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `domainmetadata`
--
DROP TABLE IF EXISTS `domainmetadata`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `domainmetadata` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`domain_id` int(11) NOT NULL,
`kind` varchar(32) DEFAULT NULL,
`content` text DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `domainmetadata_idx` (`domain_id`,`kind`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `domainmetadata`
--
LOCK TABLES `domainmetadata` WRITE;
/*!40000 ALTER TABLE `domainmetadata` DISABLE KEYS */;
INSERT INTO `domainmetadata` VALUES
(1,1,'SOA-EDIT-API','DEFAULT');
/*!40000 ALTER TABLE `domainmetadata` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `domains`
--
DROP TABLE IF EXISTS `domains`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `domains` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`master` varchar(128) DEFAULT NULL,
`last_check` int(11) DEFAULT NULL,
`type` varchar(8) NOT NULL,
`notified_serial` int(10) unsigned DEFAULT NULL,
`account` varchar(40) CHARACTER SET utf8mb3 DEFAULT NULL,
`options` varchar(64000) DEFAULT NULL,
`catalog` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `name_index` (`name`),
KEY `catalog_idx` (`catalog`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `domains`
--
LOCK TABLES `domains` WRITE;
/*!40000 ALTER TABLE `domains` DISABLE KEYS */;
INSERT INTO `domains` VALUES
(1,'example.com','',NULL,'NATIVE',NULL,'',NULL,NULL);
/*!40000 ALTER TABLE `domains` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `records`
--
DROP TABLE IF EXISTS `records`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `records` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`domain_id` int(11) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
`type` varchar(10) DEFAULT NULL,
`content` varchar(64000) DEFAULT NULL,
`ttl` int(11) DEFAULT NULL,
`prio` int(11) DEFAULT NULL,
`disabled` tinyint(1) DEFAULT 0,
`ordername` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL,
`auth` tinyint(1) DEFAULT 1,
PRIMARY KEY (`id`),
KEY `nametype_index` (`name`,`type`),
KEY `domain_id` (`domain_id`),
KEY `ordername` (`ordername`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `records`
--
LOCK TABLES `records` WRITE;
/*!40000 ALTER TABLE `records` DISABLE KEYS */;
INSERT INTO `records` VALUES
(1,1,'example.com','NS','ns1.pdns',1500,0,0,NULL,1),
(2,1,'example.com','NS','ns2.pdns',1500,0,0,NULL,1),
(3,1,'example.com','SOA','a.misconfigured.dns.server.invalid hostmaster.example.com 2023030501 10800 3600 604800 3600',1500,0,0,NULL,1);
/*!40000 ALTER TABLE `records` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `supermasters`
--
DROP TABLE IF EXISTS `supermasters`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `supermasters` (
`ip` varchar(64) NOT NULL,
`nameserver` varchar(255) NOT NULL,
`account` varchar(40) CHARACTER SET utf8mb3 NOT NULL,
PRIMARY KEY (`ip`,`nameserver`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `supermasters`
--
LOCK TABLES `supermasters` WRITE;
/*!40000 ALTER TABLE `supermasters` DISABLE KEYS */;
/*!40000 ALTER TABLE `supermasters` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `tsigkeys`
--
DROP TABLE IF EXISTS `tsigkeys`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `tsigkeys` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`algorithm` varchar(50) DEFAULT NULL,
`secret` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `namealgoindex` (`name`,`algorithm`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `tsigkeys`
--
LOCK TABLES `tsigkeys` WRITE;
/*!40000 ALTER TABLE `tsigkeys` DISABLE KEYS */;
/*!40000 ALTER TABLE `tsigkeys` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

View File

@ -0,0 +1,12 @@
{
"pebble": {
"listenAddress": "0.0.0.0:443",
"managementListenAddress": "0.0.0.0:15000",
"certificate": "test/certs/localhost/cert.pem",
"privateKey": "test/certs/localhost/key.pem",
"httpPort": 80,
"tlsPort": 443,
"ocspResponderURL": "",
"externalAccountBindingRequired": false
}
}

View File

@ -0,0 +1,27 @@
# WARNING: This is a CI docker-compose file used for building and testing of the entire app, it should not be used for production.
services:
fullstack:
environment:
DB_MYSQL_HOST: 'db-mysql'
DB_MYSQL_PORT: '3306'
DB_MYSQL_USER: 'npm'
DB_MYSQL_PASSWORD: 'npmpass'
DB_MYSQL_NAME: 'npm'
depends_on:
- db-mysql
db-mysql:
image: jc21/mariadb-aria
environment:
MYSQL_ROOT_PASSWORD: 'npm'
MYSQL_DATABASE: 'npm'
MYSQL_USER: 'npm'
MYSQL_PASSWORD: 'npmpass'
volumes:
- mysql_vol:/var/lib/mysql
networks:
- fulltest
volumes:
mysql_vol:

View File

@ -0,0 +1,9 @@
# WARNING: This is a CI docker-compose file used for building and testing of the entire app, it should not be used for production.
services:
fullstack:
environment:
DB_SQLITE_FILE: '/data/mydb.sqlite'
PUID: 1000
PGID: 1000
DISABLE_IPV6: 'true'

View File

@ -1,91 +1,110 @@
# WARNING: This is a CI docker-compose file used for building and testing of the entire app, it should not be used for production. # WARNING: This is a CI docker-compose file used for building
version: '3.8' # and testing of the entire app, it should not be used for production.
# This is a base compose file, it should be extended with a
# docker-compose.ci.*.yml file
services: services:
fullstack-mysql: fullstack:
image: "${IMAGE}:ci-${BUILD_NUMBER}" image: "${IMAGE}:${BRANCH_LOWER}-ci-${BUILD_NUMBER}"
environment: environment:
DEBUG: 'true' DEBUG: 'true'
LE_STAGING: 'true'
FORCE_COLOR: 1 FORCE_COLOR: 1
DB_MYSQL_HOST: 'db'
DB_MYSQL_PORT: '3306'
DB_MYSQL_USER: 'npm'
DB_MYSQL_PASSWORD: 'npm'
DB_MYSQL_NAME: 'npm'
volumes: volumes:
- npm_data_mysql:/data - 'npm_data_ci:/data'
- npm_le_mysql:/etc/letsencrypt - 'npm_le_ci:/etc/letsencrypt'
expose: - './dev/letsencrypt.ini:/etc/letsencrypt.ini:ro'
- 81 - './dev/resolv.conf:/etc/resolv.conf:ro'
- 80 - '/etc/localtime:/etc/localtime:ro'
- 443 healthcheck:
test: ["CMD", "/usr/bin/check-health"]
interval: 10s
timeout: 3s
networks:
fulltest:
aliases:
- website1.example.com
- website2.example.com
- website3.example.com
stepca:
image: jc21/testca
volumes:
- './dev/resolv.conf:/etc/resolv.conf:ro'
- '/etc/localtime:/etc/localtime:ro'
networks:
fulltest:
aliases:
- ca.internal
pdns:
image: pschiffe/pdns-mysql
volumes:
- '/etc/localtime:/etc/localtime:ro'
environment:
PDNS_master: 'yes'
PDNS_api: 'yes'
PDNS_api_key: 'npm'
PDNS_webserver: 'yes'
PDNS_webserver_address: '0.0.0.0'
PDNS_webserver_password: 'npm'
PDNS_webserver-allow-from: '127.0.0.0/8,192.0.0.0/8,10.0.0.0/8,172.0.0.0/8'
PDNS_version_string: 'anonymous'
PDNS_default_ttl: 1500
PDNS_allow_axfr_ips: '127.0.0.0/8,192.0.0.0/8,10.0.0.0/8,172.0.0.0/8'
PDNS_gmysql_host: pdns-db
PDNS_gmysql_port: 3306
PDNS_gmysql_user: pdns
PDNS_gmysql_password: pdns
PDNS_gmysql_dbname: pdns
depends_on: depends_on:
- db - pdns-db
healthcheck: networks:
test: ["CMD", "/usr/bin/check-health"] fulltest:
interval: 10s aliases:
timeout: 3s - ns1.pdns
- ns2.pdns
fullstack-sqlite: pdns-db:
image: "${IMAGE}:ci-${BUILD_NUMBER}" image: mariadb
environment: environment:
DEBUG: 'true' MYSQL_ROOT_PASSWORD: 'pdns'
LE_STAGING: 'true' MYSQL_DATABASE: 'pdns'
FORCE_COLOR: 1 MYSQL_USER: 'pdns'
DB_SQLITE_FILE: '/data/mydb.sqlite' MYSQL_PASSWORD: 'pdns'
PUID: 1000
PGID: 1000
DISABLE_IPV6: 'true'
volumes: volumes:
- npm_data_sqlite:/data - 'pdns_mysql_vol:/var/lib/mysql'
- npm_le_sqlite:/etc/letsencrypt - '/etc/localtime:/etc/localtime:ro'
expose: - './dev/pdns-db.sql:/docker-entrypoint-initdb.d/01_init.sql:ro'
- 81 networks:
- 80 - fulltest
- 443
healthcheck:
test: ["CMD", "/usr/bin/check-health"]
interval: 10s
timeout: 3s
db: dnsrouter:
image: jc21/mariadb-aria image: jc21/dnsrouter
environment:
MYSQL_ROOT_PASSWORD: 'npm'
MYSQL_DATABASE: 'npm'
MYSQL_USER: 'npm'
MYSQL_PASSWORD: 'npm'
volumes: volumes:
- mysql_data:/var/lib/mysql - ./dev/dnsrouter-config.json.tmp:/dnsrouter-config.json:ro
networks:
- fulltest
cypress-mysql: cypress:
image: "${IMAGE}-cypress:ci-${BUILD_NUMBER}" image: "${IMAGE}-cypress:ci-${BUILD_NUMBER}"
build: build:
context: ../test/ context: ../
dockerfile: cypress/Dockerfile dockerfile: test/cypress/Dockerfile
environment: environment:
CYPRESS_baseUrl: 'http://fullstack-mysql:81' CYPRESS_baseUrl: 'http://fullstack:81'
volumes: volumes:
- cypress_logs_mysql:/results - 'cypress_logs:/results'
command: cypress run --browser chrome --config-file=${CYPRESS_CONFIG:-cypress/config/ci.json} - './dev/resolv.conf:/etc/resolv.conf:ro'
command: cypress run --browser chrome --config-file=cypress/config/ci.js
cypress-sqlite: networks:
image: "${IMAGE}-cypress:ci-${BUILD_NUMBER}" - fulltest
build:
context: ../test/
dockerfile: cypress/Dockerfile
environment:
CYPRESS_baseUrl: "http://fullstack-sqlite:81"
volumes:
- cypress_logs_sqlite:/results
command: cypress run --browser chrome --config-file=${CYPRESS_CONFIG:-cypress/config/ci.json}
volumes: volumes:
cypress_logs_mysql: cypress_logs:
cypress_logs_sqlite: npm_data_ci:
npm_data_mysql: npm_le_ci:
npm_data_sqlite: pdns_mysql_vol:
npm_le_sqlite:
npm_le_mysql: networks:
mysql_data: fulltest:
name: "npm-${BRANCH_LOWER}-ci-${BUILD_NUMBER}"

View File

@ -1,5 +1,4 @@
# WARNING: This is a DEVELOPMENT docker-compose file, it should not be used for production. # WARNING: This is a DEVELOPMENT docker-compose file, it should not be used for production.
version: '3.8'
services: services:
npm: npm:

View File

@ -0,0 +1,4 @@
log_format proxy '[$time_local] $upstream_cache_status $upstream_status $status - $request_method $scheme $host "$request_uri" [Client $remote_addr] [Length $body_bytes_sent] [Gzip $gzip_ratio] [Sent-to $server] "$http_user_agent" "$http_referer"';
log_format standard '[$time_local] $status - $request_method $scheme $host "$request_uri" [Client $remote_addr] [Length $body_bytes_sent] [Gzip $gzip_ratio] "$http_user_agent" "$http_referer"';
access_log /data/logs/fallback_access.log proxy;

View File

@ -14,6 +14,9 @@ error_log /data/logs/fallback_error.log warn;
# Includes files with directives to load dynamic modules. # Includes files with directives to load dynamic modules.
include /etc/nginx/modules/*.conf; include /etc/nginx/modules/*.conf;
# Custom
include /data/nginx/custom/root_top[.]conf;
events { events {
include /data/nginx/custom/events[.]conf; include /data/nginx/custom/events[.]conf;
} }
@ -43,10 +46,8 @@ http {
proxy_cache_path /var/lib/nginx/cache/public levels=1:2 keys_zone=public-cache:30m max_size=192m; proxy_cache_path /var/lib/nginx/cache/public levels=1:2 keys_zone=public-cache:30m max_size=192m;
proxy_cache_path /var/lib/nginx/cache/private levels=1:2 keys_zone=private-cache:5m max_size=1024m; proxy_cache_path /var/lib/nginx/cache/private levels=1:2 keys_zone=private-cache:5m max_size=1024m;
log_format proxy '[$time_local] $upstream_cache_status $upstream_status $status - $request_method $scheme $host "$request_uri" [Client $remote_addr] [Length $body_bytes_sent] [Gzip $gzip_ratio] [Sent-to $server] "$http_user_agent" "$http_referer"'; # Log format and fallback log file
log_format standard '[$time_local] $status - $request_method $scheme $host "$request_uri" [Client $remote_addr] [Length $body_bytes_sent] [Gzip $gzip_ratio] "$http_user_agent" "$http_referer"'; include /etc/nginx/conf.d/include/log.conf;
access_log /data/logs/fallback_access.log proxy;
# Dynamically generated resolvers file # Dynamically generated resolvers file
include /etc/nginx/conf.d/include/resolvers.conf; include /etc/nginx/conf.d/include/resolvers.conf;

View File

@ -173,6 +173,7 @@ NPM has the ability to include different custom configuration snippets in differ
You can add your custom configuration snippet files at `/data/nginx/custom` as follow: You can add your custom configuration snippet files at `/data/nginx/custom` as follow:
- `/data/nginx/custom/root_top.conf`: Included at the top of nginx.conf
- `/data/nginx/custom/root.conf`: Included at the very end of nginx.conf - `/data/nginx/custom/root.conf`: Included at the very end of nginx.conf
- `/data/nginx/custom/http_top.conf`: Included at the top of the main http block - `/data/nginx/custom/http_top.conf`: Included at the top of the main http block
- `/data/nginx/custom/http.conf`: Included at the end of the main http block - `/data/nginx/custom/http.conf`: Included at the end of the main http block
@ -212,3 +213,12 @@ You can customise the logrotate configuration through a mount (if your custom co
``` ```
For reference, the default configuration can be found [here](https://github.com/NginxProxyManager/nginx-proxy-manager/blob/develop/docker/rootfs/etc/logrotate.d/nginx-proxy-manager). For reference, the default configuration can be found [here](https://github.com/NginxProxyManager/nginx-proxy-manager/blob/develop/docker/rootfs/etc/logrotate.d/nginx-proxy-manager).
## Enabling the geoip2 module
To enable the geoip2 module, you can create the custom configuration file `/data/nginx/custom/root_top.conf` and include the following snippet:
```
load_module /usr/lib/nginx/modules/ngx_http_geoip2_module.so;
load_module /usr/lib/nginx/modules/ngx_stream_geoip2_module.so;
```

View File

@ -35,8 +35,8 @@
"name": "Cloudflare", "name": "Cloudflare",
"package_name": "certbot-dns-cloudflare", "package_name": "certbot-dns-cloudflare",
"version": "=={{certbot-version}}", "version": "=={{certbot-version}}",
"dependencies": "cloudflare acme=={{certbot-version}}", "dependencies": "cloudflare==2.19.* acme=={{certbot-version}}",
"credentials": "# Cloudflare API token\ndns_cloudflare_api_token = 0123456789abcdef0123456789abcdef01234567", "credentials": "# Cloudflare API token\ndns_cloudflare_api_token=0123456789abcdef0123456789abcdef01234567",
"full_plugin_name": "dns-cloudflare" "full_plugin_name": "dns-cloudflare"
}, },
"cloudns": { "cloudns": {
@ -239,6 +239,14 @@
"credentials": "dns_hetzner_api_token = 0123456789abcdef0123456789abcdef", "credentials": "dns_hetzner_api_token = 0123456789abcdef0123456789abcdef",
"full_plugin_name": "dns-hetzner" "full_plugin_name": "dns-hetzner"
}, },
"hover": {
"name": "Hover",
"package_name": "certbot-dns-hover",
"version": "~=1.2.1",
"dependencies": "",
"credentials": "dns_hover_hoverurl = https://www.hover.com\ndns_hover_username = hover-admin-username\ndns_hover_password = hover-admin-password\ndns_hover_totpsecret = 2fa-totp-secret",
"full_plugin_name": "dns-hover"
},
"infomaniak": { "infomaniak": {
"name": "Infomaniak", "name": "Infomaniak",
"package_name": "certbot-dns-infomaniak", "package_name": "certbot-dns-infomaniak",
@ -454,5 +462,13 @@
"dependencies": "", "dependencies": "",
"credentials": "dns_websupport_identifier = <api_key>\ndns_websupport_secret_key = <secret>", "credentials": "dns_websupport_identifier = <api_key>\ndns_websupport_secret_key = <secret>",
"full_plugin_name": "dns-websupport" "full_plugin_name": "dns-websupport"
},
"wedos":{
"name": "Wedos",
"package_name": "certbot-dns-wedos",
"version": "~=2.2",
"dependencies": "",
"credentials": "dns_wedos_user = <wedos_registration>\ndns_wedos_auth = <wapi_sha256_password>",
"full_plugin_name": "dns-wedos"
} }
} }

View File

@ -16,7 +16,7 @@ if hash docker 2>/dev/null; then
-e NODE_OPTIONS=--openssl-legacy-provider \ -e NODE_OPTIONS=--openssl-legacy-provider \
-v "$(pwd)/frontend:/app/frontend" \ -v "$(pwd)/frontend:/app/frontend" \
-v "$(pwd)/global:/app/global" \ -v "$(pwd)/global:/app/global" \
-w /app/frontend "$DOCKER_IMAGE" \ -w /app/frontend "${DOCKER_IMAGE}" \
sh -c "yarn install && yarn build && yarn build && chown -R $(id -u):$(id -g) /app/frontend" sh -c "yarn install && yarn build && yarn build && chown -R $(id -u):$(id -g) /app/frontend"
echo -e "${BLUE} ${GREEN}Building Frontend Complete${RESET}" echo -e "${BLUE} ${GREEN}Building Frontend Complete${RESET}"

89
scripts/ci/fulltest-cypress Executable file
View File

@ -0,0 +1,89 @@
#!/bin/bash
set -e
STACK="${1:-sqlite}"
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# remember this is running in "ci" folder..
# Some defaults for running this script outside of CI
export COMPOSE_PROJECT_NAME="${COMPOSE_PROJECT_NAME:-npm_local_fulltest}"
export IMAGE="${IMAGE:-nginx-proxy-manager}"
export BRANCH_LOWER="${BRANCH_LOWER:-unknown}"
export BUILD_NUMBER="${BUILD_NUMBER:-0000}"
if [ "${COMPOSE_FILE:-}" = "" ]; then
export COMPOSE_FILE="docker/docker-compose.ci.yml:docker/docker-compose.ci.${STACK}.yml"
fi
# Colors
BLUE='\E[1;34m'
RED='\E[1;31m'
CYAN='\E[1;36m'
GREEN='\E[1;32m'
RESET='\E[0m'
YELLOW='\E[1;33m'
export BLUE CYAN GREEN RESET YELLOW
echo -e "${BLUE} ${CYAN}Starting fullstack cypress testing ...${RESET}"
echo -e "${BLUE} $(docker-compose config)${RESET}"
# $1: container_name
get_container_ip () {
local container_name=$1
local container
local ip
container=$(docker-compose ps --all -q "${container_name}" | tail -n1)
ip=$(docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$container")
echo "$ip"
}
# Bring up a stack, in steps so we can inject IPs everywhere
docker-compose up -d pdns pdns-db
PDNS_IP=$(get_container_ip "pdns")
echo -e "${BLUE} ${YELLOW}PDNS IP is ${PDNS_IP}${RESET}"
# adjust the dnsrouter config
LOCAL_DNSROUTER_CONFIG="$DIR/../../docker/dev/dnsrouter-config.json"
rm -rf "$LOCAL_DNSROUTER_CONFIG.tmp"
# IMPORTANT: changes to dnsrouter-config.json will affect this line:
jq --arg a "$PDNS_IP" '.servers[0].upstreams[1].upstream = $a' "$LOCAL_DNSROUTER_CONFIG" > "$LOCAL_DNSROUTER_CONFIG.tmp"
docker-compose up -d dnsrouter
DNSROUTER_IP=$(get_container_ip "dnsrouter")
echo -e "${BLUE} ${YELLOW}DNS Router IP is ${DNSROUTER_IP}"
if [ "${DNSROUTER_IP:-}" = "" ]; then
echo -e "${RED} ERROR: DNS Router IP is not set${RESET}"
exit 1
fi
# mount the resolver
LOCAL_RESOLVE="$DIR/../../docker/dev/resolv.conf"
rm -rf "${LOCAL_RESOLVE}"
printf "nameserver %s\noptions ndots:0" "${DNSROUTER_IP}" > "${LOCAL_RESOLVE}"
# bring up all remaining containers, except cypress!
docker-compose up -d --remove-orphans stepca
docker-compose pull db-mysql || true # ok to fail
docker-compose up -d --remove-orphans --pull=never fullstack
# wait for main container to be healthy
bash "$DIR/../wait-healthy" "$(docker-compose ps --all -q fullstack)" 120
# Run tests
rm -rf "$DIR/../../test/results"
docker-compose up --build cypress
# Get results
docker cp -L "$(docker-compose ps --all -q cypress):/test/results" "$DIR/../../test/"
docker cp -L "$(docker-compose ps --all -q fullstack):/data/logs" "$DIR/../../test/results/"
if [ "$2" = "cleanup" ]; then
echo -e "${BLUE} ${CYAN}Cleaning up containers ...${RESET}"
docker-compose down --remove-orphans --volumes -t 30
fi
echo -e "${BLUE} ${GREEN}Fullstack cypress testing complete${RESET}"

View File

@ -3,8 +3,8 @@
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
. "$DIR/../.common.sh" . "$DIR/../.common.sh"
DOCKER_IMAGE=nginxproxymanager/nginx-full:certbot-node TESTING_IMAGE=nginxproxymanager/nginx-full:certbot-node
docker pull "${DOCKER_IMAGE}" docker pull "${TESTING_IMAGE}"
# Test # Test
echo -e "${BLUE} ${CYAN}Testing backend ...${RESET}" echo -e "${BLUE} ${CYAN}Testing backend ...${RESET}"
@ -12,20 +12,20 @@ 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 \
"${DOCKER_IMAGE}" \ "${TESTING_IMAGE}" \
sh -c 'yarn install && yarn eslint . && rm -rf node_modules' sh -c 'yarn install && yarn eslint . && rm -rf node_modules'
echo -e "${BLUE} ${GREEN}Testing Complete${RESET}" echo -e "${BLUE} ${GREEN}Testing Complete${RESET}"
# Build # Build
echo -e "${BLUE} ${CYAN}Building ...${RESET}" echo -e "${BLUE} ${CYAN}Building ...${RESET}"
docker build --pull --no-cache --compress \ docker build --pull --no-cache --compress \
-t "${IMAGE}:ci-${BUILD_NUMBER}" \ -t "${IMAGE:-nginx-proxy-manager}:${BRANCH_LOWER:-unknown}-ci-${BUILD_NUMBER:-0000}" \
-f docker/Dockerfile \ -f docker/Dockerfile \
--progress=plain \ --progress=plain \
--build-arg TARGETPLATFORM=linux/amd64 \ --build-arg TARGETPLATFORM=linux/amd64 \
--build-arg BUILDPLATFORM=linux/amd64 \ --build-arg BUILDPLATFORM=linux/amd64 \
--build-arg BUILD_VERSION="${BUILD_VERSION}" \ --build-arg BUILD_VERSION="${BUILD_VERSION:-unknown}" \
--build-arg BUILD_COMMIT="${BUILD_COMMIT}" \ --build-arg BUILD_COMMIT="${BUILD_COMMIT:-unknown}" \
--build-arg BUILD_DATE="$(date '+%Y-%m-%d %T %Z')" \ --build-arg BUILD_DATE="$(date '+%Y-%m-%d %T %Z')" \
. .
echo -e "${BLUE} ${GREEN}Building Complete${RESET}" echo -e "${BLUE} ${GREEN}Building Complete${RESET}"

View File

@ -23,9 +23,8 @@ until [ "${HEALTHY}" = "healthy" ]; do
((LOOPCOUNT++)) ((LOOPCOUNT++))
if [ "$LOOPCOUNT" == "$LIMIT" ]; then if [ "$LOOPCOUNT" == "$LIMIT" ]; then
echo ""
echo ""
echo -e "${BLUE} ${RED}Timed out waiting for healthy${RESET}" echo -e "${BLUE} ${RED}Timed out waiting for healthy${RESET}"
docker logs --tail 50 "$SERVICE"
exit 1 exit 1
fi fi
done done

View File

@ -1 +0,0 @@
node_modules

6
test/.gitignore vendored
View File

@ -1,4 +1,2 @@
.vscode results/*
node_modules cypress/results/*
results
cypress/videos

View File

@ -1,13 +1,12 @@
FROM cypress/included:9.4.1 FROM cypress/included:13.9.0
COPY --chown=1000 ./ /test COPY --chown=1000 ./test /test
# mkcert # Disable Cypress CLI colors
ENV MKCERT=1.4.2 ENV FORCE_COLOR=0
RUN wget -O /usr/bin/mkcert "https://github.com/FiloSottile/mkcert/releases/download/v${MKCERT}/mkcert-v${MKCERT}-linux-amd64" \ ENV NO_COLOR=1
&& chmod +x /usr/bin/mkcert
WORKDIR /test WORKDIR /test
RUN yarn install RUN yarn install && yarn cache clean
ENTRYPOINT [] ENTRYPOINT []
CMD ["cypress", "run"] CMD ["cypress", "run"]

22
test/cypress/config/ci.js Normal file
View File

@ -0,0 +1,22 @@
const { defineConfig } = require('cypress');
module.exports = defineConfig({
requestTimeout: 30000,
defaultCommandTimeout: 20000,
reporter: 'cypress-multi-reporters',
reporterOptions: {
configFile: 'multi-reporter.json'
},
video: true,
videosFolder: 'results/videos',
screenshotsFolder: 'results/screenshots',
e2e: {
setupNodeEvents(on, config) {
return require("../plugins/index.js")(on, config);
},
env: {
swaggerBase: '{{baseUrl}}/api/schema',
},
baseUrl: 'http://localhost:1234',
}
});

View File

@ -1,14 +0,0 @@
{
"requestTimeout": 30000,
"defaultCommandTimeout": 20000,
"reporter": "cypress-multi-reporters",
"reporterOptions": {
"configFile": "multi-reporter.json"
},
"videosFolder": "results/videos",
"screenshotsFolder": "results/screenshots",
"env": {
"swaggerBase": "{{baseUrl}}/api/schema",
"RETRIES": 4
}
}

View File

@ -0,0 +1,22 @@
const { defineConfig } = require('cypress');
module.exports = defineConfig({
requestTimeout: 30000,
defaultCommandTimeout: 20000,
reporter: 'cypress-multi-reporters',
reporterOptions: {
configFile: 'multi-reporter.json'
},
video: false,
videosFolder: 'results/videos',
screenshotsFolder: 'results/screenshots',
e2e: {
setupNodeEvents(on, config) {
return require("../plugins/index.js")(on, config);
},
env: {
swaggerBase: '{{baseUrl}}/api/schema',
},
baseUrl: 'http://localhost:1234',
}
});

View File

@ -1,14 +0,0 @@
{
"requestTimeout": 30000,
"defaultCommandTimeout": 20000,
"reporter": "cypress-multi-reporters",
"reporterOptions": {
"configFile": "multi-reporter.json"
},
"videos": false,
"screenshotsFolder": "results/screenshots",
"env": {
"swaggerBase": "{{baseUrl}}/api/schema",
"RETRIES": 0
}
}

View File

@ -1,5 +0,0 @@
{
"name": "Using fixtures to represent data",
"email": "hello@cypress.io",
"body": "Fixtures are a great way to mock data for responses to routes"
}

View File

@ -1,8 +1,12 @@
const _ = require('lodash'); const _ = require("lodash");
const chalk = require('chalk'); const chalk = require("chalk");
module.exports = function () { module.exports = function() {
var arr = _.values(arguments); var arr = _.values(arguments);
arr.unshift(chalk.blue.bold('[') + chalk.yellow.bold('Backend API') + chalk.blue.bold(']')); arr.unshift(
chalk.blue.bold("[") +
chalk.yellow.bold("Backend API") +
chalk.blue.bold("]"),
);
console.log.apply(null, arr); console.log.apply(null, arr);
}; };

View File

@ -9,20 +9,32 @@
// *********************************************** // ***********************************************
// //
import 'cypress-wait-until';
Cypress.Commands.add('randomString', (length) => {
var result = '';
var characters = 'ABCDEFGHIJK LMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var charactersLength = characters.length;
for (var i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
});
/** /**
* Check the swagger schema: * Check the swagger schema:
* *
* @param {string} method API Method in swagger doc, "get", "put", "post", "delete" * @param {string} method API Method in swagger doc, "get", "put", "post", "delete"
* @param {number} statusCode API status code in swagger doc * @param {integer} code Swagger doc endpoint response code, exactly as defined in swagger doc
* @param {string} path Swagger doc endpoint path, exactly as defined in swagger doc * @param {string} path Swagger doc endpoint path, exactly as defined in swagger doc
* @param {*} data The API response data to check against the swagger schema * @param {*} data The API response data to check against the swagger schema
*/ */
Cypress.Commands.add('validateSwaggerSchema', (method, statusCode, path, data) => { Cypress.Commands.add('validateSwaggerSchema', (method, code, path, data) => {
cy.task('validateSwaggerSchema', { cy.task('validateSwaggerSchema', {
file: Cypress.env('swaggerBase'), file: Cypress.env('swaggerBase'),
endpoint: path, endpoint: path,
method: method, method: method,
statusCode: statusCode, statusCode: code,
responseSchema: data, responseSchema: data,
verbose: true verbose: true
}).should('equal', null); }).should('equal', null);
@ -40,3 +52,19 @@ Cypress.Commands.add('getToken', () => {
cy.wrap(res.token); cy.wrap(res.token);
}); });
}); });
// TODO: copied from v3, is this usable?
Cypress.Commands.add('waitForCertificateStatus', (token, certID, expected, timeout = 60) => {
cy.log(`Waiting for certificate (${certID}) status (${expected}) timeout (${timeout})`);
cy.waitUntil(() => cy.task('backendApiGet', {
token: token,
path: `/api/certificates/${certID}`
}).then((data) => {
return data.result.status === expected;
}), {
errorMsg: 'Waiting for certificate status failed',
timeout: timeout * 1000,
interval: 5000
});
});

View File

@ -1,5 +1,3 @@
require('cypress-plugin-retries');
import './commands'; import './commands';
Cypress.on('uncaught:exception', (/*err, runnable*/) => { Cypress.on('uncaught:exception', (/*err, runnable*/) => {

View File

@ -4,19 +4,19 @@
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"dependencies": { "dependencies": {
"@jc21/cypress-swagger-validation": "^0.0.9", "@jc21/cypress-swagger-validation": "^0.2.6",
"@jc21/restler": "^3.4.0", "@jc21/restler": "^3.4.0",
"chalk": "^4.1.0", "chalk": "^4.1.0",
"cypress": "^9.4.1", "cypress": "^13.9.0",
"cypress-multi-reporters": "^1.4.0", "cypress-multi-reporters": "^1.6.4",
"cypress-plugin-retries": "^1.5.2", "cypress-wait-until": "^3.0.1",
"eslint": "^7.6.0", "eslint": "^9.3.0",
"eslint-plugin-align-assignments": "^1.1.2", "eslint-plugin-align-assignments": "^1.1.2",
"eslint-plugin-chai-friendly": "^0.6.0", "eslint-plugin-chai-friendly": "^0.7.4",
"eslint-plugin-cypress": "^2.11.1", "eslint-plugin-cypress": "^3.2.0",
"lodash": "^4.17.19", "lodash": "^4.17.21",
"mocha": "^8.1.1", "mocha": "^10.4.0",
"mocha-junit-reporter": "^2.0.0" "mocha-junit-reporter": "^2.2.1"
}, },
"scripts": { "scripts": {
"cypress": "cypress open --config-file=cypress/config/dev.json --config baseUrl=${BASE_URL:-http://127.0.0.1:3081}", "cypress": "cypress open --config-file=cypress/config/dev.json --config baseUrl=${BASE_URL:-http://127.0.0.1:3081}",

File diff suppressed because it is too large Load Diff