Merge branch 'NginxProxyManager:develop' into develop-crowdsec

This commit is contained in:
Brian Munro 2024-07-12 06:26:45 +02:00 committed by GitHub
commit ee03105358
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
80 changed files with 2562 additions and 5930 deletions

4
.gitignore vendored
View File

@ -3,3 +3,7 @@
._*
.vscode
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

148
Jenkinsfile vendored
View File

@ -18,10 +18,8 @@ pipeline {
BUILD_VERSION = getVersion()
MAJOR_VERSION = '2'
BRANCH_LOWER = "${BRANCH_NAME.toLowerCase().replaceAll('\\\\', '-').replaceAll('/', '-').replaceAll('\\.', '-')}"
COMPOSE_PROJECT_NAME = "npm_${BRANCH_LOWER}_${BUILD_NUMBER}"
COMPOSE_FILE = 'docker/docker-compose.ci.yml'
BUILDX_NAME = "npm_${BRANCH_LOWER}_${BUILD_NUMBER}"
COMPOSE_INTERACTIVE_NO_CLI = 1
BUILDX_NAME = "${COMPOSE_PROJECT_NAME}"
}
stages {
stage('Environment') {
@ -92,81 +90,63 @@ pipeline {
sh 'yarn install'
sh 'yarn build'
}
dir(path: 'docs/.vuepress/dist') {
sh 'tar -czf ../../docs.tgz *'
}
archiveArtifacts(artifacts: 'docs/docs.tgz', allowEmptyArchive: false)
}
}
stage('Cypress') {
steps {
// Creating will also create the network prior to
// using it in parallel stages below and mitigating
// a race condition.
sh 'docker-compose build cypress-sqlite'
sh 'docker-compose build cypress-mysql'
sh 'docker-compose create cypress-sqlite'
sh 'docker-compose create cypress-mysql'
}
}
}
}
stage('Integration Tests') {
parallel {
stage('Sqlite') {
steps {
// Bring up a stack
sh 'docker-compose up -d fullstack-sqlite'
sh './scripts/wait-healthy $(docker-compose ps --all -q fullstack-sqlite) 120'
// Stop and Start it, as this will test it's ability to restart with existing data
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('Test Sqlite') {
environment {
COMPOSE_PROJECT_NAME = "npm_${BRANCH_LOWER}_${BUILD_NUMBER}_sqlite"
COMPOSE_FILE = 'docker/docker-compose.ci.yml:docker/docker-compose.ci.sqlite.yml'
}
when {
not {
equals expected: 'UNSTABLE', actual: currentBuild.result
}
stage('Mysql') {
steps {
// Bring up a stack
sh 'docker-compose up -d fullstack-mysql'
sh './scripts/wait-healthy $(docker-compose ps --all -q fullstack-mysql) 120'
// Run tests
sh 'rm -rf test/results-mysql'
sh 'docker-compose up cypress-mysql'
// Get results
sh 'docker cp -L "$(docker-compose ps --all -q cypress-mysql):/test/results" test/results-mysql'
}
post {
always {
// Dumps to analyze later
sh 'mkdir -p debug/mysql'
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/*'
}
}
}
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('Test Mysql') {
environment {
COMPOSE_PROJECT_NAME = "npm_${BRANCH_LOWER}_${BUILD_NUMBER}_mysql"
COMPOSE_FILE = 'docker/docker-compose.ci.yml:docker/docker-compose.ci.mysql.yml'
}
when {
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/mysql'
sh 'docker logs $(docker-compose ps --all -q fullstack) > debug/mysql/docker_fullstack.log 2>&1'
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'
sh 'docker logs $(docker-compose ps --all -q pdns-db) > debug/mysql/docker_pdns-db.log 2>&1'
sh 'docker logs $(docker-compose ps --all -q dnsrouter) > debug/mysql/docker_dnsrouter.log 2>&1'
junit 'test/results/junit/*'
sh 'docker-compose down --remove-orphans --volumes -t 30 || true'
}
}
}
@ -185,30 +165,17 @@ pipeline {
}
stage('Docs / Comment') {
parallel {
stage('Master Docs') {
stage('Docs Job') {
when {
allOf {
branch 'master'
branch pattern: "^(develop|master)\$", comparator: "REGEXP"
not {
equals expected: 'UNSTABLE', actual: currentBuild.result
}
}
}
steps {
npmDocsReleaseMaster()
}
}
stage('Develop Docs') {
when {
allOf {
branch 'develop'
not {
equals expected: 'UNSTABLE', actual: currentBuild.result
}
}
}
steps {
npmDocsReleaseDevelop()
build wait: false, job: 'nginx-proxy-manager-docs', parameters: [string(name: 'docs_branch', value: "$BRANCH_NAME")]
}
}
stage('PR Comment') {
@ -231,9 +198,8 @@ pipeline {
}
post {
always {
sh 'docker-compose down --remove-orphans --volumes -t 30'
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 {
juxtapose event: 'success'

View File

@ -1,7 +1,7 @@
<p align="center">
<img src="https://nginxproxymanager.com/github.png">
<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">
<img src="https://img.shields.io/docker/stars/jc21/nginx-proxy-manager.svg?style=for-the-badge">
</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:
```yml
version: '3.8'
services:
app:
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(', ')}`);
const credentialsLocation = '/etc/letsencrypt/credentials/credentials-' + certificate.id;
// Escape single quotes and backslashes
const escapedCredentials = certificate.meta.dns_provider_credentials.replaceAll('\'', '\\\'').replaceAll('\\', '\\\\');
const credentialsCmd = 'mkdir -p /etc/letsencrypt/credentials 2> /dev/null; echo \'' + escapedCredentials + '\' > \'' + credentialsLocation + '\' && chmod 600 \'' + credentialsLocation + '\'';
fs.mkdirSync('/etc/letsencrypt/credentials', { recursive: true });
fs.writeFileSync(credentialsLocation, certificate.meta.dns_provider_credentials, {mode: 0o600});
// Whether the plugin has a --<name>-credentials argument
const hasConfigArg = certificate.meta.dns_provider !== 'route53';
@ -898,17 +897,15 @@ const internalCertificate = {
mainCmd = mainCmd + ' --dns-duckdns-no-txt-restore';
}
logger.info('Command:', `${credentialsCmd} && && ${mainCmd}`);
logger.info('Command:', mainCmd);
try {
await utils.exec(credentialsCmd);
const result = await utils.exec(mainCmd);
logger.info(result);
return result;
} catch (err) {
// Don't fail if file does not exist
const delete_credentialsCmd = `rm -f '${credentialsLocation}' || true`;
await utils.exec(delete_credentialsCmd);
// Don't fail if file does not exist, so no need for action in the callback
fs.unlink(credentialsLocation, () => {});
throw err;
}
},

View File

@ -93,7 +93,7 @@ const generateKeys = () => {
try {
fs.writeFileSync(keysFile, JSON.stringify(keys, null, 2));
} 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);
}
logger.info('Wrote JWT key pair to config file: ' + keysFile);

View File

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

View File

@ -1,4 +1,6 @@
location {{ path }} {
{{ advanced_config }}
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Scheme $scheme;
proxy_set_header X-Forwarded-Proto $scheme;
@ -17,8 +19,5 @@
proxy_set_header Connection $http_connection;
proxy_http_version 1.1;
{% endif %}
{{ advanced_config }}
}

View File

@ -448,11 +448,11 @@ brace-expansion@^1.1.7:
concat-map "0.0.1"
braces@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
version "3.0.3"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789"
integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==
dependencies:
fill-range "^7.0.1"
fill-range "^7.1.1"
buffer-crc32@^0.2.1, buffer-crc32@^0.2.13:
version "0.2.13"
@ -1206,10 +1206,10 @@ file-entry-cache@^6.0.1:
dependencies:
flat-cache "^3.0.4"
fill-range@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
fill-range@^7.1.1:
version "7.1.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292"
integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==
dependencies:
to-regex-range "^5.0.1"
@ -1402,9 +1402,9 @@ glob-parent@^6.0.2:
is-glob "^4.0.3"
glob-parent@~5.1.0:
version "5.1.1"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229"
integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==
version "5.1.2"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
dependencies:
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.
version: '3.8'
# WARNING: This is a CI docker-compose file used for building
# 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:
fullstack-mysql:
image: "${IMAGE}:ci-${BUILD_NUMBER}"
fullstack:
image: "${IMAGE}:${BRANCH_LOWER}-ci-${BUILD_NUMBER}"
environment:
DEBUG: 'true'
LE_STAGING: 'true'
FORCE_COLOR: 1
DB_MYSQL_HOST: 'db'
DB_MYSQL_PORT: '3306'
DB_MYSQL_USER: 'npm'
DB_MYSQL_PASSWORD: 'npm'
DB_MYSQL_NAME: 'npm'
volumes:
- npm_data_mysql:/data
- npm_le_mysql:/etc/letsencrypt
expose:
- 81
- 80
- 443
- 'npm_data_ci:/data'
- 'npm_le_ci:/etc/letsencrypt'
- './dev/letsencrypt.ini:/etc/letsencrypt.ini:ro'
- './dev/resolv.conf:/etc/resolv.conf:ro'
- '/etc/localtime:/etc/localtime:ro'
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:
- db
healthcheck:
test: ["CMD", "/usr/bin/check-health"]
interval: 10s
timeout: 3s
- pdns-db
networks:
fulltest:
aliases:
- ns1.pdns
- ns2.pdns
fullstack-sqlite:
image: "${IMAGE}:ci-${BUILD_NUMBER}"
pdns-db:
image: mariadb
environment:
DEBUG: 'true'
LE_STAGING: 'true'
FORCE_COLOR: 1
DB_SQLITE_FILE: '/data/mydb.sqlite'
PUID: 1000
PGID: 1000
DISABLE_IPV6: 'true'
MYSQL_ROOT_PASSWORD: 'pdns'
MYSQL_DATABASE: 'pdns'
MYSQL_USER: 'pdns'
MYSQL_PASSWORD: 'pdns'
volumes:
- npm_data_sqlite:/data
- npm_le_sqlite:/etc/letsencrypt
expose:
- 81
- 80
- 443
healthcheck:
test: ["CMD", "/usr/bin/check-health"]
interval: 10s
timeout: 3s
- 'pdns_mysql_vol:/var/lib/mysql'
- '/etc/localtime:/etc/localtime:ro'
- './dev/pdns-db.sql:/docker-entrypoint-initdb.d/01_init.sql:ro'
networks:
- fulltest
db:
image: jc21/mariadb-aria
environment:
MYSQL_ROOT_PASSWORD: 'npm'
MYSQL_DATABASE: 'npm'
MYSQL_USER: 'npm'
MYSQL_PASSWORD: 'npm'
dnsrouter:
image: jc21/dnsrouter
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}"
build:
context: ../test/
dockerfile: cypress/Dockerfile
context: ../
dockerfile: test/cypress/Dockerfile
environment:
CYPRESS_baseUrl: 'http://fullstack-mysql:81'
CYPRESS_baseUrl: 'http://fullstack:81'
volumes:
- cypress_logs_mysql:/results
command: cypress run --browser chrome --config-file=${CYPRESS_CONFIG:-cypress/config/ci.json}
cypress-sqlite:
image: "${IMAGE}-cypress:ci-${BUILD_NUMBER}"
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}
- 'cypress_logs:/results'
- './dev/resolv.conf:/etc/resolv.conf:ro'
command: cypress run --browser chrome --config-file=cypress/config/ci.js
networks:
- fulltest
volumes:
cypress_logs_mysql:
cypress_logs_sqlite:
npm_data_mysql:
npm_data_sqlite:
npm_le_sqlite:
npm_le_mysql:
mysql_data:
cypress_logs:
npm_data_ci:
npm_le_ci:
pdns_mysql_vol:
networks:
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.
version: '3.8'
services:
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.
include /etc/nginx/modules/*.conf;
# Custom
include /data/nginx/custom/root_top[.]conf;
events {
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/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 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;
# Log format and fallback log file
include /etc/nginx/conf.d/include/log.conf;
# Dynamically generated resolvers file
include /etc/nginx/conf.d/include/resolvers.conf;

5
docs/.gitignore vendored
View File

@ -1,8 +1,9 @@
.vuepress/dist
dist
node_modules
ts
.temp
.cache
.vitepress/cache
.yarn/*
!.yarn/releases
@ -10,4 +11,4 @@ ts
!.yarn/sdks
!.yarn/versions
*.gz
*.tgz
*.tgz

View File

@ -0,0 +1,61 @@
import { defineConfig, type DefaultTheme } from 'vitepress';
// https://vitepress.dev/reference/site-config
export default defineConfig({
title: "Nginx Proxy Manager",
description: "Expose your services easily and securely",
head: [
["link", { rel: "icon", href: "/icon.png" }],
["meta", { name: "description", content: "Docker container and built in Web Application for managing Nginx proxy hosts with a simple, powerful interface, providing free SSL support via Let's Encrypt" }],
["meta", { property: "og:title", content: "Nginx Proxy Manager" }],
["meta", { property: "og:description", content: "Docker container and built in Web Application for managing Nginx proxy hosts with a simple, powerful interface, providing free SSL support via Let's Encrypt"}],
["meta", { property: "og:type", content: "website" }],
["meta", { property: "og:url", content: "https://nginxproxymanager.com/" }],
["meta", { property: "og:image", content: "https://nginxproxymanager.com/icon.png" }],
["meta", { name: "twitter:card", content: "summary"}],
["meta", { name: "twitter:title", content: "Nginx Proxy Manager"}],
["meta", { name: "twitter:description", content: "Docker container and built in Web Application for managing Nginx proxy hosts with a simple, powerful interface, providing free SSL support via Let's Encrypt"}],
["meta", { name: "twitter:image", content: "https://nginxproxymanager.com/icon.png"}],
["meta", { name: "twitter:alt", content: "Nginx Proxy Manager"}],
// GA
['script', { async: 'true', src: 'https://www.googletagmanager.com/gtag/js?id=G-TXT8F5WY5B'}],
['script', {}, "window.dataLayer = window.dataLayer || [];\nfunction gtag(){dataLayer.push(arguments);}\ngtag('js', new Date());\ngtag('config', 'G-TXT8F5WY5B');"],
],
sitemap: {
hostname: 'https://nginxproxymanager.com'
},
metaChunk: true,
srcDir: './src',
outDir: './dist',
themeConfig: {
// https://vitepress.dev/reference/default-theme-config
logo: { src: '/logo.svg', width: 24, height: 24 },
nav: [
{ text: 'Setup', link: '/setup/' },
],
sidebar: [
{
items: [
// { text: 'Home', link: '/' },
{ text: 'Guide', link: '/guide/' },
{ text: 'Screenshots', link: '/screenshots/' },
{ text: 'Setup Instructions', link: '/setup/' },
{ text: 'Advanced Configuration', link: '/advanced-config/' },
{ text: 'Upgrading', link: '/upgrading/' },
{ text: 'Frequently Asked Questions', link: '/faq/' },
{ text: 'Third Party', link: '/third-party/' },
]
}
],
socialLinks: [
{ icon: 'github', link: 'https://github.com/NginxProxyManager/nginx-proxy-manager' }
],
search: {
provider: 'local'
},
footer: {
message: 'Released under the MIT License.',
copyright: 'Copyright © 2016-present jc21.com'
}
}
});

View File

@ -0,0 +1,27 @@
:root {
--vp-home-hero-name-color: transparent;
--vp-home-hero-name-background: -webkit-linear-gradient(120deg, #f15833 30%, #FAA42F);
--vp-home-hero-image-background-image: linear-gradient(-45deg, #aaaaaa 50%, #777777 50%);
--vp-home-hero-image-filter: blur(44px);
--vp-c-brand-1: #f15833;
--vp-c-brand-2: #FAA42F;
--vp-c-brand-3: #f15833;
}
@media (min-width: 640px) {
:root {
--vp-home-hero-image-filter: blur(56px);
}
}
@media (min-width: 960px) {
:root {
--vp-home-hero-image-filter: blur(68px);
}
}
.inline-img img {
display: inline;
}

View File

@ -0,0 +1,4 @@
import DefaultTheme from 'vitepress/theme'
import './custom.css'
export default DefaultTheme

View File

@ -1,120 +0,0 @@
import { defineUserConfig } from 'vuepress';
import { defaultTheme } from 'vuepress'
import { googleAnalyticsPlugin } from '@vuepress/plugin-google-analytics';
import { searchPlugin } from '@vuepress/plugin-search'
import { sitemapPlugin } from 'vuepress-plugin-sitemap2';
import zoomingPlugin from 'vuepress-plugin-zooming';
export default defineUserConfig({
locales: {
"/": {
lang: "en-US",
title: "Nginx Proxy Manager",
description: "Expose your services easily and securely",
},
},
head: [
["link", { rel: "icon", href: "/icon.png" }],
["meta", { name: "description", content: "Docker container and built in Web Application for managing Nginx proxy hosts with a simple, powerful interface, providing free SSL support via Let's Encrypt" }],
["meta", { property: "og:title", content: "Nginx Proxy Manager" }],
["meta", { property: "og:description", content: "Docker container and built in Web Application for managing Nginx proxy hosts with a simple, powerful interface, providing free SSL support via Let's Encrypt"}],
["meta", { property: "og:type", content: "website" }],
["meta", { property: "og:url", content: "https://nginxproxymanager.com/" }],
["meta", { property: "og:image", content: "https://nginxproxymanager.com/icon.png" }],
["meta", { name: "twitter:card", content: "summary"}],
["meta", { name: "twitter:title", content: "Nginx Proxy Manager"}],
["meta", { name: "twitter:description", content: "Docker container and built in Web Application for managing Nginx proxy hosts with a simple, powerful interface, providing free SSL support via Let's Encrypt"}],
["meta", { name: "twitter:image", content: "https://nginxproxymanager.com/icon.png"}],
["meta", { name: "twitter:alt", content: "Nginx Proxy Manager"}],
],
theme: defaultTheme({
logo: '/icon.png',
repo: "jc21/nginx-proxy-manager",
docsRepo: 'https://github.com/jc21/nginx-proxy-manager',
docsBranch: 'develop',
docsDir: 'docs',
editLinkPattern: ':repo/edit/:branch/:path',
locales: {
'/': {
label: 'English',
selectLanguageText: 'Languages',
selectLanguageName: 'English',
editLinkText: 'Edit this page on GitHub',
navbar: [
{ text: 'Setup', link: '/setup/' }
],
sidebar: {
'/': [
{
text: 'Home',
link: '/'
},
{
text: 'Guide',
link: '/guide/',
collapsible: true,
},
{
text: 'Screenshots',
link: '/screenshots/',
collapsible: true,
},
{
text: 'Setup Instructions',
link: '/setup/',
collapsible: true,
},
{
text: 'Advanced Configuration',
link: '/advanced-config/',
collapsible: true,
},
{
text: 'Upgrading',
link: '/upgrading/',
collapsible: true,
},
{
text: 'Frequently Asked Questions',
link: '/faq/',
collapsible: true,
},
{
text: 'Third Party',
link: '/third-party/',
collapsible: true,
},
],
},
}
}
}),
markdown: {
code: {
lineNumbers: false,
},
},
plugins: [
googleAnalyticsPlugin({
id: 'UA-99675467-4'
}),
sitemapPlugin({
hostname: "https://nginxproxymanager.com",
}),
zoomingPlugin({
selector: '.zooming',
delay: 1000,
options: {
bgColor: 'black',
zIndex: 10000,
},
}),
searchPlugin({
locales: {
'/': {
placeholder: 'Search',
},
},
}),
],
});

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

View File

@ -1,258 +0,0 @@
:root {
// brand colors
--c-brand: #f15833;
--c-brand-light: #f15833;
// background colors
--c-bg: #ffffff;
--c-bg-light: #f3f4f5;
--c-bg-lighter: #eeeeee;
--c-bg-dark: #ebebec;
--c-bg-darker: #e6e6e6;
--c-bg-navbar: var(--c-bg);
--c-bg-sidebar: var(--c-bg);
--c-bg-arrow: #cccccc;
// text colors
--c-text: #663015;
--c-text-accent: var(--c-brand);
--c-text-light: #863f1c;
--c-text-lighter: #b65626;
--c-text-lightest: #f15833;
--c-text-quote: #999999;
// border colors
--c-border: #eaecef;
--c-border-dark: #dfe2e5;
// custom container colors
--c-tip: #42b983;
--c-tip-bg: var(--c-bg-light);
--c-tip-title: var(--c-text);
--c-tip-text: var(--c-text);
--c-tip-text-accent: var(--c-text-accent);
--c-warning: #ffc310;
--c-warning-bg: #fffae3;
--c-warning-bg-light: #fff3ba;
--c-warning-bg-lighter: #fff0b0;
--c-warning-border-dark: #f7dc91;
--c-warning-details-bg: #fff5ca;
--c-warning-title: #f1b300;
--c-warning-text: #746000;
--c-warning-text-accent: #edb100;
--c-warning-text-light: #c1971c;
--c-warning-text-quote: #ccab49;
--c-danger: #f11e37;
--c-danger-bg: #ffe0e0;
--c-danger-bg-light: #ffcfde;
--c-danger-bg-lighter: #ffc9c9;
--c-danger-border-dark: #f1abab;
--c-danger-details-bg: #ffd4d4;
--c-danger-title: #ed1e2c;
--c-danger-text: #660000;
--c-danger-text-accent: #bd1a1a;
--c-danger-text-light: #b5474d;
--c-danger-text-quote: #c15b5b;
--c-details-bg: #eeeeee;
// badge component colors
--c-badge-tip: var(--c-tip);
--c-badge-warning: #ecc808;
--c-badge-warning-text: var(--c-bg);
--c-badge-danger: #dc2626;
--c-badge-danger-text: var(--c-bg);
// transition vars
--t-color: 0.3s ease;
--t-transform: 0.3s ease;
// code blocks vars
--code-bg-color: #282c34;
--code-hl-bg-color: rgba(0, 0, 0, 0.66);
--code-ln-color: #9e9e9e;
--code-ln-wrapper-width: 3.5rem;
// font vars
--font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
--font-family-code: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
// layout vars
--navbar-height: 3.6rem;
--navbar-padding-v: 0.7rem;
--navbar-padding-h: 1.5rem;
--sidebar-width: 20rem;
--sidebar-width-mobile: calc(var(--sidebar-width) * 0.82);
--content-width: 740px;
--homepage-width: 960px;
}
html.dark {
// brand colors
--c-brand: #f15833;
--c-brand-light: #f15833;
// background colors
--c-bg: #22272e;
--c-bg-light: #2b313a;
--c-bg-lighter: #262c34;
--c-bg-dark: #343b44;
--c-bg-darker: #37404c;
// text colors
--c-text: #adbac7;
--c-text-light: #96a7b7;
--c-text-lighter: #8b9eb0;
--c-text-lightest: #8094a8;
// border colors
--c-border: #3e4c5a;
--c-border-dark: #34404c;
// custom container colors
--c-tip: #318a62;
--c-warning: #e0ad15;
--c-warning-bg: #2d2f2d;
--c-warning-bg-light: #423e2a;
--c-warning-bg-lighter: #44442f;
--c-warning-border-dark: #957c35;
--c-warning-details-bg: #39392d;
--c-warning-title: #fdca31;
--c-warning-text: #d8d96d;
--c-warning-text-accent: #ffbf00;
--c-warning-text-light: #ddb84b;
--c-warning-text-quote: #ccab49;
--c-danger: #fc1e38;
--c-danger-bg: #39232c;
--c-danger-bg-light: #4b2b35;
--c-danger-bg-lighter: #553040;
--c-danger-border-dark: #a25151;
--c-danger-details-bg: #482936;
--c-danger-title: #fc2d3b;
--c-danger-text: #ea9ca0;
--c-danger-text-accent: #fd3636;
--c-danger-text-light: #d9777c;
--c-danger-text-quote: #d56b6b;
--c-details-bg: #323843;
// badge component colors
--c-badge-warning: var(--c-warning);
--c-badge-warning-text: #3c2e05;
--c-badge-danger: var(--c-danger);
--c-badge-danger-text: #401416;
// code blocks vars
--code-hl-bg-color: #363b46;
}
// plugin-back-to-top
.back-to-top {
--back-to-top-color: var(--c-brand);
--back-to-top-color-hover: var(--c-brand-light);
}
// plugin-docsearch
.DocSearch {
--docsearch-primary-color: var(--c-brand);
--docsearch-text-color: var(--c-text);
--docsearch-highlight-color: var(--c-brand);
--docsearch-muted-color: var(--c-text-quote);
--docsearch-container-background: rgba(9, 10, 17, 0.8);
--docsearch-modal-background: var(--c-bg-light);
--docsearch-searchbox-background: var(--c-bg-lighter);
--docsearch-searchbox-focus-background: var(--c-bg);
--docsearch-searchbox-shadow: inset 0 0 0 2px var(--c-brand);
--docsearch-hit-color: var(--c-text-light);
--docsearch-hit-active-color: var(--c-bg);
--docsearch-hit-background: var(--c-bg);
--docsearch-hit-shadow: 0 1px 3px 0 var(--c-border-dark);
--docsearch-footer-background: var(--c-bg);
}
// dark plugin-docsearch
html.dark .DocSearch {
--docsearch-logo-color: var(--c-text);
--docsearch-modal-shadow: inset 1px 1px 0 0 #2c2e40, 0 3px 8px 0 #000309;
--docsearch-key-shadow: inset 0 -2px 0 0 #282d55, inset 0 0 1px 1px #51577d,
0 2px 2px 0 rgba(3, 4, 9, 0.3);
--docsearch-key-gradient: linear-gradient(-225deg, #444950, #1c1e21);
--docsearch-footer-shadow: inset 0 1px 0 0 rgba(73, 76, 106, 0.5),
0 -4px 8px 0 rgba(0, 0, 0, 0.2);
}
// plugin-external-link-icon
.external-link-icon {
--external-link-icon-color: var(--c-text-quote);
}
// plugin-medium-zoom
.medium-zoom-overlay {
--medium-zoom-bg-color: var(--c-bg);
}
// plugin-nprogress
#nprogress {
--nprogress-color: var(--c-brand);
}
// plugin-pwa-popup
.pwa-popup {
--pwa-popup-text-color: var(--c-text);
--pwa-popup-bg-color: var(--c-bg);
--pwa-popup-border-color: var(--c-brand);
--pwa-popup-shadow: 0 4px 16px var(--c-brand);
--pwa-popup-btn-text-color: var(--c-bg);
--pwa-popup-btn-bg-color: var(--c-brand);
--pwa-popup-btn-hover-bg-color: var(--c-brand-light);
}
// plugin-search
.search-box {
--search-bg-color: var(--c-bg);
--search-accent-color: var(--c-brand);
--search-text-color: var(--c-text);
--search-border-color: var(--c-border);
--search-item-text-color: var(--c-text-lighter);
--search-item-focus-bg-color: var(--c-bg-light);
}
.home .hero img {
max-width: 500px !important;
height: 100%;
width: 100%
}
.center {
margin: 0 auto;
width: 80%
}
#main-title {
display: none
}
.center {
margin: 0 auto;
width: 80%;
}
#main-title {
display: none;
}
.hero {
margin: 150px 25px 70px;
}
@font-face {
font-family: 'Nerd Font';
src: url("/nerd-font.woff2") format("woff2");
font-weight: 400;
font-style: normal;
}
code {
font-family: 'Nerd Font', source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;
}

File diff suppressed because one or more lines are too long

View File

@ -1,3 +0,0 @@
nodeLinker: node-modules
yarnPath: .yarn/releases/yarn-4.0.2.cjs

View File

@ -1,41 +0,0 @@
---
home: true
heroImage: /logo.png
actions:
- text: Get Started
link: /guide/
type: primary
footer: MIT Licensed | Copyright © 2016-present jc21.com
---
<div class="features">
<div class="feature">
<h2>Get Connected</h2>
<p>
Expose web services on your network &middot;
Free SSL with Let's Encrypt &middot;
Designed with security in mind &middot;
Perfect for home networks
</p>
</div>
<div class="feature">
<h2>Proxy Hosts</h2>
<p>Expose your private network Web services and get connected anywhere.</p>
</div>
<div class="feature">
<h2>Beautiful UI</h2>
<p>Based on Tabler, the interface is a pleasure to use. Configuring a server has never been so fun.</p>
</div>
<div class="feature">
<h2>Free SSL</h2>
<p>Built in Lets Encrypt support allows you to secure your Web services at no cost to you. The certificates even renew themselves!</p>
</div>
<div class="feature">
<h2>Docker FTW</h2>
<p>Built as a Docker Image, Nginx Proxy Manager only requires a database.</p>
</div>
<div class="feature">
<h2>Multiple Users</h2>
<p>Configure other users to either view or manage their own hosts. Full access permissions are available.</p>
</div>
</div>

View File

@ -1 +0,0 @@
../../README.md

View File

@ -1,23 +1,11 @@
{
"name": "docs",
"version": "1.0.0",
"description": "",
"main": "index.js",
"devDependencies": {
"vuepress": "^2.0.0-rc.0"
},
"scripts": {
"dev": "vuepress dev",
"build": "vuepress build"
"dev": "vitepress dev --host",
"build": "vitepress build",
"preview": "vitepress preview"
},
"author": "",
"license": "ISC",
"packageManager": "yarn@4.0.2",
"dependencies": {
"@vuepress/plugin-google-analytics": "2.0.0-rc.0",
"@vuepress/plugin-search": "2.0.0-rc.0",
"@vuepress/theme-default": "^2.0.0-rc.0",
"vuepress-plugin-sitemap2": "^2.0.0-rc.5",
"vuepress-plugin-zooming": "^1.1.8"
}
"devDependencies": {
"vitepress": "^1.1.4"
},
"dependencies": {}
}

View File

@ -1,12 +0,0 @@
# Screenshots
<img class="no-medium-zoom zooming" src="/screenshots/login.png" alt="Login" title="Login" width="200"/>
<img class="no-medium-zoom zooming" src="/screenshots/dashboard.png" alt="Dashboard" title="Dashboard" width="200"/>
<img class="no-medium-zoom zooming" src="/screenshots/proxy-hosts.png" alt="Proxy Hosts" title="Proxy Hosts" width="200"/>
<img class="no-medium-zoom zooming" src="/screenshots/proxy-hosts-add.png" alt="Add Proxy Host" title="Add Proxy Host" width="200"/>
<img class="no-medium-zoom zooming" src="/screenshots/redirection-hosts.png" alt="Redirection Hosts" title="Redirection Hosts" width="200"/>
<img class="no-medium-zoom zooming" src="/screenshots/dead-hosts.png" alt="404 Hosts" title="404 Hosts" width="200"/>
<img class="no-medium-zoom zooming" src="/screenshots/permissions.png" alt="User Permissions" title="User Permissions" width="200"/>
<img class="no-medium-zoom zooming" src="/screenshots/certificates.png" alt="Certificates" title="Certificates" width="200"/>
<img class="no-medium-zoom zooming" src="/screenshots/audit-log.png" alt="Audit Log" title="Audit Log" width="200"/>
<img class="no-medium-zoom zooming" src="/screenshots/custom-settings.png" alt="Custom Settings" title="Custom Settings" width="200"/>

View File

@ -1,3 +1,7 @@
---
outline: deep
---
# Advanced Configuration
## Running processes as a user/group
@ -169,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:
- `/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/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
@ -208,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).
## 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

@ -1,26 +1,26 @@
---
outline: deep
---
# FAQ
## Do I have to use Docker?
Yes, that's how this project is packaged.
This makes it easier to support the project when I have control over the version of Nginx and NodeJS
being used. In future this could change if the backend was no longer using NodeJS and it's long list
of dependencies.
This makes it easier to support the project when we have control over the version of Nginx other packages
use by the project.
## Can I run it on a Raspberry Pi?
Yes! The docker image is multi-arch and is built for a variety of architectures. If yours is
[not listed](https://hub.docker.com/r/jc21/nginx-proxy-manager/tags) please open a
[GitHub issue](https://github.com/jc21/nginx-proxy-manager/issues/new?assignees=&labels=enhancement&template=feature_request.md&title=).
[GitHub issue](https://github.com/NginxProxyManager/nginx-proxy-manager/issues/new?assignees=&labels=enhancement&template=feature_request.md&title=).
## I can't get my service to proxy properly?
Your best bet is to ask the [Reddit community for support](https://www.reddit.com/r/nginxproxymanager/). There's safety in numbers.
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.
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.

126
docs/src/guide/index.md Normal file
View File

@ -0,0 +1,126 @@
---
outline: deep
---
# Guide
::: raw
<p align="center">
<a href="https://hub.docker.com/repository/docker/jc21/nginx-proxy-manager" style="display:inline;margin-right:5px;">
<img src="https://img.shields.io/docker/stars/jc21/nginx-proxy-manager.svg?style=for-the-badge" style="display:inline;">
</a>
<a href="https://hub.docker.com/repository/docker/jc21/nginx-proxy-manager" style="display:inline;margin-right:5px;">
<img src="https://img.shields.io/docker/pulls/jc21/nginx-proxy-manager.svg?style=for-the-badge" style="display:inline;">
</a>
</p>
:::
This project comes as a pre-built docker image that enables you to easily forward to your websites
running at home or otherwise, including free SSL, without having to know too much about Nginx or Letsencrypt.
- [Quick Setup](#quick-setup)
- [Full Setup](/setup/)
- [Screenshots](/screenshots/)
## Project Goal
I created this project to fill a personal need to provide users with an easy way to accomplish reverse
proxying hosts with SSL termination and it had to be so easy that a monkey could do it. This goal hasn't changed.
While there might be advanced options they are optional and the project should be as simple as possible
so that the barrier for entry here is low.
::: raw
<a href="https://www.buymeacoffee.com/jc21" target="_blank"><img src="http://public.jc21.com/github/by-me-a-coffee.png" alt="Buy Me A Coffee" style="height: 51px !important;width: 217px !important;" ></a>
:::
## Features
- Beautiful and Secure Admin Interface based on [Tabler](https://tabler.github.io/)
- Easily create forwarding domains, redirections, streams and 404 hosts without knowing anything about Nginx
- Free SSL using Let's Encrypt or provide your own custom SSL certificates
- Access Lists and basic HTTP Authentication for your hosts
- Advanced Nginx configuration available for super users
- User management, permissions and audit log
## Hosting your home network
I won't go in to too much detail here but here are the basics for someone new to this self-hosted world.
1. Your home router will have a Port Forwarding section somewhere. Log in and find it
2. Add port forwarding for port 80 and 443 to the server hosting this project
3. Configure your domain name details to point to your home, either with a static ip or a service like DuckDNS or [Amazon Route53](https://github.com/jc21/route53-ddns)
4. Use the Nginx Proxy Manager as your gateway to forward to your other web based services
## Quick Setup
1. Install Docker and Docker-Compose
- [Docker Install documentation](https://docs.docker.com/get-docker/)
- [Docker-Compose Install documentation](https://docs.docker.com/compose/install/)
2. Create a docker-compose.yml file similar to this:
```yml
version: '3.8'
services:
app:
image: 'jc21/nginx-proxy-manager:latest'
restart: unless-stopped
ports:
- '80:80'
- '81:81'
- '443:443'
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
```
This is the bare minimum configuration required. See the [documentation](https://nginxproxymanager.com/setup/) for more.
3. Bring up your stack by running
```bash
docker-compose up -d
# If using docker-compose-plugin
docker compose up -d
```
4. Log in to the Admin UI
When your docker container is running, connect to it on port `81` for the admin interface.
Sometimes this can take a little bit because of the entropy of keys.
[http://127.0.0.1:81](http://127.0.0.1:81)
Default Admin User:
```
Email: admin@example.com
Password: changeme
```
Immediately after logging in with this default user you will be asked to modify your details and change your password.
## Contributing
All are welcome to create pull requests for this project, against the `develop` branch. Official releases are created from the `master` branch.
CI is used in this project. All PR's must pass before being considered. After passing,
docker builds for PR's are available on dockerhub for manual verifications.
Documentation within the `develop` branch is available for preview at
[https://develop.nginxproxymanager.com](https://develop.nginxproxymanager.com)
### Contributors
Special thanks to [all of our contributors](https://github.com/NginxProxyManager/nginx-proxy-manager/graphs/contributors).
## Getting Support
1. [Found a bug?](https://github.com/NginxProxyManager/nginx-proxy-manager/issues)
2. [Discussions](https://github.com/NginxProxyManager/nginx-proxy-manager/discussions)
3. [Reddit](https://reddit.com/r/nginxproxymanager)

32
docs/src/index.md Normal file
View File

@ -0,0 +1,32 @@
---
# https://vitepress.dev/reference/default-theme-home-page
layout: home
hero:
name: "Nginx Proxy Manager"
tagline: Expose your services easily and securely
image:
src: /logo.svg
alt: NPM Logo
actions:
- theme: brand
text: Get Started
link: /guide/
- theme: alt
text: GitHub
link: https://github.com/NginxProxyManager/nginx-proxy-manager
features:
- title: Get Connected
details: Expose web services on your network &middot; Free SSL with Let's Encrypt &middot; Designed with security in mind &middot; Perfect for home networks
- title: Proxy Hosts
details: Expose your private network Web services and get connected anywhere.
- title: Beautiful UI
details: Based on Tabler, the interface is a pleasure to use. Configuring a server has never been so fun.
- title: Free SSL
details: Built in Lets Encrypt support allows you to secure your Web services at no cost to you. The certificates even renew themselves!
- title: Docker FTW
details: Built as a Docker Image, Nginx Proxy Manager only requires a database.
- title: Multiple Users
details: Configure other users to either view or manage their own hosts. Full access permissions are available.
---

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 106 KiB

View File

Before

Width:  |  Height:  |  Size: 178 KiB

After

Width:  |  Height:  |  Size: 178 KiB

View File

Before

Width:  |  Height:  |  Size: 173 KiB

After

Width:  |  Height:  |  Size: 173 KiB

View File

Before

Width:  |  Height:  |  Size: 141 KiB

After

Width:  |  Height:  |  Size: 141 KiB

View File

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 102 KiB

View File

Before

Width:  |  Height:  |  Size: 150 KiB

After

Width:  |  Height:  |  Size: 150 KiB

View File

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 92 KiB

View File

Before

Width:  |  Height:  |  Size: 151 KiB

After

Width:  |  Height:  |  Size: 151 KiB

View File

Before

Width:  |  Height:  |  Size: 207 KiB

After

Width:  |  Height:  |  Size: 207 KiB

View File

Before

Width:  |  Height:  |  Size: 181 KiB

After

Width:  |  Height:  |  Size: 181 KiB

View File

Before

Width:  |  Height:  |  Size: 162 KiB

After

Width:  |  Height:  |  Size: 162 KiB

View File

@ -0,0 +1,20 @@
---
outline: deep
---
# Screenshots
::: raw
<div class="inline-img">
<a href="/screenshots/login.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/login.png" alt="Login" title="Login" width="200"/></a>
<a href="/screenshots/dashboard.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/dashboard.png" alt="Dashboard" title="Dashboard" width="200"/></a>
<a href="/screenshots/proxy-hosts.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/proxy-hosts.png" alt="Proxy Hosts" title="Proxy Hosts" width="200"/></a>
<a href="/screenshots/proxy-hosts-add.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/proxy-hosts-add.png" alt="Add Proxy Host" title="Add Proxy Host" width="200"/></a>
<a href="/screenshots/redirection-hosts.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/redirection-hosts.png" alt="Redirection Hosts" title="Redirection Hosts" width="200"/></a>
<a href="/screenshots/dead-hosts.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/dead-hosts.png" alt="404 Hosts" title="404 Hosts" width="200"/></a>
<a href="/screenshots/permissions.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/permissions.png" alt="User Permissions" title="User Permissions" width="200"/></a>
<a href="/screenshots/certificates.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/certificates.png" alt="Certificates" title="Certificates" width="200"/></a>
<a href="/screenshots/audit-log.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/audit-log.png" alt="Audit Log" title="Audit Log" width="200"/></a>
<a href="/screenshots/custom-settings.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/custom-settings.png" alt="Custom Settings" title="Custom Settings" width="200"/></a>
</div>
:::

View File

@ -1,3 +1,7 @@
---
outline: deep
---
# Full Setup Instructions
## Running the App

View File

@ -1,3 +1,7 @@
---
outline: deep
---
# 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
@ -12,5 +16,4 @@ Known integrations:
If you would like your integration of NPM listed, please open a
[Github issue](https://github.com/jc21/nginx-proxy-manager/issues/new?assignees=&labels=enhancement&template=feature_request.md&title=)
[Github issue](https://github.com/NginxProxyManager/nginx-proxy-manager/issues/new?assignees=&labels=enhancement&template=feature_request.md&title=)

View File

@ -1,8 +1,12 @@
---
outline: deep
---
# Upgrading
```bash
docker-compose pull
docker-compose up -d
docker compose pull
docker compose up -d
```
This project will automatically update any databases or other requirements so you don't have to follow

File diff suppressed because it is too large Load Diff

View File

@ -35,8 +35,8 @@
"name": "Cloudflare",
"package_name": "certbot-dns-cloudflare",
"version": "=={{certbot-version}}",
"dependencies": "cloudflare acme=={{certbot-version}}",
"credentials": "# Cloudflare API token\ndns_cloudflare_api_token = 0123456789abcdef0123456789abcdef01234567",
"dependencies": "cloudflare==2.19.* acme=={{certbot-version}}",
"credentials": "# Cloudflare API token\ndns_cloudflare_api_token=0123456789abcdef0123456789abcdef01234567",
"full_plugin_name": "dns-cloudflare"
},
"cloudns": {
@ -239,6 +239,14 @@
"credentials": "dns_hetzner_api_token = 0123456789abcdef0123456789abcdef",
"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": {
"name": "Infomaniak",
"package_name": "certbot-dns-infomaniak",
@ -454,5 +462,13 @@
"dependencies": "",
"credentials": "dns_websupport_identifier = <api_key>\ndns_websupport_secret_key = <secret>",
"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 \
-v "$(pwd)/frontend:/app/frontend" \
-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"
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/../.common.sh"
DOCKER_IMAGE=nginxproxymanager/nginx-full:certbot-node
docker pull "${DOCKER_IMAGE}"
TESTING_IMAGE=nginxproxymanager/nginx-full:certbot-node
docker pull "${TESTING_IMAGE}"
# Test
echo -e "${BLUE} ${CYAN}Testing backend ...${RESET}"
@ -12,20 +12,20 @@ docker run --rm \
-v "$(pwd)/backend:/app" \
-v "$(pwd)/global:/app/global" \
-w /app \
"${DOCKER_IMAGE}" \
"${TESTING_IMAGE}" \
sh -c 'yarn install && yarn eslint . && rm -rf node_modules'
echo -e "${BLUE} ${GREEN}Testing Complete${RESET}"
# Build
echo -e "${BLUE} ${CYAN}Building ...${RESET}"
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 \
--progress=plain \
--build-arg TARGETPLATFORM=linux/amd64 \
--build-arg BUILDPLATFORM=linux/amd64 \
--build-arg BUILD_VERSION="${BUILD_VERSION}" \
--build-arg BUILD_COMMIT="${BUILD_COMMIT}" \
--build-arg BUILD_VERSION="${BUILD_VERSION:-unknown}" \
--build-arg BUILD_COMMIT="${BUILD_COMMIT:-unknown}" \
--build-arg BUILD_DATE="$(date '+%Y-%m-%d %T %Z')" \
.
echo -e "${BLUE} ${GREEN}Building Complete${RESET}"

View File

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

View File

@ -1 +0,0 @@
node_modules

View File

@ -73,4 +73,4 @@
}
]
}
}
}

6
test/.gitignore vendored
View File

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

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
ENV MKCERT=1.4.2
RUN wget -O /usr/bin/mkcert "https://github.com/FiloSottile/mkcert/releases/download/v${MKCERT}/mkcert-v${MKCERT}-linux-amd64" \
&& chmod +x /usr/bin/mkcert
# Disable Cypress CLI colors
ENV FORCE_COLOR=0
ENV NO_COLOR=1
WORKDIR /test
RUN yarn install
RUN yarn install && yarn cache clean
ENTRYPOINT []
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 chalk = require('chalk');
const _ = require("lodash");
const chalk = require("chalk");
module.exports = function () {
module.exports = function() {
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);
};

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:
*
* @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 {*} 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', {
file: Cypress.env('swaggerBase'),
endpoint: path,
method: method,
statusCode: statusCode,
statusCode: code,
responseSchema: data,
verbose: true
}).should('equal', null);
@ -40,3 +52,19 @@ Cypress.Commands.add('getToken', () => {
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';
Cypress.on('uncaught:exception', (/*err, runnable*/) => {

View File

@ -3,4 +3,4 @@
"./node_modules/cypress",
"cypress/**/*.js"
]
}
}

View File

@ -4,19 +4,19 @@
"description": "",
"main": "index.js",
"dependencies": {
"@jc21/cypress-swagger-validation": "^0.0.9",
"@jc21/cypress-swagger-validation": "^0.2.6",
"@jc21/restler": "^3.4.0",
"chalk": "^4.1.0",
"cypress": "^9.4.1",
"cypress-multi-reporters": "^1.4.0",
"cypress-plugin-retries": "^1.5.2",
"eslint": "^7.6.0",
"cypress": "^13.9.0",
"cypress-multi-reporters": "^1.6.4",
"cypress-wait-until": "^3.0.1",
"eslint": "^9.3.0",
"eslint-plugin-align-assignments": "^1.1.2",
"eslint-plugin-chai-friendly": "^0.6.0",
"eslint-plugin-cypress": "^2.11.1",
"lodash": "^4.17.19",
"mocha": "^8.1.1",
"mocha-junit-reporter": "^2.0.0"
"eslint-plugin-chai-friendly": "^0.7.4",
"eslint-plugin-cypress": "^3.2.0",
"lodash": "^4.17.21",
"mocha": "^10.4.0",
"mocha-junit-reporter": "^2.2.1"
},
"scripts": {
"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