Merge branch 'NginxProxyManager:develop' into develop-crowdsec
4
.gitignore
vendored
@ -3,3 +3,7 @@
|
||||
._*
|
||||
.vscode
|
||||
certbot-help.txt
|
||||
test/node_modules
|
||||
*/node_modules
|
||||
docker/dev/dnsrouter-config.json.tmp
|
||||
docker/dev/resolv.conf
|
||||
|
148
Jenkinsfile
vendored
@ -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'
|
||||
|
@ -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'
|
||||
|
@ -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;
|
||||
}
|
||||
},
|
||||
|
@ -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);
|
||||
|
@ -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(() => {
|
||||
|
@ -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 }}
|
||||
}
|
||||
|
||||
|
@ -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"
|
||||
|
||||
|
28
docker/dev/dnsrouter-config.json
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
7
docker/dev/letsencrypt.ini
Normal 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
@ -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 */;
|
12
docker/dev/pebble-config.json
Normal 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
|
||||
}
|
||||
}
|
27
docker/docker-compose.ci.mysql.yml
Normal 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:
|
9
docker/docker-compose.ci.sqlite.yml
Normal 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'
|
@ -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}"
|
||||
|
@ -1,5 +1,4 @@
|
||||
# WARNING: This is a DEVELOPMENT docker-compose file, it should not be used for production.
|
||||
version: '3.8'
|
||||
services:
|
||||
|
||||
npm:
|
||||
|
4
docker/rootfs/etc/nginx/conf.d/include/log.conf
Normal 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;
|
@ -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
@ -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
|
||||
|
61
docs/.vitepress/config.mts
Normal 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'
|
||||
}
|
||||
}
|
||||
});
|
27
docs/.vitepress/theme/custom.css
Normal 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;
|
||||
}
|
4
docs/.vitepress/theme/index.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import DefaultTheme from 'vitepress/theme'
|
||||
import './custom.css'
|
||||
|
||||
export default DefaultTheme
|
@ -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',
|
||||
},
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
Before Width: | Height: | Size: 57 KiB |
@ -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;
|
||||
}
|
893
docs/.yarn/releases/yarn-4.0.2.cjs
vendored
@ -1,3 +0,0 @@
|
||||
nodeLinker: node-modules
|
||||
|
||||
yarnPath: .yarn/releases/yarn-4.0.2.cjs
|
@ -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 ·
|
||||
Free SSL with Let's Encrypt ·
|
||||
Designed with security in mind ·
|
||||
Perfect for home networks
|
||||
</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<h2>Proxy Hosts</h2>
|
||||
<p>Expose your private network Web services and get connected anywhere.</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<h2>Beautiful UI</h2>
|
||||
<p>Based on Tabler, the interface is a pleasure to use. Configuring a server has never been so fun.</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<h2>Free SSL</h2>
|
||||
<p>Built in Let’s Encrypt support allows you to secure your Web services at no cost to you. The certificates even renew themselves!</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<h2>Docker FTW</h2>
|
||||
<p>Built as a Docker Image, Nginx Proxy Manager only requires a database.</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<h2>Multiple Users</h2>
|
||||
<p>Configure other users to either view or manage their own hosts. Full access permissions are available.</p>
|
||||
</div>
|
||||
</div>
|
@ -1 +0,0 @@
|
||||
../../README.md
|
@ -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": {}
|
||||
}
|
||||
|
@ -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"/>
|
@ -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;
|
||||
```
|
@ -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
@ -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
@ -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 · Free SSL with Let's Encrypt · Designed with security in mind · 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 Let’s 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.
|
||||
---
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 106 KiB |
Before Width: | Height: | Size: 178 KiB After Width: | Height: | Size: 178 KiB |
Before Width: | Height: | Size: 173 KiB After Width: | Height: | Size: 173 KiB |
Before Width: | Height: | Size: 141 KiB After Width: | Height: | Size: 141 KiB |
Before Width: | Height: | Size: 102 KiB After Width: | Height: | Size: 102 KiB |
Before Width: | Height: | Size: 150 KiB After Width: | Height: | Size: 150 KiB |
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 92 KiB |
Before Width: | Height: | Size: 151 KiB After Width: | Height: | Size: 151 KiB |
Before Width: | Height: | Size: 207 KiB After Width: | Height: | Size: 207 KiB |
Before Width: | Height: | Size: 181 KiB After Width: | Height: | Size: 181 KiB |
Before Width: | Height: | Size: 162 KiB After Width: | Height: | Size: 162 KiB |
20
docs/src/screenshots/index.md
Normal 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>
|
||||
:::
|
@ -1,3 +1,7 @@
|
||||
---
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# Full Setup Instructions
|
||||
|
||||
## Running the App
|
@ -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=)
|
@ -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
|
4120
docs/yarn.lock
@ -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"
|
||||
}
|
||||
}
|
||||
|
@ -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
@ -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}"
|
||||
|
@ -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}"
|
||||
|
@ -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
|
||||
|
@ -1 +0,0 @@
|
||||
node_modules
|
@ -73,4 +73,4 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
6
test/.gitignore
vendored
@ -1,4 +1,2 @@
|
||||
.vscode
|
||||
node_modules
|
||||
results
|
||||
cypress/videos
|
||||
results/*
|
||||
cypress/results/*
|
||||
|
@ -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
@ -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',
|
||||
}
|
||||
});
|
@ -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
|
||||
}
|
||||
}
|
22
test/cypress/config/dev.js
Normal 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',
|
||||
}
|
||||
});
|
@ -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
|
||||
}
|
||||
}
|
@ -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"
|
||||
}
|
@ -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);
|
||||
};
|
||||
|
@ -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
|
||||
});
|
||||
});
|
||||
|
@ -1,5 +1,3 @@
|
||||
require('cypress-plugin-retries');
|
||||
|
||||
import './commands';
|
||||
|
||||
Cypress.on('uncaught:exception', (/*err, runnable*/) => {
|
@ -3,4 +3,4 @@
|
||||
"./node_modules/cypress",
|
||||
"cypress/**/*.js"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -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}",
|
||||
|