mirror of
https://github.com/NginxProxyManager/nginx-proxy-manager.git
synced 2025-06-18 02:06:25 +00:00
Compare commits
91 Commits
Author | SHA1 | Date | |
---|---|---|---|
fe93cb3474 | |||
fa851b61da | |||
3333a32612 | |||
9a79fce498 | |||
b1180f5077 | |||
5454352fe5 | |||
aee93a2f6f | |||
f38cb5b500 | |||
f1b7156c89 | |||
98465cf1b0 | |||
137e865b66 | |||
e740fb4064 | |||
f91f0ee8db | |||
1c9f751512 | |||
a602bdd514 | |||
f7b2be68cc | |||
ab4586fc6b | |||
a984a68065 | |||
52875fca6e | |||
63b50fcd95 | |||
5ab4aea03f | |||
d73135378e | |||
e19d685cb6 | |||
c8caaa56d9 | |||
11a98f4c12 | |||
4a85d4ac4e | |||
3138ba46ce | |||
cdd0b2e6d3 | |||
f458730d87 | |||
d20873dcbb | |||
d1e9407e4d | |||
63ee69f432 | |||
f39e527680 | |||
2dd4434ceb | |||
81054631f9 | |||
53d61bd626 | |||
847e879b3f | |||
824c837a38 | |||
2a06384a4a | |||
05307aa253 | |||
3d2406ac3d | |||
0127dc7f03 | |||
4349d42636 | |||
4b6f9d9419 | |||
c3f019c911 | |||
ecf0290203 | |||
4f41fe0c95 | |||
c3735fdbbb | |||
c432c34fb3 | |||
a1245bc161 | |||
db4ab1d548 | |||
86ddd9c83c | |||
67208e43cc | |||
ddf80302c6 | |||
5f2576946d | |||
9fe07fa6c3 | |||
d9b9af543e | |||
eb2e2e0478 | |||
9225d5d442 | |||
308a7149ed | |||
8a4a7d0caf | |||
5d03ede100 | |||
4a86bb42cc | |||
dad8561ea1 | |||
56a92e5c0e | |||
9d672f5813 | |||
d5ed70dbb6 | |||
c197e66d62 | |||
91cf3c8873 | |||
7f5e0414ac | |||
d179887c15 | |||
35abb4d7ae | |||
61b290e220 | |||
e1bcef6e5c | |||
81f51f9e2d | |||
661953db25 | |||
065c2dac42 | |||
c40e48e678 | |||
124cb18e17 | |||
5ac9dc0758 | |||
9a799d51ce | |||
77eb618758 | |||
79fedfcea4 | |||
8fdb8ac853 | |||
4fdc80be01 | |||
f8e6c8d018 | |||
c3469de61b | |||
ea61b15a40 | |||
60175e6d8c | |||
2a07445005 | |||
dad3e1da7c |
24
Jenkinsfile
vendored
24
Jenkinsfile
vendored
@ -14,9 +14,9 @@ pipeline {
|
||||
ansiColor('xterm')
|
||||
}
|
||||
environment {
|
||||
IMAGE = "nginx-proxy-manager"
|
||||
IMAGE = 'nginx-proxy-manager'
|
||||
BUILD_VERSION = getVersion()
|
||||
MAJOR_VERSION = "2"
|
||||
MAJOR_VERSION = '2'
|
||||
BRANCH_LOWER = "${BRANCH_NAME.toLowerCase().replaceAll('/', '-')}"
|
||||
COMPOSE_PROJECT_NAME = "npm_${BRANCH_LOWER}_${BUILD_NUMBER}"
|
||||
COMPOSE_FILE = 'docker/docker-compose.ci.yml'
|
||||
@ -90,20 +90,24 @@ pipeline {
|
||||
steps {
|
||||
// Bring up a stack
|
||||
sh 'docker-compose up -d fullstack-sqlite'
|
||||
sh './scripts/wait-healthy $(docker-compose ps -q fullstack-sqlite) 120'
|
||||
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'
|
||||
sh 'docker-compose up cypress-sqlite'
|
||||
// Get results
|
||||
sh 'docker cp -L "$(docker-compose ps -q cypress-sqlite):/test/results" test/'
|
||||
sh 'docker cp -L "$(docker-compose ps --all -q cypress-sqlite):/test/results" test/'
|
||||
}
|
||||
post {
|
||||
always {
|
||||
// Dumps to analyze later
|
||||
sh 'mkdir -p debug'
|
||||
sh 'docker-compose logs fullstack-sqlite | gzip > debug/docker_fullstack_sqlite.log.gz'
|
||||
sh 'docker-compose logs db | gzip > debug/docker_db.log.gz'
|
||||
sh 'docker-compose logs fullstack-sqlite > debug/docker_fullstack_sqlite.log'
|
||||
sh 'docker-compose logs db > debug/docker_db.log'
|
||||
// Cypress videos and screenshot artifacts
|
||||
dir(path: 'test/results') {
|
||||
archiveArtifacts allowEmptyArchive: true, artifacts: '**/*', excludes: '**/*.xml'
|
||||
@ -116,20 +120,20 @@ pipeline {
|
||||
steps {
|
||||
// Bring up a stack
|
||||
sh 'docker-compose up -d fullstack-mysql'
|
||||
sh './scripts/wait-healthy $(docker-compose ps -q fullstack-mysql) 120'
|
||||
sh './scripts/wait-healthy $(docker-compose ps --all -q fullstack-mysql) 120'
|
||||
|
||||
// Run tests
|
||||
sh 'rm -rf test/results'
|
||||
sh 'docker-compose up cypress-mysql'
|
||||
// Get results
|
||||
sh 'docker cp -L "$(docker-compose ps -q cypress-mysql):/test/results" test/'
|
||||
sh 'docker cp -L "$(docker-compose ps --all -q cypress-mysql):/test/results" test/'
|
||||
}
|
||||
post {
|
||||
always {
|
||||
// Dumps to analyze later
|
||||
sh 'mkdir -p debug'
|
||||
sh 'docker-compose logs fullstack-mysql | gzip > debug/docker_fullstack_mysql.log.gz'
|
||||
sh 'docker-compose logs db | gzip > debug/docker_db.log.gz'
|
||||
sh 'docker-compose logs fullstack-mysql > debug/docker_fullstack_mysql.log'
|
||||
sh 'docker-compose logs db > debug/docker_db.log'
|
||||
// Cypress videos and screenshot artifacts
|
||||
dir(path: 'test/results') {
|
||||
archiveArtifacts allowEmptyArchive: true, artifacts: '**/*', excludes: '**/*.xml'
|
||||
|
@ -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.9.22-green.svg?style=for-the-badge">
|
||||
<img src="https://img.shields.io/badge/version-2.10.4-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,7 @@ 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'
|
||||
version: '3.8'
|
||||
services:
|
||||
app:
|
||||
image: 'jc21/nginx-proxy-manager:latest'
|
||||
@ -70,6 +70,8 @@ services:
|
||||
- ./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
|
||||
|
@ -2,6 +2,7 @@ const express = require('express');
|
||||
const bodyParser = require('body-parser');
|
||||
const fileUpload = require('express-fileupload');
|
||||
const compression = require('compression');
|
||||
const config = require('./lib/config');
|
||||
const log = require('./logger').express;
|
||||
|
||||
/**
|
||||
@ -24,7 +25,7 @@ app.enable('trust proxy', ['loopback', 'linklocal', 'uniquelocal']);
|
||||
app.enable('strict routing');
|
||||
|
||||
// pretty print JSON when not live
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (config.debug()) {
|
||||
app.set('json spaces', 2);
|
||||
}
|
||||
|
||||
@ -65,7 +66,7 @@ app.use(function (err, req, res, next) {
|
||||
}
|
||||
};
|
||||
|
||||
if (process.env.NODE_ENV === 'development' || (req.baseUrl + req.path).includes('nginx/certificates')) {
|
||||
if (config.debug() || (req.baseUrl + req.path).includes('nginx/certificates')) {
|
||||
payload.debug = {
|
||||
stack: typeof err.stack !== 'undefined' && err.stack ? err.stack.split('\n') : null,
|
||||
previous: err.previous
|
||||
@ -74,7 +75,7 @@ app.use(function (err, req, res, next) {
|
||||
|
||||
// Not every error is worth logging - but this is good for now until it gets annoying.
|
||||
if (typeof err.stack !== 'undefined' && err.stack) {
|
||||
if (process.env.NODE_ENV === 'development' || process.env.DEBUG) {
|
||||
if (config.debug()) {
|
||||
log.debug(err.stack);
|
||||
} else if (typeof err.public == 'undefined' || !err.public) {
|
||||
log.warn(err.message);
|
||||
|
@ -1,21 +1,22 @@
|
||||
const config = require('config');
|
||||
const config = require('./lib/config');
|
||||
|
||||
if (!config.has('database')) {
|
||||
throw new Error('Database config does not exist! Please read the instructions: https://github.com/jc21/nginx-proxy-manager/blob/master/doc/INSTALL.md');
|
||||
throw new Error('Database config does not exist! Please read the instructions: https://nginxproxymanager.com/setup/');
|
||||
}
|
||||
|
||||
function generateDbConfig() {
|
||||
if (config.database.engine === 'knex-native') {
|
||||
return config.database.knex;
|
||||
} else
|
||||
const cfg = config.get('database');
|
||||
if (cfg.engine === 'knex-native') {
|
||||
return cfg.knex;
|
||||
}
|
||||
return {
|
||||
client: config.database.engine,
|
||||
client: cfg.engine,
|
||||
connection: {
|
||||
host: config.database.host,
|
||||
user: config.database.user,
|
||||
password: config.database.password,
|
||||
database: config.database.name,
|
||||
port: config.database.port
|
||||
host: cfg.host,
|
||||
user: cfg.user,
|
||||
password: cfg.password,
|
||||
database: cfg.name,
|
||||
port: cfg.port
|
||||
},
|
||||
migrations: {
|
||||
tableName: 'migrations'
|
||||
@ -23,11 +24,4 @@ function generateDbConfig() {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
let data = generateDbConfig();
|
||||
|
||||
if (typeof config.database.version !== 'undefined') {
|
||||
data.version = config.database.version;
|
||||
}
|
||||
|
||||
module.exports = require('knex')(data);
|
||||
module.exports = require('knex')(generateDbConfig());
|
||||
|
@ -40,6 +40,210 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/nginx/proxy-hosts": {
|
||||
"get": {
|
||||
"operationId": "getProxyHosts",
|
||||
"summary": "Get all proxy hosts",
|
||||
"tags": ["Proxy Hosts"],
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": ["users"]
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"in": "query",
|
||||
"name": "expand",
|
||||
"description": "Expansions",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"enum": ["access_list", "owner", "certificate"]
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "200 response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"examples": {
|
||||
"default": {
|
||||
"value": [
|
||||
{
|
||||
"id": 1,
|
||||
"created_on": "2023-03-30T01:12:23.000Z",
|
||||
"modified_on": "2023-03-30T02:15:40.000Z",
|
||||
"owner_user_id": 1,
|
||||
"domain_names": ["aasdasdad"],
|
||||
"forward_host": "asdasd",
|
||||
"forward_port": 80,
|
||||
"access_list_id": 0,
|
||||
"certificate_id": 0,
|
||||
"ssl_forced": 0,
|
||||
"caching_enabled": 0,
|
||||
"block_exploits": 0,
|
||||
"advanced_config": "sdfsdfsdf",
|
||||
"meta": {
|
||||
"letsencrypt_agree": false,
|
||||
"dns_challenge": false,
|
||||
"nginx_online": false,
|
||||
"nginx_err": "Command failed: /usr/sbin/nginx -t -g \"error_log off;\"\nnginx: [emerg] unknown directive \"sdfsdfsdf\" in /data/nginx/proxy_host/1.conf:37\nnginx: configuration file /etc/nginx/nginx.conf test failed\n"
|
||||
},
|
||||
"allow_websocket_upgrade": 0,
|
||||
"http2_support": 0,
|
||||
"forward_scheme": "http",
|
||||
"enabled": 1,
|
||||
"locations": [],
|
||||
"hsts_enabled": 0,
|
||||
"hsts_subdomains": 0,
|
||||
"owner": {
|
||||
"id": 1,
|
||||
"created_on": "2023-03-30T01:11:50.000Z",
|
||||
"modified_on": "2023-03-30T01:11:50.000Z",
|
||||
"is_deleted": 0,
|
||||
"is_disabled": 0,
|
||||
"email": "admin@example.com",
|
||||
"name": "Administrator",
|
||||
"nickname": "Admin",
|
||||
"avatar": "",
|
||||
"roles": ["admin"]
|
||||
},
|
||||
"access_list": null,
|
||||
"certificate": null
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"created_on": "2023-03-30T02:11:49.000Z",
|
||||
"modified_on": "2023-03-30T02:11:49.000Z",
|
||||
"owner_user_id": 1,
|
||||
"domain_names": ["test.example.com"],
|
||||
"forward_host": "1.1.1.1",
|
||||
"forward_port": 80,
|
||||
"access_list_id": 0,
|
||||
"certificate_id": 0,
|
||||
"ssl_forced": 0,
|
||||
"caching_enabled": 0,
|
||||
"block_exploits": 0,
|
||||
"advanced_config": "",
|
||||
"meta": {
|
||||
"letsencrypt_agree": false,
|
||||
"dns_challenge": false,
|
||||
"nginx_online": true,
|
||||
"nginx_err": null
|
||||
},
|
||||
"allow_websocket_upgrade": 0,
|
||||
"http2_support": 0,
|
||||
"forward_scheme": "http",
|
||||
"enabled": 1,
|
||||
"locations": [],
|
||||
"hsts_enabled": 0,
|
||||
"hsts_subdomains": 0,
|
||||
"owner": {
|
||||
"id": 1,
|
||||
"created_on": "2023-03-30T01:11:50.000Z",
|
||||
"modified_on": "2023-03-30T01:11:50.000Z",
|
||||
"is_deleted": 0,
|
||||
"is_disabled": 0,
|
||||
"email": "admin@example.com",
|
||||
"name": "Administrator",
|
||||
"nickname": "Admin",
|
||||
"avatar": "",
|
||||
"roles": ["admin"]
|
||||
},
|
||||
"access_list": null,
|
||||
"certificate": null
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ProxyHostsList"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"operationId": "createProxyHost",
|
||||
"summary": "Create a Proxy Host",
|
||||
"tags": ["Proxy Hosts"],
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": ["users"]
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"in": "body",
|
||||
"name": "proxyhost",
|
||||
"description": "Proxy Host Payload",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ProxyHostObject"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "201 response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"examples": {
|
||||
"default": {
|
||||
"value": {
|
||||
"id": 3,
|
||||
"created_on": "2023-03-30T02:31:27.000Z",
|
||||
"modified_on": "2023-03-30T02:31:27.000Z",
|
||||
"owner_user_id": 1,
|
||||
"domain_names": ["test2.example.com"],
|
||||
"forward_host": "1.1.1.1",
|
||||
"forward_port": 80,
|
||||
"access_list_id": 0,
|
||||
"certificate_id": 0,
|
||||
"ssl_forced": 0,
|
||||
"caching_enabled": 0,
|
||||
"block_exploits": 0,
|
||||
"advanced_config": "",
|
||||
"meta": {
|
||||
"letsencrypt_agree": false,
|
||||
"dns_challenge": false
|
||||
},
|
||||
"allow_websocket_upgrade": 0,
|
||||
"http2_support": 0,
|
||||
"forward_scheme": "http",
|
||||
"enabled": 1,
|
||||
"locations": [],
|
||||
"hsts_enabled": 0,
|
||||
"hsts_subdomains": 0,
|
||||
"certificate": null,
|
||||
"owner": {
|
||||
"id": 1,
|
||||
"created_on": "2023-03-30T01:11:50.000Z",
|
||||
"modified_on": "2023-03-30T01:11:50.000Z",
|
||||
"is_deleted": 0,
|
||||
"is_disabled": 0,
|
||||
"email": "admin@example.com",
|
||||
"name": "Administrator",
|
||||
"nickname": "Admin",
|
||||
"avatar": "",
|
||||
"roles": ["admin"]
|
||||
},
|
||||
"access_list": null,
|
||||
"use_default_location": true,
|
||||
"ipv6": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ProxyHostObject"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/schema": {
|
||||
"get": {
|
||||
"operationId": "schema",
|
||||
@ -55,14 +259,10 @@
|
||||
"get": {
|
||||
"operationId": "refreshToken",
|
||||
"summary": "Refresh your access token",
|
||||
"tags": [
|
||||
"Tokens"
|
||||
],
|
||||
"tags": ["Tokens"],
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": [
|
||||
"tokens"
|
||||
]
|
||||
"BearerAuth": ["tokens"]
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@ -104,19 +304,14 @@
|
||||
"scope": {
|
||||
"minLength": 1,
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"user"
|
||||
]
|
||||
"enum": ["user"]
|
||||
},
|
||||
"secret": {
|
||||
"minLength": 1,
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"identity",
|
||||
"secret"
|
||||
],
|
||||
"required": ["identity", "secret"],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
@ -144,23 +339,17 @@
|
||||
}
|
||||
},
|
||||
"summary": "Request a new access token from credentials",
|
||||
"tags": [
|
||||
"Tokens"
|
||||
]
|
||||
"tags": ["Tokens"]
|
||||
}
|
||||
},
|
||||
"/settings": {
|
||||
"get": {
|
||||
"operationId": "getSettings",
|
||||
"summary": "Get all settings",
|
||||
"tags": [
|
||||
"Settings"
|
||||
],
|
||||
"tags": ["Settings"],
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": [
|
||||
"settings"
|
||||
]
|
||||
"BearerAuth": ["settings"]
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@ -194,14 +383,10 @@
|
||||
"get": {
|
||||
"operationId": "getSetting",
|
||||
"summary": "Get a setting",
|
||||
"tags": [
|
||||
"Settings"
|
||||
],
|
||||
"tags": ["Settings"],
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": [
|
||||
"settings"
|
||||
]
|
||||
"BearerAuth": ["settings"]
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
@ -244,14 +429,10 @@
|
||||
"put": {
|
||||
"operationId": "updateSetting",
|
||||
"summary": "Update a setting",
|
||||
"tags": [
|
||||
"Settings"
|
||||
],
|
||||
"tags": ["Settings"],
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": [
|
||||
"settings"
|
||||
]
|
||||
"BearerAuth": ["settings"]
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
@ -305,14 +486,10 @@
|
||||
"get": {
|
||||
"operationId": "getUsers",
|
||||
"summary": "Get all users",
|
||||
"tags": [
|
||||
"Users"
|
||||
],
|
||||
"tags": ["Users"],
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": [
|
||||
"users"
|
||||
]
|
||||
"BearerAuth": ["users"]
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
@ -322,9 +499,7 @@
|
||||
"description": "Expansions",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"permissions"
|
||||
]
|
||||
"enum": ["permissions"]
|
||||
}
|
||||
}
|
||||
],
|
||||
@ -345,9 +520,7 @@
|
||||
"name": "Jamie Curnow",
|
||||
"nickname": "James",
|
||||
"avatar": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm",
|
||||
"roles": [
|
||||
"admin"
|
||||
]
|
||||
"roles": ["admin"]
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -362,9 +535,7 @@
|
||||
"name": "Jamie Curnow",
|
||||
"nickname": "James",
|
||||
"avatar": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm",
|
||||
"roles": [
|
||||
"admin"
|
||||
],
|
||||
"roles": ["admin"],
|
||||
"permissions": {
|
||||
"visibility": "all",
|
||||
"proxy_hosts": "manage",
|
||||
@ -389,14 +560,10 @@
|
||||
"post": {
|
||||
"operationId": "createUser",
|
||||
"summary": "Create a User",
|
||||
"tags": [
|
||||
"Users"
|
||||
],
|
||||
"tags": ["Users"],
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": [
|
||||
"users"
|
||||
]
|
||||
"BearerAuth": ["users"]
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
@ -426,9 +593,7 @@
|
||||
"name": "Jamie Curnow",
|
||||
"nickname": "James",
|
||||
"avatar": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm",
|
||||
"roles": [
|
||||
"admin"
|
||||
],
|
||||
"roles": ["admin"],
|
||||
"permissions": {
|
||||
"visibility": "all",
|
||||
"proxy_hosts": "manage",
|
||||
@ -454,14 +619,10 @@
|
||||
"get": {
|
||||
"operationId": "getUser",
|
||||
"summary": "Get a user",
|
||||
"tags": [
|
||||
"Users"
|
||||
],
|
||||
"tags": ["Users"],
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": [
|
||||
"users"
|
||||
]
|
||||
"BearerAuth": ["users"]
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
@ -501,9 +662,7 @@
|
||||
"name": "Jamie Curnow",
|
||||
"nickname": "James",
|
||||
"avatar": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm",
|
||||
"roles": [
|
||||
"admin"
|
||||
]
|
||||
"roles": ["admin"]
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -518,14 +677,10 @@
|
||||
"put": {
|
||||
"operationId": "updateUser",
|
||||
"summary": "Update a User",
|
||||
"tags": [
|
||||
"Users"
|
||||
],
|
||||
"tags": ["Users"],
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": [
|
||||
"users"
|
||||
]
|
||||
"BearerAuth": ["users"]
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
@ -574,9 +729,7 @@
|
||||
"name": "Jamie Curnow",
|
||||
"nickname": "James",
|
||||
"avatar": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm",
|
||||
"roles": [
|
||||
"admin"
|
||||
]
|
||||
"roles": ["admin"]
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -591,14 +744,10 @@
|
||||
"delete": {
|
||||
"operationId": "deleteUser",
|
||||
"summary": "Delete a User",
|
||||
"tags": [
|
||||
"Users"
|
||||
],
|
||||
"tags": ["Users"],
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": [
|
||||
"users"
|
||||
]
|
||||
"BearerAuth": ["users"]
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
@ -637,14 +786,10 @@
|
||||
"put": {
|
||||
"operationId": "updateUserAuth",
|
||||
"summary": "Update a User's Authentication",
|
||||
"tags": [
|
||||
"Users"
|
||||
],
|
||||
"tags": ["Users"],
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": [
|
||||
"users"
|
||||
]
|
||||
"BearerAuth": ["users"]
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
@ -700,14 +845,10 @@
|
||||
"put": {
|
||||
"operationId": "updateUserPermissions",
|
||||
"summary": "Update a User's Permissions",
|
||||
"tags": [
|
||||
"Users"
|
||||
],
|
||||
"tags": ["Users"],
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": [
|
||||
"users"
|
||||
]
|
||||
"BearerAuth": ["users"]
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
@ -755,14 +896,10 @@
|
||||
"put": {
|
||||
"operationId": "loginAsUser",
|
||||
"summary": "Login as this user",
|
||||
"tags": [
|
||||
"Users"
|
||||
],
|
||||
"tags": ["Users"],
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": [
|
||||
"users"
|
||||
]
|
||||
"BearerAuth": ["users"]
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
@ -797,9 +934,7 @@
|
||||
"name": "Jamie Curnow",
|
||||
"nickname": "James",
|
||||
"avatar": "//www.gravatar.com/avatar/3c8d73f45fd8763f827b964c76e6032a?default=mm",
|
||||
"roles": [
|
||||
"admin"
|
||||
]
|
||||
"roles": ["admin"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -807,11 +942,7 @@
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"description": "Login object",
|
||||
"required": [
|
||||
"expires",
|
||||
"token",
|
||||
"user"
|
||||
],
|
||||
"required": ["expires", "token", "user"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"expires": {
|
||||
@ -840,14 +971,10 @@
|
||||
"get": {
|
||||
"operationId": "reportsHosts",
|
||||
"summary": "Report on Host Statistics",
|
||||
"tags": [
|
||||
"Reports"
|
||||
],
|
||||
"tags": ["Reports"],
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": [
|
||||
"reports"
|
||||
]
|
||||
"BearerAuth": ["reports"]
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@ -878,14 +1005,10 @@
|
||||
"get": {
|
||||
"operationId": "getAuditLog",
|
||||
"summary": "Get Audit Log",
|
||||
"tags": [
|
||||
"Audit Log"
|
||||
],
|
||||
"tags": ["Audit Log"],
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": [
|
||||
"audit-log"
|
||||
]
|
||||
"BearerAuth": ["audit-log"]
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@ -925,10 +1048,7 @@
|
||||
"type": "object",
|
||||
"description": "Health object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"status",
|
||||
"version"
|
||||
],
|
||||
"required": ["status", "version"],
|
||||
"properties": {
|
||||
"status": {
|
||||
"type": "string",
|
||||
@ -944,11 +1064,7 @@
|
||||
"revision": 0
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"major",
|
||||
"minor",
|
||||
"revision"
|
||||
],
|
||||
"required": ["major", "minor", "revision"],
|
||||
"properties": {
|
||||
"major": {
|
||||
"type": "integer",
|
||||
@ -969,10 +1085,7 @@
|
||||
"TokenObject": {
|
||||
"type": "object",
|
||||
"description": "Token object",
|
||||
"required": [
|
||||
"expires",
|
||||
"token"
|
||||
],
|
||||
"required": ["expires", "token"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"expires": {
|
||||
@ -988,16 +1101,147 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ProxyHostObject": {
|
||||
"type": "object",
|
||||
"description": "Proxy Host object",
|
||||
"required": [
|
||||
"id",
|
||||
"created_on",
|
||||
"modified_on",
|
||||
"owner_user_id",
|
||||
"domain_names",
|
||||
"forward_host",
|
||||
"forward_port",
|
||||
"access_list_id",
|
||||
"certificate_id",
|
||||
"ssl_forced",
|
||||
"caching_enabled",
|
||||
"block_exploits",
|
||||
"advanced_config",
|
||||
"meta",
|
||||
"allow_websocket_upgrade",
|
||||
"http2_support",
|
||||
"forward_scheme",
|
||||
"enabled",
|
||||
"locations",
|
||||
"hsts_enabled",
|
||||
"hsts_subdomains",
|
||||
"certificate",
|
||||
"use_default_location",
|
||||
"ipv6"
|
||||
],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"description": "Proxy Host ID",
|
||||
"minimum": 1,
|
||||
"example": 1
|
||||
},
|
||||
"created_on": {
|
||||
"type": "string",
|
||||
"description": "Created Date",
|
||||
"example": "2020-01-30T09:36:08.000Z"
|
||||
},
|
||||
"modified_on": {
|
||||
"type": "string",
|
||||
"description": "Modified Date",
|
||||
"example": "2020-01-30T09:41:04.000Z"
|
||||
},
|
||||
"owner_user_id": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"example": 1
|
||||
},
|
||||
"domain_names": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
}
|
||||
},
|
||||
"forward_host": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"forward_port": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
"access_list_id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"certificate_id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"ssl_forced": {
|
||||
"type": "integer"
|
||||
},
|
||||
"caching_enabled": {
|
||||
"type": "integer"
|
||||
},
|
||||
"block_exploits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"advanced_config": {
|
||||
"type": "string"
|
||||
},
|
||||
"meta": {
|
||||
"type": "object"
|
||||
},
|
||||
"allow_websocket_upgrade": {
|
||||
"type": "integer"
|
||||
},
|
||||
"http2_support": {
|
||||
"type": "integer"
|
||||
},
|
||||
"forward_scheme": {
|
||||
"type": "string"
|
||||
},
|
||||
"enabled": {
|
||||
"type": "integer"
|
||||
},
|
||||
"locations": {
|
||||
"type": "array"
|
||||
},
|
||||
"hsts_enabled": {
|
||||
"type": "integer"
|
||||
},
|
||||
"hsts_subdomains": {
|
||||
"type": "integer"
|
||||
},
|
||||
"certificate": {
|
||||
"type": "object",
|
||||
"nullable": true
|
||||
},
|
||||
"owner": {
|
||||
"type": "object",
|
||||
"nullable": true
|
||||
},
|
||||
"access_list": {
|
||||
"type": "object",
|
||||
"nullable": true
|
||||
},
|
||||
"use_default_location": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"ipv6": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ProxyHostsList": {
|
||||
"type": "array",
|
||||
"description": "Proxyn Hosts list",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/ProxyHostObject"
|
||||
}
|
||||
},
|
||||
"SettingObject": {
|
||||
"type": "object",
|
||||
"description": "Setting object",
|
||||
"required": [
|
||||
"id",
|
||||
"name",
|
||||
"description",
|
||||
"value",
|
||||
"meta"
|
||||
],
|
||||
"required": ["id", "name", "description", "value", "meta"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"id": {
|
||||
@ -1057,17 +1301,7 @@
|
||||
"UserObject": {
|
||||
"type": "object",
|
||||
"description": "User object",
|
||||
"required": [
|
||||
"id",
|
||||
"created_on",
|
||||
"modified_on",
|
||||
"is_disabled",
|
||||
"email",
|
||||
"name",
|
||||
"nickname",
|
||||
"avatar",
|
||||
"roles"
|
||||
],
|
||||
"required": ["id", "created_on", "modified_on", "is_disabled", "email", "name", "nickname", "avatar", "roles"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"id": {
|
||||
@ -1117,9 +1351,7 @@
|
||||
},
|
||||
"roles": {
|
||||
"description": "Roles applied",
|
||||
"example": [
|
||||
"admin"
|
||||
],
|
||||
"example": ["admin"],
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
@ -1137,10 +1369,7 @@
|
||||
"AuthObject": {
|
||||
"type": "object",
|
||||
"description": "Authentication Object",
|
||||
"required": [
|
||||
"type",
|
||||
"secret"
|
||||
],
|
||||
"required": ["type", "secret"],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
@ -1167,64 +1396,37 @@
|
||||
"visibility": {
|
||||
"type": "string",
|
||||
"description": "Visibility Type",
|
||||
"enum": [
|
||||
"all",
|
||||
"user"
|
||||
]
|
||||
"enum": ["all", "user"]
|
||||
},
|
||||
"access_lists": {
|
||||
"type": "string",
|
||||
"description": "Access Lists Permissions",
|
||||
"enum": [
|
||||
"hidden",
|
||||
"view",
|
||||
"manage"
|
||||
]
|
||||
"enum": ["hidden", "view", "manage"]
|
||||
},
|
||||
"dead_hosts": {
|
||||
"type": "string",
|
||||
"description": "404 Hosts Permissions",
|
||||
"enum": [
|
||||
"hidden",
|
||||
"view",
|
||||
"manage"
|
||||
]
|
||||
"enum": ["hidden", "view", "manage"]
|
||||
},
|
||||
"proxy_hosts": {
|
||||
"type": "string",
|
||||
"description": "Proxy Hosts Permissions",
|
||||
"enum": [
|
||||
"hidden",
|
||||
"view",
|
||||
"manage"
|
||||
]
|
||||
"enum": ["hidden", "view", "manage"]
|
||||
},
|
||||
"redirection_hosts": {
|
||||
"type": "string",
|
||||
"description": "Redirection Permissions",
|
||||
"enum": [
|
||||
"hidden",
|
||||
"view",
|
||||
"manage"
|
||||
]
|
||||
"enum": ["hidden", "view", "manage"]
|
||||
},
|
||||
"streams": {
|
||||
"type": "string",
|
||||
"description": "Streams Permissions",
|
||||
"enum": [
|
||||
"hidden",
|
||||
"view",
|
||||
"manage"
|
||||
]
|
||||
"enum": ["hidden", "view", "manage"]
|
||||
},
|
||||
"certificates": {
|
||||
"type": "string",
|
||||
"description": "Certificates Permissions",
|
||||
"enum": [
|
||||
"hidden",
|
||||
"view",
|
||||
"manage"
|
||||
]
|
||||
"enum": ["hidden", "view", "manage"]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -3,9 +3,6 @@
|
||||
const logger = require('./logger').global;
|
||||
|
||||
async function appStart () {
|
||||
// Create config file db settings if environment variables have been set
|
||||
await createDbConfigFromEnvironment();
|
||||
|
||||
const migrate = require('./migrate');
|
||||
const setup = require('./setup');
|
||||
const app = require('./app');
|
||||
@ -42,90 +39,6 @@ async function appStart () {
|
||||
});
|
||||
}
|
||||
|
||||
async function createDbConfigFromEnvironment() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const envMysqlHost = process.env.DB_MYSQL_HOST || null;
|
||||
const envMysqlPort = process.env.DB_MYSQL_PORT || null;
|
||||
const envMysqlUser = process.env.DB_MYSQL_USER || null;
|
||||
const envMysqlName = process.env.DB_MYSQL_NAME || null;
|
||||
let envSqliteFile = process.env.DB_SQLITE_FILE || null;
|
||||
|
||||
const fs = require('fs');
|
||||
const filename = (process.env.NODE_CONFIG_DIR || './config') + '/' + (process.env.NODE_ENV || 'default') + '.json';
|
||||
let configData = {};
|
||||
|
||||
try {
|
||||
configData = require(filename);
|
||||
} catch (err) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
if (configData.database && configData.database.engine && !configData.database.fromEnv) {
|
||||
logger.info('Manual db configuration already exists, skipping config creation from environment variables');
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
if ((!envMysqlHost || !envMysqlPort || !envMysqlUser || !envMysqlName) && !envSqliteFile){
|
||||
envSqliteFile = '/data/database.sqlite';
|
||||
logger.info(`No valid environment variables for database provided, using default SQLite file '${envSqliteFile}'`);
|
||||
}
|
||||
|
||||
if (envMysqlHost && envMysqlPort && envMysqlUser && envMysqlName) {
|
||||
const newConfig = {
|
||||
fromEnv: true,
|
||||
engine: 'mysql',
|
||||
host: envMysqlHost,
|
||||
port: envMysqlPort,
|
||||
user: envMysqlUser,
|
||||
password: process.env.DB_MYSQL_PASSWORD,
|
||||
name: envMysqlName,
|
||||
};
|
||||
|
||||
if (JSON.stringify(configData.database) === JSON.stringify(newConfig)) {
|
||||
// Config is unchanged, skip overwrite
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info('Generating MySQL knex configuration from environment variables');
|
||||
configData.database = newConfig;
|
||||
|
||||
} else {
|
||||
const newConfig = {
|
||||
fromEnv: true,
|
||||
engine: 'knex-native',
|
||||
knex: {
|
||||
client: 'sqlite3',
|
||||
connection: {
|
||||
filename: envSqliteFile
|
||||
},
|
||||
useNullAsDefault: true
|
||||
}
|
||||
};
|
||||
if (JSON.stringify(configData.database) === JSON.stringify(newConfig)) {
|
||||
// Config is unchanged, skip overwrite
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info('Generating SQLite knex configuration');
|
||||
configData.database = newConfig;
|
||||
}
|
||||
|
||||
// Write config
|
||||
fs.writeFile(filename, JSON.stringify(configData, null, 2), (err) => {
|
||||
if (err) {
|
||||
logger.error('Could not write db config to config file: ' + filename);
|
||||
reject(err);
|
||||
} else {
|
||||
logger.debug('Wrote db configuration to config file: ' + filename);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
appStart();
|
||||
} catch (err) {
|
||||
|
@ -4,6 +4,7 @@ const https = require('https');
|
||||
const tempWrite = require('temp-write');
|
||||
const moment = require('moment');
|
||||
const logger = require('../logger').ssl;
|
||||
const config = require('../lib/config');
|
||||
const error = require('../lib/error');
|
||||
const utils = require('../lib/utils');
|
||||
const certificateModel = require('../models/certificate');
|
||||
@ -11,13 +12,14 @@ const dnsPlugins = require('../global/certbot-dns-plugins');
|
||||
const internalAuditLog = require('./audit-log');
|
||||
const internalNginx = require('./nginx');
|
||||
const internalHost = require('./host');
|
||||
const letsencryptStaging = process.env.NODE_ENV !== 'production';
|
||||
const letsencryptConfig = '/etc/letsencrypt.ini';
|
||||
const certbotCommand = 'certbot';
|
||||
const archiver = require('archiver');
|
||||
const path = require('path');
|
||||
const { isArray } = require('lodash');
|
||||
|
||||
const letsencryptStaging = config.useLetsencryptStaging();
|
||||
const letsencryptConfig = '/etc/letsencrypt.ini';
|
||||
const certbotCommand = 'certbot';
|
||||
|
||||
function omissions() {
|
||||
return ['is_deleted'];
|
||||
}
|
||||
@ -46,6 +48,8 @@ const internalCertificate = {
|
||||
|
||||
const cmd = certbotCommand + ' renew --non-interactive --quiet ' +
|
||||
'--config "' + letsencryptConfig + '" ' +
|
||||
'--work-dir "/tmp/letsencrypt-lib" ' +
|
||||
'--logs-dir "/tmp/letsencrypt-log" ' +
|
||||
'--preferred-challenges "dns,http" ' +
|
||||
'--disable-hook-validation ' +
|
||||
(letsencryptStaging ? '--staging' : '');
|
||||
@ -833,6 +837,8 @@ const internalCertificate = {
|
||||
|
||||
const cmd = certbotCommand + ' certonly ' +
|
||||
'--config "' + letsencryptConfig + '" ' +
|
||||
'--work-dir "/tmp/letsencrypt-lib" ' +
|
||||
'--logs-dir "/tmp/letsencrypt-log" ' +
|
||||
'--cert-name "npm-' + certificate.id + '" ' +
|
||||
'--agree-tos ' +
|
||||
'--authenticator webroot ' +
|
||||
@ -871,13 +877,15 @@ const internalCertificate = {
|
||||
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 + '\'';
|
||||
// we call `. /opt/certbot/bin/activate` (`.` is alternative to `source` in dash) to access certbot venv
|
||||
let prepareCmd = '. /opt/certbot/bin/activate && pip install ' + dns_plugin.package_name + (dns_plugin.version_requirement || '') + ' ' + dns_plugin.dependencies + ' && deactivate';
|
||||
const prepareCmd = '. /opt/certbot/bin/activate && pip install --no-cache-dir ' + dns_plugin.package_name + (dns_plugin.version_requirement || '') + ' ' + dns_plugin.dependencies + ' && deactivate';
|
||||
|
||||
// Whether the plugin has a --<name>-credentials argument
|
||||
const hasConfigArg = certificate.meta.dns_provider !== 'route53';
|
||||
|
||||
let mainCmd = certbotCommand + ' certonly ' +
|
||||
'--config "' + letsencryptConfig + '" ' +
|
||||
'--work-dir "/tmp/letsencrypt-lib" ' +
|
||||
'--logs-dir "/tmp/letsencrypt-log" ' +
|
||||
'--cert-name "npm-' + certificate.id + '" ' +
|
||||
'--agree-tos ' +
|
||||
'--email "' + certificate.meta.letsencrypt_email + '" ' +
|
||||
@ -974,6 +982,8 @@ const internalCertificate = {
|
||||
|
||||
const cmd = certbotCommand + ' renew --force-renewal ' +
|
||||
'--config "' + letsencryptConfig + '" ' +
|
||||
'--work-dir "/tmp/letsencrypt-lib" ' +
|
||||
'--logs-dir "/tmp/letsencrypt-log" ' +
|
||||
'--cert-name "npm-' + certificate.id + '" ' +
|
||||
'--preferred-challenges "dns,http" ' +
|
||||
'--no-random-sleep-on-renew ' +
|
||||
@ -1004,6 +1014,8 @@ const internalCertificate = {
|
||||
|
||||
let mainCmd = certbotCommand + ' renew ' +
|
||||
'--config "' + letsencryptConfig + '" ' +
|
||||
'--work-dir "/tmp/letsencrypt-lib" ' +
|
||||
'--logs-dir "/tmp/letsencrypt-log" ' +
|
||||
'--cert-name "npm-' + certificate.id + '" ' +
|
||||
'--disable-hook-validation ' +
|
||||
'--no-random-sleep-on-renew ' +
|
||||
|
@ -1,9 +1,9 @@
|
||||
const _ = require('lodash');
|
||||
const fs = require('fs');
|
||||
const logger = require('../logger').nginx;
|
||||
const config = require('../lib/config');
|
||||
const utils = require('../lib/utils');
|
||||
const error = require('../lib/error');
|
||||
const debug_mode = process.env.NODE_ENV !== 'production' || !!process.env.DEBUG;
|
||||
|
||||
const internalNginx = {
|
||||
|
||||
@ -65,7 +65,7 @@ const internalNginx = {
|
||||
}
|
||||
});
|
||||
|
||||
if (debug_mode) {
|
||||
if (config.debug()) {
|
||||
logger.error('Nginx test failed:', valid_lines.join('\n'));
|
||||
}
|
||||
|
||||
@ -101,7 +101,7 @@ const internalNginx = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
test: () => {
|
||||
if (debug_mode) {
|
||||
if (config.debug()) {
|
||||
logger.info('Testing Nginx configuration');
|
||||
}
|
||||
|
||||
@ -184,7 +184,7 @@ const internalNginx = {
|
||||
generateConfig: (host_type, host) => {
|
||||
const nice_host_type = internalNginx.getFileFriendlyHostType(host_type);
|
||||
|
||||
if (debug_mode) {
|
||||
if (config.debug()) {
|
||||
logger.info('Generating ' + nice_host_type + ' Config:', JSON.stringify(host, null, 2));
|
||||
}
|
||||
|
||||
@ -239,7 +239,7 @@ const internalNginx = {
|
||||
.then((config_text) => {
|
||||
fs.writeFileSync(filename, config_text, {encoding: 'utf8'});
|
||||
|
||||
if (debug_mode) {
|
||||
if (config.debug()) {
|
||||
logger.success('Wrote config:', filename, config_text);
|
||||
}
|
||||
|
||||
@ -249,7 +249,7 @@ const internalNginx = {
|
||||
resolve(true);
|
||||
})
|
||||
.catch((err) => {
|
||||
if (debug_mode) {
|
||||
if (config.debug()) {
|
||||
logger.warn('Could not write ' + filename + ':', err.message);
|
||||
}
|
||||
|
||||
@ -268,7 +268,7 @@ const internalNginx = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
generateLetsEncryptRequestConfig: (certificate) => {
|
||||
if (debug_mode) {
|
||||
if (config.debug()) {
|
||||
logger.info('Generating LetsEncrypt Request Config:', certificate);
|
||||
}
|
||||
|
||||
@ -292,14 +292,14 @@ const internalNginx = {
|
||||
.then((config_text) => {
|
||||
fs.writeFileSync(filename, config_text, {encoding: 'utf8'});
|
||||
|
||||
if (debug_mode) {
|
||||
if (config.debug()) {
|
||||
logger.success('Wrote config:', filename, config_text);
|
||||
}
|
||||
|
||||
resolve(true);
|
||||
})
|
||||
.catch((err) => {
|
||||
if (debug_mode) {
|
||||
if (config.debug()) {
|
||||
logger.warn('Could not write ' + filename + ':', err.message);
|
||||
}
|
||||
|
||||
@ -416,8 +416,8 @@ const internalNginx = {
|
||||
* @param {string} config
|
||||
* @returns {boolean}
|
||||
*/
|
||||
advancedConfigHasDefaultLocation: function (config) {
|
||||
return !!config.match(/^(?:.*;)?\s*?location\s*?\/\s*?{/im);
|
||||
advancedConfigHasDefaultLocation: function (cfg) {
|
||||
return !!cfg.match(/^(?:.*;)?\s*?location\s*?\/\s*?{/im);
|
||||
},
|
||||
|
||||
/**
|
||||
|
184
backend/lib/config.js
Normal file
184
backend/lib/config.js
Normal file
@ -0,0 +1,184 @@
|
||||
const fs = require('fs');
|
||||
const NodeRSA = require('node-rsa');
|
||||
const logger = require('../logger').global;
|
||||
|
||||
const keysFile = '/data/keys.json';
|
||||
|
||||
let instance = null;
|
||||
|
||||
// 1. Load from config file first (not recommended anymore)
|
||||
// 2. Use config env variables next
|
||||
const configure = () => {
|
||||
const filename = (process.env.NODE_CONFIG_DIR || './config') + '/' + (process.env.NODE_ENV || 'default') + '.json';
|
||||
if (fs.existsSync(filename)) {
|
||||
let configData;
|
||||
try {
|
||||
configData = require(filename);
|
||||
} catch (err) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
if (configData && configData.database) {
|
||||
logger.info(`Using configuration from file: ${filename}`);
|
||||
instance = configData;
|
||||
instance.keys = getKeys();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const envMysqlHost = process.env.DB_MYSQL_HOST || null;
|
||||
const envMysqlUser = process.env.DB_MYSQL_USER || null;
|
||||
const envMysqlName = process.env.DB_MYSQL_NAME || null;
|
||||
if (envMysqlHost && envMysqlUser && envMysqlName) {
|
||||
// we have enough mysql creds to go with mysql
|
||||
logger.info('Using MySQL configuration');
|
||||
instance = {
|
||||
database: {
|
||||
engine: 'mysql',
|
||||
host: envMysqlHost,
|
||||
port: process.env.DB_MYSQL_PORT || 3306,
|
||||
user: envMysqlUser,
|
||||
password: process.env.DB_MYSQL_PASSWORD,
|
||||
name: envMysqlName,
|
||||
},
|
||||
keys: getKeys(),
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
const envSqliteFile = process.env.DB_SQLITE_FILE || '/data/database.sqlite';
|
||||
logger.info(`Using Sqlite: ${envSqliteFile}`);
|
||||
instance = {
|
||||
database: {
|
||||
engine: 'knex-native',
|
||||
knex: {
|
||||
client: 'sqlite3',
|
||||
connection: {
|
||||
filename: envSqliteFile
|
||||
},
|
||||
useNullAsDefault: true
|
||||
}
|
||||
},
|
||||
keys: getKeys(),
|
||||
};
|
||||
};
|
||||
|
||||
const getKeys = () => {
|
||||
// Get keys from file
|
||||
if (!fs.existsSync(keysFile)) {
|
||||
generateKeys();
|
||||
} else if (process.env.DEBUG) {
|
||||
logger.info('Keys file exists OK');
|
||||
}
|
||||
try {
|
||||
return require(keysFile);
|
||||
} catch (err) {
|
||||
logger.error('Could not read JWT key pair from config file: ' + keysFile, err);
|
||||
process.exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
const generateKeys = () => {
|
||||
logger.info('Creating a new JWT key pair...');
|
||||
// Now create the keys and save them in the config.
|
||||
const key = new NodeRSA({ b: 2048 });
|
||||
key.generateKeyPair();
|
||||
|
||||
const keys = {
|
||||
key: key.exportKey('private').toString(),
|
||||
pub: key.exportKey('public').toString(),
|
||||
};
|
||||
|
||||
// Write keys config
|
||||
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);
|
||||
process.exit(1);
|
||||
}
|
||||
logger.info('Wrote JWT key pair to config file: ' + keysFile);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} key ie: 'database' or 'database.engine'
|
||||
* @returns {boolean}
|
||||
*/
|
||||
has: function(key) {
|
||||
instance === null && configure();
|
||||
const keys = key.split('.');
|
||||
let level = instance;
|
||||
let has = true;
|
||||
keys.forEach((keyItem) =>{
|
||||
if (typeof level[keyItem] === 'undefined') {
|
||||
has = false;
|
||||
} else {
|
||||
level = level[keyItem];
|
||||
}
|
||||
});
|
||||
|
||||
return has;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets a specific key from the top level
|
||||
*
|
||||
* @param {string} key
|
||||
* @returns {*}
|
||||
*/
|
||||
get: function (key) {
|
||||
instance === null && configure();
|
||||
if (key && typeof instance[key] !== 'undefined') {
|
||||
return instance[key];
|
||||
}
|
||||
return instance;
|
||||
},
|
||||
|
||||
/**
|
||||
* Is this a sqlite configuration?
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isSqlite: function () {
|
||||
instance === null && configure();
|
||||
return instance.database.knex && instance.database.knex.client === 'sqlite3';
|
||||
},
|
||||
|
||||
/**
|
||||
* Are we running in debug mdoe?
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
debug: function () {
|
||||
return !!process.env.DEBUG;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a public key
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
getPublicKey: function () {
|
||||
instance === null && configure();
|
||||
return instance.keys.pub;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a private key
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
getPrivateKey: function () {
|
||||
instance === null && configure();
|
||||
return instance.keys.key;
|
||||
},
|
||||
|
||||
/**
|
||||
* @returns {boolean}
|
||||
*/
|
||||
useLetsencryptStaging: function () {
|
||||
return !!process.env.LE_STAGING;
|
||||
}
|
||||
};
|
@ -5,7 +5,7 @@ const definitions = require('../../schema/definitions.json');
|
||||
RegExp.prototype.toJSON = RegExp.prototype.toString;
|
||||
|
||||
const ajv = require('ajv')({
|
||||
verbose: true, //process.env.NODE_ENV === 'development',
|
||||
verbose: true,
|
||||
allErrors: true,
|
||||
format: 'full', // strict regexes for format checks
|
||||
coerceTypes: true,
|
||||
|
@ -1,11 +1,11 @@
|
||||
const db = require('../db');
|
||||
const config = require('config');
|
||||
const config = require('../lib/config');
|
||||
const Model = require('objection').Model;
|
||||
|
||||
Model.knex(db);
|
||||
|
||||
module.exports = function () {
|
||||
if (config.database.knex && config.database.knex.client === 'sqlite3') {
|
||||
if (config.isSqlite()) {
|
||||
// eslint-disable-next-line
|
||||
return Model.raw("datetime('now','localtime')");
|
||||
}
|
||||
|
@ -6,44 +6,36 @@
|
||||
const _ = require('lodash');
|
||||
const jwt = require('jsonwebtoken');
|
||||
const crypto = require('crypto');
|
||||
const config = require('../lib/config');
|
||||
const error = require('../lib/error');
|
||||
const logger = require('../logger').global;
|
||||
const ALGO = 'RS256';
|
||||
|
||||
let public_key = null;
|
||||
let private_key = null;
|
||||
|
||||
function checkJWTKeyPair() {
|
||||
if (!public_key || !private_key) {
|
||||
let config = require('config');
|
||||
public_key = config.get('jwt.pub');
|
||||
private_key = config.get('jwt.key');
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = function () {
|
||||
|
||||
let token_data = {};
|
||||
|
||||
let self = {
|
||||
const self = {
|
||||
/**
|
||||
* @param {Object} payload
|
||||
* @returns {Promise}
|
||||
*/
|
||||
create: (payload) => {
|
||||
if (!config.getPrivateKey()) {
|
||||
logger.error('Private key is empty!');
|
||||
}
|
||||
// sign with RSA SHA256
|
||||
let options = {
|
||||
const options = {
|
||||
algorithm: ALGO,
|
||||
expiresIn: payload.expiresIn || '1d'
|
||||
};
|
||||
|
||||
payload.jti = crypto.randomBytes(12)
|
||||
.toString('base64')
|
||||
.substr(-8);
|
||||
|
||||
checkJWTKeyPair();
|
||||
.substring(-8);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
jwt.sign(payload, private_key, options, (err, token) => {
|
||||
jwt.sign(payload, config.getPrivateKey(), options, (err, token) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
@ -62,13 +54,15 @@ module.exports = function () {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
load: function (token) {
|
||||
if (!config.getPublicKey()) {
|
||||
logger.error('Public key is empty!');
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
checkJWTKeyPair();
|
||||
try {
|
||||
if (!token || token === null || token === 'null') {
|
||||
reject(new error.AuthError('Empty token'));
|
||||
} else {
|
||||
jwt.verify(token, public_key, {ignoreExpiration: false, algorithms: [ALGO]}, (err, result) => {
|
||||
jwt.verify(token, config.getPublicKey(), {ignoreExpiration: false, algorithms: [ALGO]}, (err, result) => {
|
||||
if (err) {
|
||||
|
||||
if (err.name === 'TokenExpiredError') {
|
||||
@ -132,7 +126,7 @@ module.exports = function () {
|
||||
* @returns {Integer}
|
||||
*/
|
||||
getUserId: (default_value) => {
|
||||
let attrs = self.get('attrs');
|
||||
const attrs = self.get('attrs');
|
||||
if (attrs && typeof attrs.id !== 'undefined' && attrs.id) {
|
||||
return attrs.id;
|
||||
}
|
||||
|
@ -10,7 +10,6 @@
|
||||
"bcrypt": "^5.0.0",
|
||||
"body-parser": "^1.19.0",
|
||||
"compression": "^1.7.4",
|
||||
"config": "^3.3.1",
|
||||
"express": "^4.17.3",
|
||||
"express-fileupload": "^1.1.9",
|
||||
"gravatar": "^1.8.0",
|
||||
@ -22,7 +21,6 @@
|
||||
"moment": "^2.29.4",
|
||||
"mysql": "^2.18.1",
|
||||
"node-rsa": "^1.0.8",
|
||||
"nodemon": "^2.0.2",
|
||||
"objection": "3.0.1",
|
||||
"path": "^0.12.7",
|
||||
"signale": "1.4.0",
|
||||
@ -36,8 +34,9 @@
|
||||
"author": "Jamie Curnow <jc@jc21.com>",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"eslint": "^6.8.0",
|
||||
"eslint": "^8.36.0",
|
||||
"eslint-plugin-align-assignments": "^1.1.2",
|
||||
"nodemon": "^2.0.2",
|
||||
"prettier": "^2.0.4"
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,4 @@
|
||||
const fs = require('fs');
|
||||
const NodeRSA = require('node-rsa');
|
||||
const config = require('config');
|
||||
const config = require('./lib/config');
|
||||
const logger = require('./logger').setup;
|
||||
const certificateModel = require('./models/certificate');
|
||||
const userModel = require('./models/user');
|
||||
@ -9,62 +7,6 @@ const utils = require('./lib/utils');
|
||||
const authModel = require('./models/auth');
|
||||
const settingModel = require('./models/setting');
|
||||
const dns_plugins = require('./global/certbot-dns-plugins');
|
||||
const debug_mode = process.env.NODE_ENV !== 'production' || !!process.env.DEBUG;
|
||||
|
||||
/**
|
||||
* Creates a new JWT RSA Keypair if not alread set on the config
|
||||
*
|
||||
* @returns {Promise}
|
||||
*/
|
||||
const setupJwt = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
// Now go and check if the jwt gpg keys have been created and if not, create them
|
||||
if (!config.has('jwt') || !config.has('jwt.key') || !config.has('jwt.pub')) {
|
||||
logger.info('Creating a new JWT key pair...');
|
||||
|
||||
// jwt keys are not configured properly
|
||||
const filename = config.util.getEnv('NODE_CONFIG_DIR') + '/' + (config.util.getEnv('NODE_ENV') || 'default') + '.json';
|
||||
let config_data = {};
|
||||
|
||||
try {
|
||||
config_data = require(filename);
|
||||
} catch (err) {
|
||||
// do nothing
|
||||
if (debug_mode) {
|
||||
logger.debug(filename + ' config file could not be required');
|
||||
}
|
||||
}
|
||||
|
||||
// Now create the keys and save them in the config.
|
||||
let key = new NodeRSA({ b: 2048 });
|
||||
key.generateKeyPair();
|
||||
|
||||
config_data.jwt = {
|
||||
key: key.exportKey('private').toString(),
|
||||
pub: key.exportKey('public').toString(),
|
||||
};
|
||||
|
||||
// Write config
|
||||
fs.writeFile(filename, JSON.stringify(config_data, null, 2), (err) => {
|
||||
if (err) {
|
||||
logger.error('Could not write JWT key pair to config file: ' + filename);
|
||||
reject(err);
|
||||
} else {
|
||||
logger.info('Wrote JWT key pair to config file: ' + filename);
|
||||
delete require.cache[require.resolve('config')];
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// JWT key pair exists
|
||||
if (debug_mode) {
|
||||
logger.debug('JWT Keypair already exists');
|
||||
}
|
||||
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a default admin users if one doesn't already exist in the database
|
||||
@ -119,8 +61,8 @@ const setupDefaultUser = () => {
|
||||
.then(() => {
|
||||
logger.info('Initial admin setup completed');
|
||||
});
|
||||
} else if (debug_mode) {
|
||||
logger.debug('Admin user setup not required');
|
||||
} else if (config.debug()) {
|
||||
logger.info('Admin user setup not required');
|
||||
}
|
||||
});
|
||||
};
|
||||
@ -151,8 +93,8 @@ const setupDefaultSettings = () => {
|
||||
logger.info('Default settings added');
|
||||
});
|
||||
}
|
||||
if (debug_mode) {
|
||||
logger.debug('Default setting setup not required');
|
||||
if (config.debug()) {
|
||||
logger.info('Default setting setup not required');
|
||||
}
|
||||
});
|
||||
};
|
||||
@ -189,7 +131,7 @@ const setupCertbotPlugins = () => {
|
||||
});
|
||||
|
||||
if (plugins.length) {
|
||||
const install_cmd = '. /opt/certbot/bin/activate && pip install ' + plugins.join(' ') + ' && deactivate';
|
||||
const install_cmd = '. /opt/certbot/bin/activate && pip install --no-cache-dir ' + plugins.join(' ') + ' && deactivate';
|
||||
promises.push(utils.exec(install_cmd));
|
||||
}
|
||||
|
||||
@ -225,8 +167,7 @@ const setupLogrotation = () => {
|
||||
};
|
||||
|
||||
module.exports = function () {
|
||||
return setupJwt()
|
||||
.then(setupDefaultUser)
|
||||
return setupDefaultUser()
|
||||
.then(setupDefaultSettings)
|
||||
.then(setupCertbotPlugins)
|
||||
.then(setupLogrotation);
|
||||
|
@ -24,6 +24,12 @@ server {
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
{%- if value == "444" %}
|
||||
location / {
|
||||
return 444;
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
{%- if value == "redirect" %}
|
||||
location / {
|
||||
return 301 {{ meta.redirect }};
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -10,9 +10,13 @@ ARG BUILD_VERSION
|
||||
ARG BUILD_COMMIT
|
||||
ARG BUILD_DATE
|
||||
|
||||
# See: https://github.com/just-containers/s6-overlay/blob/master/README.md
|
||||
ENV SUPPRESS_NO_CONFIG_WARNING=1 \
|
||||
S6_FIX_ATTRS_HIDDEN=1 \
|
||||
S6_BEHAVIOUR_IF_STAGE2_FAILS=1 \
|
||||
S6_CMD_WAIT_FOR_SERVICES_MAXTIME=0 \
|
||||
S6_FIX_ATTRS_HIDDEN=1 \
|
||||
S6_KILL_FINISH_MAXTIME=10000 \
|
||||
S6_VERBOSITY=1 \
|
||||
NODE_ENV=production \
|
||||
NPM_BUILD_VERSION="${BUILD_VERSION}" \
|
||||
NPM_BUILD_COMMIT="${BUILD_COMMIT}" \
|
||||
@ -35,21 +39,17 @@ COPY frontend/dist /app/frontend
|
||||
COPY global /app/global
|
||||
|
||||
WORKDIR /app
|
||||
RUN yarn install
|
||||
RUN yarn install \
|
||||
&& yarn cache clean
|
||||
|
||||
# add late to limit cache-busting by modifications
|
||||
COPY docker/rootfs /
|
||||
|
||||
# Remove frontend service not required for prod, dev nginx config as well
|
||||
RUN rm -rf /etc/services.d/frontend /etc/nginx/conf.d/dev.conf
|
||||
|
||||
# Change permission of logrotate config file
|
||||
RUN chmod 644 /etc/logrotate.d/nginx-proxy-manager
|
||||
|
||||
# fix for pip installs
|
||||
# https://github.com/NginxProxyManager/nginx-proxy-manager/issues/1769
|
||||
RUN pip uninstall --yes setuptools \
|
||||
&& pip install "setuptools==58.0.0"
|
||||
RUN rm -rf /etc/s6-overlay/s6-rc.d/user/contents.d/frontend /etc/nginx/conf.d/dev.conf \
|
||||
&& chmod 644 /etc/logrotate.d/nginx-proxy-manager \
|
||||
&& pip uninstall --yes setuptools \
|
||||
&& pip install --no-cache-dir "setuptools==58.0.0"
|
||||
|
||||
VOLUME [ "/data", "/etc/letsencrypt" ]
|
||||
ENTRYPOINT [ "/init" ]
|
||||
|
@ -1,9 +1,13 @@
|
||||
FROM jc21/nginx-full:certbot-node
|
||||
LABEL maintainer="Jamie Curnow <jc@jc21.com>"
|
||||
|
||||
ENV S6_LOGGING=0 \
|
||||
SUPPRESS_NO_CONFIG_WARNING=1 \
|
||||
S6_FIX_ATTRS_HIDDEN=1
|
||||
# See: https://github.com/just-containers/s6-overlay/blob/master/README.md
|
||||
ENV SUPPRESS_NO_CONFIG_WARNING=1 \
|
||||
S6_BEHAVIOUR_IF_STAGE2_FAILS=1 \
|
||||
S6_CMD_WAIT_FOR_SERVICES_MAXTIME=0 \
|
||||
S6_FIX_ATTRS_HIDDEN=1 \
|
||||
S6_KILL_FINISH_MAXTIME=10000 \
|
||||
S6_VERBOSITY=2
|
||||
|
||||
RUN echo "fs.file-max = 65535" > /etc/sysctl.conf \
|
||||
&& apt-get update \
|
||||
|
@ -1,17 +1,18 @@
|
||||
# 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"
|
||||
version: '3.8'
|
||||
services:
|
||||
|
||||
fullstack-mysql:
|
||||
image: ${IMAGE}:ci-${BUILD_NUMBER}
|
||||
image: "${IMAGE}:ci-${BUILD_NUMBER}"
|
||||
environment:
|
||||
NODE_ENV: "development"
|
||||
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"
|
||||
DB_MYSQL_HOST: 'db'
|
||||
DB_MYSQL_PORT: '3306'
|
||||
DB_MYSQL_USER: 'npm'
|
||||
DB_MYSQL_PASSWORD: 'npm'
|
||||
DB_MYSQL_NAME: 'npm'
|
||||
volumes:
|
||||
- npm_data:/data
|
||||
expose:
|
||||
@ -26,11 +27,15 @@ services:
|
||||
timeout: 3s
|
||||
|
||||
fullstack-sqlite:
|
||||
image: ${IMAGE}:ci-${BUILD_NUMBER}
|
||||
image: "${IMAGE}:ci-${BUILD_NUMBER}"
|
||||
environment:
|
||||
NODE_ENV: "development"
|
||||
DEBUG: 'true'
|
||||
LE_STAGING: 'true'
|
||||
FORCE_COLOR: 1
|
||||
DB_SQLITE_FILE: "/data/database.sqlite"
|
||||
DB_SQLITE_FILE: '/data/mydb.sqlite'
|
||||
PUID: 1000
|
||||
PGID: 1000
|
||||
DISABLE_IPV6: 'true'
|
||||
volumes:
|
||||
- npm_data:/data
|
||||
expose:
|
||||
@ -45,26 +50,26 @@ services:
|
||||
db:
|
||||
image: jc21/mariadb-aria
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: "npm"
|
||||
MYSQL_DATABASE: "npm"
|
||||
MYSQL_USER: "npm"
|
||||
MYSQL_PASSWORD: "npm"
|
||||
MYSQL_ROOT_PASSWORD: 'npm'
|
||||
MYSQL_DATABASE: 'npm'
|
||||
MYSQL_USER: 'npm'
|
||||
MYSQL_PASSWORD: 'npm'
|
||||
volumes:
|
||||
- db_data:/var/lib/mysql
|
||||
|
||||
cypress-mysql:
|
||||
image: ${IMAGE}-cypress:ci-${BUILD_NUMBER}
|
||||
image: "${IMAGE}-cypress:ci-${BUILD_NUMBER}"
|
||||
build:
|
||||
context: ../test/
|
||||
dockerfile: cypress/Dockerfile
|
||||
environment:
|
||||
CYPRESS_baseUrl: "http://fullstack-mysql:81"
|
||||
CYPRESS_baseUrl: 'http://fullstack-mysql:81'
|
||||
volumes:
|
||||
- cypress-logs:/results
|
||||
command: cypress run --browser chrome --config-file=${CYPRESS_CONFIG:-cypress/config/ci.json}
|
||||
|
||||
cypress-sqlite:
|
||||
image: ${IMAGE}-cypress:ci-${BUILD_NUMBER}
|
||||
image: "${IMAGE}-cypress:ci-${BUILD_NUMBER}"
|
||||
build:
|
||||
context: ../test/
|
||||
dockerfile: cypress/Dockerfile
|
||||
|
@ -1,6 +1,7 @@
|
||||
# WARNING: This is a DEVELOPMENT docker-compose file, it should not be used for production.
|
||||
version: "3.5"
|
||||
version: '3.8'
|
||||
services:
|
||||
|
||||
npm:
|
||||
image: nginxproxymanager:dev
|
||||
container_name: npm_core
|
||||
@ -14,14 +15,19 @@ services:
|
||||
networks:
|
||||
- nginx_proxy_manager
|
||||
environment:
|
||||
NODE_ENV: "development"
|
||||
PUID: 1000
|
||||
PGID: 1000
|
||||
FORCE_COLOR: 1
|
||||
DEVELOPMENT: "true"
|
||||
DB_MYSQL_HOST: "db"
|
||||
DB_MYSQL_PORT: 3306
|
||||
DB_MYSQL_USER: "npm"
|
||||
DB_MYSQL_PASSWORD: "npm"
|
||||
DB_MYSQL_NAME: "npm"
|
||||
# specifically for dev:
|
||||
DEBUG: 'true'
|
||||
DEVELOPMENT: 'true'
|
||||
LE_STAGING: 'true'
|
||||
# db:
|
||||
DB_MYSQL_HOST: 'db'
|
||||
DB_MYSQL_PORT: '3306'
|
||||
DB_MYSQL_USER: 'npm'
|
||||
DB_MYSQL_PASSWORD: 'npm'
|
||||
DB_MYSQL_NAME: 'npm'
|
||||
# DB_SQLITE_FILE: "/data/database.sqlite"
|
||||
# DISABLE_IPV6: "true"
|
||||
volumes:
|
||||
@ -42,10 +48,10 @@ services:
|
||||
networks:
|
||||
- nginx_proxy_manager
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: "npm"
|
||||
MYSQL_DATABASE: "npm"
|
||||
MYSQL_USER: "npm"
|
||||
MYSQL_PASSWORD: "npm"
|
||||
MYSQL_ROOT_PASSWORD: 'npm'
|
||||
MYSQL_DATABASE: 'npm'
|
||||
MYSQL_USER: 'npm'
|
||||
MYSQL_PASSWORD: 'npm'
|
||||
volumes:
|
||||
- db_data:/var/lib/mysql
|
||||
|
||||
|
54
docker/rootfs/bin/common.sh
Normal file
54
docker/rootfs/bin/common.sh
Normal file
@ -0,0 +1,54 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
CYAN='\E[1;36m'
|
||||
BLUE='\E[1;34m'
|
||||
YELLOW='\E[1;33m'
|
||||
RED='\E[1;31m'
|
||||
RESET='\E[0m'
|
||||
export CYAN BLUE YELLOW RED RESET
|
||||
|
||||
PUID=${PUID:-0}
|
||||
PGID=${PGID:-0}
|
||||
|
||||
NPMUSER=npm
|
||||
NPMGROUP=npm
|
||||
NPMHOME=/tmp/npmuserhome
|
||||
export NPMUSER NPMGROUP NPMHOME
|
||||
|
||||
if [[ "$PUID" -ne '0' ]] && [ "$PGID" = '0' ]; then
|
||||
# set group id to same as user id,
|
||||
# the user probably forgot to specify the group id and
|
||||
# it would be rediculous to intentionally use the root group
|
||||
# for a non-root user
|
||||
PGID=$PUID
|
||||
fi
|
||||
|
||||
export PUID PGID
|
||||
|
||||
log_info () {
|
||||
echo -e "${BLUE}❯ ${CYAN}$1${RESET}"
|
||||
}
|
||||
|
||||
log_error () {
|
||||
echo -e "${RED}❯ $1${RESET}"
|
||||
}
|
||||
|
||||
# The `run` file will only execute 1 line so this helps keep things
|
||||
# logically separated
|
||||
|
||||
log_fatal () {
|
||||
echo -e "${RED}--------------------------------------${RESET}"
|
||||
echo -e "${RED}ERROR: $1${RESET}"
|
||||
echo -e "${RED}--------------------------------------${RESET}"
|
||||
/run/s6/basedir/bin/halt
|
||||
exit 1
|
||||
}
|
||||
|
||||
# param $1: group_name
|
||||
get_group_id () {
|
||||
if [ "${1:-}" != '' ]; then
|
||||
getent group "$1" | cut -d: -f3
|
||||
fi
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This command reads the `DISABLE_IPV6` env var and will either enable
|
||||
# or disable ipv6 in all nginx configs based on this setting.
|
||||
|
||||
# Lowercase
|
||||
DISABLE_IPV6=$(echo "${DISABLE_IPV6:-}" | tr '[:upper:]' '[:lower:]')
|
||||
|
||||
CYAN='\E[1;36m'
|
||||
BLUE='\E[1;34m'
|
||||
YELLOW='\E[1;33m'
|
||||
RED='\E[1;31m'
|
||||
RESET='\E[0m'
|
||||
|
||||
FOLDER=$1
|
||||
if [ "$FOLDER" == "" ]; then
|
||||
echo -e "${RED}❯ $0 requires a absolute folder path as the first argument!${RESET}"
|
||||
echo -e "${YELLOW} ie: $0 /data/nginx${RESET}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
FILES=$(find "$FOLDER" -type f -name "*.conf")
|
||||
if [ "$DISABLE_IPV6" == "true" ] || [ "$DISABLE_IPV6" == "on" ] || [ "$DISABLE_IPV6" == "1" ] || [ "$DISABLE_IPV6" == "yes" ]; then
|
||||
# IPV6 is disabled
|
||||
echo "Disabling IPV6 in hosts"
|
||||
echo -e "${BLUE}❯ ${CYAN}Disabling IPV6 in hosts: ${YELLOW}${FOLDER}${RESET}"
|
||||
|
||||
# Iterate over configs and run the regex
|
||||
for FILE in $FILES
|
||||
do
|
||||
echo -e " ${BLUE}❯ ${YELLOW}${FILE}${RESET}"
|
||||
sed -E -i 's/^([^#]*)listen \[::\]/\1#listen [::]/g' "$FILE"
|
||||
done
|
||||
|
||||
else
|
||||
# IPV6 is enabled
|
||||
echo -e "${BLUE}❯ ${CYAN}Enabling IPV6 in hosts: ${YELLOW}${FOLDER}${RESET}"
|
||||
|
||||
# Iterate over configs and run the regex
|
||||
for FILE in $FILES
|
||||
do
|
||||
echo -e " ${BLUE}❯ ${YELLOW}${FILE}${RESET}"
|
||||
sed -E -i 's/^(\s*)#listen \[::\]/\1listen [::]/g' "$FILE"
|
||||
done
|
||||
|
||||
fi
|
@ -32,6 +32,7 @@ server {
|
||||
server_name localhost;
|
||||
access_log /data/logs/fallback_access.log standard;
|
||||
error_log /dev/null crit;
|
||||
include conf.d/include/ssl-ciphers.conf;
|
||||
ssl_reject_handshake on;
|
||||
|
||||
return 444;
|
||||
|
@ -1,7 +1,7 @@
|
||||
# run nginx in foreground
|
||||
daemon off;
|
||||
|
||||
user root;
|
||||
pid /run/nginx/nginx.pid;
|
||||
user npm;
|
||||
|
||||
# Set number of worker processes automatically based on number of CPU cores.
|
||||
worker_processes auto;
|
||||
|
@ -3,17 +3,19 @@
|
||||
|
||||
set -e
|
||||
|
||||
echo "❯ Starting backend ..."
|
||||
if [ "$DEVELOPMENT" == "true" ]; then
|
||||
. /bin/common.sh
|
||||
|
||||
cd /app || exit 1
|
||||
# If yarn install fails: add --verbose --network-concurrency 1
|
||||
yarn install
|
||||
node --max_old_space_size=250 --abort_on_uncaught_exception node_modules/nodemon/bin/nodemon.js
|
||||
|
||||
log_info 'Starting backend ...'
|
||||
|
||||
if [ "${DEVELOPMENT:-}" = 'true' ]; then
|
||||
s6-setuidgid "$PUID:$PGID" yarn install
|
||||
exec s6-setuidgid "$PUID:$PGID" bash -c "export HOME=$NPMHOME;node --max_old_space_size=250 --abort_on_uncaught_exception node_modules/nodemon/bin/nodemon.js"
|
||||
else
|
||||
cd /app || exit 1
|
||||
while :
|
||||
do
|
||||
node --abort_on_uncaught_exception --max_old_space_size=250 index.js
|
||||
s6-setuidgid "$PUID:$PGID" bash -c "export HOME=$NPMHOME;node --abort_on_uncaught_exception --max_old_space_size=250 index.js"
|
||||
sleep 1
|
||||
done
|
||||
fi
|
||||
|
@ -5,11 +5,17 @@ set -e
|
||||
|
||||
# This service is DEVELOPMENT only.
|
||||
|
||||
if [ "$DEVELOPMENT" == "true" ]; then
|
||||
if [ "$DEVELOPMENT" = 'true' ]; then
|
||||
. /bin/common.sh
|
||||
cd /app/frontend || exit 1
|
||||
# If yarn install fails: add --verbose --network-concurrency 1
|
||||
yarn install
|
||||
yarn watch
|
||||
HOME=$NPMHOME
|
||||
export HOME
|
||||
mkdir -p /app/frontend/dist
|
||||
chown -R "$PUID:$PGID" /app/frontend/dist
|
||||
|
||||
log_info 'Starting frontend ...'
|
||||
s6-setuidgid "$PUID:$PGID" yarn install
|
||||
exec s6-setuidgid "$PUID:$PGID" yarn watch
|
||||
else
|
||||
exit 0
|
||||
fi
|
||||
|
@ -3,5 +3,7 @@
|
||||
|
||||
set -e
|
||||
|
||||
echo "❯ Starting nginx ..."
|
||||
exec nginx
|
||||
. /bin/common.sh
|
||||
|
||||
log_info 'Starting nginx ...'
|
||||
exec s6-setuidgid "$PUID:$PGID" nginx
|
||||
|
22
docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/00-all.sh
Executable file
22
docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/00-all.sh
Executable file
@ -0,0 +1,22 @@
|
||||
#!/command/with-contenv bash
|
||||
# shellcheck shell=bash
|
||||
|
||||
set -e
|
||||
|
||||
. /bin/common.sh
|
||||
|
||||
if [ "$(id -u)" != "0" ]; then
|
||||
log_fatal "This docker container must be run as root, do not specify a user.\nYou can specify PUID and PGID env vars to run processes as that user and group after initialization."
|
||||
fi
|
||||
|
||||
if [ "$DEBUG" = "true" ]; then
|
||||
set -x
|
||||
fi
|
||||
|
||||
. /etc/s6-overlay/s6-rc.d/prepare/10-usergroup.sh
|
||||
. /etc/s6-overlay/s6-rc.d/prepare/20-paths.sh
|
||||
. /etc/s6-overlay/s6-rc.d/prepare/30-ownership.sh
|
||||
. /etc/s6-overlay/s6-rc.d/prepare/40-dynamic.sh
|
||||
. /etc/s6-overlay/s6-rc.d/prepare/50-ipv6.sh
|
||||
. /etc/s6-overlay/s6-rc.d/prepare/60-secrets.sh
|
||||
. /etc/s6-overlay/s6-rc.d/prepare/90-banner.sh
|
40
docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/10-usergroup.sh
Executable file
40
docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/10-usergroup.sh
Executable file
@ -0,0 +1,40 @@
|
||||
#!/command/with-contenv bash
|
||||
# shellcheck shell=bash
|
||||
|
||||
set -e
|
||||
|
||||
log_info "Configuring $NPMUSER user ..."
|
||||
|
||||
if id -u "$NPMUSER" 2>/dev/null; then
|
||||
# user already exists
|
||||
usermod -u "$PUID" "$NPMUSER"
|
||||
else
|
||||
# Add user
|
||||
useradd -o -u "$PUID" -U -d "$NPMHOME" -s /bin/false "$NPMUSER"
|
||||
fi
|
||||
|
||||
log_info "Configuring $NPMGROUP group ..."
|
||||
if [ "$(get_group_id "$NPMGROUP")" = '' ]; then
|
||||
# Add group. This will not set the id properly if it's already taken
|
||||
groupadd -f -g "$PGID" "$NPMGROUP"
|
||||
else
|
||||
groupmod -o -g "$PGID" "$NPMGROUP"
|
||||
fi
|
||||
|
||||
# Set the group ID and check it
|
||||
groupmod -o -g "$PGID" "$NPMGROUP"
|
||||
if [ "$(get_group_id "$NPMGROUP")" != "$PGID" ]; then
|
||||
echo "ERROR: Unable to set group id properly"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Set the group against the user and check it
|
||||
usermod -G "$PGID" "$NPMGROUP"
|
||||
if [ "$(id -g "$NPMUSER")" != "$PGID" ] ; then
|
||||
echo "ERROR: Unable to set group against the user properly"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Home for user
|
||||
mkdir -p "$NPMHOME"
|
||||
chown -R "$PUID:$PGID" "$NPMHOME"
|
41
docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/20-paths.sh
Executable file
41
docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/20-paths.sh
Executable file
@ -0,0 +1,41 @@
|
||||
#!/command/with-contenv bash
|
||||
# shellcheck shell=bash
|
||||
|
||||
set -e
|
||||
|
||||
log_info 'Checking paths ...'
|
||||
|
||||
# Ensure /data is mounted
|
||||
if [ ! -d '/data' ]; then
|
||||
log_fatal '/data is not mounted! Check your docker configuration.'
|
||||
fi
|
||||
# Ensure /etc/letsencrypt is mounted
|
||||
if [ ! -d '/etc/letsencrypt' ]; then
|
||||
log_fatal '/etc/letsencrypt is not mounted! Check your docker configuration.'
|
||||
fi
|
||||
|
||||
# Create required folders
|
||||
mkdir -p \
|
||||
/data/nginx \
|
||||
/data/custom_ssl \
|
||||
/data/logs \
|
||||
/data/access \
|
||||
/data/nginx/default_host \
|
||||
/data/nginx/default_www \
|
||||
/data/nginx/proxy_host \
|
||||
/data/nginx/redirection_host \
|
||||
/data/nginx/stream \
|
||||
/data/nginx/dead_host \
|
||||
/data/nginx/temp \
|
||||
/data/letsencrypt-acme-challenge \
|
||||
/run/nginx \
|
||||
/tmp/nginx/body \
|
||||
/var/log/nginx \
|
||||
/var/lib/nginx/cache/public \
|
||||
/var/lib/nginx/cache/private \
|
||||
/var/cache/nginx/proxy_temp
|
||||
|
||||
touch /var/log/nginx/error.log || true
|
||||
chmod 777 /var/log/nginx/error.log || true
|
||||
chmod -R 777 /var/cache/nginx || true
|
||||
chmod 644 /etc/logrotate.d/nginx-proxy-manager
|
27
docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/30-ownership.sh
Executable file
27
docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/30-ownership.sh
Executable file
@ -0,0 +1,27 @@
|
||||
#!/command/with-contenv bash
|
||||
# shellcheck shell=bash
|
||||
|
||||
set -e
|
||||
|
||||
log_info 'Setting ownership ...'
|
||||
|
||||
# root
|
||||
chown root /tmp/nginx
|
||||
|
||||
# npm user and group
|
||||
chown -R "$PUID:$PGID" /data
|
||||
chown -R "$PUID:$PGID" /etc/letsencrypt
|
||||
chown -R "$PUID:$PGID" /run/nginx
|
||||
chown -R "$PUID:$PGID" /tmp/nginx
|
||||
chown -R "$PUID:$PGID" /var/cache/nginx
|
||||
chown -R "$PUID:$PGID" /var/lib/logrotate
|
||||
chown -R "$PUID:$PGID" /var/lib/nginx
|
||||
chown -R "$PUID:$PGID" /var/log/nginx
|
||||
|
||||
# Don't chown entire /etc/nginx folder as this causes crashes on some systems
|
||||
chown -R "$PUID:$PGID" /etc/nginx/nginx
|
||||
chown -R "$PUID:$PGID" /etc/nginx/nginx.conf
|
||||
chown -R "$PUID:$PGID" /etc/nginx/conf.d
|
||||
|
||||
# Prevents errors when installing python certbot plugins when non-root
|
||||
chown -R "$PUID:$PGID" /opt/certbot
|
17
docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/40-dynamic.sh
Executable file
17
docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/40-dynamic.sh
Executable file
@ -0,0 +1,17 @@
|
||||
#!/command/with-contenv bash
|
||||
# shellcheck shell=bash
|
||||
|
||||
set -e
|
||||
|
||||
log_info 'Dynamic resolvers ...'
|
||||
|
||||
DISABLE_IPV6=$(echo "${DISABLE_IPV6:-}" | tr '[:upper:]' '[:lower:]')
|
||||
|
||||
# Dynamically generate resolvers file, if resolver is IPv6, enclose in `[]`
|
||||
# thanks @tfmm
|
||||
if [ "$DISABLE_IPV6" == "true" ] || [ "$DISABLE_IPV6" == "on" ] || [ "$DISABLE_IPV6" == "1" ] || [ "$DISABLE_IPV6" == "yes" ];
|
||||
then
|
||||
echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" { sub(/%.*$/,"",$2); print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf) ipv6=off valid=10s;" > /etc/nginx/conf.d/include/resolvers.conf
|
||||
else
|
||||
echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" { sub(/%.*$/,"",$2); print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf) valid=10s;" > /etc/nginx/conf.d/include/resolvers.conf
|
||||
fi
|
39
docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/50-ipv6.sh
Executable file
39
docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/50-ipv6.sh
Executable file
@ -0,0 +1,39 @@
|
||||
#!/command/with-contenv bash
|
||||
# shellcheck shell=bash
|
||||
|
||||
# This command reads the `DISABLE_IPV6` env var and will either enable
|
||||
# or disable ipv6 in all nginx configs based on this setting.
|
||||
|
||||
set -e
|
||||
|
||||
log_info 'IPv6 ...'
|
||||
|
||||
# Lowercase
|
||||
DISABLE_IPV6=$(echo "${DISABLE_IPV6:-}" | tr '[:upper:]' '[:lower:]')
|
||||
|
||||
process_folder () {
|
||||
FILES=$(find "$1" -type f -name "*.conf")
|
||||
SED_REGEX=
|
||||
|
||||
if [ "$DISABLE_IPV6" == "true" ] || [ "$DISABLE_IPV6" == "on" ] || [ "$DISABLE_IPV6" == "1" ] || [ "$DISABLE_IPV6" == "yes" ]; then
|
||||
# IPV6 is disabled
|
||||
echo "Disabling IPV6 in hosts in: $1"
|
||||
SED_REGEX='s/^([^#]*)listen \[::\]/\1#listen [::]/g'
|
||||
else
|
||||
# IPV6 is enabled
|
||||
echo "Enabling IPV6 in hosts in: $1"
|
||||
SED_REGEX='s/^(\s*)#listen \[::\]/\1listen [::]/g'
|
||||
fi
|
||||
|
||||
for FILE in $FILES
|
||||
do
|
||||
echo "- ${FILE}"
|
||||
echo "$(sed -E "$SED_REGEX" "$FILE")" > $FILE
|
||||
done
|
||||
|
||||
# ensure the files are still owned by the npm user
|
||||
chown -R "$PUID:$PGID" "$1"
|
||||
}
|
||||
|
||||
process_folder /etc/nginx/conf.d
|
||||
process_folder /data/nginx
|
30
docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/60-secrets.sh
Executable file
30
docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/60-secrets.sh
Executable file
@ -0,0 +1,30 @@
|
||||
#!/command/with-contenv bash
|
||||
# shellcheck shell=bash
|
||||
|
||||
set -e
|
||||
|
||||
# in s6, environmental variables are written as text files for s6 to monitor
|
||||
# search through full-path filenames for files ending in "__FILE"
|
||||
log_info 'Docker secrets ...'
|
||||
|
||||
for FILENAME in $(find /var/run/s6/container_environment/ | grep "__FILE$"); do
|
||||
echo "[secret-init] Evaluating ${FILENAME##*/} ..."
|
||||
|
||||
# set SECRETFILE to the contents of the full-path textfile
|
||||
SECRETFILE=$(cat "${FILENAME}")
|
||||
# if SECRETFILE exists / is not null
|
||||
if [[ -f "${SECRETFILE}" ]]; then
|
||||
# strip the appended "__FILE" from environmental variable name ...
|
||||
STRIPFILE=$(echo "${FILENAME}" | sed "s/__FILE//g")
|
||||
# echo "[secret-init] Set STRIPFILE to ${STRIPFILE}" # DEBUG - rm for prod!
|
||||
|
||||
# ... and set value to contents of secretfile
|
||||
# since s6 uses text files, this is effectively "export ..."
|
||||
printf $(cat "${SECRETFILE}") > "${STRIPFILE}"
|
||||
# echo "[secret-init] Set ${STRIPFILE##*/} to $(cat ${STRIPFILE})" # DEBUG - rm for prod!"
|
||||
echo "Success: ${STRIPFILE##*/} set from ${FILENAME##*/}"
|
||||
|
||||
else
|
||||
echo "Cannot find secret in ${FILENAME}"
|
||||
fi
|
||||
done
|
18
docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/90-banner.sh
Executable file
18
docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/90-banner.sh
Executable file
@ -0,0 +1,18 @@
|
||||
#!/command/with-contenv bash
|
||||
# shellcheck shell=bash
|
||||
|
||||
set -e
|
||||
set +x
|
||||
|
||||
echo "
|
||||
-------------------------------------
|
||||
_ _ ____ __ __
|
||||
| \ | | _ \| \/ |
|
||||
| \| | |_) | |\/| |
|
||||
| |\ | __/| | | |
|
||||
|_| \_|_| |_| |_|
|
||||
-------------------------------------
|
||||
User: $NPMUSER PUID:$PUID ID:$(id -u "$NPMUSER") GROUP:$(id -g "$NPMUSER")
|
||||
Group: $NPMGROUP PGID:$PGID ID:$(get_group_id "$NPMGROUP")
|
||||
-------------------------------------
|
||||
"
|
@ -1,93 +0,0 @@
|
||||
#!/command/with-contenv bash
|
||||
# shellcheck shell=bash
|
||||
|
||||
set -e
|
||||
|
||||
DATA_PATH=/data
|
||||
|
||||
# Ensure /data is mounted
|
||||
if [ ! -d "$DATA_PATH" ]; then
|
||||
echo '--------------------------------------'
|
||||
echo "ERROR: $DATA_PATH is not mounted! Check your docker configuration."
|
||||
echo '--------------------------------------'
|
||||
/run/s6/basedir/bin/halt
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "❯ Checking folder structure ..."
|
||||
|
||||
# Create required folders
|
||||
mkdir -p /tmp/nginx/body \
|
||||
/run/nginx \
|
||||
/var/log/nginx \
|
||||
/data/nginx \
|
||||
/data/custom_ssl \
|
||||
/data/logs \
|
||||
/data/access \
|
||||
/data/nginx/default_host \
|
||||
/data/nginx/default_www \
|
||||
/data/nginx/proxy_host \
|
||||
/data/nginx/redirection_host \
|
||||
/data/nginx/stream \
|
||||
/data/nginx/dead_host \
|
||||
/data/nginx/temp \
|
||||
/var/lib/nginx/cache/public \
|
||||
/var/lib/nginx/cache/private \
|
||||
/var/cache/nginx/proxy_temp \
|
||||
/data/letsencrypt-acme-challenge
|
||||
|
||||
touch /var/log/nginx/error.log && chmod 777 /var/log/nginx/error.log && chmod -R 777 /var/cache/nginx
|
||||
chown root /tmp/nginx
|
||||
|
||||
# Dynamically generate resolvers file, if resolver is IPv6, enclose in `[]`
|
||||
# thanks @tfmm
|
||||
if [ "$DISABLE_IPV6" == "true" ] || [ "$DISABLE_IPV6" == "on" ] || [ "$DISABLE_IPV6" == "1" ] || [ "$DISABLE_IPV6" == "yes" ];
|
||||
then
|
||||
echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" { sub(/%.*$/,"",$2); print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf) ipv6=off valid=10s;" > /etc/nginx/conf.d/include/resolvers.conf
|
||||
else
|
||||
echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" { sub(/%.*$/,"",$2); print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf) valid=10s;" > /etc/nginx/conf.d/include/resolvers.conf
|
||||
fi
|
||||
|
||||
echo "Changing ownership of /data/logs to $(id -u):$(id -g)"
|
||||
chown -R "$(id -u):$(id -g)" /data/logs
|
||||
|
||||
# Handle IPV6 settings
|
||||
/bin/handle-ipv6-setting /etc/nginx/conf.d
|
||||
/bin/handle-ipv6-setting /data/nginx
|
||||
|
||||
# ref: https://github.com/linuxserver/docker-baseimage-alpine/blob/master/root/etc/cont-init.d/01-envfile
|
||||
|
||||
# in s6, environmental variables are written as text files for s6 to monitor
|
||||
# search through full-path filenames for files ending in "__FILE"
|
||||
echo "❯ Secrets-init ..."
|
||||
for FILENAME in $(find /var/run/s6/container_environment/ | grep "__FILE$"); do
|
||||
echo "[secret-init] Evaluating ${FILENAME##*/} ..."
|
||||
|
||||
# set SECRETFILE to the contents of the full-path textfile
|
||||
SECRETFILE=$(cat "${FILENAME}")
|
||||
# if SECRETFILE exists / is not null
|
||||
if [[ -f "${SECRETFILE}" ]]; then
|
||||
# strip the appended "__FILE" from environmental variable name ...
|
||||
STRIPFILE=$(echo "${FILENAME}" | sed "s/__FILE//g")
|
||||
# echo "[secret-init] Set STRIPFILE to ${STRIPFILE}" # DEBUG - rm for prod!
|
||||
|
||||
# ... and set value to contents of secretfile
|
||||
# since s6 uses text files, this is effectively "export ..."
|
||||
printf $(cat "${SECRETFILE}") > "${STRIPFILE}"
|
||||
# echo "[secret-init] Set ${STRIPFILE##*/} to $(cat ${STRIPFILE})" # DEBUG - rm for prod!"
|
||||
echo "[secret-init] Success! ${STRIPFILE##*/} set from ${FILENAME##*/}"
|
||||
|
||||
else
|
||||
echo "[secret-init] cannot find secret in ${FILENAME}"
|
||||
fi
|
||||
done
|
||||
|
||||
echo
|
||||
echo "-------------------------------------
|
||||
_ _ ____ __ __
|
||||
| \ | | _ \| \/ |
|
||||
| \| | |_) | |\/| |
|
||||
| |\ | __/| | | |
|
||||
|_| \_|_| |_| |_|
|
||||
-------------------------------------
|
||||
"
|
@ -1,2 +1,2 @@
|
||||
# shellcheck shell=bash
|
||||
/etc/s6-overlay/s6-rc.d/prepare/script.sh
|
||||
/etc/s6-overlay/s6-rc.d/prepare/00-all.sh
|
||||
|
@ -8,8 +8,8 @@ BLUE='\E[1;34m'
|
||||
GREEN='\E[1;32m'
|
||||
RESET='\E[0m'
|
||||
|
||||
S6_OVERLAY_VERSION=3.1.4.1
|
||||
TARGETPLATFORM=${1:unspecified}
|
||||
S6_OVERLAY_VERSION=3.1.5.0
|
||||
TARGETPLATFORM=${1:-linux/amd64}
|
||||
|
||||
# Determine the correct binary file for the architecture given
|
||||
case $TARGETPLATFORM in
|
||||
|
@ -1,5 +1,26 @@
|
||||
# Advanced Configuration
|
||||
|
||||
## Running processes as a user/group
|
||||
|
||||
By default, the services (nginx etc) will run as `root` user inside the docker container.
|
||||
You can change this behaviour by setting the following environment variables.
|
||||
Not only will they run the services as this user/group, they will change the ownership
|
||||
on the `data` and `letsencrypt` folders at startup.
|
||||
|
||||
```yml
|
||||
services:
|
||||
app:
|
||||
image: 'jc21/nginx-proxy-manager:latest'
|
||||
environment:
|
||||
PUID: 1000
|
||||
PGID: 1000
|
||||
# ...
|
||||
```
|
||||
|
||||
This may have the side effect of a failed container start due to permission denied trying
|
||||
to open port 80 on some systems. The only course to fix that is to remove the variables
|
||||
and run as the default root user.
|
||||
|
||||
## Best Practice: Use a Docker network
|
||||
|
||||
For those who have a few of their upstream services running in Docker on the same Docker
|
||||
@ -25,7 +46,7 @@ networks:
|
||||
Let's look at a Portainer example:
|
||||
|
||||
```yml
|
||||
version: '3'
|
||||
version: '3.8'
|
||||
services:
|
||||
|
||||
portainer:
|
||||
@ -60,14 +81,14 @@ healthcheck:
|
||||
timeout: 3s
|
||||
```
|
||||
|
||||
## Docker Secrets
|
||||
## Docker File Secrets
|
||||
|
||||
This image supports the use of Docker secrets to import from file and keep sensitive usernames or passwords from being passed or preserved in plaintext.
|
||||
This image supports the use of Docker secrets to import from files and keep sensitive usernames or passwords from being passed or preserved in plaintext.
|
||||
|
||||
You can set any environment variable from a file by appending `__FILE` (double-underscore FILE) to the environmental variable name.
|
||||
|
||||
```yml
|
||||
version: "3.7"
|
||||
version: '3.8'
|
||||
|
||||
secrets:
|
||||
# Secrets are single-line text files where the sole content is the secret
|
||||
@ -96,9 +117,7 @@ services:
|
||||
# DB_MYSQL_PASSWORD: "npm" # use secret instead
|
||||
DB_MYSQL_PASSWORD__FILE: /run/secrets/MYSQL_PWD
|
||||
DB_MYSQL_NAME: "npm"
|
||||
# If you would rather use Sqlite uncomment this
|
||||
# and remove all DB_MYSQL_* lines above
|
||||
# DB_SQLITE_FILE: "/data/database.sqlite"
|
||||
# If you would rather use Sqlite, remove all DB_MYSQL_* lines above
|
||||
# Uncomment this if IPv6 is not enabled on your host
|
||||
# DISABLE_IPV6: 'true'
|
||||
volumes:
|
||||
@ -108,6 +127,7 @@ services:
|
||||
- MYSQL_PWD
|
||||
depends_on:
|
||||
- db
|
||||
|
||||
db:
|
||||
image: jc21/mariadb-aria
|
||||
restart: unless-stopped
|
||||
@ -119,7 +139,7 @@ services:
|
||||
# MYSQL_PASSWORD: "npm" # use secret instead
|
||||
MYSQL_PASSWORD__FILE: /run/secrets/MYSQL_PWD
|
||||
volumes:
|
||||
- ./data/mysql:/var/lib/mysql
|
||||
- ./mysql:/var/lib/mysql
|
||||
secrets:
|
||||
- DB_ROOT_PWD
|
||||
- MYSQL_PWD
|
||||
|
@ -5,7 +5,7 @@
|
||||
Create a `docker-compose.yml` file:
|
||||
|
||||
```yml
|
||||
version: "3"
|
||||
version: '3.8'
|
||||
services:
|
||||
app:
|
||||
image: 'jc21/nginx-proxy-manager:latest'
|
||||
@ -51,7 +51,7 @@ are going to use.
|
||||
Here is an example of what your `docker-compose.yml` will look like when using a MariaDB container:
|
||||
|
||||
```yml
|
||||
version: "3"
|
||||
version: '3.8'
|
||||
services:
|
||||
app:
|
||||
image: 'jc21/nginx-proxy-manager:latest'
|
||||
@ -64,6 +64,7 @@ services:
|
||||
# Add any other Stream port you want to expose
|
||||
# - '21:21' # FTP
|
||||
environment:
|
||||
# Mysql/Maria connection parameters:
|
||||
DB_MYSQL_HOST: "db"
|
||||
DB_MYSQL_PORT: 3306
|
||||
DB_MYSQL_USER: "npm"
|
||||
@ -86,7 +87,7 @@ services:
|
||||
MYSQL_USER: 'npm'
|
||||
MYSQL_PASSWORD: 'npm'
|
||||
volumes:
|
||||
- ./data/mysql:/var/lib/mysql
|
||||
- ./mysql:/var/lib/mysql
|
||||
```
|
||||
|
||||
::: warning
|
||||
@ -118,13 +119,12 @@ Please note that the `jc21/mariadb-aria:latest` image might have some problems o
|
||||
|
||||
After the app is running for the first time, the following will happen:
|
||||
|
||||
1. The database will initialize with table structures
|
||||
2. GPG keys will be generated and saved in the configuration file
|
||||
1. GPG keys will be generated and saved in the data folder
|
||||
2. The database will initialize with table structures
|
||||
3. A default admin user will be created
|
||||
|
||||
This process can take a couple of minutes depending on your machine.
|
||||
|
||||
|
||||
## Default Administrator User
|
||||
|
||||
```
|
||||
@ -134,49 +134,3 @@ Password: changeme
|
||||
|
||||
Immediately after logging in with this default user you will be asked to modify your details and change your password.
|
||||
|
||||
## Configuration File
|
||||
|
||||
::: warning
|
||||
|
||||
This section is meant for advanced users
|
||||
|
||||
:::
|
||||
|
||||
If you would like more control over the database settings you can define a custom config JSON file.
|
||||
|
||||
|
||||
Here's an example for `sqlite` configuration as it is generated from the environment variables:
|
||||
|
||||
```json
|
||||
{
|
||||
"database": {
|
||||
"engine": "knex-native",
|
||||
"knex": {
|
||||
"client": "sqlite3",
|
||||
"connection": {
|
||||
"filename": "/data/database.sqlite"
|
||||
},
|
||||
"useNullAsDefault": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can modify the `knex` object with your custom configuration, but note that not all knex clients might be installed in the image.
|
||||
|
||||
Once you've created your configuration file you can mount it to `/app/config/production.json` inside you container using:
|
||||
|
||||
```
|
||||
[...]
|
||||
services:
|
||||
app:
|
||||
image: 'jc21/nginx-proxy-manager:latest'
|
||||
[...]
|
||||
volumes:
|
||||
- ./config.json:/app/config/production.json
|
||||
[...]
|
||||
[...]
|
||||
```
|
||||
|
||||
**Note:** After the first run of the application, the config file will be altered to include generated encryption keys unique to your installation.
|
||||
These keys affect the login and session management of the application. If these keys change for any reason, all users will be logged out.
|
||||
|
@ -9,3 +9,4 @@ This project will automatically update any databases or other requirements so yo
|
||||
any crazy instructions. These steps above will pull the latest updates and recreate the docker
|
||||
containers.
|
||||
|
||||
See the [list of releases](https://github.com/NginxProxyManager/nginx-proxy-manager/releases) for any upgrade steps specific to each release.
|
||||
|
@ -8477,9 +8477,11 @@ semver@^6.0.0, semver@^6.1.0, semver@^6.2.0, semver@^6.3.0:
|
||||
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
|
||||
|
||||
semver@^7.3.2:
|
||||
version "7.3.2"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938"
|
||||
integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==
|
||||
version "7.5.2"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.2.tgz#5b851e66d1be07c1cdaf37dfc856f543325a2beb"
|
||||
integrity sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ==
|
||||
dependencies:
|
||||
lru-cache "^6.0.0"
|
||||
|
||||
send@0.17.2, send@^0.17.1:
|
||||
version "0.17.2"
|
||||
@ -9498,13 +9500,14 @@ toposort@^2.0.2:
|
||||
integrity sha1-riF2gXXRVZ1IvvNUILL0li8JwzA=
|
||||
|
||||
tough-cookie@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4"
|
||||
integrity sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==
|
||||
version "4.1.3"
|
||||
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.3.tgz#97b9adb0728b42280aa3d814b6b999b2ff0318bf"
|
||||
integrity sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==
|
||||
dependencies:
|
||||
psl "^1.1.33"
|
||||
punycode "^2.1.1"
|
||||
universalify "^0.1.2"
|
||||
universalify "^0.2.0"
|
||||
url-parse "^1.5.3"
|
||||
|
||||
tough-cookie@~2.5.0:
|
||||
version "2.5.0"
|
||||
@ -9690,11 +9693,16 @@ unique-string@^2.0.0:
|
||||
dependencies:
|
||||
crypto-random-string "^2.0.0"
|
||||
|
||||
universalify@^0.1.0, universalify@^0.1.2:
|
||||
universalify@^0.1.0:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
|
||||
integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
|
||||
|
||||
universalify@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0"
|
||||
integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==
|
||||
|
||||
universalify@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/universalify/-/universalify-1.0.0.tgz#b61a1da173e8435b2fe3c67d29b9adf8594bd16d"
|
||||
@ -9796,10 +9804,10 @@ url-parse-lax@^3.0.0:
|
||||
dependencies:
|
||||
prepend-http "^2.0.0"
|
||||
|
||||
url-parse@^1.4.3, url-parse@^1.4.7:
|
||||
version "1.5.9"
|
||||
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.9.tgz#05ff26484a0b5e4040ac64dcee4177223d74675e"
|
||||
integrity sha512-HpOvhKBvre8wYez+QhHcYiVvVmeF6DVnuSOOPhe3cTum3BnqHhvKaZm8FU5yTiOu/Jut2ZpB2rA/SbBA1JIGlQ==
|
||||
url-parse@^1.4.3, url-parse@^1.4.7, url-parse@^1.5.3:
|
||||
version "1.5.10"
|
||||
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1"
|
||||
integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==
|
||||
dependencies:
|
||||
querystringify "^2.1.1"
|
||||
requires-port "^1.0.0"
|
||||
|
@ -18,6 +18,10 @@
|
||||
<input class="custom-control-input" name="value" value="404" type="radio" required <%- value === '404' ? 'checked' : '' %>>
|
||||
<div class="custom-control-label"><%- i18n('settings', 'default-site-404') %></div>
|
||||
</label>
|
||||
<label class="custom-control custom-radio">
|
||||
<input class="custom-control-input" name="value" value="444" type="radio" required <%- value === '444' ? 'checked' : '' %>>
|
||||
<div class="custom-control-label"><%- i18n('settings', 'default-site-444') %></div>
|
||||
</label>
|
||||
<label class="custom-control custom-radio">
|
||||
<input class="custom-control-input" name="value" value="redirect" type="radio" required <%- value === 'redirect' ? 'checked' : '' %>>
|
||||
<div class="custom-control-label"><%- i18n('settings', 'default-site-redirect') %></div>
|
||||
|
@ -60,7 +60,7 @@
|
||||
},
|
||||
"footer": {
|
||||
"fork-me": "Fork me on Github",
|
||||
"copy": "© 2022 <a href=\"{url}\" target=\"_blank\">jc21.com</a>.",
|
||||
"copy": "© 2023 <a href=\"{url}\" target=\"_blank\">jc21.com</a>.",
|
||||
"theme": "Theme by <a href=\"{url}\" target=\"_blank\">Tabler</a>"
|
||||
},
|
||||
"dashboard": {
|
||||
@ -287,6 +287,7 @@
|
||||
"default-site": "Default Site",
|
||||
"default-site-congratulations": "Congratulations Page",
|
||||
"default-site-404": "404 Page",
|
||||
"default-site-444": "No Response (444)",
|
||||
"default-site-html": "Custom Page",
|
||||
"default-site-redirect": "Redirect"
|
||||
}
|
||||
|
@ -5698,19 +5698,19 @@ semver-diff@^3.1.1:
|
||||
semver "^6.3.0"
|
||||
|
||||
"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.6.0, semver@^5.7.1:
|
||||
version "5.7.1"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
|
||||
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
|
||||
version "5.7.2"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8"
|
||||
integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==
|
||||
|
||||
semver@^6.0.0, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0:
|
||||
version "6.3.0"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
|
||||
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
|
||||
version "6.3.1"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
|
||||
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
|
||||
|
||||
semver@^7.3.2, semver@^7.3.4:
|
||||
version "7.3.5"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7"
|
||||
integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==
|
||||
version "7.5.4"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e"
|
||||
integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==
|
||||
dependencies:
|
||||
lru-cache "^6.0.0"
|
||||
|
||||
@ -6742,9 +6742,9 @@ window-size@0.1.0:
|
||||
integrity sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=
|
||||
|
||||
word-wrap@~1.2.3:
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
|
||||
integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
|
||||
version "1.2.4"
|
||||
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.4.tgz#cb4b50ec9aca570abd1f52f33cd45b6c61739a9f"
|
||||
integrity sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==
|
||||
|
||||
wordwrap@0.0.2:
|
||||
version "0.0.2"
|
||||
|
@ -66,6 +66,16 @@ dns_azure_zone2 = example.org:/subscriptions/99800903-fb14-4992-9aff-12eaf274462
|
||||
full_plugin_name: 'dns-azure',
|
||||
},
|
||||
//####################################################//
|
||||
bunny: {
|
||||
display_name: 'bunny.net',
|
||||
package_name: 'certbot-dns-bunny',
|
||||
version_requirement: '~=0.0.9',
|
||||
dependencies: '',
|
||||
credentials: `# Bunny API token used by Certbot (see https://dash.bunny.net/account/settings)
|
||||
dns_bunny_api_key = xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx`,
|
||||
full_plugin_name: 'dns-bunny',
|
||||
},
|
||||
//####################################################//
|
||||
cloudflare: {
|
||||
display_name: 'Cloudflare',
|
||||
package_name: 'certbot-dns-cloudflare',
|
||||
@ -521,6 +531,19 @@ aws_secret_access_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY`,
|
||||
full_plugin_name: 'dns-route53',
|
||||
},
|
||||
//####################################################//
|
||||
strato: {
|
||||
display_name: 'Strato',
|
||||
package_name: 'certbot-dns-strato',
|
||||
version_requirement: '~=0.1.1',
|
||||
dependencies: '',
|
||||
credentials: `dns_strato_username = user
|
||||
dns_strato_password = pass
|
||||
# uncomment if domain name contains special characters
|
||||
# insert domain display name as seen on your account page here
|
||||
# dns_strato_domain_display_name = my-punicode-url.de`,
|
||||
full_plugin_name: 'dns-strato',
|
||||
},
|
||||
//####################################################//
|
||||
transip: {
|
||||
display_name: 'TransIP',
|
||||
package_name: 'certbot-dns-transip',
|
||||
|
48
test/cypress/integration/api/Hosts.spec.js
Normal file
48
test/cypress/integration/api/Hosts.spec.js
Normal file
@ -0,0 +1,48 @@
|
||||
/// <reference types="Cypress" />
|
||||
|
||||
describe('Hosts endpoints', () => {
|
||||
let token;
|
||||
|
||||
before(() => {
|
||||
cy.getToken().then((tok) => {
|
||||
token = tok;
|
||||
});
|
||||
});
|
||||
|
||||
it('Should be able to create a http host', function() {
|
||||
cy.task('backendApiPost', {
|
||||
token: token,
|
||||
path: '/api/nginx/proxy-hosts',
|
||||
data: {
|
||||
domain_names: ['test.example.com'],
|
||||
forward_scheme: 'http',
|
||||
forward_host: '1.1.1.1',
|
||||
forward_port: 80,
|
||||
access_list_id: '0',
|
||||
certificate_id: 0,
|
||||
meta: {
|
||||
letsencrypt_agree: false,
|
||||
dns_challenge: false
|
||||
},
|
||||
advanced_config: '',
|
||||
locations: [],
|
||||
block_exploits: false,
|
||||
caching_enabled: false,
|
||||
allow_websocket_upgrade: false,
|
||||
http2_support: false,
|
||||
hsts_enabled: false,
|
||||
hsts_subdomains: false,
|
||||
ssl_forced: false
|
||||
}
|
||||
}).then((data) => {
|
||||
cy.validateSwaggerSchema('post', 201, '/nginx/proxy-hosts', data);
|
||||
expect(data).to.have.property('id');
|
||||
expect(data.id).to.be.greaterThan(0);
|
||||
expect(data).to.have.property('enabled');
|
||||
expect(data.enabled).to.be.greaterThan(0);
|
||||
expect(data).to.have.property('meta');
|
||||
expect(typeof data.meta.nginx_online).to.be.equal('undefined');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
@ -126,7 +126,7 @@ BackendApi.prototype._putPostJson = function(fn, path, data, returnOnError) {
|
||||
logger('Response data:', data);
|
||||
if (!returnOnError && data instanceof Error) {
|
||||
reject(data);
|
||||
} else if (!returnOnError && response.statusCode != 200) {
|
||||
} else if (!returnOnError && (response.statusCode < 200 || response.statusCode >= 300)) {
|
||||
if (typeof data === 'object' && typeof data.error === 'object' && typeof data.error.message !== 'undefined') {
|
||||
reject(new Error(data.error.code + ': ' + data.error.message));
|
||||
} else {
|
||||
|
@ -2061,15 +2061,10 @@ sax@0.5.x:
|
||||
resolved "https://registry.yarnpkg.com/sax/-/sax-0.5.8.tgz#d472db228eb331c2506b0e8c15524adb939d12c1"
|
||||
integrity sha1-1HLbIo6zMcJQaw6MFVJK25OdEsE=
|
||||
|
||||
semver@^7.2.1:
|
||||
version "7.3.2"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938"
|
||||
integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==
|
||||
|
||||
semver@^7.3.2:
|
||||
version "7.3.8"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798"
|
||||
integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==
|
||||
semver@^7.2.1, semver@^7.3.2:
|
||||
version "7.5.4"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e"
|
||||
integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==
|
||||
dependencies:
|
||||
lru-cache "^6.0.0"
|
||||
|
||||
@ -2450,9 +2445,9 @@ wide-align@1.1.3:
|
||||
string-width "^1.0.2 || 2"
|
||||
|
||||
word-wrap@^1.2.3, word-wrap@~1.2.3:
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
|
||||
integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
|
||||
version "1.2.4"
|
||||
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.4.tgz#cb4b50ec9aca570abd1f52f33cd45b6c61739a9f"
|
||||
integrity sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==
|
||||
|
||||
workerpool@6.0.0:
|
||||
version "6.0.0"
|
||||
|
Reference in New Issue
Block a user