mirror of
https://github.com/NginxProxyManager/nginx-proxy-manager.git
synced 2025-10-06 21:00:10 +00:00
Compare commits
269 Commits
7c97516de6
...
develop
Author | SHA1 | Date | |
---|---|---|---|
|
487fa6d31b | ||
|
5b6ca1bf00 | ||
|
5039738aa3 | ||
|
4451be8f1c | ||
|
bee2fd1978 | ||
|
c8adbdfc15 | ||
|
aff4182ab8 | ||
|
8c9d2745e2 | ||
|
076d14b5e4 | ||
|
8a6d815152 | ||
|
54d463ac36 | ||
|
a23dc24021 | ||
|
4f9df893c8 | ||
|
304b38e82b | ||
|
1b0929ade6 | ||
|
ddbafb62a6 | ||
|
9a0383bc73 | ||
|
307cb94e84 | ||
|
63ae924fbc | ||
|
1710a263c0 | ||
|
1357774f21 | ||
|
5f54490d86 | ||
|
c97b8a339d | ||
|
ed1d90ee7f | ||
|
70894e55b8 | ||
|
817021a43d | ||
|
36e3449a56 | ||
|
db9f25638f | ||
|
ddd3355d95 | ||
|
aade8b42fc | ||
|
3735f3c11d | ||
|
b84762b5b9 | ||
|
953faeac15 | ||
|
c58f3f3ec9 | ||
|
0ee4d04d5f | ||
|
94f6756250 | ||
|
27e3f73854 | ||
|
d98f4b43dc | ||
|
ff3116a626 | ||
|
7047750b04 | ||
|
0792fc0768 | ||
|
9758c12ca3 | ||
|
ccd69c8867 | ||
|
23fd1fec6c | ||
|
6f04543744 | ||
|
cbb1fe44ca | ||
|
4c23f22d5b | ||
|
af5d3eccd6 | ||
|
a87283b030 | ||
|
97dbbdd60f | ||
|
ec81f2489a | ||
|
d0ec8e89aa | ||
|
9a96fbb5f4 | ||
|
a573450bb8 | ||
|
60a25ffbd5 | ||
|
7d2369b380 | ||
|
64f00e8dba | ||
|
c99143f548 | ||
|
cc4ee6919a | ||
|
8a69c65b40 | ||
|
95ee5ca958 | ||
|
40f22d30c4 | ||
|
30dfa9e3de | ||
|
b873499feb | ||
|
ef69be2036 | ||
|
7580e65dd4 | ||
|
f11dc5d7c1 | ||
|
77061a7bd6 | ||
|
b6afc19135 | ||
|
09ba400d09 | ||
|
0291cfc270 | ||
|
34267e0af9 | ||
|
f327c1e825 | ||
|
6f539979ec | ||
|
3d8079a137 | ||
|
6d6d83c0d0 | ||
|
100a4888d0 | ||
|
34a46bd733 | ||
|
7f8adc7e50 | ||
|
98d118cb74 | ||
|
4fb93542c3 | ||
|
4fe305520a | ||
|
76be31cf76 | ||
|
55dadb2004 | ||
|
d9cdb3dc2c | ||
|
0cab720f23 | ||
|
f5879dff6c | ||
|
5e66d677f1 | ||
|
18830f81b0 | ||
|
341ac65587 | ||
|
078baa255a | ||
|
bf9d9bd43b | ||
|
a394b25e61 | ||
|
1c47fc2ba4 | ||
|
312e2ab80c | ||
|
d147ccd88d | ||
|
03fd292c61 | ||
|
79d28f03d0 | ||
|
b09147eca8 | ||
|
c5a319cb20 | ||
|
c4df89df1f | ||
|
34c703f8b4 | ||
|
0a05d8f0ad | ||
|
0a9141fad5 | ||
|
42836774b7 | ||
|
2a07544f58 | ||
|
dc9d884743 | ||
|
0d5d2b1b7c | ||
|
df48b835c4 | ||
|
8a1557154a | ||
|
a6af5ec2c7 | ||
|
14d7c35fd7 | ||
|
cfcf78aaee | ||
|
3a01b2c84f | ||
|
e1c84a5c10 | ||
|
c56c95a59a | ||
|
6a60627833 | ||
|
b4793d3c16 | ||
|
68a7803513 | ||
|
2657af97cf | ||
|
4452f014b9 | ||
|
cd80cc8e4d | ||
|
ee4250d770 | ||
|
3dbc70faa6 | ||
|
3091c21cae | ||
|
57cd2a1919 | ||
|
ad5936c530 | ||
|
498109addb | ||
|
3f3aacd7ec | ||
|
bb4ecf812d | ||
|
c05f9695d0 | ||
|
6343b398f0 | ||
|
59362b7477 | ||
|
aedaaa18e0 | ||
|
080bd0b749 | ||
|
9687e9e450 | ||
|
5a234bb88c | ||
|
4de4b65036 | ||
|
f1c97c7c36 | ||
|
b4f49969d6 | ||
|
ec12d8f9bf | ||
|
e50e3def9d | ||
|
6415f284f9 | ||
|
98e5997f0a | ||
|
fc30a92bd4 | ||
|
e2011ee45c | ||
|
1406e75c2c | ||
|
ca3ee98c68 | ||
|
f90d839ebe | ||
|
be5278f31e | ||
|
73110d5e1e | ||
|
356b98bf7e | ||
|
3eecf7a38b | ||
|
7f9240dda7 | ||
|
f537619ffe | ||
|
805968aac6 | ||
|
2a4093c1b8 | ||
|
ae2ac8a733 | ||
|
5d087f1256 | ||
|
c6eca2578e | ||
|
56033bee9c | ||
|
c6630e87bb | ||
|
d6b98f51b0 | ||
|
1e322804ce | ||
|
b3de76c945 | ||
|
fcf4117f8e | ||
|
d26e8c1d0c | ||
|
19ed4c1212 | ||
|
03018d252b | ||
|
8351dd41f6 | ||
|
97212f2686 | ||
|
fe068a8b51 | ||
|
61e2bde98f | ||
|
81c9038929 | ||
|
4ea50ca40c | ||
|
53ed12bcf2 | ||
|
cb3e4ed59c | ||
|
b20dc5eade | ||
|
586afc0c91 | ||
|
93ea17a9bb | ||
|
151160a834 | ||
|
2075f98cad | ||
|
07a4e5791f | ||
|
640a1eeb68 | ||
|
126d3d44ca | ||
|
20646e7bb5 | ||
|
87998a03ce | ||
|
2cee211fb0 | ||
|
a56342c76a | ||
|
4c89379671 | ||
|
10b9a49274 | ||
|
595a742c40 | ||
|
c171752137 | ||
|
a0b26b9e98 | ||
|
d6791f4e38 | ||
|
62c94f3099 | ||
|
25a26d6175 | ||
|
17246e418f | ||
|
f7d3ca0b07 | ||
|
a55de386e7 | ||
|
e9d4f5b827 | ||
|
1c1cee3836 | ||
|
eaf6335694 | ||
|
ffe05ebd41 | ||
|
2e9a4f1aed | ||
|
d17c85e4c8 | ||
|
dad8d0ca00 | ||
|
d7e0558a35 | ||
|
ee41bb5562 | ||
|
0cf6b9caa4 | ||
|
68a9baf206 | ||
|
d92421d098 | ||
|
96c58b203e | ||
|
d499e2bfef | ||
|
5084cb7296 | ||
|
2f9e062718 | ||
|
edbed1af90 | ||
|
8497022e41 | ||
|
fa2c814fcb | ||
|
d96a3987c0 | ||
|
e677bfa2e8 | ||
|
fe2d8895d6 | ||
|
5bdc05878f | ||
|
929ac3bd7c | ||
|
f48e1b46a8 | ||
|
351ba8dacd | ||
|
3b89d5f380 | ||
|
e5aa880ec4 | ||
|
7322d35bd7 | ||
|
81b89185f2 | ||
|
f2bb8f2b3d | ||
|
b01817bc7f | ||
|
5aeb99b856 | ||
|
e7e4003d15 | ||
|
78f3e7281b | ||
|
c9d97aff38 | ||
|
9813071e76 | ||
|
d7a7fa3496 | ||
|
2e72f253a0 | ||
|
ac47eab23b | ||
|
0bfa6c9d4f | ||
|
f71de7474d | ||
|
3a2617e6bf | ||
|
6c6722464d | ||
|
02a7b43932 | ||
|
42a5bb6af3 | ||
|
a08d18bdb2 | ||
|
d2d104b723 | ||
|
e0352ecc48 | ||
|
4e035f285d | ||
|
b046bb3229 | ||
|
304899e604 | ||
|
e525b5470e | ||
|
aacb2302bf | ||
|
d21403ca1e | ||
|
c39d5433bc | ||
|
6f7963ee08 | ||
|
a8f1f7f017 | ||
|
e401095707 | ||
|
d69cb26157 | ||
|
48a9f5f9db | ||
|
ca9eeb5118 | ||
|
a03009056c | ||
|
554d1ff2b6 | ||
|
0042726477 | ||
|
ed5d87b021 | ||
|
894cd25534 | ||
|
4446e2f760 | ||
|
35d7a3a407 |
84
Jenkinsfile
vendored
84
Jenkinsfile
vendored
@@ -43,7 +43,7 @@ pipeline {
|
|||||||
steps {
|
steps {
|
||||||
script {
|
script {
|
||||||
// Defaults to the Branch name, which is applies to all branches AND pr's
|
// Defaults to the Branch name, which is applies to all branches AND pr's
|
||||||
buildxPushTags = "-t docker.io/jc21/${IMAGE}:github-${BRANCH_LOWER}"
|
buildxPushTags = "-t docker.io/nginxproxymanager/${IMAGE}-dev:${BRANCH_LOWER}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -56,6 +56,13 @@ pipeline {
|
|||||||
sh 'sed -i -E "s/(version-)[0-9]+\\.[0-9]+\\.[0-9]+(-green)/\\1${BUILD_VERSION}\\2/" README.md'
|
sh 'sed -i -E "s/(version-)[0-9]+\\.[0-9]+\\.[0-9]+(-green)/\\1${BUILD_VERSION}\\2/" README.md'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
stage('Docker Login') {
|
||||||
|
steps {
|
||||||
|
withCredentials([usernamePassword(credentialsId: 'jc21-dockerhub', passwordVariable: 'dpass', usernameVariable: 'duser')]) {
|
||||||
|
sh 'docker login -u "${duser}" -p "${dpass}"'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage('Builds') {
|
stage('Builds') {
|
||||||
@@ -120,6 +127,11 @@ pipeline {
|
|||||||
junit 'test/results/junit/*'
|
junit 'test/results/junit/*'
|
||||||
sh 'docker-compose down --remove-orphans --volumes -t 30 || true'
|
sh 'docker-compose down --remove-orphans --volumes -t 30 || true'
|
||||||
}
|
}
|
||||||
|
unstable {
|
||||||
|
dir(path: 'test/results') {
|
||||||
|
archiveArtifacts(allowEmptyArchive: true, artifacts: '**/*', excludes: '**/*.xml')
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage('Test Mysql') {
|
stage('Test Mysql') {
|
||||||
@@ -148,6 +160,49 @@ pipeline {
|
|||||||
junit 'test/results/junit/*'
|
junit 'test/results/junit/*'
|
||||||
sh 'docker-compose down --remove-orphans --volumes -t 30 || true'
|
sh 'docker-compose down --remove-orphans --volumes -t 30 || true'
|
||||||
}
|
}
|
||||||
|
unstable {
|
||||||
|
dir(path: 'test/results') {
|
||||||
|
archiveArtifacts(allowEmptyArchive: true, artifacts: '**/*', excludes: '**/*.xml')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Test Postgres') {
|
||||||
|
environment {
|
||||||
|
COMPOSE_PROJECT_NAME = "npm_${BRANCH_LOWER}_${BUILD_NUMBER}_postgres"
|
||||||
|
COMPOSE_FILE = 'docker/docker-compose.ci.yml:docker/docker-compose.ci.postgres.yml'
|
||||||
|
}
|
||||||
|
when {
|
||||||
|
not {
|
||||||
|
equals expected: 'UNSTABLE', actual: currentBuild.result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
sh 'rm -rf ./test/results/junit/*'
|
||||||
|
sh './scripts/ci/fulltest-cypress'
|
||||||
|
}
|
||||||
|
post {
|
||||||
|
always {
|
||||||
|
// Dumps to analyze later
|
||||||
|
sh 'mkdir -p debug/postgres'
|
||||||
|
sh 'docker logs $(docker-compose ps --all -q fullstack) > debug/postgres/docker_fullstack.log 2>&1'
|
||||||
|
sh 'docker logs $(docker-compose ps --all -q stepca) > debug/postgres/docker_stepca.log 2>&1'
|
||||||
|
sh 'docker logs $(docker-compose ps --all -q pdns) > debug/postgres/docker_pdns.log 2>&1'
|
||||||
|
sh 'docker logs $(docker-compose ps --all -q pdns-db) > debug/postgres/docker_pdns-db.log 2>&1'
|
||||||
|
sh 'docker logs $(docker-compose ps --all -q dnsrouter) > debug/postgres/docker_dnsrouter.log 2>&1'
|
||||||
|
sh 'docker logs $(docker-compose ps --all -q db-postgres) > debug/postgres/docker_db-postgres.log 2>&1'
|
||||||
|
sh 'docker logs $(docker-compose ps --all -q authentik) > debug/postgres/docker_authentik.log 2>&1'
|
||||||
|
sh 'docker logs $(docker-compose ps --all -q authentik-redis) > debug/postgres/docker_authentik-redis.log 2>&1'
|
||||||
|
sh 'docker logs $(docker-compose ps --all -q authentik-ldap) > debug/postgres/docker_authentik-ldap.log 2>&1'
|
||||||
|
|
||||||
|
junit 'test/results/junit/*'
|
||||||
|
sh 'docker-compose down --remove-orphans --volumes -t 30 || true'
|
||||||
|
}
|
||||||
|
unstable {
|
||||||
|
dir(path: 'test/results') {
|
||||||
|
archiveArtifacts(allowEmptyArchive: true, artifacts: '**/*', excludes: '**/*.xml')
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage('MultiArch Build') {
|
stage('MultiArch Build') {
|
||||||
@@ -157,10 +212,7 @@ pipeline {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
steps {
|
steps {
|
||||||
withCredentials([usernamePassword(credentialsId: 'jc21-dockerhub', passwordVariable: 'dpass', usernameVariable: 'duser')]) {
|
sh "./scripts/buildx --push ${buildxPushTags}"
|
||||||
sh 'docker login -u "${duser}" -p "${dpass}"'
|
|
||||||
sh "./scripts/buildx --push ${buildxPushTags}"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage('Docs / Comment') {
|
stage('Docs / Comment') {
|
||||||
@@ -189,7 +241,18 @@ pipeline {
|
|||||||
}
|
}
|
||||||
steps {
|
steps {
|
||||||
script {
|
script {
|
||||||
npmGithubPrComment("Docker Image for build ${BUILD_NUMBER} is available on [DockerHub](https://cloud.docker.com/repository/docker/jc21/${IMAGE}) as `jc21/${IMAGE}:github-${BRANCH_LOWER}`\n\n**Note:** ensure you backup your NPM instance before testing this PR image! Especially if this PR contains database changes.", true)
|
npmGithubPrComment("""Docker Image for build ${BUILD_NUMBER} is available on [DockerHub](https://cloud.docker.com/repository/docker/nginxproxymanager/${IMAGE}-dev):
|
||||||
|
```
|
||||||
|
nginxproxymanager/${IMAGE}-dev:${BRANCH_LOWER}
|
||||||
|
```
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> Ensure you backup your NPM instance before testing this image! Especially if there are database changes.
|
||||||
|
> This is a different docker image namespace than the official image.
|
||||||
|
|
||||||
|
> [!WARNING]
|
||||||
|
> Changes and additions to DNS Providers require verification by at least 2 members of the community!
|
||||||
|
""", true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -200,20 +263,13 @@ pipeline {
|
|||||||
always {
|
always {
|
||||||
sh 'echo Reverting ownership'
|
sh 'echo Reverting ownership'
|
||||||
sh 'docker run --rm -v "$(pwd):/data" jc21/ci-tools chown -R "$(id -u):$(id -g)" /data'
|
sh 'docker run --rm -v "$(pwd):/data" jc21/ci-tools chown -R "$(id -u):$(id -g)" /data'
|
||||||
}
|
printResult(true)
|
||||||
success {
|
|
||||||
juxtapose event: 'success'
|
|
||||||
sh 'figlet "SUCCESS"'
|
|
||||||
}
|
}
|
||||||
failure {
|
failure {
|
||||||
archiveArtifacts(artifacts: 'debug/**/*.*', allowEmptyArchive: true)
|
archiveArtifacts(artifacts: 'debug/**/*.*', allowEmptyArchive: true)
|
||||||
juxtapose event: 'failure'
|
|
||||||
sh 'figlet "FAILURE"'
|
|
||||||
}
|
}
|
||||||
unstable {
|
unstable {
|
||||||
archiveArtifacts(artifacts: 'debug/**/*.*', allowEmptyArchive: true)
|
archiveArtifacts(artifacts: 'debug/**/*.*', allowEmptyArchive: true)
|
||||||
juxtapose event: 'unstable'
|
|
||||||
sh 'figlet "UNSTABLE"'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="https://nginxproxymanager.com/github.png">
|
<img src="https://nginxproxymanager.com/github.png">
|
||||||
<br><br>
|
<br><br>
|
||||||
<img src="https://img.shields.io/badge/version-2.11.3-green.svg?style=for-the-badge">
|
<img src="https://img.shields.io/badge/version-2.12.6-green.svg?style=for-the-badge">
|
||||||
<a href="https://hub.docker.com/repository/docker/jc21/nginx-proxy-manager">
|
<a href="https://hub.docker.com/repository/docker/jc21/nginx-proxy-manager">
|
||||||
<img src="https://img.shields.io/docker/stars/jc21/nginx-proxy-manager.svg?style=for-the-badge">
|
<img src="https://img.shields.io/docker/stars/jc21/nginx-proxy-manager.svg?style=for-the-badge">
|
||||||
</a>
|
</a>
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"database": {
|
"database": {
|
||||||
"engine": "mysql",
|
"engine": "mysql2",
|
||||||
"host": "db",
|
"host": "db",
|
||||||
"name": "npm",
|
"name": "npm",
|
||||||
"user": "npm",
|
"user": "npm",
|
||||||
|
@@ -3,6 +3,8 @@
|
|||||||
const schema = require('./schema');
|
const schema = require('./schema');
|
||||||
const logger = require('./logger').global;
|
const logger = require('./logger').global;
|
||||||
|
|
||||||
|
const IP_RANGES_FETCH_ENABLED = process.env.IP_RANGES_FETCH_ENABLED !== 'false';
|
||||||
|
|
||||||
async function appStart () {
|
async function appStart () {
|
||||||
const migrate = require('./migrate');
|
const migrate = require('./migrate');
|
||||||
const setup = require('./setup');
|
const setup = require('./setup');
|
||||||
@@ -13,7 +15,16 @@ async function appStart () {
|
|||||||
return migrate.latest()
|
return migrate.latest()
|
||||||
.then(setup)
|
.then(setup)
|
||||||
.then(schema.getCompiledSchema)
|
.then(schema.getCompiledSchema)
|
||||||
.then(internalIpRanges.fetch)
|
.then(() => {
|
||||||
|
if (IP_RANGES_FETCH_ENABLED) {
|
||||||
|
logger.info('IP Ranges fetch is enabled');
|
||||||
|
return internalIpRanges.fetch().catch((err) => {
|
||||||
|
logger.error('IP Ranges fetch failed, continuing anyway:', err.message);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
logger.info('IP Ranges fetch is disabled by environment variable');
|
||||||
|
}
|
||||||
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
internalCertificate.initTimer();
|
internalCertificate.initTimer();
|
||||||
internalIpRanges.initTimer();
|
internalIpRanges.initTimer();
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const fs = require('fs');
|
const fs = require('node:fs');
|
||||||
const batchflow = require('batchflow');
|
const batchflow = require('batchflow');
|
||||||
const logger = require('../logger').access;
|
const logger = require('../logger').access;
|
||||||
const error = require('../lib/error');
|
const error = require('../lib/error');
|
||||||
@@ -38,7 +38,7 @@ const internalAccessList = {
|
|||||||
.then((row) => {
|
.then((row) => {
|
||||||
data.id = row.id;
|
data.id = row.id;
|
||||||
|
|
||||||
let promises = [];
|
const promises = [];
|
||||||
|
|
||||||
// Now add the items
|
// Now add the items
|
||||||
data.items.map((item) => {
|
data.items.map((item) => {
|
||||||
@@ -81,7 +81,7 @@ const internalAccessList = {
|
|||||||
|
|
||||||
return internalAccessList.build(row)
|
return internalAccessList.build(row)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
if (row.proxy_host_count) {
|
if (parseInt(row.proxy_host_count, 10)) {
|
||||||
return internalNginx.bulkGenerateConfigs('proxy_host', row.proxy_hosts);
|
return internalNginx.bulkGenerateConfigs('proxy_host', row.proxy_hosts);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -116,7 +116,7 @@ const internalAccessList = {
|
|||||||
.then((row) => {
|
.then((row) => {
|
||||||
if (row.id !== data.id) {
|
if (row.id !== data.id) {
|
||||||
// Sanity check that something crazy hasn't happened
|
// Sanity check that something crazy hasn't happened
|
||||||
throw new error.InternalValidationError('Access List could not be updated, IDs do not match: ' + row.id + ' !== ' + data.id);
|
throw new error.InternalValidationError(`Access List could not be updated, IDs do not match: ${row.id} !== ${data.id}`);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
@@ -135,10 +135,10 @@ const internalAccessList = {
|
|||||||
.then(() => {
|
.then(() => {
|
||||||
// Check for items and add/update/remove them
|
// Check for items and add/update/remove them
|
||||||
if (typeof data.items !== 'undefined' && data.items) {
|
if (typeof data.items !== 'undefined' && data.items) {
|
||||||
let promises = [];
|
const promises = [];
|
||||||
let items_to_keep = [];
|
const items_to_keep = [];
|
||||||
|
|
||||||
data.items.map(function (item) {
|
data.items.map((item) => {
|
||||||
if (item.password) {
|
if (item.password) {
|
||||||
promises.push(accessListAuthModel
|
promises.push(accessListAuthModel
|
||||||
.query()
|
.query()
|
||||||
@@ -154,7 +154,7 @@ const internalAccessList = {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let query = accessListAuthModel
|
const query = accessListAuthModel
|
||||||
.query()
|
.query()
|
||||||
.delete()
|
.delete()
|
||||||
.where('access_list_id', data.id);
|
.where('access_list_id', data.id);
|
||||||
@@ -175,9 +175,9 @@ const internalAccessList = {
|
|||||||
.then(() => {
|
.then(() => {
|
||||||
// Check for clients and add/update/remove them
|
// Check for clients and add/update/remove them
|
||||||
if (typeof data.clients !== 'undefined' && data.clients) {
|
if (typeof data.clients !== 'undefined' && data.clients) {
|
||||||
let promises = [];
|
const promises = [];
|
||||||
|
|
||||||
data.clients.map(function (client) {
|
data.clients.map((client) => {
|
||||||
if (client.address) {
|
if (client.address) {
|
||||||
promises.push(accessListClientModel
|
promises.push(accessListClientModel
|
||||||
.query()
|
.query()
|
||||||
@@ -190,7 +190,7 @@ const internalAccessList = {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let query = accessListClientModel
|
const query = accessListClientModel
|
||||||
.query()
|
.query()
|
||||||
.delete()
|
.delete()
|
||||||
.where('access_list_id', data.id);
|
.where('access_list_id', data.id);
|
||||||
@@ -223,7 +223,7 @@ const internalAccessList = {
|
|||||||
.then((row) => {
|
.then((row) => {
|
||||||
return internalAccessList.build(row)
|
return internalAccessList.build(row)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
if (row.proxy_host_count) {
|
if (parseInt(row.proxy_host_count, 10)) {
|
||||||
return internalNginx.bulkGenerateConfigs('proxy_host', row.proxy_hosts);
|
return internalNginx.bulkGenerateConfigs('proxy_host', row.proxy_hosts);
|
||||||
}
|
}
|
||||||
}).then(internalNginx.reload)
|
}).then(internalNginx.reload)
|
||||||
@@ -249,12 +249,16 @@ const internalAccessList = {
|
|||||||
|
|
||||||
return access.can('access_lists:get', data.id)
|
return access.can('access_lists:get', data.id)
|
||||||
.then((access_data) => {
|
.then((access_data) => {
|
||||||
let query = accessListModel
|
const query = accessListModel
|
||||||
.query()
|
.query()
|
||||||
.select('access_list.*', accessListModel.raw('COUNT(proxy_host.id) as proxy_host_count'))
|
.select('access_list.*', accessListModel.raw('COUNT(proxy_host.id) as proxy_host_count'))
|
||||||
.joinRaw('LEFT JOIN `proxy_host` ON `proxy_host`.`access_list_id` = `access_list`.`id` AND `proxy_host`.`is_deleted` = 0')
|
.leftJoin('proxy_host', function() {
|
||||||
|
this.on('proxy_host.access_list_id', '=', 'access_list.id')
|
||||||
|
.andOn('proxy_host.is_deleted', '=', 0);
|
||||||
|
})
|
||||||
.where('access_list.is_deleted', 0)
|
.where('access_list.is_deleted', 0)
|
||||||
.andWhere('access_list.id', data.id)
|
.andWhere('access_list.id', data.id)
|
||||||
|
.groupBy('access_list.id')
|
||||||
.allowGraph('[owner,items,clients,proxy_hosts.[certificate,access_list.[clients,items]]]')
|
.allowGraph('[owner,items,clients,proxy_hosts.[certificate,access_list.[clients,items]]]')
|
||||||
.first();
|
.first();
|
||||||
|
|
||||||
@@ -263,7 +267,7 @@ const internalAccessList = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (typeof data.expand !== 'undefined' && data.expand !== null) {
|
if (typeof data.expand !== 'undefined' && data.expand !== null) {
|
||||||
query.withGraphFetched('[' + data.expand.join(', ') + ']');
|
query.withGraphFetched(`[${data.expand.join(', ')}]`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return query.then(utils.omitRow(omissions()));
|
return query.then(utils.omitRow(omissions()));
|
||||||
@@ -323,7 +327,7 @@ const internalAccessList = {
|
|||||||
// 3. reconfigure those hosts, then reload nginx
|
// 3. reconfigure those hosts, then reload nginx
|
||||||
|
|
||||||
// set the access_list_id to zero for these items
|
// set the access_list_id to zero for these items
|
||||||
row.proxy_hosts.map(function (val, idx) {
|
row.proxy_hosts.map((_val, idx) => {
|
||||||
row.proxy_hosts[idx].access_list_id = 0;
|
row.proxy_hosts[idx].access_list_id = 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -336,11 +340,11 @@ const internalAccessList = {
|
|||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// delete the htpasswd file
|
// delete the htpasswd file
|
||||||
let htpasswd_file = internalAccessList.getFilename(row);
|
const htpasswd_file = internalAccessList.getFilename(row);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
fs.unlinkSync(htpasswd_file);
|
fs.unlinkSync(htpasswd_file);
|
||||||
} catch (err) {
|
} catch (_err) {
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -370,10 +374,13 @@ const internalAccessList = {
|
|||||||
getAll: (access, expand, search_query) => {
|
getAll: (access, expand, search_query) => {
|
||||||
return access.can('access_lists:list')
|
return access.can('access_lists:list')
|
||||||
.then((access_data) => {
|
.then((access_data) => {
|
||||||
let query = accessListModel
|
const query = accessListModel
|
||||||
.query()
|
.query()
|
||||||
.select('access_list.*', accessListModel.raw('COUNT(proxy_host.id) as proxy_host_count'))
|
.select('access_list.*', accessListModel.raw('COUNT(proxy_host.id) as proxy_host_count'))
|
||||||
.joinRaw('LEFT JOIN `proxy_host` ON `proxy_host`.`access_list_id` = `access_list`.`id` AND `proxy_host`.`is_deleted` = 0')
|
.leftJoin('proxy_host', function() {
|
||||||
|
this.on('proxy_host.access_list_id', '=', 'access_list.id')
|
||||||
|
.andOn('proxy_host.is_deleted', '=', 0);
|
||||||
|
})
|
||||||
.where('access_list.is_deleted', 0)
|
.where('access_list.is_deleted', 0)
|
||||||
.groupBy('access_list.id')
|
.groupBy('access_list.id')
|
||||||
.allowGraph('[owner,items,clients]')
|
.allowGraph('[owner,items,clients]')
|
||||||
@@ -386,19 +393,19 @@ const internalAccessList = {
|
|||||||
// Query is used for searching
|
// Query is used for searching
|
||||||
if (typeof search_query === 'string') {
|
if (typeof search_query === 'string') {
|
||||||
query.where(function () {
|
query.where(function () {
|
||||||
this.where('name', 'like', '%' + search_query + '%');
|
this.where('name', 'like', `%${search_query}%`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof expand !== 'undefined' && expand !== null) {
|
if (typeof expand !== 'undefined' && expand !== null) {
|
||||||
query.withGraphFetched('[' + expand.join(', ') + ']');
|
query.withGraphFetched(`[${expand.join(', ')}]`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return query.then(utils.omitRows(omissions()));
|
return query.then(utils.omitRows(omissions()));
|
||||||
})
|
})
|
||||||
.then((rows) => {
|
.then((rows) => {
|
||||||
if (rows) {
|
if (rows) {
|
||||||
rows.map(function (row, idx) {
|
rows.map((row, idx) => {
|
||||||
if (typeof row.items !== 'undefined' && row.items) {
|
if (typeof row.items !== 'undefined' && row.items) {
|
||||||
rows[idx] = internalAccessList.maskItems(row);
|
rows[idx] = internalAccessList.maskItems(row);
|
||||||
}
|
}
|
||||||
@@ -417,7 +424,7 @@ const internalAccessList = {
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
getCount: (user_id, visibility) => {
|
getCount: (user_id, visibility) => {
|
||||||
let query = accessListModel
|
const query = accessListModel
|
||||||
.query()
|
.query()
|
||||||
.count('id as count')
|
.count('id as count')
|
||||||
.where('is_deleted', 0);
|
.where('is_deleted', 0);
|
||||||
@@ -438,7 +445,7 @@ const internalAccessList = {
|
|||||||
*/
|
*/
|
||||||
maskItems: (list) => {
|
maskItems: (list) => {
|
||||||
if (list && typeof list.items !== 'undefined') {
|
if (list && typeof list.items !== 'undefined') {
|
||||||
list.items.map(function (val, idx) {
|
list.items.map((val, idx) => {
|
||||||
let repeat_for = 8;
|
let repeat_for = 8;
|
||||||
let first_char = '*';
|
let first_char = '*';
|
||||||
|
|
||||||
@@ -461,7 +468,7 @@ const internalAccessList = {
|
|||||||
* @returns {String}
|
* @returns {String}
|
||||||
*/
|
*/
|
||||||
getFilename: (list) => {
|
getFilename: (list) => {
|
||||||
return '/data/access/' + list.id;
|
return `/data/access/${list.id}`;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -472,15 +479,15 @@ const internalAccessList = {
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
build: (list) => {
|
build: (list) => {
|
||||||
logger.info('Building Access file #' + list.id + ' for: ' + list.name);
|
logger.info(`Building Access file #${list.id} for: ${list.name}`);
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let htpasswd_file = internalAccessList.getFilename(list);
|
const htpasswd_file = internalAccessList.getFilename(list);
|
||||||
|
|
||||||
// 1. remove any existing access file
|
// 1. remove any existing access file
|
||||||
try {
|
try {
|
||||||
fs.unlinkSync(htpasswd_file);
|
fs.unlinkSync(htpasswd_file);
|
||||||
} catch (err) {
|
} catch (_err) {
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -497,12 +504,17 @@ const internalAccessList = {
|
|||||||
if (list.items.length) {
|
if (list.items.length) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
batchflow(list.items).sequential()
|
batchflow(list.items).sequential()
|
||||||
.each((i, item, next) => {
|
.each((_i, item, next) => {
|
||||||
if (typeof item.password !== 'undefined' && item.password.length) {
|
if (typeof item.password !== 'undefined' && item.password.length) {
|
||||||
logger.info('Adding: ' + item.username);
|
logger.info(`Adding: ${item.username}`);
|
||||||
|
|
||||||
utils.execFile('/usr/bin/htpasswd', ['-b', htpasswd_file, item.username, item.password])
|
utils.execFile('openssl', ['passwd', '-apr1', item.password])
|
||||||
.then((/*result*/) => {
|
.then((res) => {
|
||||||
|
try {
|
||||||
|
fs.appendFileSync(htpasswd_file, `${item.username}:${res}\n`, {encoding: 'utf8'});
|
||||||
|
} catch (err) {
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
next();
|
next();
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
@@ -516,7 +528,7 @@ const internalAccessList = {
|
|||||||
reject(err);
|
reject(err);
|
||||||
})
|
})
|
||||||
.end((results) => {
|
.end((results) => {
|
||||||
logger.success('Built Access file #' + list.id + ' for: ' + list.name);
|
logger.success(`Built Access file #${list.id} for: ${list.name}`);
|
||||||
resolve(results);
|
resolve(results);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
const error = require('../lib/error');
|
const error = require('../lib/error');
|
||||||
const auditLogModel = require('../models/audit-log');
|
const auditLogModel = require('../models/audit-log');
|
||||||
|
const {castJsonIfNeed} = require('../lib/helpers');
|
||||||
|
|
||||||
const internalAuditLog = {
|
const internalAuditLog = {
|
||||||
|
|
||||||
@@ -22,9 +23,9 @@ const internalAuditLog = {
|
|||||||
.allowGraph('[user]');
|
.allowGraph('[user]');
|
||||||
|
|
||||||
// Query is used for searching
|
// Query is used for searching
|
||||||
if (typeof search_query === 'string') {
|
if (typeof search_query === 'string' && search_query.length > 0) {
|
||||||
query.where(function () {
|
query.where(function () {
|
||||||
this.where('meta', 'like', '%' + search_query + '%');
|
this.where(castJsonIfNeed('meta'), 'like', '%' + search_query + '%');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,29 +1,31 @@
|
|||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const fs = require('fs');
|
const fs = require('node:fs');
|
||||||
const https = require('https');
|
const https = require('node:https');
|
||||||
const tempWrite = require('temp-write');
|
const tempWrite = require('temp-write');
|
||||||
const moment = require('moment');
|
const moment = require('moment');
|
||||||
|
const archiver = require('archiver');
|
||||||
|
const path = require('path');
|
||||||
|
const { isArray } = require('lodash');
|
||||||
const logger = require('../logger').ssl;
|
const logger = require('../logger').ssl;
|
||||||
const config = require('../lib/config');
|
const config = require('../lib/config');
|
||||||
const error = require('../lib/error');
|
const error = require('../lib/error');
|
||||||
const utils = require('../lib/utils');
|
const utils = require('../lib/utils');
|
||||||
|
const certbot = require('../lib/certbot');
|
||||||
const certificateModel = require('../models/certificate');
|
const certificateModel = require('../models/certificate');
|
||||||
const tokenModel = require('../models/token');
|
const tokenModel = require('../models/token');
|
||||||
const dnsPlugins = require('../global/certbot-dns-plugins.json');
|
const dnsPlugins = require('../global/certbot-dns-plugins.json');
|
||||||
const internalAuditLog = require('./audit-log');
|
const internalAuditLog = require('./audit-log');
|
||||||
const internalNginx = require('./nginx');
|
const internalNginx = require('./nginx');
|
||||||
const internalHost = require('./host');
|
const internalHost = require('./host');
|
||||||
const certbot = require('../lib/certbot');
|
|
||||||
const archiver = require('archiver');
|
|
||||||
const path = require('path');
|
|
||||||
const { isArray } = require('lodash');
|
|
||||||
|
|
||||||
const letsencryptStaging = config.useLetsencryptStaging();
|
const letsencryptStaging = config.useLetsencryptStaging();
|
||||||
|
const letsencryptServer = config.useLetsencryptServer();
|
||||||
const letsencryptConfig = '/etc/letsencrypt.ini';
|
const letsencryptConfig = '/etc/letsencrypt.ini';
|
||||||
const certbotCommand = 'certbot';
|
const certbotCommand = 'certbot';
|
||||||
|
|
||||||
function omissions() {
|
function omissions() {
|
||||||
return ['is_deleted'];
|
return ['is_deleted', 'owner.is_deleted'];
|
||||||
}
|
}
|
||||||
|
|
||||||
const internalCertificate = {
|
const internalCertificate = {
|
||||||
@@ -47,7 +49,7 @@ const internalCertificate = {
|
|||||||
processExpiringHosts: () => {
|
processExpiringHosts: () => {
|
||||||
if (!internalCertificate.intervalProcessing) {
|
if (!internalCertificate.intervalProcessing) {
|
||||||
internalCertificate.intervalProcessing = true;
|
internalCertificate.intervalProcessing = true;
|
||||||
logger.info('Renewing SSL certs expiring within ' + internalCertificate.renewBeforeExpirationBy[0] + ' ' + internalCertificate.renewBeforeExpirationBy[1] + ' ...');
|
logger.info(`Renewing SSL certs expiring within ${internalCertificate.renewBeforeExpirationBy[0]} ${internalCertificate.renewBeforeExpirationBy[1]} ...`);
|
||||||
|
|
||||||
const expirationThreshold = moment().add(internalCertificate.renewBeforeExpirationBy[0], internalCertificate.renewBeforeExpirationBy[1]).format('YYYY-MM-DD HH:mm:ss');
|
const expirationThreshold = moment().add(internalCertificate.renewBeforeExpirationBy[0], internalCertificate.renewBeforeExpirationBy[1]).format('YYYY-MM-DD HH:mm:ss');
|
||||||
|
|
||||||
@@ -68,7 +70,7 @@ const internalCertificate = {
|
|||||||
*/
|
*/
|
||||||
let sequence = Promise.resolve();
|
let sequence = Promise.resolve();
|
||||||
|
|
||||||
certificates.forEach(function (certificate) {
|
certificates.forEach((certificate) => {
|
||||||
sequence = sequence.then(() =>
|
sequence = sequence.then(() =>
|
||||||
internalCertificate
|
internalCertificate
|
||||||
.renew(
|
.renew(
|
||||||
@@ -200,13 +202,14 @@ const internalCertificate = {
|
|||||||
.then(() => {
|
.then(() => {
|
||||||
// At this point, the letsencrypt cert should exist on disk.
|
// At this point, the letsencrypt cert should exist on disk.
|
||||||
// Lets get the expiry date from the file and update the row silently
|
// Lets get the expiry date from the file and update the row silently
|
||||||
return internalCertificate.getCertificateInfoFromFile('/etc/letsencrypt/live/npm-' + certificate.id + '/fullchain.pem')
|
return internalCertificate.getCertificateInfoFromFile(`${internalCertificate.getLiveCertPath(certificate.id)}/fullchain.pem`)
|
||||||
.then((cert_info) => {
|
.then((cert_info) => {
|
||||||
return certificateModel
|
return certificateModel
|
||||||
.query()
|
.query()
|
||||||
.patchAndFetchById(certificate.id, {
|
.patchAndFetchById(certificate.id, {
|
||||||
expires_on: moment(cert_info.dates.to, 'X').format('YYYY-MM-DD HH:mm:ss')
|
expires_on: moment(cert_info.dates.to, 'X').format('YYYY-MM-DD HH:mm:ss')
|
||||||
})
|
})
|
||||||
|
.then(utils.omitRow(omissions()))
|
||||||
.then((saved_row) => {
|
.then((saved_row) => {
|
||||||
// Add cert data for audit log
|
// Add cert data for audit log
|
||||||
saved_row.meta = _.assign({}, saved_row.meta, {
|
saved_row.meta = _.assign({}, saved_row.meta, {
|
||||||
@@ -260,7 +263,7 @@ const internalCertificate = {
|
|||||||
.then((row) => {
|
.then((row) => {
|
||||||
if (row.id !== data.id) {
|
if (row.id !== data.id) {
|
||||||
// Sanity check that something crazy hasn't happened
|
// Sanity check that something crazy hasn't happened
|
||||||
throw new error.InternalValidationError('Certificate could not be updated, IDs do not match: ' + row.id + ' !== ' + data.id);
|
throw new error.InternalValidationError(`Certificate could not be updated, IDs do not match: ${row.id} !== ${data.id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return certificateModel
|
return certificateModel
|
||||||
@@ -305,11 +308,14 @@ const internalCertificate = {
|
|||||||
|
|
||||||
return access.can('certificates:get', data.id)
|
return access.can('certificates:get', data.id)
|
||||||
.then((access_data) => {
|
.then((access_data) => {
|
||||||
let query = certificateModel
|
const query = certificateModel
|
||||||
.query()
|
.query()
|
||||||
.where('is_deleted', 0)
|
.where('is_deleted', 0)
|
||||||
.andWhere('id', data.id)
|
.andWhere('id', data.id)
|
||||||
.allowGraph('[owner]')
|
.allowGraph('[owner]')
|
||||||
|
.allowGraph('[proxy_hosts]')
|
||||||
|
.allowGraph('[redirection_hosts]')
|
||||||
|
.allowGraph('[dead_hosts]')
|
||||||
.first();
|
.first();
|
||||||
|
|
||||||
if (access_data.permission_visibility !== 'all') {
|
if (access_data.permission_visibility !== 'all') {
|
||||||
@@ -317,7 +323,7 @@ const internalCertificate = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (typeof data.expand !== 'undefined' && data.expand !== null) {
|
if (typeof data.expand !== 'undefined' && data.expand !== null) {
|
||||||
query.withGraphFetched('[' + data.expand.join(', ') + ']');
|
query.withGraphFetched(`[${data.expand.join(', ')}]`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return query.then(utils.omitRow(omissions()));
|
return query.then(utils.omitRow(omissions()));
|
||||||
@@ -348,17 +354,17 @@ const internalCertificate = {
|
|||||||
})
|
})
|
||||||
.then((certificate) => {
|
.then((certificate) => {
|
||||||
if (certificate.provider === 'letsencrypt') {
|
if (certificate.provider === 'letsencrypt') {
|
||||||
const zipDirectory = '/etc/letsencrypt/live/npm-' + data.id;
|
const zipDirectory = internalCertificate.getLiveCertPath(data.id);
|
||||||
|
|
||||||
if (!fs.existsSync(zipDirectory)) {
|
if (!fs.existsSync(zipDirectory)) {
|
||||||
throw new error.ItemNotFoundError('Certificate ' + certificate.nice_name + ' does not exists');
|
throw new error.ItemNotFoundError(`Certificate ${certificate.nice_name} does not exists`);
|
||||||
}
|
}
|
||||||
|
|
||||||
let certFiles = fs.readdirSync(zipDirectory)
|
const certFiles = fs.readdirSync(zipDirectory)
|
||||||
.filter((fn) => fn.endsWith('.pem'))
|
.filter((fn) => fn.endsWith('.pem'))
|
||||||
.map((fn) => fs.realpathSync(path.join(zipDirectory, fn)));
|
.map((fn) => fs.realpathSync(path.join(zipDirectory, fn)));
|
||||||
const downloadName = 'npm-' + data.id + '-' + `${Date.now()}.zip`;
|
const downloadName = `npm-${data.id}-${Date.now()}.zip`;
|
||||||
const opName = '/tmp/' + downloadName;
|
const opName = `/tmp/${downloadName}`;
|
||||||
internalCertificate.zipFiles(certFiles, opName)
|
internalCertificate.zipFiles(certFiles, opName)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
logger.debug('zip completed : ', opName);
|
logger.debug('zip completed : ', opName);
|
||||||
@@ -386,7 +392,7 @@ const internalCertificate = {
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
source
|
source
|
||||||
.map((fl) => {
|
.map((fl) => {
|
||||||
let fileName = path.basename(fl);
|
const fileName = path.basename(fl);
|
||||||
logger.debug(fl, 'added to certificate zip');
|
logger.debug(fl, 'added to certificate zip');
|
||||||
archive.file(fl, { name: fileName });
|
archive.file(fl, { name: fileName });
|
||||||
});
|
});
|
||||||
@@ -456,11 +462,14 @@ const internalCertificate = {
|
|||||||
getAll: (access, expand, search_query) => {
|
getAll: (access, expand, search_query) => {
|
||||||
return access.can('certificates:list')
|
return access.can('certificates:list')
|
||||||
.then((access_data) => {
|
.then((access_data) => {
|
||||||
let query = certificateModel
|
const query = certificateModel
|
||||||
.query()
|
.query()
|
||||||
.where('is_deleted', 0)
|
.where('is_deleted', 0)
|
||||||
.groupBy('id')
|
.groupBy('id')
|
||||||
.allowGraph('[owner]')
|
.allowGraph('[owner]')
|
||||||
|
.allowGraph('[proxy_hosts]')
|
||||||
|
.allowGraph('[redirection_hosts]')
|
||||||
|
.allowGraph('[dead_hosts]')
|
||||||
.orderBy('nice_name', 'ASC');
|
.orderBy('nice_name', 'ASC');
|
||||||
|
|
||||||
if (access_data.permission_visibility !== 'all') {
|
if (access_data.permission_visibility !== 'all') {
|
||||||
@@ -470,12 +479,12 @@ const internalCertificate = {
|
|||||||
// Query is used for searching
|
// Query is used for searching
|
||||||
if (typeof search_query === 'string') {
|
if (typeof search_query === 'string') {
|
||||||
query.where(function () {
|
query.where(function () {
|
||||||
this.where('nice_name', 'like', '%' + search_query + '%');
|
this.where('nice_name', 'like', `%${search_query}%`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof expand !== 'undefined' && expand !== null) {
|
if (typeof expand !== 'undefined' && expand !== null) {
|
||||||
query.withGraphFetched('[' + expand.join(', ') + ']');
|
query.withGraphFetched(`[${expand.join(', ')}]`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return query.then(utils.omitRows(omissions()));
|
return query.then(utils.omitRows(omissions()));
|
||||||
@@ -490,7 +499,7 @@ const internalCertificate = {
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
getCount: (user_id, visibility) => {
|
getCount: (user_id, visibility) => {
|
||||||
let query = certificateModel
|
const query = certificateModel
|
||||||
.query()
|
.query()
|
||||||
.count('id as count')
|
.count('id as count')
|
||||||
.where('is_deleted', 0);
|
.where('is_deleted', 0);
|
||||||
@@ -512,7 +521,7 @@ const internalCertificate = {
|
|||||||
writeCustomCert: (certificate) => {
|
writeCustomCert: (certificate) => {
|
||||||
logger.info('Writing Custom Certificate:', certificate);
|
logger.info('Writing Custom Certificate:', certificate);
|
||||||
|
|
||||||
const dir = '/data/custom_ssl/npm-' + certificate.id;
|
const dir = `/data/custom_ssl/npm-${certificate.id}`;
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (certificate.provider === 'letsencrypt') {
|
if (certificate.provider === 'letsencrypt') {
|
||||||
@@ -522,7 +531,7 @@ const internalCertificate = {
|
|||||||
|
|
||||||
let certData = certificate.meta.certificate;
|
let certData = certificate.meta.certificate;
|
||||||
if (typeof certificate.meta.intermediate_certificate !== 'undefined') {
|
if (typeof certificate.meta.intermediate_certificate !== 'undefined') {
|
||||||
certData = certData + '\n' + certificate.meta.intermediate_certificate;
|
certData = `${certData}\n${certificate.meta.intermediate_certificate}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -534,7 +543,7 @@ const internalCertificate = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fs.writeFile(dir + '/fullchain.pem', certData, function (err) {
|
fs.writeFile(`${dir}/fullchain.pem`, certData, (err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
} else {
|
} else {
|
||||||
@@ -544,7 +553,7 @@ const internalCertificate = {
|
|||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
fs.writeFile(dir + '/privkey.pem', certificate.meta.certificate_key, function (err) {
|
fs.writeFile(`${dir}/privkey.pem`, certificate.meta.certificate_key, (err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
} else {
|
} else {
|
||||||
@@ -582,7 +591,7 @@ const internalCertificate = {
|
|||||||
validate: (data) => {
|
validate: (data) => {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
// Put file contents into an object
|
// Put file contents into an object
|
||||||
let files = {};
|
const files = {};
|
||||||
_.map(data.files, (file, name) => {
|
_.map(data.files, (file, name) => {
|
||||||
if (internalCertificate.allowedSslFiles.indexOf(name) !== -1) {
|
if (internalCertificate.allowedSslFiles.indexOf(name) !== -1) {
|
||||||
files[name] = file.data.toString();
|
files[name] = file.data.toString();
|
||||||
@@ -594,7 +603,7 @@ const internalCertificate = {
|
|||||||
.then((files) => {
|
.then((files) => {
|
||||||
// For each file, create a temp file and write the contents to it
|
// For each file, create a temp file and write the contents to it
|
||||||
// Then test it depending on the file type
|
// Then test it depending on the file type
|
||||||
let promises = [];
|
const promises = [];
|
||||||
_.map(files, (content, type) => {
|
_.map(files, (content, type) => {
|
||||||
promises.push(new Promise((resolve) => {
|
promises.push(new Promise((resolve) => {
|
||||||
if (type === 'certificate_key') {
|
if (type === 'certificate_key') {
|
||||||
@@ -679,11 +688,11 @@ const internalCertificate = {
|
|||||||
reject(new error.ValidationError('Result Validation Error: Validation timed out. This could be due to the key being passphrase-protected.'));
|
reject(new error.ValidationError('Result Validation Error: Validation timed out. This could be due to the key being passphrase-protected.'));
|
||||||
}, 10000);
|
}, 10000);
|
||||||
utils
|
utils
|
||||||
.exec('openssl pkey -in ' + filepath + ' -check -noout 2>&1 ')
|
.exec(`openssl pkey -in ${filepath} -check -noout 2>&1 `)
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
clearTimeout(failTimeout);
|
clearTimeout(failTimeout);
|
||||||
if (!result.toLowerCase().includes('key is valid')) {
|
if (!result.toLowerCase().includes('key is valid')) {
|
||||||
reject(new error.ValidationError('Result Validation Error: ' + result));
|
reject(new error.ValidationError(`Result Validation Error: ${result}`));
|
||||||
}
|
}
|
||||||
fs.unlinkSync(filepath);
|
fs.unlinkSync(filepath);
|
||||||
resolve(true);
|
resolve(true);
|
||||||
@@ -691,7 +700,7 @@ const internalCertificate = {
|
|||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
clearTimeout(failTimeout);
|
clearTimeout(failTimeout);
|
||||||
fs.unlinkSync(filepath);
|
fs.unlinkSync(filepath);
|
||||||
reject(new error.ValidationError('Certificate Key is not valid (' + err.message + ')', err));
|
reject(new error.ValidationError(`Certificate Key is not valid (${err.message})`, err));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -726,36 +735,36 @@ const internalCertificate = {
|
|||||||
* @param {Boolean} [throw_expired] Throw when the certificate is out of date
|
* @param {Boolean} [throw_expired] Throw when the certificate is out of date
|
||||||
*/
|
*/
|
||||||
getCertificateInfoFromFile: (certificate_file, throw_expired) => {
|
getCertificateInfoFromFile: (certificate_file, throw_expired) => {
|
||||||
let certData = {};
|
const certData = {};
|
||||||
|
|
||||||
return utils.exec('openssl x509 -in ' + certificate_file + ' -subject -noout')
|
return utils.execFile('openssl', ['x509', '-in', certificate_file, '-subject', '-noout'])
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
|
// Examples:
|
||||||
|
// subject=CN = *.jc21.com
|
||||||
// subject=CN = something.example.com
|
// subject=CN = something.example.com
|
||||||
const regex = /(?:subject=)?[^=]+=\s+(\S+)/gim;
|
const regex = /(?:subject=)?[^=]+=\s+(\S+)/gim;
|
||||||
const match = regex.exec(result);
|
const match = regex.exec(result);
|
||||||
|
if (match && typeof match[1] !== 'undefined') {
|
||||||
if (typeof match[1] === 'undefined') {
|
certData.cn = match[1];
|
||||||
throw new error.ValidationError('Could not determine subject from certificate: ' + result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
certData['cn'] = match[1];
|
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return utils.exec('openssl x509 -in ' + certificate_file + ' -issuer -noout');
|
return utils.execFile('openssl', ['x509', '-in', certificate_file, '-issuer', '-noout']);
|
||||||
})
|
})
|
||||||
|
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
|
// Examples:
|
||||||
// issuer=C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
|
// issuer=C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
|
||||||
|
// issuer=C = US, O = Let's Encrypt, CN = E5
|
||||||
|
// issuer=O = NginxProxyManager, CN = NginxProxyManager Intermediate CA","O = NginxProxyManager, CN = NginxProxyManager Intermediate CA
|
||||||
const regex = /^(?:issuer=)?(.*)$/gim;
|
const regex = /^(?:issuer=)?(.*)$/gim;
|
||||||
const match = regex.exec(result);
|
const match = regex.exec(result);
|
||||||
|
if (match && typeof match[1] !== 'undefined') {
|
||||||
if (typeof match[1] === 'undefined') {
|
certData.issuer = match[1];
|
||||||
throw new error.ValidationError('Could not determine issuer from certificate: ' + result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
certData['issuer'] = match[1];
|
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return utils.exec('openssl x509 -in ' + certificate_file + ' -dates -noout');
|
return utils.execFile('openssl', ['x509', '-in', certificate_file, '-dates', '-noout']);
|
||||||
})
|
})
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
// notBefore=Jul 14 04:04:29 2018 GMT
|
// notBefore=Jul 14 04:04:29 2018 GMT
|
||||||
@@ -764,7 +773,7 @@ const internalCertificate = {
|
|||||||
let validTo = null;
|
let validTo = null;
|
||||||
|
|
||||||
const lines = result.split('\n');
|
const lines = result.split('\n');
|
||||||
lines.map(function (str) {
|
lines.map((str) => {
|
||||||
const regex = /^(\S+)=(.*)$/gim;
|
const regex = /^(\S+)=(.*)$/gim;
|
||||||
const match = regex.exec(str.trim());
|
const match = regex.exec(str.trim());
|
||||||
|
|
||||||
@@ -780,21 +789,21 @@ const internalCertificate = {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!validFrom || !validTo) {
|
if (!validFrom || !validTo) {
|
||||||
throw new error.ValidationError('Could not determine dates from certificate: ' + result);
|
throw new error.ValidationError(`Could not determine dates from certificate: ${result}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (throw_expired && validTo < parseInt(moment().format('X'), 10)) {
|
if (throw_expired && validTo < parseInt(moment().format('X'), 10)) {
|
||||||
throw new error.ValidationError('Certificate has expired');
|
throw new error.ValidationError('Certificate has expired');
|
||||||
}
|
}
|
||||||
|
|
||||||
certData['dates'] = {
|
certData.dates = {
|
||||||
from: validFrom,
|
from: validFrom,
|
||||||
to: validTo
|
to: validTo
|
||||||
};
|
};
|
||||||
|
|
||||||
return certData;
|
return certData;
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
throw new error.ValidationError('Certificate is not valid (' + err.message + ')', err);
|
throw new error.ValidationError(`Certificate is not valid (${err.message})`, err);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -805,7 +814,7 @@ const internalCertificate = {
|
|||||||
* @param {Boolean} [remove]
|
* @param {Boolean} [remove]
|
||||||
* @returns {Object}
|
* @returns {Object}
|
||||||
*/
|
*/
|
||||||
cleanMeta: function (meta, remove) {
|
cleanMeta: (meta, remove) => {
|
||||||
internalCertificate.allowedSslFiles.map((key) => {
|
internalCertificate.allowedSslFiles.map((key) => {
|
||||||
if (typeof meta[key] !== 'undefined' && meta[key]) {
|
if (typeof meta[key] !== 'undefined' && meta[key]) {
|
||||||
if (remove) {
|
if (remove) {
|
||||||
@@ -825,23 +834,35 @@ const internalCertificate = {
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
requestLetsEncryptSsl: (certificate) => {
|
requestLetsEncryptSsl: (certificate) => {
|
||||||
logger.info('Requesting Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
|
logger.info(`Requesting LetsEncrypt certificates for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);
|
||||||
|
|
||||||
const cmd = certbotCommand + ' certonly ' +
|
const args = [
|
||||||
'--config "' + letsencryptConfig + '" ' +
|
'certonly',
|
||||||
'--work-dir "/tmp/letsencrypt-lib" ' +
|
'--config',
|
||||||
'--logs-dir "/tmp/letsencrypt-log" ' +
|
letsencryptConfig,
|
||||||
'--cert-name "npm-' + certificate.id + '" ' +
|
'--work-dir',
|
||||||
'--agree-tos ' +
|
'/tmp/letsencrypt-lib',
|
||||||
'--authenticator webroot ' +
|
'--logs-dir',
|
||||||
'--email "' + certificate.meta.letsencrypt_email + '" ' +
|
'/tmp/letsencrypt-log',
|
||||||
'--preferred-challenges "dns,http" ' +
|
'--cert-name',
|
||||||
'--domains "' + certificate.domain_names.join(',') + '" ' +
|
`npm-${certificate.id}`,
|
||||||
(letsencryptStaging ? '--staging' : '');
|
'--agree-tos',
|
||||||
|
'--authenticator',
|
||||||
|
'webroot',
|
||||||
|
'--email',
|
||||||
|
certificate.meta.letsencrypt_email,
|
||||||
|
'--preferred-challenges',
|
||||||
|
'dns,http',
|
||||||
|
'--domains',
|
||||||
|
certificate.domain_names.join(','),
|
||||||
|
];
|
||||||
|
|
||||||
logger.info('Command:', cmd);
|
const adds = internalCertificate.getAdditionalCertbotArgs(certificate.id);
|
||||||
|
args.push(...adds.args);
|
||||||
|
|
||||||
return utils.exec(cmd)
|
logger.info(`Command: ${certbotCommand} ${args ? args.join(' ') : ''}`);
|
||||||
|
|
||||||
|
return utils.execFile(certbotCommand, args, adds.opts)
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
logger.success(result);
|
logger.success(result);
|
||||||
return result;
|
return result;
|
||||||
@@ -858,49 +879,48 @@ const internalCertificate = {
|
|||||||
requestLetsEncryptSslWithDnsChallenge: async (certificate) => {
|
requestLetsEncryptSslWithDnsChallenge: async (certificate) => {
|
||||||
await certbot.installPlugin(certificate.meta.dns_provider);
|
await certbot.installPlugin(certificate.meta.dns_provider);
|
||||||
const dnsPlugin = dnsPlugins[certificate.meta.dns_provider];
|
const dnsPlugin = dnsPlugins[certificate.meta.dns_provider];
|
||||||
logger.info(`Requesting Let'sEncrypt certificates via ${dnsPlugin.name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);
|
logger.info(`Requesting LetsEncrypt certificates via ${dnsPlugin.name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);
|
||||||
|
|
||||||
const credentialsLocation = '/etc/letsencrypt/credentials/credentials-' + certificate.id;
|
const credentialsLocation = `/etc/letsencrypt/credentials/credentials-${certificate.id}`;
|
||||||
fs.mkdirSync('/etc/letsencrypt/credentials', { recursive: true });
|
fs.mkdirSync('/etc/letsencrypt/credentials', { recursive: true });
|
||||||
fs.writeFileSync(credentialsLocation, certificate.meta.dns_provider_credentials, {mode: 0o600});
|
fs.writeFileSync(credentialsLocation, certificate.meta.dns_provider_credentials, {mode: 0o600});
|
||||||
|
|
||||||
// Whether the plugin has a --<name>-credentials argument
|
// Whether the plugin has a --<name>-credentials argument
|
||||||
const hasConfigArg = certificate.meta.dns_provider !== 'route53';
|
const hasConfigArg = certificate.meta.dns_provider !== 'route53';
|
||||||
|
|
||||||
let mainCmd = certbotCommand + ' certonly ' +
|
const args = [
|
||||||
'--config "' + letsencryptConfig + '" ' +
|
'certonly',
|
||||||
'--work-dir "/tmp/letsencrypt-lib" ' +
|
'--config',
|
||||||
'--logs-dir "/tmp/letsencrypt-log" ' +
|
letsencryptConfig,
|
||||||
'--cert-name "npm-' + certificate.id + '" ' +
|
'--work-dir',
|
||||||
'--agree-tos ' +
|
'/tmp/letsencrypt-lib',
|
||||||
'--email "' + certificate.meta.letsencrypt_email + '" ' +
|
'--logs-dir',
|
||||||
'--domains "' + certificate.domain_names.join(',') + '" ' +
|
'/tmp/letsencrypt-log',
|
||||||
'--authenticator ' + dnsPlugin.full_plugin_name + ' ' +
|
'--cert-name',
|
||||||
(
|
`npm-${certificate.id}`,
|
||||||
hasConfigArg
|
'--agree-tos',
|
||||||
? '--' + dnsPlugin.full_plugin_name + '-credentials "' + credentialsLocation + '"'
|
'--email',
|
||||||
: ''
|
certificate.meta.letsencrypt_email,
|
||||||
) +
|
'--domains',
|
||||||
(
|
certificate.domain_names.join(','),
|
||||||
certificate.meta.propagation_seconds !== undefined
|
'--authenticator',
|
||||||
? ' --' + dnsPlugin.full_plugin_name + '-propagation-seconds ' + certificate.meta.propagation_seconds
|
dnsPlugin.full_plugin_name,
|
||||||
: ''
|
];
|
||||||
) +
|
|
||||||
(letsencryptStaging ? ' --staging' : '');
|
|
||||||
|
|
||||||
// Prepend the path to the credentials file as an environment variable
|
if (hasConfigArg) {
|
||||||
if (certificate.meta.dns_provider === 'route53') {
|
args.push(`--${dnsPlugin.full_plugin_name}-credentials`, credentialsLocation);
|
||||||
mainCmd = 'AWS_CONFIG_FILE=\'' + credentialsLocation + '\' ' + mainCmd;
|
}
|
||||||
|
if (certificate.meta.propagation_seconds !== undefined) {
|
||||||
|
args.push(`--${dnsPlugin.full_plugin_name}-propagation-seconds`, certificate.meta.propagation_seconds.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (certificate.meta.dns_provider === 'duckdns') {
|
const adds = internalCertificate.getAdditionalCertbotArgs(certificate.id, certificate.meta.dns_provider);
|
||||||
mainCmd = mainCmd + ' --dns-duckdns-no-txt-restore';
|
args.push(...adds.args);
|
||||||
}
|
|
||||||
|
|
||||||
logger.info('Command:', mainCmd);
|
logger.info(`Command: ${certbotCommand} ${args ? args.join(' ') : ''}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await utils.exec(mainCmd);
|
const result = await utils.execFile(certbotCommand, args, adds.opts);
|
||||||
logger.info(result);
|
logger.info(result);
|
||||||
return result;
|
return result;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -928,7 +948,7 @@ const internalCertificate = {
|
|||||||
|
|
||||||
return renewMethod(certificate)
|
return renewMethod(certificate)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return internalCertificate.getCertificateInfoFromFile('/etc/letsencrypt/live/npm-' + certificate.id + '/fullchain.pem');
|
return internalCertificate.getCertificateInfoFromFile(`${internalCertificate.getLiveCertPath(certificate.id)}/fullchain.pem`);
|
||||||
})
|
})
|
||||||
.then((cert_info) => {
|
.then((cert_info) => {
|
||||||
return certificateModel
|
return certificateModel
|
||||||
@@ -960,21 +980,31 @@ const internalCertificate = {
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
renewLetsEncryptSsl: (certificate) => {
|
renewLetsEncryptSsl: (certificate) => {
|
||||||
logger.info('Renewing Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
|
logger.info(`Renewing LetsEncrypt certificates for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);
|
||||||
|
|
||||||
const cmd = certbotCommand + ' renew --force-renewal ' +
|
const args = [
|
||||||
'--config "' + letsencryptConfig + '" ' +
|
'renew',
|
||||||
'--work-dir "/tmp/letsencrypt-lib" ' +
|
'--force-renewal',
|
||||||
'--logs-dir "/tmp/letsencrypt-log" ' +
|
'--config',
|
||||||
'--cert-name "npm-' + certificate.id + '" ' +
|
letsencryptConfig,
|
||||||
'--preferred-challenges "dns,http" ' +
|
'--work-dir',
|
||||||
'--no-random-sleep-on-renew ' +
|
'/tmp/letsencrypt-lib',
|
||||||
'--disable-hook-validation ' +
|
'--logs-dir',
|
||||||
(letsencryptStaging ? '--staging' : '');
|
'/tmp/letsencrypt-log',
|
||||||
|
'--cert-name',
|
||||||
|
`npm-${certificate.id}`,
|
||||||
|
'--preferred-challenges',
|
||||||
|
'dns,http',
|
||||||
|
'--no-random-sleep-on-renew',
|
||||||
|
'--disable-hook-validation',
|
||||||
|
];
|
||||||
|
|
||||||
logger.info('Command:', cmd);
|
const adds = internalCertificate.getAdditionalCertbotArgs(certificate.id, certificate.meta.dns_provider);
|
||||||
|
args.push(...adds.args);
|
||||||
|
|
||||||
return utils.exec(cmd)
|
logger.info(`Command: ${certbotCommand} ${args ? args.join(' ') : ''}`);
|
||||||
|
|
||||||
|
return utils.execFile(certbotCommand, args, adds.opts)
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
logger.info(result);
|
logger.info(result);
|
||||||
return result;
|
return result;
|
||||||
@@ -992,26 +1022,29 @@ const internalCertificate = {
|
|||||||
throw Error(`Unknown DNS provider '${certificate.meta.dns_provider}'`);
|
throw Error(`Unknown DNS provider '${certificate.meta.dns_provider}'`);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info(`Renewing Let'sEncrypt certificates via ${dnsPlugin.name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);
|
logger.info(`Renewing LetsEncrypt certificates via ${dnsPlugin.name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);
|
||||||
|
|
||||||
let mainCmd = certbotCommand + ' renew --force-renewal ' +
|
const args = [
|
||||||
'--config "' + letsencryptConfig + '" ' +
|
'renew',
|
||||||
'--work-dir "/tmp/letsencrypt-lib" ' +
|
'--force-renewal',
|
||||||
'--logs-dir "/tmp/letsencrypt-log" ' +
|
'--config',
|
||||||
'--cert-name "npm-' + certificate.id + '" ' +
|
letsencryptConfig,
|
||||||
'--disable-hook-validation ' +
|
'--work-dir',
|
||||||
'--no-random-sleep-on-renew ' +
|
'/tmp/letsencrypt-lib',
|
||||||
(letsencryptStaging ? ' --staging' : '');
|
'--logs-dir',
|
||||||
|
'/tmp/letsencrypt-log',
|
||||||
|
'--cert-name',
|
||||||
|
`npm-${certificate.id}`,
|
||||||
|
'--disable-hook-validation',
|
||||||
|
'--no-random-sleep-on-renew',
|
||||||
|
];
|
||||||
|
|
||||||
// Prepend the path to the credentials file as an environment variable
|
const adds = internalCertificate.getAdditionalCertbotArgs(certificate.id, certificate.meta.dns_provider);
|
||||||
if (certificate.meta.dns_provider === 'route53') {
|
args.push(...adds.args);
|
||||||
const credentialsLocation = '/etc/letsencrypt/credentials/credentials-' + certificate.id;
|
|
||||||
mainCmd = 'AWS_CONFIG_FILE=\'' + credentialsLocation + '\' ' + mainCmd;
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info('Command:', mainCmd);
|
logger.info(`Command: ${certbotCommand} ${args ? args.join(' ') : ''}`);
|
||||||
|
|
||||||
return utils.exec(mainCmd)
|
return utils.execFile(certbotCommand, args, adds.opts)
|
||||||
.then(async (result) => {
|
.then(async (result) => {
|
||||||
logger.info(result);
|
logger.info(result);
|
||||||
return result;
|
return result;
|
||||||
@@ -1024,24 +1057,29 @@ const internalCertificate = {
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
revokeLetsEncryptSsl: (certificate, throw_errors) => {
|
revokeLetsEncryptSsl: (certificate, throw_errors) => {
|
||||||
logger.info('Revoking Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
|
logger.info(`Revoking LetsEncrypt certificates for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);
|
||||||
|
|
||||||
const mainCmd = certbotCommand + ' revoke ' +
|
const args = [
|
||||||
'--config "' + letsencryptConfig + '" ' +
|
'revoke',
|
||||||
'--work-dir "/tmp/letsencrypt-lib" ' +
|
'--config',
|
||||||
'--logs-dir "/tmp/letsencrypt-log" ' +
|
letsencryptConfig,
|
||||||
'--cert-path "/etc/letsencrypt/live/npm-' + certificate.id + '/fullchain.pem" ' +
|
'--work-dir',
|
||||||
'--delete-after-revoke ' +
|
'/tmp/letsencrypt-lib',
|
||||||
(letsencryptStaging ? '--staging' : '');
|
'--logs-dir',
|
||||||
|
'/tmp/letsencrypt-log',
|
||||||
|
'--cert-path',
|
||||||
|
`${internalCertificate.getLiveCertPath(certificate.id)}/fullchain.pem`,
|
||||||
|
'--delete-after-revoke',
|
||||||
|
];
|
||||||
|
|
||||||
// Don't fail command if file does not exist
|
const adds = internalCertificate.getAdditionalCertbotArgs(certificate.id);
|
||||||
const delete_credentialsCmd = `rm -f '/etc/letsencrypt/credentials/credentials-${certificate.id}' || true`;
|
args.push(...adds.args);
|
||||||
|
|
||||||
logger.info('Command:', mainCmd + '; ' + delete_credentialsCmd);
|
logger.info(`Command: ${certbotCommand} ${args ? args.join(' ') : ''}`);
|
||||||
|
|
||||||
return utils.exec(mainCmd)
|
return utils.execFile(certbotCommand, args, adds.opts)
|
||||||
.then(async (result) => {
|
.then(async (result) => {
|
||||||
await utils.exec(delete_credentialsCmd);
|
await utils.exec(`rm -f '/etc/letsencrypt/credentials/credentials-${certificate.id}' || true`);
|
||||||
logger.info(result);
|
logger.info(result);
|
||||||
return result;
|
return result;
|
||||||
})
|
})
|
||||||
@@ -1059,9 +1097,8 @@ const internalCertificate = {
|
|||||||
* @returns {Boolean}
|
* @returns {Boolean}
|
||||||
*/
|
*/
|
||||||
hasLetsEncryptSslCerts: (certificate) => {
|
hasLetsEncryptSslCerts: (certificate) => {
|
||||||
const letsencryptPath = '/etc/letsencrypt/live/npm-' + certificate.id;
|
const letsencryptPath = internalCertificate.getLiveCertPath(certificate.id);
|
||||||
|
return fs.existsSync(`${letsencryptPath}/fullchain.pem`) && fs.existsSync(`${letsencryptPath}/privkey.pem`);
|
||||||
return fs.existsSync(letsencryptPath + '/fullchain.pem') && fs.existsSync(letsencryptPath + '/privkey.pem');
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1073,7 +1110,7 @@ const internalCertificate = {
|
|||||||
*/
|
*/
|
||||||
disableInUseHosts: (in_use_result) => {
|
disableInUseHosts: (in_use_result) => {
|
||||||
if (in_use_result.total_count) {
|
if (in_use_result.total_count) {
|
||||||
let promises = [];
|
const promises = [];
|
||||||
|
|
||||||
if (in_use_result.proxy_hosts.length) {
|
if (in_use_result.proxy_hosts.length) {
|
||||||
promises.push(internalNginx.bulkDeleteConfigs('proxy_host', in_use_result.proxy_hosts));
|
promises.push(internalNginx.bulkDeleteConfigs('proxy_host', in_use_result.proxy_hosts));
|
||||||
@@ -1103,7 +1140,7 @@ const internalCertificate = {
|
|||||||
*/
|
*/
|
||||||
enableInUseHosts: (in_use_result) => {
|
enableInUseHosts: (in_use_result) => {
|
||||||
if (in_use_result.total_count) {
|
if (in_use_result.total_count) {
|
||||||
let promises = [];
|
const promises = [];
|
||||||
|
|
||||||
if (in_use_result.proxy_hosts.length) {
|
if (in_use_result.proxy_hosts.length) {
|
||||||
promises.push(internalNginx.bulkGenerateConfigs('proxy_host', in_use_result.proxy_hosts));
|
promises.push(internalNginx.bulkGenerateConfigs('proxy_host', in_use_result.proxy_hosts));
|
||||||
@@ -1136,12 +1173,12 @@ const internalCertificate = {
|
|||||||
|
|
||||||
// Create a test challenge file
|
// Create a test challenge file
|
||||||
const testChallengeDir = '/data/letsencrypt-acme-challenge/.well-known/acme-challenge';
|
const testChallengeDir = '/data/letsencrypt-acme-challenge/.well-known/acme-challenge';
|
||||||
const testChallengeFile = testChallengeDir + '/test-challenge';
|
const testChallengeFile = `${testChallengeDir}/test-challenge`;
|
||||||
fs.mkdirSync(testChallengeDir, {recursive: true});
|
fs.mkdirSync(testChallengeDir, {recursive: true});
|
||||||
fs.writeFileSync(testChallengeFile, 'Success', {encoding: 'utf8'});
|
fs.writeFileSync(testChallengeFile, 'Success', {encoding: 'utf8'});
|
||||||
|
|
||||||
async function performTestForDomain (domain) {
|
async function performTestForDomain (domain) {
|
||||||
logger.info('Testing http challenge for ' + domain);
|
logger.info(`Testing http challenge for ${domain}`);
|
||||||
const url = `http://${domain}/.well-known/acme-challenge/test-challenge`;
|
const url = `http://${domain}/.well-known/acme-challenge/test-challenge`;
|
||||||
const formBody = `method=G&url=${encodeURI(url)}&bodytype=T&requestbody=&headername=User-Agent&headervalue=None&locationid=1&ch=false&cc=false`;
|
const formBody = `method=G&url=${encodeURI(url)}&bodytype=T&requestbody=&headername=User-Agent&headervalue=None&locationid=1&ch=false&cc=false`;
|
||||||
const options = {
|
const options = {
|
||||||
@@ -1155,13 +1192,16 @@ const internalCertificate = {
|
|||||||
|
|
||||||
const result = await new Promise((resolve) => {
|
const result = await new Promise((resolve) => {
|
||||||
|
|
||||||
const req = https.request('https://www.site24x7.com/tools/restapi-tester', options, function (res) {
|
const req = https.request('https://www.site24x7.com/tools/restapi-tester', options, (res) => {
|
||||||
let responseBody = '';
|
let responseBody = '';
|
||||||
|
|
||||||
res.on('data', (chunk) => responseBody = responseBody + chunk);
|
res.on('data', (chunk) => {
|
||||||
res.on('end', function () {
|
responseBody = responseBody + chunk;
|
||||||
|
});
|
||||||
|
|
||||||
|
res.on('end', () => {
|
||||||
try {
|
try {
|
||||||
const parsedBody = JSON.parse(responseBody + '');
|
const parsedBody = JSON.parse(`${responseBody}`);
|
||||||
if (res.statusCode !== 200) {
|
if (res.statusCode !== 200) {
|
||||||
logger.warn(`Failed to test HTTP challenge for domain ${domain} because HTTP status code ${res.statusCode} was returned: ${parsedBody.message}`);
|
logger.warn(`Failed to test HTTP challenge for domain ${domain} because HTTP status code ${res.statusCode} was returned: ${parsedBody.message}`);
|
||||||
resolve(undefined);
|
resolve(undefined);
|
||||||
@@ -1182,7 +1222,7 @@ const internalCertificate = {
|
|||||||
// Make sure to write the request body.
|
// Make sure to write the request body.
|
||||||
req.write(formBody);
|
req.write(formBody);
|
||||||
req.end();
|
req.end();
|
||||||
req.on('error', function (e) { logger.warn(`Failed to test HTTP challenge for domain ${domain}`, e);
|
req.on('error', (e) => { logger.warn(`Failed to test HTTP challenge for domain ${domain}`, e);
|
||||||
resolve(undefined); });
|
resolve(undefined); });
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1224,6 +1264,34 @@ const internalCertificate = {
|
|||||||
fs.unlinkSync(testChallengeFile);
|
fs.unlinkSync(testChallengeFile);
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
|
},
|
||||||
|
|
||||||
|
getAdditionalCertbotArgs: (certificate_id, dns_provider) => {
|
||||||
|
const args = [];
|
||||||
|
if (letsencryptServer !== null) {
|
||||||
|
args.push('--server', letsencryptServer);
|
||||||
|
}
|
||||||
|
if (letsencryptStaging && letsencryptServer === null) {
|
||||||
|
args.push('--staging');
|
||||||
|
}
|
||||||
|
|
||||||
|
// For route53, add the credentials file as an environment variable,
|
||||||
|
// inheriting the process env
|
||||||
|
const opts = {};
|
||||||
|
if (certificate_id && dns_provider === 'route53') {
|
||||||
|
opts.env = process.env;
|
||||||
|
opts.env.AWS_CONFIG_FILE = `/etc/letsencrypt/credentials/credentials-${certificate_id}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dns_provider === 'duckdns') {
|
||||||
|
args.push('--dns-duckdns-no-txt-restore');
|
||||||
|
}
|
||||||
|
|
||||||
|
return {args: args, opts: opts};
|
||||||
|
},
|
||||||
|
|
||||||
|
getLiveCertPath: (certificate_id) => {
|
||||||
|
return `/etc/letsencrypt/live/npm-${certificate_id}`;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -6,6 +6,7 @@ const internalHost = require('./host');
|
|||||||
const internalNginx = require('./nginx');
|
const internalNginx = require('./nginx');
|
||||||
const internalAuditLog = require('./audit-log');
|
const internalAuditLog = require('./audit-log');
|
||||||
const internalCertificate = require('./certificate');
|
const internalCertificate = require('./certificate');
|
||||||
|
const {castJsonIfNeed} = require('../lib/helpers');
|
||||||
|
|
||||||
function omissions () {
|
function omissions () {
|
||||||
return ['is_deleted'];
|
return ['is_deleted'];
|
||||||
@@ -409,16 +410,16 @@ const internalDeadHost = {
|
|||||||
.where('is_deleted', 0)
|
.where('is_deleted', 0)
|
||||||
.groupBy('id')
|
.groupBy('id')
|
||||||
.allowGraph('[owner,certificate]')
|
.allowGraph('[owner,certificate]')
|
||||||
.orderBy('domain_names', 'ASC');
|
.orderBy(castJsonIfNeed('domain_names'), 'ASC');
|
||||||
|
|
||||||
if (access_data.permission_visibility !== 'all') {
|
if (access_data.permission_visibility !== 'all') {
|
||||||
query.andWhere('owner_user_id', access.token.getUserId(1));
|
query.andWhere('owner_user_id', access.token.getUserId(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query is used for searching
|
// Query is used for searching
|
||||||
if (typeof search_query === 'string') {
|
if (typeof search_query === 'string' && search_query.length > 0) {
|
||||||
query.where(function () {
|
query.where(function () {
|
||||||
this.where('domain_names', 'like', '%' + search_query + '%');
|
this.where(castJsonIfNeed('domain_names'), 'like', '%' + search_query + '%');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2,6 +2,7 @@ const _ = require('lodash');
|
|||||||
const proxyHostModel = require('../models/proxy_host');
|
const proxyHostModel = require('../models/proxy_host');
|
||||||
const redirectionHostModel = require('../models/redirection_host');
|
const redirectionHostModel = require('../models/redirection_host');
|
||||||
const deadHostModel = require('../models/dead_host');
|
const deadHostModel = require('../models/dead_host');
|
||||||
|
const {castJsonIfNeed} = require('../lib/helpers');
|
||||||
|
|
||||||
const internalHost = {
|
const internalHost = {
|
||||||
|
|
||||||
@@ -17,7 +18,7 @@ const internalHost = {
|
|||||||
cleanSslHstsData: function (data, existing_data) {
|
cleanSslHstsData: function (data, existing_data) {
|
||||||
existing_data = existing_data === undefined ? {} : existing_data;
|
existing_data = existing_data === undefined ? {} : existing_data;
|
||||||
|
|
||||||
let combined_data = _.assign({}, existing_data, data);
|
const combined_data = _.assign({}, existing_data, data);
|
||||||
|
|
||||||
if (!combined_data.certificate_id) {
|
if (!combined_data.certificate_id) {
|
||||||
combined_data.ssl_forced = false;
|
combined_data.ssl_forced = false;
|
||||||
@@ -73,7 +74,7 @@ const internalHost = {
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
getHostsWithDomains: function (domain_names) {
|
getHostsWithDomains: function (domain_names) {
|
||||||
let promises = [
|
const promises = [
|
||||||
proxyHostModel
|
proxyHostModel
|
||||||
.query()
|
.query()
|
||||||
.where('is_deleted', 0),
|
.where('is_deleted', 0),
|
||||||
@@ -125,19 +126,19 @@ const internalHost = {
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
isHostnameTaken: function (hostname, ignore_type, ignore_id) {
|
isHostnameTaken: function (hostname, ignore_type, ignore_id) {
|
||||||
let promises = [
|
const promises = [
|
||||||
proxyHostModel
|
proxyHostModel
|
||||||
.query()
|
.query()
|
||||||
.where('is_deleted', 0)
|
.where('is_deleted', 0)
|
||||||
.andWhere('domain_names', 'like', '%' + hostname + '%'),
|
.andWhere(castJsonIfNeed('domain_names'), 'like', '%' + hostname + '%'),
|
||||||
redirectionHostModel
|
redirectionHostModel
|
||||||
.query()
|
.query()
|
||||||
.where('is_deleted', 0)
|
.where('is_deleted', 0)
|
||||||
.andWhere('domain_names', 'like', '%' + hostname + '%'),
|
.andWhere(castJsonIfNeed('domain_names'), 'like', '%' + hostname + '%'),
|
||||||
deadHostModel
|
deadHostModel
|
||||||
.query()
|
.query()
|
||||||
.where('is_deleted', 0)
|
.where('is_deleted', 0)
|
||||||
.andWhere('domain_names', 'like', '%' + hostname + '%')
|
.andWhere(castJsonIfNeed('domain_names'), 'like', '%' + hostname + '%')
|
||||||
];
|
];
|
||||||
|
|
||||||
return Promise.all(promises)
|
return Promise.all(promises)
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const fs = require('fs');
|
const fs = require('node:fs');
|
||||||
const logger = require('../logger').nginx;
|
const logger = require('../logger').nginx;
|
||||||
const config = require('../lib/config');
|
const config = require('../lib/config');
|
||||||
const utils = require('../lib/utils');
|
const utils = require('../lib/utils');
|
||||||
@@ -57,9 +57,9 @@ const internalNginx = {
|
|||||||
// It will always look like this:
|
// It will always look like this:
|
||||||
// nginx: [alert] could not open error log file: open() "/var/log/nginx/error.log" failed (6: No such device or address)
|
// nginx: [alert] could not open error log file: open() "/var/log/nginx/error.log" failed (6: No such device or address)
|
||||||
|
|
||||||
let valid_lines = [];
|
const valid_lines = [];
|
||||||
let err_lines = err.message.split('\n');
|
const err_lines = err.message.split('\n');
|
||||||
err_lines.map(function (line) {
|
err_lines.map((line) => {
|
||||||
if (line.indexOf('/var/log/nginx/error.log') === -1) {
|
if (line.indexOf('/var/log/nginx/error.log') === -1) {
|
||||||
valid_lines.push(line);
|
valid_lines.push(line);
|
||||||
}
|
}
|
||||||
@@ -105,7 +105,7 @@ const internalNginx = {
|
|||||||
logger.info('Testing Nginx configuration');
|
logger.info('Testing Nginx configuration');
|
||||||
}
|
}
|
||||||
|
|
||||||
return utils.exec('/usr/sbin/nginx -t -g "error_log off;"');
|
return utils.execFile('/usr/sbin/nginx', ['-t', '-g', 'error_log off;']);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -115,7 +115,7 @@ const internalNginx = {
|
|||||||
return internalNginx.test()
|
return internalNginx.test()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
logger.info('Reloading Nginx');
|
logger.info('Reloading Nginx');
|
||||||
return utils.exec('/usr/sbin/nginx -s reload');
|
return utils.execFile('/usr/sbin/nginx', ['-s', 'reload']);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -128,7 +128,7 @@ const internalNginx = {
|
|||||||
if (host_type === 'default') {
|
if (host_type === 'default') {
|
||||||
return '/data/nginx/default_host/site.conf';
|
return '/data/nginx/default_host/site.conf';
|
||||||
}
|
}
|
||||||
return '/data/nginx/' + internalNginx.getFileFriendlyHostType(host_type) + '/' + host_id + '.conf';
|
return `/data/nginx/${internalNginx.getFileFriendlyHostType(host_type)}/${host_id}.conf`;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -141,7 +141,7 @@ const internalNginx = {
|
|||||||
let template;
|
let template;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
template = fs.readFileSync(__dirname + '/../templates/_location.conf', {encoding: 'utf8'});
|
template = fs.readFileSync(`${__dirname}/../templates/_location.conf`, {encoding: 'utf8'});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
reject(new error.ConfigurationError(err.message));
|
reject(new error.ConfigurationError(err.message));
|
||||||
return;
|
return;
|
||||||
@@ -152,7 +152,7 @@ const internalNginx = {
|
|||||||
|
|
||||||
const locationRendering = async () => {
|
const locationRendering = async () => {
|
||||||
for (let i = 0; i < host.locations.length; i++) {
|
for (let i = 0; i < host.locations.length; i++) {
|
||||||
let locationCopy = Object.assign({}, {access_list_id: host.access_list_id}, {certificate_id: host.certificate_id},
|
const locationCopy = Object.assign({}, {access_list_id: host.access_list_id}, {certificate_id: host.certificate_id},
|
||||||
{ssl_forced: host.ssl_forced}, {caching_enabled: host.caching_enabled}, {block_exploits: host.block_exploits},
|
{ssl_forced: host.ssl_forced}, {caching_enabled: host.caching_enabled}, {block_exploits: host.block_exploits},
|
||||||
{allow_websocket_upgrade: host.allow_websocket_upgrade}, {http2_support: host.http2_support},
|
{allow_websocket_upgrade: host.allow_websocket_upgrade}, {http2_support: host.http2_support},
|
||||||
{hsts_enabled: host.hsts_enabled}, {hsts_subdomains: host.hsts_subdomains}, {access_list: host.access_list},
|
{hsts_enabled: host.hsts_enabled}, {hsts_subdomains: host.hsts_subdomains}, {access_list: host.access_list},
|
||||||
@@ -181,21 +181,23 @@ const internalNginx = {
|
|||||||
* @param {Object} host
|
* @param {Object} host
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
generateConfig: (host_type, host) => {
|
generateConfig: (host_type, host_row) => {
|
||||||
|
// Prevent modifying the original object:
|
||||||
|
const host = JSON.parse(JSON.stringify(host_row));
|
||||||
const nice_host_type = internalNginx.getFileFriendlyHostType(host_type);
|
const nice_host_type = internalNginx.getFileFriendlyHostType(host_type);
|
||||||
|
|
||||||
if (config.debug()) {
|
if (config.debug()) {
|
||||||
logger.info('Generating ' + nice_host_type + ' Config:', JSON.stringify(host, null, 2));
|
logger.info(`Generating ${nice_host_type} Config:`, JSON.stringify(host, null, 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderEngine = utils.getRenderEngine();
|
const renderEngine = utils.getRenderEngine();
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let template = null;
|
let template = null;
|
||||||
let filename = internalNginx.getConfigName(nice_host_type, host.id);
|
const filename = internalNginx.getConfigName(nice_host_type, host.id);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
template = fs.readFileSync(__dirname + '/../templates/' + nice_host_type + '.conf', {encoding: 'utf8'});
|
template = fs.readFileSync(`${__dirname}/../templates/${nice_host_type}.conf`, {encoding: 'utf8'});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
reject(new error.ConfigurationError(err.message));
|
reject(new error.ConfigurationError(err.message));
|
||||||
return;
|
return;
|
||||||
@@ -250,7 +252,7 @@ const internalNginx = {
|
|||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
if (config.debug()) {
|
if (config.debug()) {
|
||||||
logger.warn('Could not write ' + filename + ':', err.message);
|
logger.warn(`Could not write ${filename}:`, err.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
reject(new error.ConfigurationError(err.message));
|
reject(new error.ConfigurationError(err.message));
|
||||||
@@ -275,11 +277,11 @@ const internalNginx = {
|
|||||||
const renderEngine = utils.getRenderEngine();
|
const renderEngine = utils.getRenderEngine();
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let template = null;
|
let template = null;
|
||||||
let filename = '/data/nginx/temp/letsencrypt_' + certificate.id + '.conf';
|
const filename = `/data/nginx/temp/letsencrypt_${certificate.id}.conf`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
template = fs.readFileSync(__dirname + '/../templates/letsencrypt-request.conf', {encoding: 'utf8'});
|
template = fs.readFileSync(`${__dirname}/../templates/letsencrypt-request.conf`, {encoding: 'utf8'});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
reject(new error.ConfigurationError(err.message));
|
reject(new error.ConfigurationError(err.message));
|
||||||
return;
|
return;
|
||||||
@@ -300,7 +302,7 @@ const internalNginx = {
|
|||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
if (config.debug()) {
|
if (config.debug()) {
|
||||||
logger.warn('Could not write ' + filename + ':', err.message);
|
logger.warn(`Could not write ${filename}:`, err.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
reject(new error.ConfigurationError(err.message));
|
reject(new error.ConfigurationError(err.message));
|
||||||
@@ -314,7 +316,7 @@ const internalNginx = {
|
|||||||
* @param {String} filename
|
* @param {String} filename
|
||||||
*/
|
*/
|
||||||
deleteFile: (filename) => {
|
deleteFile: (filename) => {
|
||||||
logger.debug('Deleting file: ' + filename);
|
logger.debug(`Deleting file: ${filename}`);
|
||||||
try {
|
try {
|
||||||
fs.unlinkSync(filename);
|
fs.unlinkSync(filename);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -328,7 +330,7 @@ const internalNginx = {
|
|||||||
* @returns String
|
* @returns String
|
||||||
*/
|
*/
|
||||||
getFileFriendlyHostType: (host_type) => {
|
getFileFriendlyHostType: (host_type) => {
|
||||||
return host_type.replace(new RegExp('-', 'g'), '_');
|
return host_type.replace(/-/g, '_');
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -338,7 +340,7 @@ const internalNginx = {
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
deleteLetsEncryptRequestConfig: (certificate) => {
|
deleteLetsEncryptRequestConfig: (certificate) => {
|
||||||
const config_file = '/data/nginx/temp/letsencrypt_' + certificate.id + '.conf';
|
const config_file = `/data/nginx/temp/letsencrypt_${certificate.id}.conf`;
|
||||||
return new Promise((resolve/*, reject*/) => {
|
return new Promise((resolve/*, reject*/) => {
|
||||||
internalNginx.deleteFile(config_file);
|
internalNginx.deleteFile(config_file);
|
||||||
resolve();
|
resolve();
|
||||||
@@ -353,7 +355,7 @@ const internalNginx = {
|
|||||||
*/
|
*/
|
||||||
deleteConfig: (host_type, host, delete_err_file) => {
|
deleteConfig: (host_type, host, delete_err_file) => {
|
||||||
const config_file = internalNginx.getConfigName(internalNginx.getFileFriendlyHostType(host_type), typeof host === 'undefined' ? 0 : host.id);
|
const config_file = internalNginx.getConfigName(internalNginx.getFileFriendlyHostType(host_type), typeof host === 'undefined' ? 0 : host.id);
|
||||||
const config_file_err = config_file + '.err';
|
const config_file_err = `${config_file}.err`;
|
||||||
|
|
||||||
return new Promise((resolve/*, reject*/) => {
|
return new Promise((resolve/*, reject*/) => {
|
||||||
internalNginx.deleteFile(config_file);
|
internalNginx.deleteFile(config_file);
|
||||||
@@ -371,7 +373,7 @@ const internalNginx = {
|
|||||||
*/
|
*/
|
||||||
renameConfigAsError: (host_type, host) => {
|
renameConfigAsError: (host_type, host) => {
|
||||||
const config_file = internalNginx.getConfigName(internalNginx.getFileFriendlyHostType(host_type), typeof host === 'undefined' ? 0 : host.id);
|
const config_file = internalNginx.getConfigName(internalNginx.getFileFriendlyHostType(host_type), typeof host === 'undefined' ? 0 : host.id);
|
||||||
const config_file_err = config_file + '.err';
|
const config_file_err = `${config_file}.err`;
|
||||||
|
|
||||||
return new Promise((resolve/*, reject*/) => {
|
return new Promise((resolve/*, reject*/) => {
|
||||||
fs.unlink(config_file, () => {
|
fs.unlink(config_file, () => {
|
||||||
@@ -390,8 +392,8 @@ const internalNginx = {
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
bulkGenerateConfigs: (host_type, hosts) => {
|
bulkGenerateConfigs: (host_type, hosts) => {
|
||||||
let promises = [];
|
const promises = [];
|
||||||
hosts.map(function (host) {
|
hosts.map((host) => {
|
||||||
promises.push(internalNginx.generateConfig(host_type, host));
|
promises.push(internalNginx.generateConfig(host_type, host));
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -404,8 +406,8 @@ const internalNginx = {
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
bulkDeleteConfigs: (host_type, hosts) => {
|
bulkDeleteConfigs: (host_type, hosts) => {
|
||||||
let promises = [];
|
const promises = [];
|
||||||
hosts.map(function (host) {
|
hosts.map((host) => {
|
||||||
promises.push(internalNginx.deleteConfig(host_type, host, true));
|
promises.push(internalNginx.deleteConfig(host_type, host, true));
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -416,14 +418,12 @@ const internalNginx = {
|
|||||||
* @param {string} config
|
* @param {string} config
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
advancedConfigHasDefaultLocation: function (cfg) {
|
advancedConfigHasDefaultLocation: (cfg) => !!cfg.match(/^(?:.*;)?\s*?location\s*?\/\s*?{/im),
|
||||||
return !!cfg.match(/^(?:.*;)?\s*?location\s*?\/\s*?{/im);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
ipv6Enabled: function () {
|
ipv6Enabled: () => {
|
||||||
if (typeof process.env.DISABLE_IPV6 !== 'undefined') {
|
if (typeof process.env.DISABLE_IPV6 !== 'undefined') {
|
||||||
const disabled = process.env.DISABLE_IPV6.toLowerCase();
|
const disabled = process.env.DISABLE_IPV6.toLowerCase();
|
||||||
return !(disabled === 'on' || disabled === 'true' || disabled === '1' || disabled === 'yes');
|
return !(disabled === 'on' || disabled === 'true' || disabled === '1' || disabled === 'yes');
|
||||||
|
@@ -6,6 +6,7 @@ const internalHost = require('./host');
|
|||||||
const internalNginx = require('./nginx');
|
const internalNginx = require('./nginx');
|
||||||
const internalAuditLog = require('./audit-log');
|
const internalAuditLog = require('./audit-log');
|
||||||
const internalCertificate = require('./certificate');
|
const internalCertificate = require('./certificate');
|
||||||
|
const {castJsonIfNeed} = require('../lib/helpers');
|
||||||
|
|
||||||
function omissions () {
|
function omissions () {
|
||||||
return ['is_deleted', 'owner.is_deleted'];
|
return ['is_deleted', 'owner.is_deleted'];
|
||||||
@@ -416,16 +417,16 @@ const internalProxyHost = {
|
|||||||
.where('is_deleted', 0)
|
.where('is_deleted', 0)
|
||||||
.groupBy('id')
|
.groupBy('id')
|
||||||
.allowGraph('[owner,access_list,certificate]')
|
.allowGraph('[owner,access_list,certificate]')
|
||||||
.orderBy('domain_names', 'ASC');
|
.orderBy(castJsonIfNeed('domain_names'), 'ASC');
|
||||||
|
|
||||||
if (access_data.permission_visibility !== 'all') {
|
if (access_data.permission_visibility !== 'all') {
|
||||||
query.andWhere('owner_user_id', access.token.getUserId(1));
|
query.andWhere('owner_user_id', access.token.getUserId(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query is used for searching
|
// Query is used for searching
|
||||||
if (typeof search_query === 'string') {
|
if (typeof search_query === 'string' && search_query.length > 0) {
|
||||||
query.where(function () {
|
query.where(function () {
|
||||||
this.where('domain_names', 'like', '%' + search_query + '%');
|
this.where(castJsonIfNeed('domain_names'), 'like', `%${search_query}%`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -6,6 +6,7 @@ const internalHost = require('./host');
|
|||||||
const internalNginx = require('./nginx');
|
const internalNginx = require('./nginx');
|
||||||
const internalAuditLog = require('./audit-log');
|
const internalAuditLog = require('./audit-log');
|
||||||
const internalCertificate = require('./certificate');
|
const internalCertificate = require('./certificate');
|
||||||
|
const {castJsonIfNeed} = require('../lib/helpers');
|
||||||
|
|
||||||
function omissions () {
|
function omissions () {
|
||||||
return ['is_deleted'];
|
return ['is_deleted'];
|
||||||
@@ -409,16 +410,16 @@ const internalRedirectionHost = {
|
|||||||
.where('is_deleted', 0)
|
.where('is_deleted', 0)
|
||||||
.groupBy('id')
|
.groupBy('id')
|
||||||
.allowGraph('[owner,certificate]')
|
.allowGraph('[owner,certificate]')
|
||||||
.orderBy('domain_names', 'ASC');
|
.orderBy(castJsonIfNeed('domain_names'), 'ASC');
|
||||||
|
|
||||||
if (access_data.permission_visibility !== 'all') {
|
if (access_data.permission_visibility !== 'all') {
|
||||||
query.andWhere('owner_user_id', access.token.getUserId(1));
|
query.andWhere('owner_user_id', access.token.getUserId(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query is used for searching
|
// Query is used for searching
|
||||||
if (typeof search_query === 'string') {
|
if (typeof search_query === 'string' && search_query.length > 0) {
|
||||||
query.where(function () {
|
query.where(function () {
|
||||||
this.where('domain_names', 'like', '%' + search_query + '%');
|
this.where(castJsonIfNeed('domain_names'), 'like', `%${search_query}%`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,12 +1,15 @@
|
|||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const error = require('../lib/error');
|
const error = require('../lib/error');
|
||||||
const utils = require('../lib/utils');
|
const utils = require('../lib/utils');
|
||||||
const streamModel = require('../models/stream');
|
const streamModel = require('../models/stream');
|
||||||
const internalNginx = require('./nginx');
|
const internalNginx = require('./nginx');
|
||||||
const internalAuditLog = require('./audit-log');
|
const internalAuditLog = require('./audit-log');
|
||||||
|
const internalCertificate = require('./certificate');
|
||||||
|
const internalHost = require('./host');
|
||||||
|
const {castJsonIfNeed} = require('../lib/helpers');
|
||||||
|
|
||||||
function omissions () {
|
function omissions () {
|
||||||
return ['is_deleted'];
|
return ['is_deleted', 'owner.is_deleted', 'certificate.is_deleted'];
|
||||||
}
|
}
|
||||||
|
|
||||||
const internalStream = {
|
const internalStream = {
|
||||||
@@ -17,6 +20,12 @@ const internalStream = {
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
create: (access, data) => {
|
create: (access, data) => {
|
||||||
|
const create_certificate = data.certificate_id === 'new';
|
||||||
|
|
||||||
|
if (create_certificate) {
|
||||||
|
delete data.certificate_id;
|
||||||
|
}
|
||||||
|
|
||||||
return access.can('streams:create', data)
|
return access.can('streams:create', data)
|
||||||
.then((/*access_data*/) => {
|
.then((/*access_data*/) => {
|
||||||
// TODO: At this point the existing ports should have been checked
|
// TODO: At this point the existing ports should have been checked
|
||||||
@@ -26,16 +35,44 @@ const internalStream = {
|
|||||||
data.meta = {};
|
data.meta = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// streams aren't routed by domain name so don't store domain names in the DB
|
||||||
|
let data_no_domains = structuredClone(data);
|
||||||
|
delete data_no_domains.domain_names;
|
||||||
|
|
||||||
return streamModel
|
return streamModel
|
||||||
.query()
|
.query()
|
||||||
.insertAndFetch(data)
|
.insertAndFetch(data_no_domains)
|
||||||
.then(utils.omitRow(omissions()));
|
.then(utils.omitRow(omissions()));
|
||||||
})
|
})
|
||||||
|
.then((row) => {
|
||||||
|
if (create_certificate) {
|
||||||
|
return internalCertificate.createQuickCertificate(access, data)
|
||||||
|
.then((cert) => {
|
||||||
|
// update host with cert id
|
||||||
|
return internalStream.update(access, {
|
||||||
|
id: row.id,
|
||||||
|
certificate_id: cert.id
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
return row;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((row) => {
|
||||||
|
// re-fetch with cert
|
||||||
|
return internalStream.get(access, {
|
||||||
|
id: row.id,
|
||||||
|
expand: ['certificate', 'owner']
|
||||||
|
});
|
||||||
|
})
|
||||||
.then((row) => {
|
.then((row) => {
|
||||||
// Configure nginx
|
// Configure nginx
|
||||||
return internalNginx.configure(streamModel, 'stream', row)
|
return internalNginx.configure(streamModel, 'stream', row)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return internalStream.get(access, {id: row.id, expand: ['owner']});
|
return row;
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.then((row) => {
|
.then((row) => {
|
||||||
@@ -59,6 +96,12 @@ const internalStream = {
|
|||||||
* @return {Promise}
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
update: (access, data) => {
|
update: (access, data) => {
|
||||||
|
const create_certificate = data.certificate_id === 'new';
|
||||||
|
|
||||||
|
if (create_certificate) {
|
||||||
|
delete data.certificate_id;
|
||||||
|
}
|
||||||
|
|
||||||
return access.can('streams:update', data.id)
|
return access.can('streams:update', data.id)
|
||||||
.then((/*access_data*/) => {
|
.then((/*access_data*/) => {
|
||||||
// TODO: at this point the existing streams should have been checked
|
// TODO: at this point the existing streams should have been checked
|
||||||
@@ -70,16 +113,32 @@ const internalStream = {
|
|||||||
throw new error.InternalValidationError('Stream could not be updated, IDs do not match: ' + row.id + ' !== ' + data.id);
|
throw new error.InternalValidationError('Stream could not be updated, IDs do not match: ' + row.id + ' !== ' + data.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (create_certificate) {
|
||||||
|
return internalCertificate.createQuickCertificate(access, {
|
||||||
|
domain_names: data.domain_names || row.domain_names,
|
||||||
|
meta: _.assign({}, row.meta, data.meta)
|
||||||
|
})
|
||||||
|
.then((cert) => {
|
||||||
|
// update host with cert id
|
||||||
|
data.certificate_id = cert.id;
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
return row;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((row) => {
|
||||||
|
// Add domain_names to the data in case it isn't there, so that the audit log renders correctly. The order is important here.
|
||||||
|
data = _.assign({}, {
|
||||||
|
domain_names: row.domain_names
|
||||||
|
}, data);
|
||||||
|
|
||||||
return streamModel
|
return streamModel
|
||||||
.query()
|
.query()
|
||||||
.patchAndFetchById(row.id, data)
|
.patchAndFetchById(row.id, data)
|
||||||
.then(utils.omitRow(omissions()))
|
.then(utils.omitRow(omissions()))
|
||||||
.then((saved_row) => {
|
|
||||||
return internalNginx.configure(streamModel, 'stream', saved_row)
|
|
||||||
.then(() => {
|
|
||||||
return internalStream.get(access, {id: row.id, expand: ['owner']});
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.then((saved_row) => {
|
.then((saved_row) => {
|
||||||
// Add to audit log
|
// Add to audit log
|
||||||
return internalAuditLog.add(access, {
|
return internalAuditLog.add(access, {
|
||||||
@@ -92,6 +151,17 @@ const internalStream = {
|
|||||||
return saved_row;
|
return saved_row;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
return internalStream.get(access, {id: data.id, expand: ['owner', 'certificate']})
|
||||||
|
.then((row) => {
|
||||||
|
return internalNginx.configure(streamModel, 'stream', row)
|
||||||
|
.then((new_meta) => {
|
||||||
|
row.meta = new_meta;
|
||||||
|
row = internalHost.cleanRowCertificateMeta(row);
|
||||||
|
return _.omit(row, omissions());
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -114,7 +184,7 @@ const internalStream = {
|
|||||||
.query()
|
.query()
|
||||||
.where('is_deleted', 0)
|
.where('is_deleted', 0)
|
||||||
.andWhere('id', data.id)
|
.andWhere('id', data.id)
|
||||||
.allowGraph('[owner]')
|
.allowGraph('[owner,certificate]')
|
||||||
.first();
|
.first();
|
||||||
|
|
||||||
if (access_data.permission_visibility !== 'all') {
|
if (access_data.permission_visibility !== 'all') {
|
||||||
@@ -131,6 +201,7 @@ const internalStream = {
|
|||||||
if (!row || !row.id) {
|
if (!row || !row.id) {
|
||||||
throw new error.ItemNotFoundError(data.id);
|
throw new error.ItemNotFoundError(data.id);
|
||||||
}
|
}
|
||||||
|
row = internalHost.cleanRowCertificateMeta(row);
|
||||||
// Custom omissions
|
// Custom omissions
|
||||||
if (typeof data.omit !== 'undefined' && data.omit !== null) {
|
if (typeof data.omit !== 'undefined' && data.omit !== null) {
|
||||||
row = _.omit(row, data.omit);
|
row = _.omit(row, data.omit);
|
||||||
@@ -196,14 +267,14 @@ const internalStream = {
|
|||||||
.then(() => {
|
.then(() => {
|
||||||
return internalStream.get(access, {
|
return internalStream.get(access, {
|
||||||
id: data.id,
|
id: data.id,
|
||||||
expand: ['owner']
|
expand: ['certificate', 'owner']
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.then((row) => {
|
.then((row) => {
|
||||||
if (!row || !row.id) {
|
if (!row || !row.id) {
|
||||||
throw new error.ItemNotFoundError(data.id);
|
throw new error.ItemNotFoundError(data.id);
|
||||||
} else if (row.enabled) {
|
} else if (row.enabled) {
|
||||||
throw new error.ValidationError('Host is already enabled');
|
throw new error.ValidationError('Stream is already enabled');
|
||||||
}
|
}
|
||||||
|
|
||||||
row.enabled = 1;
|
row.enabled = 1;
|
||||||
@@ -249,7 +320,7 @@ const internalStream = {
|
|||||||
if (!row || !row.id) {
|
if (!row || !row.id) {
|
||||||
throw new error.ItemNotFoundError(data.id);
|
throw new error.ItemNotFoundError(data.id);
|
||||||
} else if (!row.enabled) {
|
} else if (!row.enabled) {
|
||||||
throw new error.ValidationError('Host is already disabled');
|
throw new error.ValidationError('Stream is already disabled');
|
||||||
}
|
}
|
||||||
|
|
||||||
row.enabled = 0;
|
row.enabled = 0;
|
||||||
@@ -293,11 +364,11 @@ const internalStream = {
|
|||||||
getAll: (access, expand, search_query) => {
|
getAll: (access, expand, search_query) => {
|
||||||
return access.can('streams:list')
|
return access.can('streams:list')
|
||||||
.then((access_data) => {
|
.then((access_data) => {
|
||||||
let query = streamModel
|
const query = streamModel
|
||||||
.query()
|
.query()
|
||||||
.where('is_deleted', 0)
|
.where('is_deleted', 0)
|
||||||
.groupBy('id')
|
.groupBy('id')
|
||||||
.allowGraph('[owner]')
|
.allowGraph('[owner,certificate]')
|
||||||
.orderBy('incoming_port', 'ASC');
|
.orderBy('incoming_port', 'ASC');
|
||||||
|
|
||||||
if (access_data.permission_visibility !== 'all') {
|
if (access_data.permission_visibility !== 'all') {
|
||||||
@@ -305,9 +376,9 @@ const internalStream = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Query is used for searching
|
// Query is used for searching
|
||||||
if (typeof search_query === 'string') {
|
if (typeof search_query === 'string' && search_query.length > 0) {
|
||||||
query.where(function () {
|
query.where(function () {
|
||||||
this.where('incoming_port', 'like', '%' + search_query + '%');
|
this.where(castJsonIfNeed('incoming_port'), 'like', `%${search_query}%`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -316,6 +387,13 @@ const internalStream = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return query.then(utils.omitRows(omissions()));
|
return query.then(utils.omitRows(omissions()));
|
||||||
|
})
|
||||||
|
.then((rows) => {
|
||||||
|
if (typeof expand !== 'undefined' && expand !== null && expand.indexOf('certificate') !== -1) {
|
||||||
|
return internalHost.cleanAllRowsCertificateMeta(rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rows;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -327,9 +405,9 @@ const internalStream = {
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
getCount: (user_id, visibility) => {
|
getCount: (user_id, visibility) => {
|
||||||
let query = streamModel
|
const query = streamModel
|
||||||
.query()
|
.query()
|
||||||
.count('id as count')
|
.count('id AS count')
|
||||||
.where('is_deleted', 0);
|
.where('is_deleted', 0);
|
||||||
|
|
||||||
if (visibility !== 'all') {
|
if (visibility !== 'all') {
|
||||||
|
@@ -5,6 +5,8 @@ const authModel = require('../models/auth');
|
|||||||
const helpers = require('../lib/helpers');
|
const helpers = require('../lib/helpers');
|
||||||
const TokenModel = require('../models/token');
|
const TokenModel = require('../models/token');
|
||||||
|
|
||||||
|
const ERROR_MESSAGE_INVALID_AUTH = 'Invalid email or password';
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -69,15 +71,15 @@ module.exports = {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
throw new error.AuthError('Invalid password');
|
throw new error.AuthError(ERROR_MESSAGE_INVALID_AUTH);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
throw new error.AuthError('No password auth for user');
|
throw new error.AuthError(ERROR_MESSAGE_INVALID_AUTH);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
throw new error.AuthError('No relevant user found');
|
throw new error.AuthError(ERROR_MESSAGE_INVALID_AUTH);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
development: {
|
development: {
|
||||||
client: 'mysql',
|
client: 'mysql2',
|
||||||
migrations: {
|
migrations: {
|
||||||
tableName: 'migrations',
|
tableName: 'migrations',
|
||||||
stub: 'lib/migrate_template.js',
|
stub: 'lib/migrate_template.js',
|
||||||
@@ -9,7 +9,7 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
production: {
|
production: {
|
||||||
client: 'mysql',
|
client: 'mysql2',
|
||||||
migrations: {
|
migrations: {
|
||||||
tableName: 'migrations',
|
tableName: 'migrations',
|
||||||
stub: 'lib/migrate_template.js',
|
stub: 'lib/migrate_template.js',
|
||||||
|
@@ -11,7 +11,7 @@ const certbot = {
|
|||||||
/**
|
/**
|
||||||
* @param {array} pluginKeys
|
* @param {array} pluginKeys
|
||||||
*/
|
*/
|
||||||
installPlugins: async function (pluginKeys) {
|
installPlugins: async (pluginKeys) => {
|
||||||
let hasErrors = false;
|
let hasErrors = false;
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
@@ -21,7 +21,7 @@ const certbot = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
batchflow(pluginKeys).sequential()
|
batchflow(pluginKeys).sequential()
|
||||||
.each((i, pluginKey, next) => {
|
.each((_i, pluginKey, next) => {
|
||||||
certbot.installPlugin(pluginKey)
|
certbot.installPlugin(pluginKey)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
next();
|
next();
|
||||||
@@ -51,7 +51,7 @@ const certbot = {
|
|||||||
* @param {string} pluginKey
|
* @param {string} pluginKey
|
||||||
* @returns {Object}
|
* @returns {Object}
|
||||||
*/
|
*/
|
||||||
installPlugin: async function (pluginKey) {
|
installPlugin: async (pluginKey) => {
|
||||||
if (typeof dnsPlugins[pluginKey] === 'undefined') {
|
if (typeof dnsPlugins[pluginKey] === 'undefined') {
|
||||||
// throw Error(`Certbot plugin ${pluginKey} not found`);
|
// throw Error(`Certbot plugin ${pluginKey} not found`);
|
||||||
throw new error.ItemNotFoundError(pluginKey);
|
throw new error.ItemNotFoundError(pluginKey);
|
||||||
@@ -63,8 +63,15 @@ const certbot = {
|
|||||||
plugin.version = plugin.version.replace(/{{certbot-version}}/g, CERTBOT_VERSION_REPLACEMENT);
|
plugin.version = plugin.version.replace(/{{certbot-version}}/g, CERTBOT_VERSION_REPLACEMENT);
|
||||||
plugin.dependencies = plugin.dependencies.replace(/{{certbot-version}}/g, CERTBOT_VERSION_REPLACEMENT);
|
plugin.dependencies = plugin.dependencies.replace(/{{certbot-version}}/g, CERTBOT_VERSION_REPLACEMENT);
|
||||||
|
|
||||||
const cmd = '. /opt/certbot/bin/activate && pip install --no-cache-dir ' + plugin.dependencies + ' ' + plugin.package_name + plugin.version + ' ' + ' && deactivate';
|
// SETUPTOOLS_USE_DISTUTILS is required for certbot plugins to install correctly
|
||||||
return utils.exec(cmd)
|
// in new versions of Python
|
||||||
|
let env = Object.assign({}, process.env, {SETUPTOOLS_USE_DISTUTILS: 'stdlib'});
|
||||||
|
if (typeof plugin.env === 'object') {
|
||||||
|
env = Object.assign(env, plugin.env);
|
||||||
|
}
|
||||||
|
|
||||||
|
const cmd = `. /opt/certbot/bin/activate && pip install --no-cache-dir ${plugin.dependencies} ${plugin.package_name}${plugin.version} && deactivate`;
|
||||||
|
return utils.exec(cmd, {env})
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
logger.complete(`Installed ${pluginKey}`);
|
logger.complete(`Installed ${pluginKey}`);
|
||||||
return result;
|
return result;
|
||||||
|
@@ -2,7 +2,10 @@ const fs = require('fs');
|
|||||||
const NodeRSA = require('node-rsa');
|
const NodeRSA = require('node-rsa');
|
||||||
const logger = require('../logger').global;
|
const logger = require('../logger').global;
|
||||||
|
|
||||||
const keysFile = '/data/keys.json';
|
const keysFile = '/data/keys.json';
|
||||||
|
const mysqlEngine = 'mysql2';
|
||||||
|
const postgresEngine = 'pg';
|
||||||
|
const sqliteClientName = 'sqlite3';
|
||||||
|
|
||||||
let instance = null;
|
let instance = null;
|
||||||
|
|
||||||
@@ -14,7 +17,7 @@ const configure = () => {
|
|||||||
let configData;
|
let configData;
|
||||||
try {
|
try {
|
||||||
configData = require(filename);
|
configData = require(filename);
|
||||||
} catch (err) {
|
} catch (_) {
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,7 +37,7 @@ const configure = () => {
|
|||||||
logger.info('Using MySQL configuration');
|
logger.info('Using MySQL configuration');
|
||||||
instance = {
|
instance = {
|
||||||
database: {
|
database: {
|
||||||
engine: 'mysql',
|
engine: mysqlEngine,
|
||||||
host: envMysqlHost,
|
host: envMysqlHost,
|
||||||
port: process.env.DB_MYSQL_PORT || 3306,
|
port: process.env.DB_MYSQL_PORT || 3306,
|
||||||
user: envMysqlUser,
|
user: envMysqlUser,
|
||||||
@@ -46,13 +49,33 @@ const configure = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const envPostgresHost = process.env.DB_POSTGRES_HOST || null;
|
||||||
|
const envPostgresUser = process.env.DB_POSTGRES_USER || null;
|
||||||
|
const envPostgresName = process.env.DB_POSTGRES_NAME || null;
|
||||||
|
if (envPostgresHost && envPostgresUser && envPostgresName) {
|
||||||
|
// we have enough postgres creds to go with postgres
|
||||||
|
logger.info('Using Postgres configuration');
|
||||||
|
instance = {
|
||||||
|
database: {
|
||||||
|
engine: postgresEngine,
|
||||||
|
host: envPostgresHost,
|
||||||
|
port: process.env.DB_POSTGRES_PORT || 5432,
|
||||||
|
user: envPostgresUser,
|
||||||
|
password: process.env.DB_POSTGRES_PASSWORD,
|
||||||
|
name: envPostgresName,
|
||||||
|
},
|
||||||
|
keys: getKeys(),
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const envSqliteFile = process.env.DB_SQLITE_FILE || '/data/database.sqlite';
|
const envSqliteFile = process.env.DB_SQLITE_FILE || '/data/database.sqlite';
|
||||||
logger.info(`Using Sqlite: ${envSqliteFile}`);
|
logger.info(`Using Sqlite: ${envSqliteFile}`);
|
||||||
instance = {
|
instance = {
|
||||||
database: {
|
database: {
|
||||||
engine: 'knex-native',
|
engine: 'knex-native',
|
||||||
knex: {
|
knex: {
|
||||||
client: 'sqlite3',
|
client: sqliteClientName,
|
||||||
connection: {
|
connection: {
|
||||||
filename: envSqliteFile
|
filename: envSqliteFile
|
||||||
},
|
},
|
||||||
@@ -143,7 +166,27 @@ module.exports = {
|
|||||||
*/
|
*/
|
||||||
isSqlite: function () {
|
isSqlite: function () {
|
||||||
instance === null && configure();
|
instance === null && configure();
|
||||||
return instance.database.knex && instance.database.knex.client === 'sqlite3';
|
return instance.database.knex && instance.database.knex.client === sqliteClientName;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this a mysql configuration?
|
||||||
|
*
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
isMysql: function () {
|
||||||
|
instance === null && configure();
|
||||||
|
return instance.database.engine === mysqlEngine;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this a postgres configuration?
|
||||||
|
*
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
isPostgres: function () {
|
||||||
|
instance === null && configure();
|
||||||
|
return instance.database.engine === postgresEngine;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -180,5 +223,15 @@ module.exports = {
|
|||||||
*/
|
*/
|
||||||
useLetsencryptStaging: function () {
|
useLetsencryptStaging: function () {
|
||||||
return !!process.env.LE_STAGING;
|
return !!process.env.LE_STAGING;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {string|null}
|
||||||
|
*/
|
||||||
|
useLetsencryptServer: function () {
|
||||||
|
if (process.env.LE_SERVER) {
|
||||||
|
return process.env.LE_SERVER;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
const moment = require('moment');
|
const moment = require('moment');
|
||||||
|
const {isPostgres} = require('./config');
|
||||||
|
const {ref} = require('objection');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
||||||
@@ -45,6 +47,16 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
return obj;
|
return obj;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Casts a column to json if using postgres
|
||||||
|
*
|
||||||
|
* @param {string} colName
|
||||||
|
* @returns {string|Objection.ReferenceBuilder}
|
||||||
|
*/
|
||||||
|
castJsonIfNeed: function (colName) {
|
||||||
|
return isPostgres() ? ref(colName).castText() : colName;
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@@ -1,13 +1,13 @@
|
|||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const exec = require('child_process').exec;
|
const exec = require('node:child_process').exec;
|
||||||
const execFile = require('child_process').execFile;
|
const execFile = require('node:child_process').execFile;
|
||||||
const { Liquid } = require('liquidjs');
|
const { Liquid } = require('liquidjs');
|
||||||
const logger = require('../logger').global;
|
const logger = require('../logger').global;
|
||||||
const error = require('./error');
|
const error = require('./error');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
||||||
exec: async function(cmd, options = {}) {
|
exec: async (cmd, options = {}) => {
|
||||||
logger.debug('CMD:', cmd);
|
logger.debug('CMD:', cmd);
|
||||||
|
|
||||||
const { stdout, stderr } = await new Promise((resolve, reject) => {
|
const { stdout, stderr } = await new Promise((resolve, reject) => {
|
||||||
@@ -29,15 +29,19 @@ module.exports = {
|
|||||||
/**
|
/**
|
||||||
* @param {String} cmd
|
* @param {String} cmd
|
||||||
* @param {Array} args
|
* @param {Array} args
|
||||||
|
* @param {Object|undefined} options
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
execFile: function (cmd, args) {
|
execFile: (cmd, args, options) => {
|
||||||
// logger.debug('CMD: ' + cmd + ' ' + (args ? args.join(' ') : ''));
|
logger.debug(`CMD: ${cmd} ${args ? args.join(' ') : ''}`);
|
||||||
|
if (typeof options === 'undefined') {
|
||||||
|
options = {};
|
||||||
|
}
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
execFile(cmd, args, function (err, stdout, /*stderr*/) {
|
execFile(cmd, args, options, (err, stdout, stderr) => {
|
||||||
if (err && typeof err === 'object') {
|
if (err && typeof err === 'object') {
|
||||||
reject(err);
|
reject(new error.CommandError(stderr, 1, err));
|
||||||
} else {
|
} else {
|
||||||
resolve(stdout.trim());
|
resolve(stdout.trim());
|
||||||
}
|
}
|
||||||
@@ -51,7 +55,7 @@ module.exports = {
|
|||||||
* @param {Array} omissions
|
* @param {Array} omissions
|
||||||
* @returns {Function}
|
* @returns {Function}
|
||||||
*/
|
*/
|
||||||
omitRow: function (omissions) {
|
omitRow: (omissions) => {
|
||||||
/**
|
/**
|
||||||
* @param {Object} row
|
* @param {Object} row
|
||||||
* @returns {Object}
|
* @returns {Object}
|
||||||
@@ -67,7 +71,7 @@ module.exports = {
|
|||||||
* @param {Array} omissions
|
* @param {Array} omissions
|
||||||
* @returns {Function}
|
* @returns {Function}
|
||||||
*/
|
*/
|
||||||
omitRows: function (omissions) {
|
omitRows: (omissions) => {
|
||||||
/**
|
/**
|
||||||
* @param {Array} rows
|
* @param {Array} rows
|
||||||
* @returns {Object}
|
* @returns {Object}
|
||||||
@@ -83,9 +87,9 @@ module.exports = {
|
|||||||
/**
|
/**
|
||||||
* @returns {Object} Liquid render engine
|
* @returns {Object} Liquid render engine
|
||||||
*/
|
*/
|
||||||
getRenderEngine: function () {
|
getRenderEngine: () => {
|
||||||
const renderEngine = new Liquid({
|
const renderEngine = new Liquid({
|
||||||
root: __dirname + '/../templates/'
|
root: `${__dirname}/../templates/`
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
38
backend/migrations/20240427161436_stream_ssl.js
Normal file
38
backend/migrations/20240427161436_stream_ssl.js
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
const migrate_name = 'stream_ssl';
|
||||||
|
const logger = require('../logger').migrate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Migrate
|
||||||
|
*
|
||||||
|
* @see http://knexjs.org/#Schema
|
||||||
|
*
|
||||||
|
* @param {Object} knex
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
exports.up = function (knex) {
|
||||||
|
logger.info('[' + migrate_name + '] Migrating Up...');
|
||||||
|
|
||||||
|
return knex.schema.table('stream', (table) => {
|
||||||
|
table.integer('certificate_id').notNull().unsigned().defaultTo(0);
|
||||||
|
})
|
||||||
|
.then(function () {
|
||||||
|
logger.info('[' + migrate_name + '] stream Table altered');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undo Migrate
|
||||||
|
*
|
||||||
|
* @param {Object} knex
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
exports.down = function (knex) {
|
||||||
|
logger.info('[' + migrate_name + '] Migrating Down...');
|
||||||
|
|
||||||
|
return knex.schema.table('stream', (table) => {
|
||||||
|
table.dropColumn('certificate_id');
|
||||||
|
})
|
||||||
|
.then(function () {
|
||||||
|
logger.info('[' + migrate_name + '] stream Table altered');
|
||||||
|
});
|
||||||
|
};
|
@@ -4,7 +4,6 @@
|
|||||||
const db = require('../db');
|
const db = require('../db');
|
||||||
const helpers = require('../lib/helpers');
|
const helpers = require('../lib/helpers');
|
||||||
const Model = require('objection').Model;
|
const Model = require('objection').Model;
|
||||||
const User = require('./user');
|
|
||||||
const now = require('./now_helper');
|
const now = require('./now_helper');
|
||||||
|
|
||||||
Model.knex(db);
|
Model.knex(db);
|
||||||
@@ -68,6 +67,11 @@ class Certificate extends Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static get relationMappings () {
|
static get relationMappings () {
|
||||||
|
const ProxyHost = require('./proxy_host');
|
||||||
|
const DeadHost = require('./dead_host');
|
||||||
|
const User = require('./user');
|
||||||
|
const RedirectionHost = require('./redirection_host');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
owner: {
|
owner: {
|
||||||
relation: Model.HasOneRelation,
|
relation: Model.HasOneRelation,
|
||||||
@@ -79,6 +83,39 @@ class Certificate extends Model {
|
|||||||
modify: function (qb) {
|
modify: function (qb) {
|
||||||
qb.where('user.is_deleted', 0);
|
qb.where('user.is_deleted', 0);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
proxy_hosts: {
|
||||||
|
relation: Model.HasManyRelation,
|
||||||
|
modelClass: ProxyHost,
|
||||||
|
join: {
|
||||||
|
from: 'certificate.id',
|
||||||
|
to: 'proxy_host.certificate_id'
|
||||||
|
},
|
||||||
|
modify: function (qb) {
|
||||||
|
qb.where('proxy_host.is_deleted', 0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dead_hosts: {
|
||||||
|
relation: Model.HasManyRelation,
|
||||||
|
modelClass: DeadHost,
|
||||||
|
join: {
|
||||||
|
from: 'certificate.id',
|
||||||
|
to: 'dead_host.certificate_id'
|
||||||
|
},
|
||||||
|
modify: function (qb) {
|
||||||
|
qb.where('dead_host.is_deleted', 0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
redirection_hosts: {
|
||||||
|
relation: Model.HasManyRelation,
|
||||||
|
modelClass: RedirectionHost,
|
||||||
|
join: {
|
||||||
|
from: 'certificate.id',
|
||||||
|
to: 'redirection_host.certificate_id'
|
||||||
|
},
|
||||||
|
modify: function (qb) {
|
||||||
|
qb.where('redirection_host.is_deleted', 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -12,7 +12,11 @@ Model.knex(db);
|
|||||||
|
|
||||||
const boolFields = [
|
const boolFields = [
|
||||||
'is_deleted',
|
'is_deleted',
|
||||||
|
'ssl_forced',
|
||||||
|
'http2_support',
|
||||||
'enabled',
|
'enabled',
|
||||||
|
'hsts_enabled',
|
||||||
|
'hsts_subdomains',
|
||||||
];
|
];
|
||||||
|
|
||||||
class DeadHost extends Model {
|
class DeadHost extends Model {
|
||||||
|
@@ -17,6 +17,9 @@ const boolFields = [
|
|||||||
'preserve_path',
|
'preserve_path',
|
||||||
'ssl_forced',
|
'ssl_forced',
|
||||||
'block_exploits',
|
'block_exploits',
|
||||||
|
'hsts_enabled',
|
||||||
|
'hsts_subdomains',
|
||||||
|
'http2_support',
|
||||||
];
|
];
|
||||||
|
|
||||||
class RedirectionHost extends Model {
|
class RedirectionHost extends Model {
|
||||||
|
@@ -1,16 +1,15 @@
|
|||||||
// Objection Docs:
|
const Model = require('objection').Model;
|
||||||
// http://vincit.github.io/objection.js/
|
const db = require('../db');
|
||||||
|
const helpers = require('../lib/helpers');
|
||||||
const db = require('../db');
|
const User = require('./user');
|
||||||
const helpers = require('../lib/helpers');
|
const Certificate = require('./certificate');
|
||||||
const Model = require('objection').Model;
|
const now = require('./now_helper');
|
||||||
const User = require('./user');
|
|
||||||
const now = require('./now_helper');
|
|
||||||
|
|
||||||
Model.knex(db);
|
Model.knex(db);
|
||||||
|
|
||||||
const boolFields = [
|
const boolFields = [
|
||||||
'is_deleted',
|
'is_deleted',
|
||||||
|
'enabled',
|
||||||
'tcp_forwarding',
|
'tcp_forwarding',
|
||||||
'udp_forwarding',
|
'udp_forwarding',
|
||||||
];
|
];
|
||||||
@@ -64,6 +63,17 @@ class Stream extends Model {
|
|||||||
modify: function (qb) {
|
modify: function (qb) {
|
||||||
qb.where('user.is_deleted', 0);
|
qb.where('user.is_deleted', 0);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
certificate: {
|
||||||
|
relation: Model.HasOneRelation,
|
||||||
|
modelClass: Certificate,
|
||||||
|
join: {
|
||||||
|
from: 'stream.certificate_id',
|
||||||
|
to: 'certificate.id'
|
||||||
|
},
|
||||||
|
modify: function (qb) {
|
||||||
|
qb.where('certificate.is_deleted', 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -9,9 +9,9 @@
|
|||||||
"archiver": "^5.3.0",
|
"archiver": "^5.3.0",
|
||||||
"batchflow": "^0.4.0",
|
"batchflow": "^0.4.0",
|
||||||
"bcrypt": "^5.0.0",
|
"bcrypt": "^5.0.0",
|
||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.20.3",
|
||||||
"compression": "^1.7.4",
|
"compression": "^1.7.4",
|
||||||
"express": "^4.19.2",
|
"express": "^4.20.0",
|
||||||
"express-fileupload": "^1.1.9",
|
"express-fileupload": "^1.1.9",
|
||||||
"gravatar": "^1.8.0",
|
"gravatar": "^1.8.0",
|
||||||
"jsonwebtoken": "^9.0.0",
|
"jsonwebtoken": "^9.0.0",
|
||||||
@@ -19,10 +19,11 @@
|
|||||||
"liquidjs": "10.6.1",
|
"liquidjs": "10.6.1",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"mysql": "^2.18.1",
|
"mysql2": "^3.11.1",
|
||||||
"node-rsa": "^1.0.8",
|
"node-rsa": "^1.0.8",
|
||||||
"objection": "3.0.1",
|
"objection": "3.0.1",
|
||||||
"path": "^0.12.7",
|
"path": "^0.12.7",
|
||||||
|
"pg": "^8.13.1",
|
||||||
"signale": "1.4.0",
|
"signale": "1.4.0",
|
||||||
"sqlite3": "5.1.6",
|
"sqlite3": "5.1.6",
|
||||||
"temp-write": "^4.0.0"
|
"temp-write": "^4.0.0"
|
||||||
|
@@ -6,7 +6,7 @@ const apiValidator = require('../../lib/validator/api');
|
|||||||
const internalCertificate = require('../../internal/certificate');
|
const internalCertificate = require('../../internal/certificate');
|
||||||
const schema = require('../../schema');
|
const schema = require('../../schema');
|
||||||
|
|
||||||
let router = express.Router({
|
const router = express.Router({
|
||||||
caseSensitive: true,
|
caseSensitive: true,
|
||||||
strict: true,
|
strict: true,
|
||||||
mergeParams: true
|
mergeParams: true
|
||||||
@@ -231,7 +231,7 @@ router
|
|||||||
*/
|
*/
|
||||||
router
|
router
|
||||||
.route('/:certificate_id/download')
|
.route('/:certificate_id/download')
|
||||||
.options((req, res) => {
|
.options((_req, res) => {
|
||||||
res.sendStatus(204);
|
res.sendStatus(204);
|
||||||
})
|
})
|
||||||
.all(jwtdecode())
|
.all(jwtdecode())
|
||||||
|
@@ -181,7 +181,7 @@ router
|
|||||||
return internalUser.setPassword(res.locals.access, payload);
|
return internalUser.setPassword(res.locals.access, payload);
|
||||||
})
|
})
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
res.status(201)
|
res.status(200)
|
||||||
.send(result);
|
.send(result);
|
||||||
})
|
})
|
||||||
.catch(next);
|
.catch(next);
|
||||||
@@ -212,7 +212,7 @@ router
|
|||||||
return internalUser.setPermissions(res.locals.access, payload);
|
return internalUser.setPermissions(res.locals.access, payload);
|
||||||
})
|
})
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
res.status(201)
|
res.status(200)
|
||||||
.send(result);
|
.send(result);
|
||||||
})
|
})
|
||||||
.catch(next);
|
.catch(next);
|
||||||
@@ -238,7 +238,7 @@ router
|
|||||||
.post((req, res, next) => {
|
.post((req, res, next) => {
|
||||||
internalUser.loginAs(res.locals.access, {id: parseInt(req.params.user_id, 10)})
|
internalUser.loginAs(res.locals.access, {id: parseInt(req.params.user_id, 10)})
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
res.status(201)
|
res.status(200)
|
||||||
.send(result);
|
.send(result);
|
||||||
})
|
})
|
||||||
.catch(next);
|
.catch(next);
|
||||||
|
@@ -76,7 +76,7 @@
|
|||||||
"uniqueItems": true,
|
"uniqueItems": true,
|
||||||
"items": {
|
"items": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"pattern": "^(?:\\*\\.)?(?:[^.*]+\\.?)+[^.]$"
|
"pattern": "^[^&| @!#%^();:/\\\\}{=+?<>,~`'\"]+$"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"enabled": {
|
"enabled": {
|
||||||
@@ -110,6 +110,11 @@
|
|||||||
"caching_enabled": {
|
"caching_enabled": {
|
||||||
"description": "Should we cache assets",
|
"description": "Should we cache assets",
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
"description": "Email address",
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -24,22 +24,34 @@
|
|||||||
"description": "Nice Name for the custom certificate"
|
"description": "Nice Name for the custom certificate"
|
||||||
},
|
},
|
||||||
"domain_names": {
|
"domain_names": {
|
||||||
"$ref": "../common.json#/properties/domain_names"
|
"description": "Domain Names separated by a comma",
|
||||||
|
"type": "array",
|
||||||
|
"maxItems": 100,
|
||||||
|
"uniqueItems": true,
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[^&| @!#%^();:/\\\\}{=+?<>,~`'\"]+$"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"expires_on": {
|
"expires_on": {
|
||||||
"description": "Date and time of expiration",
|
"description": "Date and time of expiration",
|
||||||
"readOnly": true,
|
"readOnly": true,
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"owner": {
|
||||||
|
"$ref": "./user-object.json"
|
||||||
|
},
|
||||||
"meta": {
|
"meta": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
"letsencrypt_email": {
|
"certificate": {
|
||||||
"type": "string"
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
},
|
},
|
||||||
"letsencrypt_agree": {
|
"certificate_key": {
|
||||||
"type": "boolean"
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
},
|
},
|
||||||
"dns_challenge": {
|
"dns_challenge": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
@@ -50,13 +62,18 @@
|
|||||||
"dns_provider_credentials": {
|
"dns_provider_credentials": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"letsencrypt_agree": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"letsencrypt_certificate": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"letsencrypt_email": {
|
||||||
|
"$ref": "../common.json#/properties/email"
|
||||||
|
},
|
||||||
"propagation_seconds": {
|
"propagation_seconds": {
|
||||||
"anyOf": [
|
"type": "integer",
|
||||||
{
|
"minimum": 0
|
||||||
"type": "integer",
|
|
||||||
"minimum": 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
9
backend/schema/components/error.json
Normal file
9
backend/schema/components/error.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"description": "Error",
|
||||||
|
"properties": {
|
||||||
|
"error": {
|
||||||
|
"$ref": "./error-object.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -22,10 +22,7 @@
|
|||||||
"enabled",
|
"enabled",
|
||||||
"locations",
|
"locations",
|
||||||
"hsts_enabled",
|
"hsts_enabled",
|
||||||
"hsts_subdomains",
|
"hsts_subdomains"
|
||||||
"certificate",
|
|
||||||
"use_default_location",
|
|
||||||
"ipv6"
|
|
||||||
],
|
],
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -151,12 +148,6 @@
|
|||||||
"$ref": "./access-list-object.json"
|
"$ref": "./access-list-object.json"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
|
||||||
"use_default_location": {
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"ipv6": {
|
|
||||||
"type": "boolean"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -28,7 +28,7 @@
|
|||||||
},
|
},
|
||||||
"forward_scheme": {
|
"forward_scheme": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["http", "https"]
|
"enum": ["auto", "http", "https"]
|
||||||
},
|
},
|
||||||
"forward_domain_name": {
|
"forward_domain_name": {
|
||||||
"description": "Domain Name",
|
"description": "Domain Name",
|
||||||
|
@@ -25,7 +25,7 @@
|
|||||||
"value": {
|
"value": {
|
||||||
"description": "Value in almost any form",
|
"description": "Value in almost any form",
|
||||||
"example": "congratulations",
|
"example": "congratulations",
|
||||||
"oneOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"minLength": 1
|
"minLength": 1
|
||||||
@@ -46,7 +46,10 @@
|
|||||||
},
|
},
|
||||||
"meta": {
|
"meta": {
|
||||||
"description": "Extra metadata",
|
"description": "Extra metadata",
|
||||||
"example": {},
|
"example": {
|
||||||
|
"redirect": "http://example.com",
|
||||||
|
"html": "<h1>404</h1>"
|
||||||
|
},
|
||||||
"type": "object"
|
"type": "object"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"description": "Proxy Hosts list",
|
"description": "Streams list",
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "./proxy-host-object.json"
|
"$ref": "./stream-object.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -53,8 +53,24 @@
|
|||||||
"enabled": {
|
"enabled": {
|
||||||
"$ref": "../common.json#/properties/enabled"
|
"$ref": "../common.json#/properties/enabled"
|
||||||
},
|
},
|
||||||
|
"certificate_id": {
|
||||||
|
"$ref": "../common.json#/properties/certificate_id"
|
||||||
|
},
|
||||||
"meta": {
|
"meta": {
|
||||||
"type": "object"
|
"type": "object"
|
||||||
|
},
|
||||||
|
"owner": {
|
||||||
|
"$ref": "./user-object.json"
|
||||||
|
},
|
||||||
|
"certificate": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "./certificate-object.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -5,10 +5,9 @@
|
|||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
"expires": {
|
"expires": {
|
||||||
"description": "Token Expiry Unix Time",
|
"description": "Token Expiry ISO Time String",
|
||||||
"example": 1566540249,
|
"example": "2025-02-04T20:40:46.340Z",
|
||||||
"minimum": 1,
|
"type": "string"
|
||||||
"type": "number"
|
|
||||||
},
|
},
|
||||||
"token": {
|
"token": {
|
||||||
"description": "JWT Token",
|
"description": "JWT Token",
|
||||||
|
@@ -49,8 +49,7 @@
|
|||||||
"minLength": 1
|
"minLength": 1
|
||||||
},
|
},
|
||||||
"password": {
|
"password": {
|
||||||
"type": "string",
|
"type": "string"
|
||||||
"minLength": 1
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -55,6 +55,25 @@
|
|||||||
"certificate_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC1n9j9C5Bes1nd\nqACDckERauxXVNKCnUlUM1buGBx1xc+j2e2Ar23wUJJuWBY18VfT8yqfqVDktO2w\nrbmvZvLuPmXePOKbIKS+XXh+2NG9L5bDG9rwGFCRXnbQj+GWCdMfzx14+CR1IHge\nYz6Cv/Si2/LJPCh/CoBfM4hUQJON3lxAWrWBpdbZnKYMrxuPBRfW9OuzTbCVXToQ\noxRAHiOR9081Xn1WeoKr7kVBIa5UphlvWXa12w1YmUwJu7YndnJGIavLWeNCVc7Z\nEo+nS8Wr/4QWicatIWZXpVaEOPhRoeplQDxNWg5b/Q26rYoVd7PrCmRs7sVcH79X\nzGONeH1PAgMBAAECggEAANb3Wtwl07pCjRrMvc7WbC0xYIn82yu8/g2qtjkYUJcU\nia5lQbYN7RGCS85Oc/tkq48xQEG5JQWNH8b918jDEMTrFab0aUEyYcru1q9L8PL6\nYHaNgZSrMrDcHcS8h0QOXNRJT5jeGkiHJaTR0irvB526tqF3knbK9yW22KTfycUe\na0Z9voKn5xRk1DCbHi/nk2EpT7xnjeQeLFaTIRXbS68omkr4YGhwWm5OizoyEGZu\nW0Zum5BkQyMr6kor3wdxOTG97ske2rcyvvHi+ErnwL0xBv0qY0Dhe8DpuXpDezqw\no72yY8h31Fu84i7sAj24YuE5Df8DozItFXQpkgbQ6QKBgQDPrufhvIFm2S/MzBdW\nH8JxY7CJlJPyxOvc1NIl9RczQGAQR90kx52cgIcuIGEG6/wJ/xnGfMmW40F0DnQ+\nN+oLgB9SFxeLkRb7s9Z/8N3uIN8JJFYcerEOiRQeN2BXEEWJ7bUThNtsVrAcKoUh\nELsDmnHW/3V+GKwhd0vpk842+wKBgQDf4PGLG9PTE5tlAoyHFodJRd2RhTJQkwsU\nMDNjLJ+KecLv+Nl+QiJhoflG1ccqtSFlBSCG067CDQ5LV0xm3mLJ7pfJoMgjcq31\nqjEmX4Ls91GuVOPtbwst3yFKjsHaSoKB5fBvWRcKFpBUezM7Qcw2JP3+dQT+bQIq\ncMTkRWDSvQKBgQDOdCQFDjxg/lR7NQOZ1PaZe61aBz5P3pxNqa7ClvMaOsuEQ7w9\nvMYcdtRq8TsjA2JImbSI0TIg8gb2FQxPcYwTJKl+FICOeIwtaSg5hTtJZpnxX5LO\nutTaC0DZjNkTk5RdOdWA8tihyUdGqKoxJY2TVmwGe2rUEDjFB++J4inkEwKBgB6V\ng0nmtkxanFrzOzFlMXwgEEHF+Xaqb9QFNa/xs6XeNnREAapO7JV75Cr6H2hFMFe1\nmJjyqCgYUoCWX3iaHtLJRnEkBtNY4kzyQB6m46LtsnnnXO/dwKA2oDyoPfFNRoDq\nYatEd3JIXNU9s2T/+x7WdOBjKhh72dTkbPFmTPDdAoGAU6rlPBevqOFdObYxdPq8\nEQWu44xqky3Mf5sBpOwtu6rqCYuziLiN7K4sjN5GD5mb1cEU+oS92ZiNcUQ7MFXk\n8yTYZ7U0VcXyAcpYreWwE8thmb0BohJBr+Mp3wLTx32x0HKdO6vpUa0d35LUTUmM\nRrKmPK/msHKK/sVHiL+NFqo=\n-----END PRIVATE KEY-----\n"
|
"certificate_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC1n9j9C5Bes1nd\nqACDckERauxXVNKCnUlUM1buGBx1xc+j2e2Ar23wUJJuWBY18VfT8yqfqVDktO2w\nrbmvZvLuPmXePOKbIKS+XXh+2NG9L5bDG9rwGFCRXnbQj+GWCdMfzx14+CR1IHge\nYz6Cv/Si2/LJPCh/CoBfM4hUQJON3lxAWrWBpdbZnKYMrxuPBRfW9OuzTbCVXToQ\noxRAHiOR9081Xn1WeoKr7kVBIa5UphlvWXa12w1YmUwJu7YndnJGIavLWeNCVc7Z\nEo+nS8Wr/4QWicatIWZXpVaEOPhRoeplQDxNWg5b/Q26rYoVd7PrCmRs7sVcH79X\nzGONeH1PAgMBAAECggEAANb3Wtwl07pCjRrMvc7WbC0xYIn82yu8/g2qtjkYUJcU\nia5lQbYN7RGCS85Oc/tkq48xQEG5JQWNH8b918jDEMTrFab0aUEyYcru1q9L8PL6\nYHaNgZSrMrDcHcS8h0QOXNRJT5jeGkiHJaTR0irvB526tqF3knbK9yW22KTfycUe\na0Z9voKn5xRk1DCbHi/nk2EpT7xnjeQeLFaTIRXbS68omkr4YGhwWm5OizoyEGZu\nW0Zum5BkQyMr6kor3wdxOTG97ske2rcyvvHi+ErnwL0xBv0qY0Dhe8DpuXpDezqw\no72yY8h31Fu84i7sAj24YuE5Df8DozItFXQpkgbQ6QKBgQDPrufhvIFm2S/MzBdW\nH8JxY7CJlJPyxOvc1NIl9RczQGAQR90kx52cgIcuIGEG6/wJ/xnGfMmW40F0DnQ+\nN+oLgB9SFxeLkRb7s9Z/8N3uIN8JJFYcerEOiRQeN2BXEEWJ7bUThNtsVrAcKoUh\nELsDmnHW/3V+GKwhd0vpk842+wKBgQDf4PGLG9PTE5tlAoyHFodJRd2RhTJQkwsU\nMDNjLJ+KecLv+Nl+QiJhoflG1ccqtSFlBSCG067CDQ5LV0xm3mLJ7pfJoMgjcq31\nqjEmX4Ls91GuVOPtbwst3yFKjsHaSoKB5fBvWRcKFpBUezM7Qcw2JP3+dQT+bQIq\ncMTkRWDSvQKBgQDOdCQFDjxg/lR7NQOZ1PaZe61aBz5P3pxNqa7ClvMaOsuEQ7w9\nvMYcdtRq8TsjA2JImbSI0TIg8gb2FQxPcYwTJKl+FICOeIwtaSg5hTtJZpnxX5LO\nutTaC0DZjNkTk5RdOdWA8tihyUdGqKoxJY2TVmwGe2rUEDjFB++J4inkEwKBgB6V\ng0nmtkxanFrzOzFlMXwgEEHF+Xaqb9QFNa/xs6XeNnREAapO7JV75Cr6H2hFMFe1\nmJjyqCgYUoCWX3iaHtLJRnEkBtNY4kzyQB6m46LtsnnnXO/dwKA2oDyoPfFNRoDq\nYatEd3JIXNU9s2T/+x7WdOBjKhh72dTkbPFmTPDdAoGAU6rlPBevqOFdObYxdPq8\nEQWu44xqky3Mf5sBpOwtu6rqCYuziLiN7K4sjN5GD5mb1cEU+oS92ZiNcUQ7MFXk\n8yTYZ7U0VcXyAcpYreWwE8thmb0BohJBr+Mp3wLTx32x0HKdO6vpUa0d35LUTUmM\nRrKmPK/msHKK/sVHiL+NFqo=\n-----END PRIVATE KEY-----\n"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["certificate", "certificate_key"],
|
||||||
|
"properties": {
|
||||||
|
"certificate": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"certificate_key": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"intermediate_certificate": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -72,6 +72,26 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "400 response",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"examples": {
|
||||||
|
"default": {
|
||||||
|
"value": {
|
||||||
|
"error": {
|
||||||
|
"code": 400,
|
||||||
|
"message": "Domains are invalid"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"schema": {
|
||||||
|
"$ref": "../../../components/error.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -50,6 +50,42 @@
|
|||||||
"certificate_key": true
|
"certificate_key": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["certificate", "certificate_key"],
|
||||||
|
"properties": {
|
||||||
|
"certificate": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["cn", "issuer", "dates"],
|
||||||
|
"properties": {
|
||||||
|
"cn": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"issuer": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"dates": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["from", "to"],
|
||||||
|
"properties": {
|
||||||
|
"from": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"to": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"certificate_key": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -67,6 +103,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"schema": {
|
||||||
|
"$ref": "../../../../components/error.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -50,7 +50,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "../../../../../components/error-object.json"
|
"$ref": "../../../../../components/error.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -50,7 +50,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "../../../../../components/error-object.json"
|
"$ref": "../../../../../components/error.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -94,9 +94,7 @@
|
|||||||
"avatar": "",
|
"avatar": "",
|
||||||
"roles": ["admin"]
|
"roles": ["admin"]
|
||||||
},
|
},
|
||||||
"certificate": null,
|
"certificate": null
|
||||||
"use_default_location": true,
|
|
||||||
"ipv6": true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@@ -79,9 +79,7 @@
|
|||||||
"nickname": "Admin",
|
"nickname": "Admin",
|
||||||
"avatar": "",
|
"avatar": "",
|
||||||
"roles": ["admin"]
|
"roles": ["admin"]
|
||||||
},
|
}
|
||||||
"use_default_location": true,
|
|
||||||
"ipv6": true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@@ -50,7 +50,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "../../../../../components/error-object.json"
|
"$ref": "../../../../../components/error.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -50,7 +50,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "../../../../../components/error-object.json"
|
"$ref": "../../../../../components/error.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -129,9 +129,7 @@
|
|||||||
"roles": ["admin"]
|
"roles": ["admin"]
|
||||||
},
|
},
|
||||||
"certificate": null,
|
"certificate": null,
|
||||||
"access_list": null,
|
"access_list": null
|
||||||
"use_default_location": true,
|
|
||||||
"ipv6": true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@@ -114,9 +114,7 @@
|
|||||||
"avatar": "//www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?default=mm",
|
"avatar": "//www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?default=mm",
|
||||||
"roles": ["admin"]
|
"roles": ["admin"]
|
||||||
},
|
},
|
||||||
"access_list": null,
|
"access_list": null
|
||||||
"use_default_location": true,
|
|
||||||
"ipv6": true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@@ -50,7 +50,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "../../../../../components/error-object.json"
|
"$ref": "../../../../../components/error.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -50,7 +50,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "../../../../../components/error-object.json"
|
"$ref": "../../../../../components/error.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -114,9 +114,7 @@
|
|||||||
"avatar": "",
|
"avatar": "",
|
||||||
"roles": ["admin"]
|
"roles": ["admin"]
|
||||||
},
|
},
|
||||||
"certificate": null,
|
"certificate": null
|
||||||
"use_default_location": true,
|
|
||||||
"ipv6": true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@@ -99,9 +99,7 @@
|
|||||||
"nickname": "Admin",
|
"nickname": "Admin",
|
||||||
"avatar": "",
|
"avatar": "",
|
||||||
"roles": ["admin"]
|
"roles": ["admin"]
|
||||||
},
|
}
|
||||||
"use_default_location": true,
|
|
||||||
"ipv6": true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@@ -14,7 +14,7 @@
|
|||||||
"description": "Expansions",
|
"description": "Expansions",
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["access_list", "owner", "certificate"]
|
"enum": ["owner", "certificate"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -40,7 +40,8 @@
|
|||||||
"nginx_online": true,
|
"nginx_online": true,
|
||||||
"nginx_err": null
|
"nginx_err": null
|
||||||
},
|
},
|
||||||
"enabled": true
|
"enabled": true,
|
||||||
|
"certificate_id": 0
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@@ -32,6 +32,9 @@
|
|||||||
"udp_forwarding": {
|
"udp_forwarding": {
|
||||||
"$ref": "../../../components/stream-object.json#/properties/udp_forwarding"
|
"$ref": "../../../components/stream-object.json#/properties/udp_forwarding"
|
||||||
},
|
},
|
||||||
|
"certificate_id": {
|
||||||
|
"$ref": "../../../components/stream-object.json#/properties/certificate_id"
|
||||||
|
},
|
||||||
"meta": {
|
"meta": {
|
||||||
"$ref": "../../../components/stream-object.json#/properties/meta"
|
"$ref": "../../../components/stream-object.json#/properties/meta"
|
||||||
}
|
}
|
||||||
@@ -73,7 +76,8 @@
|
|||||||
"nickname": "Admin",
|
"nickname": "Admin",
|
||||||
"avatar": "",
|
"avatar": "",
|
||||||
"roles": ["admin"]
|
"roles": ["admin"]
|
||||||
}
|
},
|
||||||
|
"certificate_id": 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@@ -50,7 +50,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "../../../../../components/error-object.json"
|
"$ref": "../../../../../components/error.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -50,7 +50,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "../../../../../components/error-object.json"
|
"$ref": "../../../../../components/error.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -40,7 +40,8 @@
|
|||||||
"nginx_online": true,
|
"nginx_online": true,
|
||||||
"nginx_err": null
|
"nginx_err": null
|
||||||
},
|
},
|
||||||
"enabled": true
|
"enabled": true,
|
||||||
|
"certificate_id": 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@@ -29,56 +29,26 @@
|
|||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"minProperties": 1,
|
"minProperties": 1,
|
||||||
"properties": {
|
"properties": {
|
||||||
"domain_names": {
|
"incoming_port": {
|
||||||
"$ref": "../../../../components/proxy-host-object.json#/properties/domain_names"
|
"$ref": "../../../../components/stream-object.json#/properties/incoming_port"
|
||||||
},
|
},
|
||||||
"forward_scheme": {
|
"forwarding_host": {
|
||||||
"$ref": "../../../../components/proxy-host-object.json#/properties/forward_scheme"
|
"$ref": "../../../../components/stream-object.json#/properties/forwarding_host"
|
||||||
},
|
},
|
||||||
"forward_host": {
|
"forwarding_port": {
|
||||||
"$ref": "../../../../components/proxy-host-object.json#/properties/forward_host"
|
"$ref": "../../../../components/stream-object.json#/properties/forwarding_port"
|
||||||
},
|
},
|
||||||
"forward_port": {
|
"tcp_forwarding": {
|
||||||
"$ref": "../../../../components/proxy-host-object.json#/properties/forward_port"
|
"$ref": "../../../../components/stream-object.json#/properties/tcp_forwarding"
|
||||||
|
},
|
||||||
|
"udp_forwarding": {
|
||||||
|
"$ref": "../../../../components/stream-object.json#/properties/udp_forwarding"
|
||||||
},
|
},
|
||||||
"certificate_id": {
|
"certificate_id": {
|
||||||
"$ref": "../../../../components/proxy-host-object.json#/properties/certificate_id"
|
"$ref": "../../../../components/stream-object.json#/properties/certificate_id"
|
||||||
},
|
|
||||||
"ssl_forced": {
|
|
||||||
"$ref": "../../../../components/proxy-host-object.json#/properties/ssl_forced"
|
|
||||||
},
|
|
||||||
"hsts_enabled": {
|
|
||||||
"$ref": "../../../../components/proxy-host-object.json#/properties/hsts_enabled"
|
|
||||||
},
|
|
||||||
"hsts_subdomains": {
|
|
||||||
"$ref": "../../../../components/proxy-host-object.json#/properties/hsts_subdomains"
|
|
||||||
},
|
|
||||||
"http2_support": {
|
|
||||||
"$ref": "../../../../components/proxy-host-object.json#/properties/http2_support"
|
|
||||||
},
|
|
||||||
"block_exploits": {
|
|
||||||
"$ref": "../../../../components/proxy-host-object.json#/properties/block_exploits"
|
|
||||||
},
|
|
||||||
"caching_enabled": {
|
|
||||||
"$ref": "../../../../components/proxy-host-object.json#/properties/caching_enabled"
|
|
||||||
},
|
|
||||||
"allow_websocket_upgrade": {
|
|
||||||
"$ref": "../../../../components/proxy-host-object.json#/properties/allow_websocket_upgrade"
|
|
||||||
},
|
|
||||||
"access_list_id": {
|
|
||||||
"$ref": "../../../../components/proxy-host-object.json#/properties/access_list_id"
|
|
||||||
},
|
|
||||||
"advanced_config": {
|
|
||||||
"$ref": "../../../../components/proxy-host-object.json#/properties/advanced_config"
|
|
||||||
},
|
|
||||||
"enabled": {
|
|
||||||
"$ref": "../../../../components/proxy-host-object.json#/properties/enabled"
|
|
||||||
},
|
},
|
||||||
"meta": {
|
"meta": {
|
||||||
"$ref": "../../../../components/proxy-host-object.json#/properties/meta"
|
"$ref": "../../../../components/stream-object.json#/properties/meta"
|
||||||
},
|
|
||||||
"locations": {
|
|
||||||
"$ref": "../../../../components/proxy-host-object.json#/properties/locations"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -94,44 +64,32 @@
|
|||||||
"default": {
|
"default": {
|
||||||
"value": {
|
"value": {
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"created_on": "2024-10-08T23:23:03.000Z",
|
"created_on": "2024-10-09T02:33:45.000Z",
|
||||||
"modified_on": "2024-10-08T23:26:37.000Z",
|
"modified_on": "2024-10-09T02:33:45.000Z",
|
||||||
"owner_user_id": 1,
|
"owner_user_id": 1,
|
||||||
"domain_names": ["test.example.com"],
|
"incoming_port": 9090,
|
||||||
"forward_host": "192.168.0.10",
|
"forwarding_host": "router.internal",
|
||||||
"forward_port": 8989,
|
"forwarding_port": 80,
|
||||||
"access_list_id": 0,
|
"tcp_forwarding": true,
|
||||||
"certificate_id": 0,
|
"udp_forwarding": false,
|
||||||
"ssl_forced": false,
|
|
||||||
"caching_enabled": false,
|
|
||||||
"block_exploits": false,
|
|
||||||
"advanced_config": "",
|
|
||||||
"meta": {
|
"meta": {
|
||||||
"nginx_online": true,
|
"nginx_online": true,
|
||||||
"nginx_err": null
|
"nginx_err": null
|
||||||
},
|
},
|
||||||
"allow_websocket_upgrade": false,
|
|
||||||
"http2_support": false,
|
|
||||||
"forward_scheme": "http",
|
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"hsts_enabled": false,
|
|
||||||
"hsts_subdomains": false,
|
|
||||||
"owner": {
|
"owner": {
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"created_on": "2024-10-07T22:43:55.000Z",
|
"created_on": "2024-10-09T02:33:16.000Z",
|
||||||
"modified_on": "2024-10-08T12:52:54.000Z",
|
"modified_on": "2024-10-09T02:33:16.000Z",
|
||||||
"is_deleted": false,
|
"is_deleted": false,
|
||||||
"is_disabled": false,
|
"is_disabled": false,
|
||||||
"email": "admin@example.com",
|
"email": "admin@example.com",
|
||||||
"name": "Administrator",
|
"name": "Administrator",
|
||||||
"nickname": "some guy",
|
"nickname": "Admin",
|
||||||
"avatar": "//www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?default=mm",
|
"avatar": "",
|
||||||
"roles": ["admin"]
|
"roles": ["admin"]
|
||||||
},
|
},
|
||||||
"certificate": null,
|
"certificate_id": 0
|
||||||
"access_list": null,
|
|
||||||
"use_default_location": true,
|
|
||||||
"ipv6": true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@@ -13,7 +13,8 @@
|
|||||||
"name": "settingID",
|
"name": "settingID",
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"minLength": 1
|
"minLength": 1,
|
||||||
|
"enum": ["default-site"]
|
||||||
},
|
},
|
||||||
"required": true,
|
"required": true,
|
||||||
"description": "Setting ID",
|
"description": "Setting ID",
|
||||||
@@ -31,10 +32,21 @@
|
|||||||
"minProperties": 1,
|
"minProperties": 1,
|
||||||
"properties": {
|
"properties": {
|
||||||
"value": {
|
"value": {
|
||||||
"$ref": "../../../components/setting-object.json#/properties/value"
|
"type": "string",
|
||||||
|
"minLength": 1,
|
||||||
|
"enum": ["congratulations", "404", "444", "redirect", "html"]
|
||||||
},
|
},
|
||||||
"meta": {
|
"meta": {
|
||||||
"$ref": "../../../components/setting-object.json#/properties/meta"
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"redirect": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"html": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -15,7 +15,7 @@
|
|||||||
"examples": {
|
"examples": {
|
||||||
"default": {
|
"default": {
|
||||||
"value": {
|
"value": {
|
||||||
"expires": 1566540510,
|
"expires": "2025-02-04T20:40:46.340Z",
|
||||||
"token": "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.ey...xaHKYr3Kk6MvkUjcC4"
|
"token": "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.ey...xaHKYr3Kk6MvkUjcC4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -38,7 +38,7 @@
|
|||||||
"default": {
|
"default": {
|
||||||
"value": {
|
"value": {
|
||||||
"result": {
|
"result": {
|
||||||
"expires": 1566540510,
|
"expires": "2025-02-04T20:40:46.340Z",
|
||||||
"token": "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.ey...xaHKYr3Kk6MvkUjcC4"
|
"token": "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.ey...xaHKYr3Kk6MvkUjcC4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -9,6 +9,15 @@
|
|||||||
"url": "http://127.0.0.1:81/api"
|
"url": "http://127.0.0.1:81/api"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"components": {
|
||||||
|
"securitySchemes": {
|
||||||
|
"bearerAuth": {
|
||||||
|
"type": "http",
|
||||||
|
"scheme": "bearer",
|
||||||
|
"bearerFormat": "JWT"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"paths": {
|
"paths": {
|
||||||
"/": {
|
"/": {
|
||||||
"get": {
|
"get": {
|
||||||
|
@@ -15,18 +15,18 @@ const certbot = require('./lib/certbot');
|
|||||||
const setupDefaultUser = () => {
|
const setupDefaultUser = () => {
|
||||||
return userModel
|
return userModel
|
||||||
.query()
|
.query()
|
||||||
.select(userModel.raw('COUNT(`id`) as `count`'))
|
.select('id', )
|
||||||
.where('is_deleted', 0)
|
.where('is_deleted', 0)
|
||||||
.first()
|
.first()
|
||||||
.then((row) => {
|
.then((row) => {
|
||||||
if (!row.count) {
|
if (!row || !row.id) {
|
||||||
// Create a new user and set password
|
// Create a new user and set password
|
||||||
let email = process.env.INITIAL_ADMIN_EMAIL || 'admin@example.com';
|
const email = (process.env.INITIAL_ADMIN_EMAIL || 'admin@example.com').toLowerCase();
|
||||||
let password = process.env.INITIAL_ADMIN_PASSWORD || 'changeme';
|
const password = process.env.INITIAL_ADMIN_PASSWORD || 'changeme';
|
||||||
|
|
||||||
logger.info('Creating a new user: ' + email + ' with password: ' + password);
|
logger.info(`Creating a new user: ${email} with password: ${password}`);
|
||||||
|
|
||||||
let data = {
|
const data = {
|
||||||
is_deleted: 0,
|
is_deleted: 0,
|
||||||
email: email,
|
email: email,
|
||||||
name: 'Administrator',
|
name: 'Administrator',
|
||||||
@@ -77,11 +77,11 @@ const setupDefaultUser = () => {
|
|||||||
const setupDefaultSettings = () => {
|
const setupDefaultSettings = () => {
|
||||||
return settingModel
|
return settingModel
|
||||||
.query()
|
.query()
|
||||||
.select(settingModel.raw('COUNT(`id`) as `count`'))
|
.select('id')
|
||||||
.where({id: 'default-site'})
|
.where({id: 'default-site'})
|
||||||
.first()
|
.first()
|
||||||
.then((row) => {
|
.then((row) => {
|
||||||
if (!row.count) {
|
if (!row || !row.id) {
|
||||||
settingModel
|
settingModel
|
||||||
.query()
|
.query()
|
||||||
.insert({
|
.insert({
|
||||||
@@ -113,20 +113,20 @@ const setupCertbotPlugins = () => {
|
|||||||
.andWhere('provider', 'letsencrypt')
|
.andWhere('provider', 'letsencrypt')
|
||||||
.then((certificates) => {
|
.then((certificates) => {
|
||||||
if (certificates && certificates.length) {
|
if (certificates && certificates.length) {
|
||||||
let plugins = [];
|
const plugins = [];
|
||||||
let promises = [];
|
const promises = [];
|
||||||
|
|
||||||
certificates.map(function (certificate) {
|
certificates.map((certificate) => {
|
||||||
if (certificate.meta && certificate.meta.dns_challenge === true) {
|
if (certificate.meta && certificate.meta.dns_challenge === true) {
|
||||||
if (plugins.indexOf(certificate.meta.dns_provider) === -1) {
|
if (plugins.indexOf(certificate.meta.dns_provider) === -1) {
|
||||||
plugins.push(certificate.meta.dns_provider);
|
plugins.push(certificate.meta.dns_provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure credentials file exists
|
// Make sure credentials file exists
|
||||||
const credentials_loc = '/etc/letsencrypt/credentials/credentials-' + certificate.id;
|
const credentials_loc = `/etc/letsencrypt/credentials/credentials-${certificate.id}`;
|
||||||
// Escape single quotes and backslashes
|
// Escape single quotes and backslashes
|
||||||
const escapedCredentials = certificate.meta.dns_provider_credentials.replaceAll('\'', '\\\'').replaceAll('\\', '\\\\');
|
const escapedCredentials = certificate.meta.dns_provider_credentials.replaceAll('\'', '\\\'').replaceAll('\\', '\\\\');
|
||||||
const credentials_cmd = '[ -f \'' + credentials_loc + '\' ] || { mkdir -p /etc/letsencrypt/credentials 2> /dev/null; echo \'' + escapedCredentials + '\' > \'' + credentials_loc + '\' && chmod 600 \'' + credentials_loc + '\'; }';
|
const credentials_cmd = `[ -f '${credentials_loc}' ] || { mkdir -p /etc/letsencrypt/credentials 2> /dev/null; echo '${escapedCredentials}' > '${credentials_loc}' && chmod 600 '${credentials_loc}'; }`;
|
||||||
promises.push(utils.exec(credentials_cmd));
|
promises.push(utils.exec(credentials_cmd));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -136,7 +136,7 @@ const setupCertbotPlugins = () => {
|
|||||||
if (promises.length) {
|
if (promises.length) {
|
||||||
return Promise.all(promises)
|
return Promise.all(promises)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
logger.info('Added Certbot plugins ' + plugins.join(', '));
|
logger.info(`Added Certbot plugins ${plugins.join(', ')}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -165,9 +165,7 @@ const setupLogrotation = () => {
|
|||||||
return runLogrotate();
|
return runLogrotate();
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = function () {
|
module.exports = () => setupDefaultUser()
|
||||||
return setupDefaultUser()
|
.then(setupDefaultSettings)
|
||||||
.then(setupDefaultSettings)
|
.then(setupCertbotPlugins)
|
||||||
.then(setupCertbotPlugins)
|
.then(setupLogrotation);
|
||||||
.then(setupLogrotation);
|
|
||||||
};
|
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
auth_basic "Authorization required";
|
auth_basic "Authorization required";
|
||||||
auth_basic_user_file /data/access/{{ access_list_id }};
|
auth_basic_user_file /data/access/{{ access_list_id }};
|
||||||
|
|
||||||
{% if access_list.pass_auth == 0 %}
|
{% if access_list.pass_auth == 0 or access_list.pass_auth == true %}
|
||||||
proxy_set_header Authorization "";
|
proxy_set_header Authorization "";
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
deny all;
|
deny all;
|
||||||
|
|
||||||
# Access checks must...
|
# Access checks must...
|
||||||
{% if access_list.satisfy_any == 1 %}
|
{% if access_list.satisfy_any == 1 or access_list.satisfy_any == true %}
|
||||||
satisfy any;
|
satisfy any;
|
||||||
{% else %}
|
{% else %}
|
||||||
satisfy all;
|
satisfy all;
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
{% if certificate.provider == "letsencrypt" %}
|
{% if certificate.provider == "letsencrypt" %}
|
||||||
# Let's Encrypt SSL
|
# Let's Encrypt SSL
|
||||||
include conf.d/include/letsencrypt-acme-challenge.conf;
|
include conf.d/include/letsencrypt-acme-challenge.conf;
|
||||||
|
include conf.d/include/ssl-cache.conf;
|
||||||
include conf.d/include/ssl-ciphers.conf;
|
include conf.d/include/ssl-ciphers.conf;
|
||||||
ssl_certificate /etc/letsencrypt/live/npm-{{ certificate_id }}/fullchain.pem;
|
ssl_certificate /etc/letsencrypt/live/npm-{{ certificate_id }}/fullchain.pem;
|
||||||
ssl_certificate_key /etc/letsencrypt/live/npm-{{ certificate_id }}/privkey.pem;
|
ssl_certificate_key /etc/letsencrypt/live/npm-{{ certificate_id }}/privkey.pem;
|
||||||
|
13
backend/templates/_certificates_stream.conf
Normal file
13
backend/templates/_certificates_stream.conf
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{% if certificate and certificate_id > 0 %}
|
||||||
|
{% if certificate.provider == "letsencrypt" %}
|
||||||
|
# Let's Encrypt SSL
|
||||||
|
include conf.d/include/ssl-cache-stream.conf;
|
||||||
|
include conf.d/include/ssl-ciphers.conf;
|
||||||
|
ssl_certificate /etc/letsencrypt/live/npm-{{ certificate_id }}/fullchain.pem;
|
||||||
|
ssl_certificate_key /etc/letsencrypt/live/npm-{{ certificate_id }}/privkey.pem;
|
||||||
|
{%- else %}
|
||||||
|
# Custom SSL
|
||||||
|
ssl_certificate /data/custom_ssl/npm-{{ certificate_id }}/fullchain.pem;
|
||||||
|
ssl_certificate_key /data/custom_ssl/npm-{{ certificate_id }}/privkey.pem;
|
||||||
|
{%- endif -%}
|
||||||
|
{%- endif -%}
|
@@ -5,11 +5,16 @@
|
|||||||
#listen [::]:80;
|
#listen [::]:80;
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if certificate -%}
|
{% if certificate -%}
|
||||||
listen 443 ssl{% if http2_support == 1 or http2_support == true %} http2{% endif %};
|
listen 443 ssl;
|
||||||
{% if ipv6 -%}
|
{% if ipv6 -%}
|
||||||
listen [::]:443 ssl{% if http2_support == 1 or http2_support == true %} http2{% endif %};
|
listen [::]:443 ssl;
|
||||||
{% else -%}
|
{% else -%}
|
||||||
#listen [::]:443;
|
#listen [::]:443;
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
server_name {{ domain_names | join: " " }};
|
server_name {{ domain_names | join: " " }};
|
||||||
|
{% if http2_support == 1 or http2_support == true %}
|
||||||
|
http2 on;
|
||||||
|
{% else -%}
|
||||||
|
http2 off;
|
||||||
|
{% endif %}
|
@@ -6,6 +6,7 @@
|
|||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
proxy_set_header X-Forwarded-For $remote_addr;
|
proxy_set_header X-Forwarded-For $remote_addr;
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
|
||||||
proxy_pass {{ forward_scheme }}://{{ forward_host }}:{{ forward_port }}{{ forward_path }};
|
proxy_pass {{ forward_scheme }}://{{ forward_host }}:{{ forward_port }}{{ forward_path }};
|
||||||
|
|
||||||
{% include "_access.conf" %}
|
{% include "_access.conf" %}
|
||||||
|
@@ -22,5 +22,7 @@ server {
|
|||||||
}
|
}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
# Custom
|
||||||
|
include /data/nginx/custom/server_dead[.]conf;
|
||||||
}
|
}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@@ -5,12 +5,10 @@
|
|||||||
{% if enabled %}
|
{% if enabled %}
|
||||||
{% if tcp_forwarding == 1 or tcp_forwarding == true -%}
|
{% if tcp_forwarding == 1 or tcp_forwarding == true -%}
|
||||||
server {
|
server {
|
||||||
listen {{ incoming_port }};
|
listen {{ incoming_port }} {%- if certificate %} ssl {%- endif %};
|
||||||
{% if ipv6 -%}
|
{% unless ipv6 -%} # {%- endunless -%} listen [::]:{{ incoming_port }} {%- if certificate %} ssl {%- endif %};
|
||||||
listen [::]:{{ incoming_port }};
|
|
||||||
{% else -%}
|
{%- include "_certificates_stream.conf" %}
|
||||||
#listen [::]:{{ incoming_port }};
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
proxy_pass {{ forwarding_host }}:{{ forwarding_port }};
|
proxy_pass {{ forwarding_host }}:{{ forwarding_port }};
|
||||||
|
|
||||||
@@ -19,14 +17,12 @@ server {
|
|||||||
include /data/nginx/custom/server_stream_tcp[.]conf;
|
include /data/nginx/custom/server_stream_tcp[.]conf;
|
||||||
}
|
}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if udp_forwarding == 1 or udp_forwarding == true %}
|
|
||||||
|
{% if udp_forwarding == 1 or udp_forwarding == true -%}
|
||||||
server {
|
server {
|
||||||
listen {{ incoming_port }} udp;
|
listen {{ incoming_port }} udp;
|
||||||
{% if ipv6 -%}
|
{% unless ipv6 -%} # {%- endunless -%} listen [::]:{{ incoming_port }} udp;
|
||||||
listen [::]:{{ incoming_port }} udp;
|
|
||||||
{% else -%}
|
|
||||||
#listen [::]:{{ incoming_port }} udp;
|
|
||||||
{% endif %}
|
|
||||||
proxy_pass {{ forwarding_host }}:{{ forwarding_port }};
|
proxy_pass {{ forwarding_host }}:{{ forwarding_port }};
|
||||||
|
|
||||||
# Custom
|
# Custom
|
||||||
|
@@ -412,6 +412,11 @@ async@^3.2.0:
|
|||||||
resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c"
|
resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c"
|
||||||
integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==
|
integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==
|
||||||
|
|
||||||
|
aws-ssl-profiles@^1.1.1:
|
||||||
|
version "1.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz#157dd77e9f19b1d123678e93f120e6f193022641"
|
||||||
|
integrity sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==
|
||||||
|
|
||||||
balanced-match@^1.0.0:
|
balanced-match@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||||
@@ -435,11 +440,6 @@ bcrypt@^5.0.0:
|
|||||||
node-addon-api "^3.0.0"
|
node-addon-api "^3.0.0"
|
||||||
node-pre-gyp "0.15.0"
|
node-pre-gyp "0.15.0"
|
||||||
|
|
||||||
bignumber.js@9.0.0:
|
|
||||||
version "9.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.0.tgz#805880f84a329b5eac6e7cb6f8274b6d82bdf075"
|
|
||||||
integrity sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==
|
|
||||||
|
|
||||||
binary-extensions@^2.0.0:
|
binary-extensions@^2.0.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.1.0.tgz#30fa40c9e7fe07dbc895678cd287024dea241dd9"
|
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.1.0.tgz#30fa40c9e7fe07dbc895678cd287024dea241dd9"
|
||||||
@@ -459,10 +459,10 @@ blueimp-md5@^2.16.0:
|
|||||||
resolved "https://registry.yarnpkg.com/blueimp-md5/-/blueimp-md5-2.17.0.tgz#f4fcac088b115f7b4045f19f5da59e9d01b1bb96"
|
resolved "https://registry.yarnpkg.com/blueimp-md5/-/blueimp-md5-2.17.0.tgz#f4fcac088b115f7b4045f19f5da59e9d01b1bb96"
|
||||||
integrity sha512-x5PKJHY5rHQYaADj6NwPUR2QRCUVSggPzrUKkeENpj871o9l9IefJbO2jkT5UvYykeOK9dx0VmkIo6dZ+vThYw==
|
integrity sha512-x5PKJHY5rHQYaADj6NwPUR2QRCUVSggPzrUKkeENpj871o9l9IefJbO2jkT5UvYykeOK9dx0VmkIo6dZ+vThYw==
|
||||||
|
|
||||||
body-parser@1.20.2, body-parser@^1.19.0:
|
body-parser@1.20.3, body-parser@^1.20.3:
|
||||||
version "1.20.2"
|
version "1.20.3"
|
||||||
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd"
|
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6"
|
||||||
integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==
|
integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==
|
||||||
dependencies:
|
dependencies:
|
||||||
bytes "3.1.2"
|
bytes "3.1.2"
|
||||||
content-type "~1.0.5"
|
content-type "~1.0.5"
|
||||||
@@ -472,7 +472,7 @@ body-parser@1.20.2, body-parser@^1.19.0:
|
|||||||
http-errors "2.0.0"
|
http-errors "2.0.0"
|
||||||
iconv-lite "0.4.24"
|
iconv-lite "0.4.24"
|
||||||
on-finished "2.4.1"
|
on-finished "2.4.1"
|
||||||
qs "6.11.0"
|
qs "6.13.0"
|
||||||
raw-body "2.5.2"
|
raw-body "2.5.2"
|
||||||
type-is "~1.6.18"
|
type-is "~1.6.18"
|
||||||
unpipe "1.0.0"
|
unpipe "1.0.0"
|
||||||
@@ -492,9 +492,9 @@ boxen@^4.2.0:
|
|||||||
widest-line "^3.1.0"
|
widest-line "^3.1.0"
|
||||||
|
|
||||||
brace-expansion@^1.1.7:
|
brace-expansion@^1.1.7:
|
||||||
version "1.1.11"
|
version "1.1.12"
|
||||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
|
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.12.tgz#ab9b454466e5a8cc3a187beaad580412a9c5b843"
|
||||||
integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
|
integrity sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==
|
||||||
dependencies:
|
dependencies:
|
||||||
balanced-match "^1.0.0"
|
balanced-match "^1.0.0"
|
||||||
concat-map "0.0.1"
|
concat-map "0.0.1"
|
||||||
@@ -830,9 +830,9 @@ crc32-stream@^4.0.2:
|
|||||||
readable-stream "^3.4.0"
|
readable-stream "^3.4.0"
|
||||||
|
|
||||||
cross-spawn@^7.0.2:
|
cross-spawn@^7.0.2:
|
||||||
version "7.0.3"
|
version "7.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
|
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f"
|
||||||
integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
|
integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==
|
||||||
dependencies:
|
dependencies:
|
||||||
path-key "^3.1.0"
|
path-key "^3.1.0"
|
||||||
shebang-command "^2.0.0"
|
shebang-command "^2.0.0"
|
||||||
@@ -910,6 +910,11 @@ delegates@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
|
resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
|
||||||
integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=
|
integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=
|
||||||
|
|
||||||
|
denque@^2.1.0:
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/denque/-/denque-2.1.0.tgz#e93e1a6569fb5e66f16a3c2a2964617d349d6ab1"
|
||||||
|
integrity sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==
|
||||||
|
|
||||||
depd@2.0.0, depd@^2.0.0:
|
depd@2.0.0, depd@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
|
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
|
||||||
@@ -988,6 +993,11 @@ encodeurl@~1.0.2:
|
|||||||
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
|
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
|
||||||
integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
|
integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
|
||||||
|
|
||||||
|
encodeurl@~2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58"
|
||||||
|
integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==
|
||||||
|
|
||||||
encoding@^0.1.12:
|
encoding@^0.1.12:
|
||||||
version "0.1.13"
|
version "0.1.13"
|
||||||
resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9"
|
resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9"
|
||||||
@@ -1185,37 +1195,37 @@ express-fileupload@^1.1.9:
|
|||||||
dependencies:
|
dependencies:
|
||||||
busboy "^0.3.1"
|
busboy "^0.3.1"
|
||||||
|
|
||||||
express@^4.19.2:
|
express@^4.20.0:
|
||||||
version "4.19.2"
|
version "4.20.0"
|
||||||
resolved "https://registry.yarnpkg.com/express/-/express-4.19.2.tgz#e25437827a3aa7f2a827bc8171bbbb664a356465"
|
resolved "https://registry.yarnpkg.com/express/-/express-4.20.0.tgz#f1d08e591fcec770c07be4767af8eb9bcfd67c48"
|
||||||
integrity sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==
|
integrity sha512-pLdae7I6QqShF5PnNTCVn4hI91Dx0Grkn2+IAsMTgMIKuQVte2dN9PeGSSAME2FR8anOhVA62QDIUaWVfEXVLw==
|
||||||
dependencies:
|
dependencies:
|
||||||
accepts "~1.3.8"
|
accepts "~1.3.8"
|
||||||
array-flatten "1.1.1"
|
array-flatten "1.1.1"
|
||||||
body-parser "1.20.2"
|
body-parser "1.20.3"
|
||||||
content-disposition "0.5.4"
|
content-disposition "0.5.4"
|
||||||
content-type "~1.0.4"
|
content-type "~1.0.4"
|
||||||
cookie "0.6.0"
|
cookie "0.6.0"
|
||||||
cookie-signature "1.0.6"
|
cookie-signature "1.0.6"
|
||||||
debug "2.6.9"
|
debug "2.6.9"
|
||||||
depd "2.0.0"
|
depd "2.0.0"
|
||||||
encodeurl "~1.0.2"
|
encodeurl "~2.0.0"
|
||||||
escape-html "~1.0.3"
|
escape-html "~1.0.3"
|
||||||
etag "~1.8.1"
|
etag "~1.8.1"
|
||||||
finalhandler "1.2.0"
|
finalhandler "1.2.0"
|
||||||
fresh "0.5.2"
|
fresh "0.5.2"
|
||||||
http-errors "2.0.0"
|
http-errors "2.0.0"
|
||||||
merge-descriptors "1.0.1"
|
merge-descriptors "1.0.3"
|
||||||
methods "~1.1.2"
|
methods "~1.1.2"
|
||||||
on-finished "2.4.1"
|
on-finished "2.4.1"
|
||||||
parseurl "~1.3.3"
|
parseurl "~1.3.3"
|
||||||
path-to-regexp "0.1.7"
|
path-to-regexp "0.1.10"
|
||||||
proxy-addr "~2.0.7"
|
proxy-addr "~2.0.7"
|
||||||
qs "6.11.0"
|
qs "6.11.0"
|
||||||
range-parser "~1.2.1"
|
range-parser "~1.2.1"
|
||||||
safe-buffer "5.2.1"
|
safe-buffer "5.2.1"
|
||||||
send "0.18.0"
|
send "0.19.0"
|
||||||
serve-static "1.15.0"
|
serve-static "1.16.0"
|
||||||
setprototypeof "1.2.0"
|
setprototypeof "1.2.0"
|
||||||
statuses "2.0.1"
|
statuses "2.0.1"
|
||||||
type-is "~1.6.18"
|
type-is "~1.6.18"
|
||||||
@@ -1411,6 +1421,13 @@ gauge@~2.7.3:
|
|||||||
strip-ansi "^3.0.1"
|
strip-ansi "^3.0.1"
|
||||||
wide-align "^1.1.0"
|
wide-align "^1.1.0"
|
||||||
|
|
||||||
|
generate-function@^2.3.1:
|
||||||
|
version "2.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.3.1.tgz#f069617690c10c868e73b8465746764f97c3479f"
|
||||||
|
integrity sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==
|
||||||
|
dependencies:
|
||||||
|
is-property "^1.0.2"
|
||||||
|
|
||||||
get-caller-file@^2.0.1:
|
get-caller-file@^2.0.1:
|
||||||
version "2.0.5"
|
version "2.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
|
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
|
||||||
@@ -1655,7 +1672,7 @@ iconv-lite@0.4.24, iconv-lite@^0.4.4:
|
|||||||
dependencies:
|
dependencies:
|
||||||
safer-buffer ">= 2.1.2 < 3"
|
safer-buffer ">= 2.1.2 < 3"
|
||||||
|
|
||||||
iconv-lite@^0.6.2:
|
iconv-lite@^0.6.2, iconv-lite@^0.6.3:
|
||||||
version "0.6.3"
|
version "0.6.3"
|
||||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
|
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
|
||||||
integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==
|
integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==
|
||||||
@@ -1858,6 +1875,11 @@ is-path-inside@^3.0.3:
|
|||||||
resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283"
|
resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283"
|
||||||
integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==
|
integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==
|
||||||
|
|
||||||
|
is-property@^1.0.2:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84"
|
||||||
|
integrity sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==
|
||||||
|
|
||||||
is-stream@^2.0.0:
|
is-stream@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3"
|
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3"
|
||||||
@@ -2078,6 +2100,11 @@ lodash@^4.17.21:
|
|||||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||||
|
|
||||||
|
long@^5.2.1:
|
||||||
|
version "5.2.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/long/-/long-5.2.3.tgz#a3ba97f3877cf1d778eccbcb048525ebb77499e1"
|
||||||
|
integrity sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==
|
||||||
|
|
||||||
lowercase-keys@^1.0.0, lowercase-keys@^1.0.1:
|
lowercase-keys@^1.0.0, lowercase-keys@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f"
|
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f"
|
||||||
@@ -2095,6 +2122,16 @@ lru-cache@^6.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
yallist "^4.0.0"
|
yallist "^4.0.0"
|
||||||
|
|
||||||
|
lru-cache@^7.14.1:
|
||||||
|
version "7.18.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89"
|
||||||
|
integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==
|
||||||
|
|
||||||
|
lru-cache@^8.0.0:
|
||||||
|
version "8.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-8.0.5.tgz#983fe337f3e176667f8e567cfcce7cb064ea214e"
|
||||||
|
integrity sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA==
|
||||||
|
|
||||||
make-dir@^3.0.0, make-dir@^3.1.0:
|
make-dir@^3.0.0, make-dir@^3.1.0:
|
||||||
version "3.1.0"
|
version "3.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
|
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
|
||||||
@@ -2129,10 +2166,10 @@ media-typer@0.3.0:
|
|||||||
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
|
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
|
||||||
integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
|
integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
|
||||||
|
|
||||||
merge-descriptors@1.0.1:
|
merge-descriptors@1.0.3:
|
||||||
version "1.0.1"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
|
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5"
|
||||||
integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=
|
integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==
|
||||||
|
|
||||||
methods@~1.1.2:
|
methods@~1.1.2:
|
||||||
version "1.1.2"
|
version "1.1.2"
|
||||||
@@ -2298,15 +2335,27 @@ ms@2.1.3, ms@^2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
|
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
|
||||||
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
|
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
|
||||||
|
|
||||||
mysql@^2.18.1:
|
mysql2@^3.11.1:
|
||||||
version "2.18.1"
|
version "3.11.1"
|
||||||
resolved "https://registry.yarnpkg.com/mysql/-/mysql-2.18.1.tgz#2254143855c5a8c73825e4522baf2ea021766717"
|
resolved "https://registry.yarnpkg.com/mysql2/-/mysql2-3.11.1.tgz#edfb856e2176fcf43d2cc066dd4959e9fc76ea85"
|
||||||
integrity sha512-Bca+gk2YWmqp2Uf6k5NFEurwY/0td0cpebAucFpY/3jhrwrVGuxU2uQFCHjU19SJfje0yQvi+rVWdq78hR5lig==
|
integrity sha512-Oc8Zffd0gpIJnJ/NOMp6IiiJJDdWc7nmWpS+UE3K9feTpYia8XkbgL6EaOJYz52f6+2pAoC0eAQqUzal4lnNGQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
bignumber.js "9.0.0"
|
aws-ssl-profiles "^1.1.1"
|
||||||
readable-stream "2.3.7"
|
denque "^2.1.0"
|
||||||
safe-buffer "5.1.2"
|
generate-function "^2.3.1"
|
||||||
sqlstring "2.3.1"
|
iconv-lite "^0.6.3"
|
||||||
|
long "^5.2.1"
|
||||||
|
lru-cache "^8.0.0"
|
||||||
|
named-placeholders "^1.1.3"
|
||||||
|
seq-queue "^0.0.5"
|
||||||
|
sqlstring "^2.3.2"
|
||||||
|
|
||||||
|
named-placeholders@^1.1.3:
|
||||||
|
version "1.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/named-placeholders/-/named-placeholders-1.1.3.tgz#df595799a36654da55dda6152ba7a137ad1d9351"
|
||||||
|
integrity sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==
|
||||||
|
dependencies:
|
||||||
|
lru-cache "^7.14.1"
|
||||||
|
|
||||||
natural-compare@^1.4.0:
|
natural-compare@^1.4.0:
|
||||||
version "1.4.0"
|
version "1.4.0"
|
||||||
@@ -2673,10 +2722,10 @@ path-parse@^1.0.7:
|
|||||||
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
|
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
|
||||||
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
|
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
|
||||||
|
|
||||||
path-to-regexp@0.1.7:
|
path-to-regexp@0.1.10:
|
||||||
version "0.1.7"
|
version "0.1.10"
|
||||||
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
|
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.10.tgz#67e9108c5c0551b9e5326064387de4763c4d5f8b"
|
||||||
integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=
|
integrity sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==
|
||||||
|
|
||||||
path@^0.12.7:
|
path@^0.12.7:
|
||||||
version "0.12.7"
|
version "0.12.7"
|
||||||
@@ -2686,11 +2735,67 @@ path@^0.12.7:
|
|||||||
process "^0.11.1"
|
process "^0.11.1"
|
||||||
util "^0.10.3"
|
util "^0.10.3"
|
||||||
|
|
||||||
|
pg-cloudflare@^1.1.1:
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz#e6d5833015b170e23ae819e8c5d7eaedb472ca98"
|
||||||
|
integrity sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==
|
||||||
|
|
||||||
pg-connection-string@2.5.0:
|
pg-connection-string@2.5.0:
|
||||||
version "2.5.0"
|
version "2.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.5.0.tgz#538cadd0f7e603fc09a12590f3b8a452c2c0cf34"
|
resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.5.0.tgz#538cadd0f7e603fc09a12590f3b8a452c2c0cf34"
|
||||||
integrity sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==
|
integrity sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==
|
||||||
|
|
||||||
|
pg-connection-string@^2.7.0:
|
||||||
|
version "2.7.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.7.0.tgz#f1d3489e427c62ece022dba98d5262efcb168b37"
|
||||||
|
integrity sha512-PI2W9mv53rXJQEOb8xNR8lH7Hr+EKa6oJa38zsK0S/ky2er16ios1wLKhZyxzD7jUReiWokc9WK5nxSnC7W1TA==
|
||||||
|
|
||||||
|
pg-int8@1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/pg-int8/-/pg-int8-1.0.1.tgz#943bd463bf5b71b4170115f80f8efc9a0c0eb78c"
|
||||||
|
integrity sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==
|
||||||
|
|
||||||
|
pg-pool@^3.7.0:
|
||||||
|
version "3.7.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-3.7.0.tgz#d4d3c7ad640f8c6a2245adc369bafde4ebb8cbec"
|
||||||
|
integrity sha512-ZOBQForurqh4zZWjrgSwwAtzJ7QiRX0ovFkZr2klsen3Nm0aoh33Ls0fzfv3imeH/nw/O27cjdz5kzYJfeGp/g==
|
||||||
|
|
||||||
|
pg-protocol@^1.7.0:
|
||||||
|
version "1.7.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/pg-protocol/-/pg-protocol-1.7.0.tgz#ec037c87c20515372692edac8b63cf4405448a93"
|
||||||
|
integrity sha512-hTK/mE36i8fDDhgDFjy6xNOG+LCorxLG3WO17tku+ij6sVHXh1jQUJ8hYAnRhNla4QVD2H8er/FOjc/+EgC6yQ==
|
||||||
|
|
||||||
|
pg-types@^2.1.0:
|
||||||
|
version "2.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/pg-types/-/pg-types-2.2.0.tgz#2d0250d636454f7cfa3b6ae0382fdfa8063254a3"
|
||||||
|
integrity sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==
|
||||||
|
dependencies:
|
||||||
|
pg-int8 "1.0.1"
|
||||||
|
postgres-array "~2.0.0"
|
||||||
|
postgres-bytea "~1.0.0"
|
||||||
|
postgres-date "~1.0.4"
|
||||||
|
postgres-interval "^1.1.0"
|
||||||
|
|
||||||
|
pg@^8.13.1:
|
||||||
|
version "8.13.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/pg/-/pg-8.13.1.tgz#6498d8b0a87ff76c2df7a32160309d3168c0c080"
|
||||||
|
integrity sha512-OUir1A0rPNZlX//c7ksiu7crsGZTKSOXJPgtNiHGIlC9H0lO+NC6ZDYksSgBYY/thSWhnSRBv8w1lieNNGATNQ==
|
||||||
|
dependencies:
|
||||||
|
pg-connection-string "^2.7.0"
|
||||||
|
pg-pool "^3.7.0"
|
||||||
|
pg-protocol "^1.7.0"
|
||||||
|
pg-types "^2.1.0"
|
||||||
|
pgpass "1.x"
|
||||||
|
optionalDependencies:
|
||||||
|
pg-cloudflare "^1.1.1"
|
||||||
|
|
||||||
|
pgpass@1.x:
|
||||||
|
version "1.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/pgpass/-/pgpass-1.0.5.tgz#9b873e4a564bb10fa7a7dbd55312728d422a223d"
|
||||||
|
integrity sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==
|
||||||
|
dependencies:
|
||||||
|
split2 "^4.1.0"
|
||||||
|
|
||||||
picomatch@^2.0.4, picomatch@^2.2.1:
|
picomatch@^2.0.4, picomatch@^2.2.1:
|
||||||
version "2.2.2"
|
version "2.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad"
|
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad"
|
||||||
@@ -2709,6 +2814,28 @@ pkg-conf@^2.1.0:
|
|||||||
find-up "^2.0.0"
|
find-up "^2.0.0"
|
||||||
load-json-file "^4.0.0"
|
load-json-file "^4.0.0"
|
||||||
|
|
||||||
|
postgres-array@~2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/postgres-array/-/postgres-array-2.0.0.tgz#48f8fce054fbc69671999329b8834b772652d82e"
|
||||||
|
integrity sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==
|
||||||
|
|
||||||
|
postgres-bytea@~1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/postgres-bytea/-/postgres-bytea-1.0.0.tgz#027b533c0aa890e26d172d47cf9ccecc521acd35"
|
||||||
|
integrity sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==
|
||||||
|
|
||||||
|
postgres-date@~1.0.4:
|
||||||
|
version "1.0.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/postgres-date/-/postgres-date-1.0.7.tgz#51bc086006005e5061c591cee727f2531bf641a8"
|
||||||
|
integrity sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==
|
||||||
|
|
||||||
|
postgres-interval@^1.1.0:
|
||||||
|
version "1.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/postgres-interval/-/postgres-interval-1.2.0.tgz#b460c82cb1587507788819a06aa0fffdb3544695"
|
||||||
|
integrity sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==
|
||||||
|
dependencies:
|
||||||
|
xtend "^4.0.0"
|
||||||
|
|
||||||
prelude-ls@^1.2.1:
|
prelude-ls@^1.2.1:
|
||||||
version "1.2.1"
|
version "1.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
|
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
|
||||||
@@ -2792,6 +2919,13 @@ qs@6.11.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
side-channel "^1.0.4"
|
side-channel "^1.0.4"
|
||||||
|
|
||||||
|
qs@6.13.0:
|
||||||
|
version "6.13.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906"
|
||||||
|
integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==
|
||||||
|
dependencies:
|
||||||
|
side-channel "^1.0.6"
|
||||||
|
|
||||||
querystring@0.2.0:
|
querystring@0.2.0:
|
||||||
version "0.2.0"
|
version "0.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
|
resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
|
||||||
@@ -2827,7 +2961,7 @@ rc@^1.2.7, rc@^1.2.8:
|
|||||||
minimist "^1.2.0"
|
minimist "^1.2.0"
|
||||||
strip-json-comments "~2.0.1"
|
strip-json-comments "~2.0.1"
|
||||||
|
|
||||||
readable-stream@2.3.7, readable-stream@^2.0.0, readable-stream@^2.0.5, readable-stream@^2.0.6:
|
readable-stream@^2.0.0, readable-stream@^2.0.5, readable-stream@^2.0.6:
|
||||||
version "2.3.7"
|
version "2.3.7"
|
||||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
|
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
|
||||||
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
|
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
|
||||||
@@ -3019,10 +3153,34 @@ send@0.18.0:
|
|||||||
range-parser "~1.2.1"
|
range-parser "~1.2.1"
|
||||||
statuses "2.0.1"
|
statuses "2.0.1"
|
||||||
|
|
||||||
serve-static@1.15.0:
|
send@0.19.0:
|
||||||
version "1.15.0"
|
version "0.19.0"
|
||||||
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540"
|
resolved "https://registry.yarnpkg.com/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8"
|
||||||
integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==
|
integrity sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==
|
||||||
|
dependencies:
|
||||||
|
debug "2.6.9"
|
||||||
|
depd "2.0.0"
|
||||||
|
destroy "1.2.0"
|
||||||
|
encodeurl "~1.0.2"
|
||||||
|
escape-html "~1.0.3"
|
||||||
|
etag "~1.8.1"
|
||||||
|
fresh "0.5.2"
|
||||||
|
http-errors "2.0.0"
|
||||||
|
mime "1.6.0"
|
||||||
|
ms "2.1.3"
|
||||||
|
on-finished "2.4.1"
|
||||||
|
range-parser "~1.2.1"
|
||||||
|
statuses "2.0.1"
|
||||||
|
|
||||||
|
seq-queue@^0.0.5:
|
||||||
|
version "0.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/seq-queue/-/seq-queue-0.0.5.tgz#d56812e1c017a6e4e7c3e3a37a1da6d78dd3c93e"
|
||||||
|
integrity sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==
|
||||||
|
|
||||||
|
serve-static@1.16.0:
|
||||||
|
version "1.16.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.16.0.tgz#2bf4ed49f8af311b519c46f272bf6ac3baf38a92"
|
||||||
|
integrity sha512-pDLK8zwl2eKaYrs8mrPZBJua4hMplRWJ1tIFksVC3FtBEBnl8dxgeHtsaMS8DhS9i4fLObaon6ABoc4/hQGdPA==
|
||||||
dependencies:
|
dependencies:
|
||||||
encodeurl "~1.0.2"
|
encodeurl "~1.0.2"
|
||||||
escape-html "~1.0.3"
|
escape-html "~1.0.3"
|
||||||
@@ -3063,7 +3221,7 @@ shebang-regex@^3.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
|
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
|
||||||
integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
|
integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
|
||||||
|
|
||||||
side-channel@^1.0.4:
|
side-channel@^1.0.4, side-channel@^1.0.6:
|
||||||
version "1.0.6"
|
version "1.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2"
|
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2"
|
||||||
integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==
|
integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==
|
||||||
@@ -3114,6 +3272,11 @@ socks@^2.6.2:
|
|||||||
ip "^2.0.0"
|
ip "^2.0.0"
|
||||||
smart-buffer "^4.2.0"
|
smart-buffer "^4.2.0"
|
||||||
|
|
||||||
|
split2@^4.1.0:
|
||||||
|
version "4.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/split2/-/split2-4.2.0.tgz#c9c5920904d148bab0b9f67145f245a86aadbfa4"
|
||||||
|
integrity sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==
|
||||||
|
|
||||||
sprintf-js@~1.0.2:
|
sprintf-js@~1.0.2:
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
|
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
|
||||||
@@ -3130,10 +3293,10 @@ sqlite3@5.1.6:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
node-gyp "8.x"
|
node-gyp "8.x"
|
||||||
|
|
||||||
sqlstring@2.3.1:
|
sqlstring@^2.3.2:
|
||||||
version "2.3.1"
|
version "2.3.3"
|
||||||
resolved "https://registry.yarnpkg.com/sqlstring/-/sqlstring-2.3.1.tgz#475393ff9e91479aea62dcaf0ca3d14983a7fb40"
|
resolved "https://registry.yarnpkg.com/sqlstring/-/sqlstring-2.3.3.tgz#2ddc21f03bce2c387ed60680e739922c65751d0c"
|
||||||
integrity sha1-R1OT/56RR5rqYtyvDKPRSYOn+0A=
|
integrity sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==
|
||||||
|
|
||||||
ssri@^8.0.0, ssri@^8.0.1:
|
ssri@^8.0.0, ssri@^8.0.1:
|
||||||
version "8.0.1"
|
version "8.0.1"
|
||||||
@@ -3585,6 +3748,11 @@ xdg-basedir@^4.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13"
|
resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13"
|
||||||
integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==
|
integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==
|
||||||
|
|
||||||
|
xtend@^4.0.0:
|
||||||
|
version "4.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
|
||||||
|
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
|
||||||
|
|
||||||
y18n@^4.0.0:
|
y18n@^4.0.0:
|
||||||
version "4.0.1"
|
version "4.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.1.tgz#8db2b83c31c5d75099bb890b23f3094891e247d4"
|
resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.1.tgz#8db2b83c31c5d75099bb890b23f3094891e247d4"
|
||||||
|
@@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
# This file assumes that the frontend has been built using ./scripts/frontend-build
|
# This file assumes that the frontend has been built using ./scripts/frontend-build
|
||||||
|
|
||||||
|
FROM nginxproxymanager/testca AS testca
|
||||||
|
FROM letsencrypt/pebble AS pebbleca
|
||||||
FROM nginxproxymanager/nginx-full:certbot-node
|
FROM nginxproxymanager/nginx-full:certbot-node
|
||||||
|
|
||||||
ARG TARGETPLATFORM
|
ARG TARGETPLATFORM
|
||||||
@@ -45,6 +47,8 @@ RUN yarn install \
|
|||||||
|
|
||||||
# add late to limit cache-busting by modifications
|
# add late to limit cache-busting by modifications
|
||||||
COPY docker/rootfs /
|
COPY docker/rootfs /
|
||||||
|
COPY --from=pebbleca /test/certs/pebble.minica.pem /etc/ssl/certs/pebble.minica.pem
|
||||||
|
COPY --from=testca /home/step/certs/root_ca.crt /etc/ssl/certs/NginxProxyManager.crt
|
||||||
|
|
||||||
# Remove frontend service not required for prod, dev nginx config as well
|
# Remove frontend service not required for prod, dev nginx config as well
|
||||||
RUN rm -rf /etc/s6-overlay/s6-rc.d/user/contents.d/frontend /etc/nginx/conf.d/dev.conf \
|
RUN rm -rf /etc/s6-overlay/s6-rc.d/user/contents.d/frontend /etc/nginx/conf.d/dev.conf \
|
||||||
|
8
docker/ci.env
Normal file
8
docker/ci.env
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
AUTHENTIK_SECRET_KEY=gl8woZe8L6IIX8SC0c5Ocsj0xPkX5uJo5DVZCFl+L/QGbzuplfutYuua2ODNLEiDD3aFd9H2ylJmrke0
|
||||||
|
AUTHENTIK_REDIS__HOST=authentik-redis
|
||||||
|
AUTHENTIK_POSTGRESQL__HOST=db-postgres
|
||||||
|
AUTHENTIK_POSTGRESQL__USER=authentik
|
||||||
|
AUTHENTIK_POSTGRESQL__NAME=authentik
|
||||||
|
AUTHENTIK_POSTGRESQL__PASSWORD=07EKS5NLI6Tpv68tbdvrxfvj
|
||||||
|
AUTHENTIK_BOOTSTRAP_PASSWORD=admin
|
||||||
|
AUTHENTIK_BOOTSTRAP_EMAIL=admin@example.com
|
BIN
docker/ci/postgres/authentik.sql.gz
Normal file
BIN
docker/ci/postgres/authentik.sql.gz
Normal file
Binary file not shown.
@@ -1,7 +1,10 @@
|
|||||||
|
FROM nginxproxymanager/testca AS testca
|
||||||
|
FROM letsencrypt/pebble AS pebbleca
|
||||||
FROM nginxproxymanager/nginx-full:certbot-node
|
FROM nginxproxymanager/nginx-full:certbot-node
|
||||||
LABEL maintainer="Jamie Curnow <jc@jc21.com>"
|
LABEL maintainer="Jamie Curnow <jc@jc21.com>"
|
||||||
|
|
||||||
# See: https://github.com/just-containers/s6-overlay/blob/master/README.md
|
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||||
|
|
||||||
ENV SUPPRESS_NO_CONFIG_WARNING=1 \
|
ENV SUPPRESS_NO_CONFIG_WARNING=1 \
|
||||||
S6_BEHAVIOUR_IF_STAGE2_FAILS=1 \
|
S6_BEHAVIOUR_IF_STAGE2_FAILS=1 \
|
||||||
S6_CMD_WAIT_FOR_SERVICES_MAXTIME=0 \
|
S6_CMD_WAIT_FOR_SERVICES_MAXTIME=0 \
|
||||||
@@ -17,17 +20,21 @@ RUN echo "fs.file-max = 65535" > /etc/sysctl.conf \
|
|||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Task
|
# Task
|
||||||
RUN cd /usr \
|
WORKDIR /usr
|
||||||
&& curl -sL https://taskfile.dev/install.sh | sh \
|
RUN curl -sL https://taskfile.dev/install.sh | sh
|
||||||
&& cd /root
|
WORKDIR /root
|
||||||
|
|
||||||
COPY rootfs /
|
COPY rootfs /
|
||||||
RUN rm -f /etc/nginx/conf.d/production.conf
|
|
||||||
RUN chmod 644 /etc/logrotate.d/nginx-proxy-manager
|
|
||||||
|
|
||||||
# s6 overlay
|
|
||||||
COPY scripts/install-s6 /tmp/install-s6
|
COPY scripts/install-s6 /tmp/install-s6
|
||||||
RUN /tmp/install-s6 "${TARGETPLATFORM}" && rm -f /tmp/install-s6
|
RUN rm -f /etc/nginx/conf.d/production.conf \
|
||||||
|
&& chmod 644 /etc/logrotate.d/nginx-proxy-manager \
|
||||||
|
&& /tmp/install-s6 "${TARGETPLATFORM}" \
|
||||||
|
&& rm -f /tmp/install-s6 \
|
||||||
|
&& chmod 644 -R /root/.cache
|
||||||
|
|
||||||
|
# Certs for testing purposes
|
||||||
|
COPY --from=pebbleca /test/certs/pebble.minica.pem /etc/ssl/certs/pebble.minica.pem
|
||||||
|
COPY --from=testca /home/step/certs/root_ca.crt /etc/ssl/certs/NginxProxyManager.crt
|
||||||
|
|
||||||
EXPOSE 80 81 443
|
EXPOSE 80 81 443
|
||||||
ENTRYPOINT [ "/init" ]
|
ENTRYPOINT [ "/init" ]
|
||||||
|
92
docker/dev/squid.conf
Normal file
92
docker/dev/squid.conf
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
# WELCOME TO SQUID 6.6
|
||||||
|
# ----------------------------
|
||||||
|
#
|
||||||
|
# This is the documentation for the Squid configuration file.
|
||||||
|
# This documentation can also be found online at:
|
||||||
|
# http://www.squid-cache.org/Doc/config/
|
||||||
|
#
|
||||||
|
# You may wish to look at the Squid home page and wiki for the
|
||||||
|
# FAQ and other documentation:
|
||||||
|
# http://www.squid-cache.org/
|
||||||
|
# https://wiki.squid-cache.org/SquidFaq
|
||||||
|
# https://wiki.squid-cache.org/ConfigExamples
|
||||||
|
#
|
||||||
|
|
||||||
|
# Example rule allowing access from your local networks.
|
||||||
|
# Adapt to list your (internal) IP networks from where browsing
|
||||||
|
# should be allowed
|
||||||
|
acl localnet src 0.0.0.1-0.255.255.255 # RFC 1122 "this" network (LAN)
|
||||||
|
acl localnet src 10.0.0.0/8 # RFC 1918 local private network (LAN)
|
||||||
|
acl localnet src 100.64.0.0/10 # RFC 6598 shared address space (CGN)
|
||||||
|
acl localnet src 169.254.0.0/16 # RFC 3927 link-local (directly plugged) machines
|
||||||
|
acl localnet src 172.0.0.0/8
|
||||||
|
acl localnet src 192.168.0.0/16 # RFC 1918 local private network (LAN)
|
||||||
|
acl localnet src fc00::/7 # RFC 4193 local private network range
|
||||||
|
acl localnet src fe80::/10 # RFC 4291 link-local (directly plugged) machines
|
||||||
|
|
||||||
|
acl SSL_ports port 443
|
||||||
|
acl Safe_ports port 80 # http
|
||||||
|
acl Safe_ports port 81
|
||||||
|
acl Safe_ports port 443 # https
|
||||||
|
|
||||||
|
#
|
||||||
|
# Recommended minimum Access Permission configuration:
|
||||||
|
#
|
||||||
|
# Deny requests to certain unsafe ports
|
||||||
|
http_access deny !Safe_ports
|
||||||
|
|
||||||
|
# Deny CONNECT to other than secure SSL ports
|
||||||
|
http_access deny CONNECT !SSL_ports
|
||||||
|
|
||||||
|
# Only allow cachemgr access from localhost
|
||||||
|
http_access allow localhost manager
|
||||||
|
http_access deny manager
|
||||||
|
|
||||||
|
# This default configuration only allows localhost requests because a more
|
||||||
|
# permissive Squid installation could introduce new attack vectors into the
|
||||||
|
# network by proxying external TCP connections to unprotected services.
|
||||||
|
http_access allow localhost
|
||||||
|
|
||||||
|
# The two deny rules below are unnecessary in this default configuration
|
||||||
|
# because they are followed by a "deny all" rule. However, they may become
|
||||||
|
# critically important when you start allowing external requests below them.
|
||||||
|
|
||||||
|
# Protect web applications running on the same server as Squid. They often
|
||||||
|
# assume that only local users can access them at "localhost" ports.
|
||||||
|
http_access deny to_localhost
|
||||||
|
|
||||||
|
# Protect cloud servers that provide local users with sensitive info about
|
||||||
|
# their server via certain well-known link-local (a.k.a. APIPA) addresses.
|
||||||
|
http_access deny to_linklocal
|
||||||
|
|
||||||
|
#
|
||||||
|
# INSERT YOUR OWN RULE(S) HERE TO ALLOW ACCESS FROM YOUR CLIENTS
|
||||||
|
#
|
||||||
|
include /etc/squid/conf.d/*.conf
|
||||||
|
|
||||||
|
# For example, to allow access from your local networks, you may uncomment the
|
||||||
|
# following rule (and/or add rules that match your definition of "local"):
|
||||||
|
# http_access allow localnet
|
||||||
|
|
||||||
|
# And finally deny all other access to this proxy
|
||||||
|
http_access deny all
|
||||||
|
|
||||||
|
# Squid normally listens to port 3128
|
||||||
|
http_port 3128
|
||||||
|
|
||||||
|
# Leave coredumps in the first cache dir
|
||||||
|
coredump_dir /var/spool/squid
|
||||||
|
|
||||||
|
#
|
||||||
|
# Add any of your own refresh_pattern entries above these.
|
||||||
|
#
|
||||||
|
refresh_pattern ^ftp: 1440 20% 10080
|
||||||
|
refresh_pattern -i (/cgi-bin/|\?) 0 0% 0
|
||||||
|
refresh_pattern \/(Packages|Sources)(|\.bz2|\.gz|\.xz)$ 0 0% 0 refresh-ims
|
||||||
|
refresh_pattern \/Release(|\.gpg)$ 0 0% 0 refresh-ims
|
||||||
|
refresh_pattern \/InRelease$ 0 0% 0 refresh-ims
|
||||||
|
refresh_pattern \/(Translation-.*)(|\.bz2|\.gz|\.xz)$ 0 0% 0 refresh-ims
|
||||||
|
# example pattern for deb packages
|
||||||
|
#refresh_pattern (\.deb|\.udeb)$ 129600 100% 129600
|
||||||
|
refresh_pattern . 0 20% 4320
|
||||||
|
|
@@ -18,6 +18,7 @@ services:
|
|||||||
MYSQL_DATABASE: 'npm'
|
MYSQL_DATABASE: 'npm'
|
||||||
MYSQL_USER: 'npm'
|
MYSQL_USER: 'npm'
|
||||||
MYSQL_PASSWORD: 'npmpass'
|
MYSQL_PASSWORD: 'npmpass'
|
||||||
|
MARIADB_AUTO_UPGRADE: '1'
|
||||||
volumes:
|
volumes:
|
||||||
- mysql_vol:/var/lib/mysql
|
- mysql_vol:/var/lib/mysql
|
||||||
networks:
|
networks:
|
||||||
|
78
docker/docker-compose.ci.postgres.yml
Normal file
78
docker/docker-compose.ci.postgres.yml
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
# WARNING: This is a CI docker-compose file used for building and testing of the entire app, it should not be used for production.
|
||||||
|
services:
|
||||||
|
|
||||||
|
cypress:
|
||||||
|
environment:
|
||||||
|
CYPRESS_stack: 'postgres'
|
||||||
|
|
||||||
|
fullstack:
|
||||||
|
environment:
|
||||||
|
DB_POSTGRES_HOST: 'db-postgres'
|
||||||
|
DB_POSTGRES_PORT: '5432'
|
||||||
|
DB_POSTGRES_USER: 'npm'
|
||||||
|
DB_POSTGRES_PASSWORD: 'npmpass'
|
||||||
|
DB_POSTGRES_NAME: 'npm'
|
||||||
|
depends_on:
|
||||||
|
- db-postgres
|
||||||
|
- authentik
|
||||||
|
- authentik-worker
|
||||||
|
- authentik-ldap
|
||||||
|
|
||||||
|
db-postgres:
|
||||||
|
image: postgres:latest
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: 'npm'
|
||||||
|
POSTGRES_PASSWORD: 'npmpass'
|
||||||
|
POSTGRES_DB: 'npm'
|
||||||
|
volumes:
|
||||||
|
- psql_vol:/var/lib/postgresql/data
|
||||||
|
- ./ci/postgres:/docker-entrypoint-initdb.d
|
||||||
|
networks:
|
||||||
|
- fulltest
|
||||||
|
|
||||||
|
authentik-redis:
|
||||||
|
image: 'redis:alpine'
|
||||||
|
command: --save 60 1 --loglevel warning
|
||||||
|
restart: unless-stopped
|
||||||
|
healthcheck:
|
||||||
|
test: ['CMD-SHELL', 'redis-cli ping | grep PONG']
|
||||||
|
start_period: 20s
|
||||||
|
interval: 30s
|
||||||
|
retries: 5
|
||||||
|
timeout: 3s
|
||||||
|
volumes:
|
||||||
|
- redis_vol:/data
|
||||||
|
|
||||||
|
authentik:
|
||||||
|
image: ghcr.io/goauthentik/server:2024.10.1
|
||||||
|
restart: unless-stopped
|
||||||
|
command: server
|
||||||
|
env_file:
|
||||||
|
- ci.env
|
||||||
|
depends_on:
|
||||||
|
- authentik-redis
|
||||||
|
- db-postgres
|
||||||
|
|
||||||
|
authentik-worker:
|
||||||
|
image: ghcr.io/goauthentik/server:2024.10.1
|
||||||
|
restart: unless-stopped
|
||||||
|
command: worker
|
||||||
|
env_file:
|
||||||
|
- ci.env
|
||||||
|
depends_on:
|
||||||
|
- authentik-redis
|
||||||
|
- db-postgres
|
||||||
|
|
||||||
|
authentik-ldap:
|
||||||
|
image: ghcr.io/goauthentik/ldap:2024.10.1
|
||||||
|
environment:
|
||||||
|
AUTHENTIK_HOST: 'http://authentik:9000'
|
||||||
|
AUTHENTIK_INSECURE: 'true'
|
||||||
|
AUTHENTIK_TOKEN: 'wKYZuRcI0ETtb8vWzMCr04oNbhrQUUICy89hSpDln1OEKLjiNEuQ51044Vkp'
|
||||||
|
restart: unless-stopped
|
||||||
|
depends_on:
|
||||||
|
- authentik
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
psql_vol:
|
||||||
|
redis_vol:
|
@@ -9,6 +9,9 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
DEBUG: 'true'
|
DEBUG: 'true'
|
||||||
FORCE_COLOR: 1
|
FORCE_COLOR: 1
|
||||||
|
# Required for DNS Certificate provisioning in CI
|
||||||
|
LE_SERVER: 'https://ca.internal/acme/acme/directory'
|
||||||
|
REQUESTS_CA_BUNDLE: '/etc/ssl/certs/NginxProxyManager.crt'
|
||||||
volumes:
|
volumes:
|
||||||
- 'npm_data_ci:/data'
|
- 'npm_data_ci:/data'
|
||||||
- 'npm_le_ci:/etc/letsencrypt'
|
- 'npm_le_ci:/etc/letsencrypt'
|
||||||
@@ -19,6 +22,10 @@ services:
|
|||||||
test: ["CMD", "/usr/bin/check-health"]
|
test: ["CMD", "/usr/bin/check-health"]
|
||||||
interval: 10s
|
interval: 10s
|
||||||
timeout: 3s
|
timeout: 3s
|
||||||
|
expose:
|
||||||
|
- '80-81/tcp'
|
||||||
|
- '443/tcp'
|
||||||
|
- '1500-1503/tcp'
|
||||||
networks:
|
networks:
|
||||||
fulltest:
|
fulltest:
|
||||||
aliases:
|
aliases:
|
||||||
@@ -37,7 +44,7 @@ services:
|
|||||||
- ca.internal
|
- ca.internal
|
||||||
|
|
||||||
pdns:
|
pdns:
|
||||||
image: pschiffe/pdns-mysql
|
image: pschiffe/pdns-mysql:4.8
|
||||||
volumes:
|
volumes:
|
||||||
- '/etc/localtime:/etc/localtime:ro'
|
- '/etc/localtime:/etc/localtime:ro'
|
||||||
environment:
|
environment:
|
||||||
@@ -91,14 +98,25 @@ services:
|
|||||||
context: ../
|
context: ../
|
||||||
dockerfile: test/cypress/Dockerfile
|
dockerfile: test/cypress/Dockerfile
|
||||||
environment:
|
environment:
|
||||||
CYPRESS_baseUrl: 'http://fullstack:81'
|
HTTP_PROXY: 'squid:3128'
|
||||||
|
HTTPS_PROXY: 'squid:3128'
|
||||||
volumes:
|
volumes:
|
||||||
- 'cypress_logs:/results'
|
- 'cypress_logs:/test/results'
|
||||||
- './dev/resolv.conf:/etc/resolv.conf:ro'
|
- './dev/resolv.conf:/etc/resolv.conf:ro'
|
||||||
|
- '/etc/localtime:/etc/localtime:ro'
|
||||||
command: cypress run --browser chrome --config-file=cypress/config/ci.js
|
command: cypress run --browser chrome --config-file=cypress/config/ci.js
|
||||||
networks:
|
networks:
|
||||||
- fulltest
|
- fulltest
|
||||||
|
|
||||||
|
squid:
|
||||||
|
image: ubuntu/squid
|
||||||
|
volumes:
|
||||||
|
- './dev/squid.conf:/etc/squid/squid.conf:ro'
|
||||||
|
- './dev/resolv.conf:/etc/resolv.conf:ro'
|
||||||
|
- '/etc/localtime:/etc/localtime:ro'
|
||||||
|
networks:
|
||||||
|
- fulltest
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
cypress_logs:
|
cypress_logs:
|
||||||
npm_data_ci:
|
npm_data_ci:
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
# WARNING: This is a DEVELOPMENT docker-compose file, it should not be used for production.
|
# WARNING: This is a DEVELOPMENT docker-compose file, it should not be used for production.
|
||||||
services:
|
services:
|
||||||
|
|
||||||
npm:
|
fullstack:
|
||||||
image: nginxproxymanager:dev
|
image: npm2dev:core
|
||||||
container_name: npm_core
|
container_name: npm2dev.core
|
||||||
build:
|
build:
|
||||||
context: ./
|
context: ./
|
||||||
dockerfile: ./dev/Dockerfile
|
dockerfile: ./dev/Dockerfile
|
||||||
@@ -12,7 +12,11 @@ services:
|
|||||||
- 3081:81
|
- 3081:81
|
||||||
- 3443:443
|
- 3443:443
|
||||||
networks:
|
networks:
|
||||||
- nginx_proxy_manager
|
nginx_proxy_manager:
|
||||||
|
aliases:
|
||||||
|
- website1.example.com
|
||||||
|
- website2.example.com
|
||||||
|
- website3.example.com
|
||||||
environment:
|
environment:
|
||||||
PUID: 1000
|
PUID: 1000
|
||||||
PGID: 1000
|
PGID: 1000
|
||||||
@@ -22,26 +26,44 @@ services:
|
|||||||
DEVELOPMENT: 'true'
|
DEVELOPMENT: 'true'
|
||||||
LE_STAGING: 'true'
|
LE_STAGING: 'true'
|
||||||
# db:
|
# db:
|
||||||
DB_MYSQL_HOST: 'db'
|
# DB_MYSQL_HOST: 'db'
|
||||||
DB_MYSQL_PORT: '3306'
|
# DB_MYSQL_PORT: '3306'
|
||||||
DB_MYSQL_USER: 'npm'
|
# DB_MYSQL_USER: 'npm'
|
||||||
DB_MYSQL_PASSWORD: 'npm'
|
# DB_MYSQL_PASSWORD: 'npm'
|
||||||
DB_MYSQL_NAME: 'npm'
|
# DB_MYSQL_NAME: 'npm'
|
||||||
|
# db-postgres:
|
||||||
|
DB_POSTGRES_HOST: 'db-postgres'
|
||||||
|
DB_POSTGRES_PORT: '5432'
|
||||||
|
DB_POSTGRES_USER: 'npm'
|
||||||
|
DB_POSTGRES_PASSWORD: 'npmpass'
|
||||||
|
DB_POSTGRES_NAME: 'npm'
|
||||||
# DB_SQLITE_FILE: "/data/database.sqlite"
|
# DB_SQLITE_FILE: "/data/database.sqlite"
|
||||||
# DISABLE_IPV6: "true"
|
# DISABLE_IPV6: "true"
|
||||||
|
# Required for DNS Certificate provisioning testing:
|
||||||
|
LE_SERVER: 'https://ca.internal/acme/acme/directory'
|
||||||
|
REQUESTS_CA_BUNDLE: '/etc/ssl/certs/NginxProxyManager.crt'
|
||||||
volumes:
|
volumes:
|
||||||
- npm_data:/data
|
- npm_data:/data
|
||||||
- le_data:/etc/letsencrypt
|
- le_data:/etc/letsencrypt
|
||||||
|
- './dev/resolv.conf:/etc/resolv.conf:ro'
|
||||||
- ../backend:/app
|
- ../backend:/app
|
||||||
- ../frontend:/app/frontend
|
- ../frontend:/app/frontend
|
||||||
- ../global:/app/global
|
- ../global:/app/global
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "/usr/bin/check-health"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 3s
|
||||||
depends_on:
|
depends_on:
|
||||||
- db
|
- db
|
||||||
|
- db-postgres
|
||||||
|
- authentik
|
||||||
|
- authentik-worker
|
||||||
|
- authentik-ldap
|
||||||
working_dir: /app
|
working_dir: /app
|
||||||
|
|
||||||
db:
|
db:
|
||||||
image: jc21/mariadb-aria
|
image: jc21/mariadb-aria
|
||||||
container_name: npm_db
|
container_name: npm2dev.db
|
||||||
ports:
|
ports:
|
||||||
- 33306:3306
|
- 33306:3306
|
||||||
networks:
|
networks:
|
||||||
@@ -54,25 +76,193 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- db_data:/var/lib/mysql
|
- db_data:/var/lib/mysql
|
||||||
|
|
||||||
|
db-postgres:
|
||||||
|
image: postgres:latest
|
||||||
|
container_name: npm2dev.db-postgres
|
||||||
|
networks:
|
||||||
|
- nginx_proxy_manager
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: 'npm'
|
||||||
|
POSTGRES_PASSWORD: 'npmpass'
|
||||||
|
POSTGRES_DB: 'npm'
|
||||||
|
volumes:
|
||||||
|
- psql_data:/var/lib/postgresql/data
|
||||||
|
- ./ci/postgres:/docker-entrypoint-initdb.d
|
||||||
|
|
||||||
|
stepca:
|
||||||
|
image: jc21/testca
|
||||||
|
container_name: npm2dev.stepca
|
||||||
|
volumes:
|
||||||
|
- './dev/resolv.conf:/etc/resolv.conf:ro'
|
||||||
|
- '/etc/localtime:/etc/localtime:ro'
|
||||||
|
networks:
|
||||||
|
nginx_proxy_manager:
|
||||||
|
aliases:
|
||||||
|
- ca.internal
|
||||||
|
|
||||||
|
dnsrouter:
|
||||||
|
image: jc21/dnsrouter
|
||||||
|
container_name: npm2dev.dnsrouter
|
||||||
|
volumes:
|
||||||
|
- ./dev/dnsrouter-config.json.tmp:/dnsrouter-config.json:ro
|
||||||
|
networks:
|
||||||
|
- nginx_proxy_manager
|
||||||
|
|
||||||
swagger:
|
swagger:
|
||||||
image: swaggerapi/swagger-ui:latest
|
image: swaggerapi/swagger-ui:latest
|
||||||
container_name: npm_swagger
|
container_name: npm2dev.swagger
|
||||||
ports:
|
ports:
|
||||||
- 3082:80
|
- 3082:80
|
||||||
environment:
|
environment:
|
||||||
URL: "http://npm:81/api/schema"
|
URL: "http://npm:81/api/schema"
|
||||||
PORT: '80'
|
PORT: '80'
|
||||||
depends_on:
|
depends_on:
|
||||||
- npm
|
- fullstack
|
||||||
|
|
||||||
|
squid:
|
||||||
|
image: ubuntu/squid
|
||||||
|
container_name: npm2dev.squid
|
||||||
|
volumes:
|
||||||
|
- './dev/squid.conf:/etc/squid/squid.conf:ro'
|
||||||
|
- './dev/resolv.conf:/etc/resolv.conf:ro'
|
||||||
|
- '/etc/localtime:/etc/localtime:ro'
|
||||||
|
networks:
|
||||||
|
- nginx_proxy_manager
|
||||||
|
ports:
|
||||||
|
- 8128:3128
|
||||||
|
|
||||||
|
pdns:
|
||||||
|
image: pschiffe/pdns-mysql:4.8
|
||||||
|
container_name: npm2dev.pdns
|
||||||
|
volumes:
|
||||||
|
- '/etc/localtime:/etc/localtime:ro'
|
||||||
|
environment:
|
||||||
|
PDNS_master: 'yes'
|
||||||
|
PDNS_api: 'yes'
|
||||||
|
PDNS_api_key: 'npm'
|
||||||
|
PDNS_webserver: 'yes'
|
||||||
|
PDNS_webserver_address: '0.0.0.0'
|
||||||
|
PDNS_webserver_password: 'npm'
|
||||||
|
PDNS_webserver-allow-from: '127.0.0.0/8,192.0.0.0/8,10.0.0.0/8,172.0.0.0/8'
|
||||||
|
PDNS_version_string: 'anonymous'
|
||||||
|
PDNS_default_ttl: 1500
|
||||||
|
PDNS_allow_axfr_ips: '127.0.0.0/8,192.0.0.0/8,10.0.0.0/8,172.0.0.0/8'
|
||||||
|
PDNS_gmysql_host: pdns-db
|
||||||
|
PDNS_gmysql_port: 3306
|
||||||
|
PDNS_gmysql_user: pdns
|
||||||
|
PDNS_gmysql_password: pdns
|
||||||
|
PDNS_gmysql_dbname: pdns
|
||||||
|
depends_on:
|
||||||
|
- pdns-db
|
||||||
|
networks:
|
||||||
|
nginx_proxy_manager:
|
||||||
|
aliases:
|
||||||
|
- ns1.pdns
|
||||||
|
- ns2.pdns
|
||||||
|
|
||||||
|
pdns-db:
|
||||||
|
image: mariadb
|
||||||
|
container_name: npm2dev.pdns-db
|
||||||
|
environment:
|
||||||
|
MYSQL_ROOT_PASSWORD: 'pdns'
|
||||||
|
MYSQL_DATABASE: 'pdns'
|
||||||
|
MYSQL_USER: 'pdns'
|
||||||
|
MYSQL_PASSWORD: 'pdns'
|
||||||
|
volumes:
|
||||||
|
- 'pdns_mysql:/var/lib/mysql'
|
||||||
|
- '/etc/localtime:/etc/localtime:ro'
|
||||||
|
- './dev/pdns-db.sql:/docker-entrypoint-initdb.d/01_init.sql:ro'
|
||||||
|
networks:
|
||||||
|
- nginx_proxy_manager
|
||||||
|
|
||||||
|
cypress:
|
||||||
|
image: npm2dev:cypress
|
||||||
|
container_name: npm2dev.cypress
|
||||||
|
build:
|
||||||
|
context: ../
|
||||||
|
dockerfile: test/cypress/Dockerfile
|
||||||
|
environment:
|
||||||
|
HTTP_PROXY: 'squid:3128'
|
||||||
|
HTTPS_PROXY: 'squid:3128'
|
||||||
|
volumes:
|
||||||
|
- '../test/results:/results'
|
||||||
|
- './dev/resolv.conf:/etc/resolv.conf:ro'
|
||||||
|
- '/etc/localtime:/etc/localtime:ro'
|
||||||
|
command: cypress run --browser chrome --config-file=cypress/config/ci.js
|
||||||
|
networks:
|
||||||
|
- nginx_proxy_manager
|
||||||
|
|
||||||
|
authentik-redis:
|
||||||
|
image: 'redis:alpine'
|
||||||
|
container_name: npm2dev.authentik-redis
|
||||||
|
command: --save 60 1 --loglevel warning
|
||||||
|
networks:
|
||||||
|
- nginx_proxy_manager
|
||||||
|
restart: unless-stopped
|
||||||
|
healthcheck:
|
||||||
|
test: ['CMD-SHELL', 'redis-cli ping | grep PONG']
|
||||||
|
start_period: 20s
|
||||||
|
interval: 30s
|
||||||
|
retries: 5
|
||||||
|
timeout: 3s
|
||||||
|
volumes:
|
||||||
|
- redis_data:/data
|
||||||
|
|
||||||
|
authentik:
|
||||||
|
image: ghcr.io/goauthentik/server:2024.10.1
|
||||||
|
container_name: npm2dev.authentik
|
||||||
|
restart: unless-stopped
|
||||||
|
command: server
|
||||||
|
networks:
|
||||||
|
- nginx_proxy_manager
|
||||||
|
env_file:
|
||||||
|
- ci.env
|
||||||
|
ports:
|
||||||
|
- 9000:9000
|
||||||
|
depends_on:
|
||||||
|
- authentik-redis
|
||||||
|
- db-postgres
|
||||||
|
|
||||||
|
authentik-worker:
|
||||||
|
image: ghcr.io/goauthentik/server:2024.10.1
|
||||||
|
container_name: npm2dev.authentik-worker
|
||||||
|
restart: unless-stopped
|
||||||
|
command: worker
|
||||||
|
networks:
|
||||||
|
- nginx_proxy_manager
|
||||||
|
env_file:
|
||||||
|
- ci.env
|
||||||
|
depends_on:
|
||||||
|
- authentik-redis
|
||||||
|
- db-postgres
|
||||||
|
|
||||||
|
authentik-ldap:
|
||||||
|
image: ghcr.io/goauthentik/ldap:2024.10.1
|
||||||
|
container_name: npm2dev.authentik-ldap
|
||||||
|
networks:
|
||||||
|
- nginx_proxy_manager
|
||||||
|
environment:
|
||||||
|
AUTHENTIK_HOST: 'http://authentik:9000'
|
||||||
|
AUTHENTIK_INSECURE: 'true'
|
||||||
|
AUTHENTIK_TOKEN: 'wKYZuRcI0ETtb8vWzMCr04oNbhrQUUICy89hSpDln1OEKLjiNEuQ51044Vkp'
|
||||||
|
restart: unless-stopped
|
||||||
|
depends_on:
|
||||||
|
- authentik
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
npm_data:
|
npm_data:
|
||||||
name: npm_core_data
|
name: npm2dev_core_data
|
||||||
le_data:
|
le_data:
|
||||||
name: npm_le_data
|
name: npm2dev_le_data
|
||||||
db_data:
|
db_data:
|
||||||
name: npm_db_data
|
name: npm2dev_db_data
|
||||||
|
pdns_mysql:
|
||||||
|
name: npnpm2dev_pdns_mysql
|
||||||
|
psql_data:
|
||||||
|
name: npm2dev_psql_data
|
||||||
|
redis_data:
|
||||||
|
name: npm2dev_redis_data
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
nginx_proxy_manager:
|
nginx_proxy_manager:
|
||||||
name: npm_network
|
name: npm2dev_network
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
compress
|
compress
|
||||||
sharedscripts
|
sharedscripts
|
||||||
postrotate
|
postrotate
|
||||||
/bin/kill -USR1 `cat /run/nginx.pid 2>/dev/null` 2>/dev/null || true
|
kill -USR1 `cat /run/nginx/nginx.pid 2>/dev/null` 2>/dev/null || true
|
||||||
endscript
|
endscript
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,6 +22,6 @@
|
|||||||
compress
|
compress
|
||||||
sharedscripts
|
sharedscripts
|
||||||
postrotate
|
postrotate
|
||||||
/bin/kill -USR1 `cat /run/nginx.pid 2>/dev/null` 2>/dev/null || true
|
kill -USR1 `cat /run/nginx/nginx.pid 2>/dev/null` 2>/dev/null || true
|
||||||
endscript
|
endscript
|
||||||
}
|
}
|
@@ -1,4 +1,4 @@
|
|||||||
location ~* ^.*\.(css|js|jpe?g|gif|png|webp|woff|eot|ttf|svg|ico|css\.map|js\.map)$ {
|
location ~* ^.*\.(css|js|jpe?g|gif|png|webp|woff|woff2|eot|ttf|svg|ico|css\.map|js\.map)$ {
|
||||||
if_modified_since off;
|
if_modified_since off;
|
||||||
|
|
||||||
# use the public cache
|
# use the public cache
|
||||||
|
@@ -0,0 +1,2 @@
|
|||||||
|
ssl_session_timeout 5m;
|
||||||
|
ssl_session_cache shared:SSL_stream:50m;
|
2
docker/rootfs/etc/nginx/conf.d/include/ssl-cache.conf
Normal file
2
docker/rootfs/etc/nginx/conf.d/include/ssl-cache.conf
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
ssl_session_timeout 5m;
|
||||||
|
ssl_session_cache shared:SSL:50m;
|
@@ -1,6 +1,3 @@
|
|||||||
ssl_session_timeout 5m;
|
|
||||||
ssl_session_cache shared:SSL:50m;
|
|
||||||
|
|
||||||
# intermediate configuration. tweak to your needs.
|
# intermediate configuration. tweak to your needs.
|
||||||
ssl_protocols TLSv1.2 TLSv1.3;
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';
|
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';
|
||||||
|
@@ -8,21 +8,53 @@ log_info 'Setting ownership ...'
|
|||||||
# root
|
# root
|
||||||
chown root /tmp/nginx
|
chown root /tmp/nginx
|
||||||
|
|
||||||
# npm user and group
|
locations=(
|
||||||
chown -R "$PUID:$PGID" /data
|
"/data"
|
||||||
chown -R "$PUID:$PGID" /etc/letsencrypt
|
"/etc/letsencrypt"
|
||||||
chown -R "$PUID:$PGID" /run/nginx
|
"/run/nginx"
|
||||||
chown -R "$PUID:$PGID" /tmp/nginx
|
"/tmp/nginx"
|
||||||
chown -R "$PUID:$PGID" /var/cache/nginx
|
"/var/cache/nginx"
|
||||||
chown -R "$PUID:$PGID" /var/lib/logrotate
|
"/var/lib/logrotate"
|
||||||
chown -R "$PUID:$PGID" /var/lib/nginx
|
"/var/lib/nginx"
|
||||||
chown -R "$PUID:$PGID" /var/log/nginx
|
"/var/log/nginx"
|
||||||
|
"/etc/nginx/nginx"
|
||||||
|
"/etc/nginx/nginx.conf"
|
||||||
|
"/etc/nginx/conf.d"
|
||||||
|
)
|
||||||
|
|
||||||
# Don't chown entire /etc/nginx folder as this causes crashes on some systems
|
chownit() {
|
||||||
chown -R "$PUID:$PGID" /etc/nginx/nginx
|
local dir="$1"
|
||||||
chown -R "$PUID:$PGID" /etc/nginx/nginx.conf
|
local recursive="${2:-true}"
|
||||||
chown -R "$PUID:$PGID" /etc/nginx/conf.d
|
|
||||||
|
|
||||||
# Prevents errors when installing python certbot plugins when non-root
|
local have
|
||||||
chown "$PUID:$PGID" /opt/certbot /opt/certbot/bin
|
have="$(stat -c '%u:%g' "$dir")"
|
||||||
find /opt/certbot/lib/python*/site-packages -not -user "$PUID" -execdir chown "$PUID:$PGID" {} \+
|
echo "- $dir ... "
|
||||||
|
|
||||||
|
if [ "$have" != "$PUID:$PGID" ]; then
|
||||||
|
if [ "$recursive" = 'true' ] && [ -d "$dir" ]; then
|
||||||
|
chown -R "$PUID:$PGID" "$dir"
|
||||||
|
else
|
||||||
|
chown "$PUID:$PGID" "$dir"
|
||||||
|
fi
|
||||||
|
echo " DONE"
|
||||||
|
else
|
||||||
|
echo " SKIPPED"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
for loc in "${locations[@]}"; do
|
||||||
|
chownit "$loc"
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$(is_true "${SKIP_CERTBOT_OWNERSHIP:-}")" = '1' ]; then
|
||||||
|
log_info 'Skipping ownership change of certbot directories'
|
||||||
|
else
|
||||||
|
log_info 'Changing ownership of certbot directories, this may take some time ...'
|
||||||
|
chownit "/opt/certbot" false
|
||||||
|
chownit "/opt/certbot/bin" false
|
||||||
|
|
||||||
|
# Handle all site-packages directories efficiently
|
||||||
|
find /opt/certbot/lib -type d -name "site-packages" | while read -r SITE_PACKAGES_DIR; do
|
||||||
|
chownit "$SITE_PACKAGES_DIR"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
@@ -5,12 +5,9 @@ set -e
|
|||||||
|
|
||||||
log_info 'Dynamic resolvers ...'
|
log_info 'Dynamic resolvers ...'
|
||||||
|
|
||||||
DISABLE_IPV6=$(echo "${DISABLE_IPV6:-}" | tr '[:upper:]' '[:lower:]')
|
|
||||||
|
|
||||||
# Dynamically generate resolvers file, if resolver is IPv6, enclose in `[]`
|
# Dynamically generate resolvers file, if resolver is IPv6, enclose in `[]`
|
||||||
# thanks @tfmm
|
# thanks @tfmm
|
||||||
if [ "$DISABLE_IPV6" == "true" ] || [ "$DISABLE_IPV6" == "on" ] || [ "$DISABLE_IPV6" == "1" ] || [ "$DISABLE_IPV6" == "yes" ];
|
if [ "$(is_true "$DISABLE_IPV6")" = '1' ]; then
|
||||||
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
|
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
|
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
|
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
|
||||||
|
@@ -8,14 +8,11 @@ set -e
|
|||||||
|
|
||||||
log_info 'IPv6 ...'
|
log_info 'IPv6 ...'
|
||||||
|
|
||||||
# Lowercase
|
|
||||||
DISABLE_IPV6=$(echo "${DISABLE_IPV6:-}" | tr '[:upper:]' '[:lower:]')
|
|
||||||
|
|
||||||
process_folder () {
|
process_folder () {
|
||||||
FILES=$(find "$1" -type f -name "*.conf")
|
FILES=$(find "$1" -type f -name "*.conf")
|
||||||
SED_REGEX=
|
SED_REGEX=
|
||||||
|
|
||||||
if [ "$DISABLE_IPV6" == "true" ] || [ "$DISABLE_IPV6" == "on" ] || [ "$DISABLE_IPV6" == "1" ] || [ "$DISABLE_IPV6" == "yes" ]; then
|
if [ "$(is_true "$DISABLE_IPV6")" = '1' ]; then
|
||||||
# IPV6 is disabled
|
# IPV6 is disabled
|
||||||
echo "Disabling IPV6 in hosts in: $1"
|
echo "Disabling IPV6 in hosts in: $1"
|
||||||
SED_REGEX='s/^([^#]*)listen \[::\]/\1#listen [::]/g'
|
SED_REGEX='s/^([^#]*)listen \[::\]/\1#listen [::]/g'
|
||||||
|
@@ -56,3 +56,13 @@ get_group_id () {
|
|||||||
getent group "$1" | cut -d: -f3
|
getent group "$1" | cut -d: -f3
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# param $1: value
|
||||||
|
is_true () {
|
||||||
|
VAL=$(echo "${1:-}" | tr '[:upper:]' '[:lower:]')
|
||||||
|
if [ "$VAL" == 'true' ] || [ "$VAL" == 'on' ] || [ "$VAL" == '1' ] || [ "$VAL" == 'yes' ]; then
|
||||||
|
echo '1'
|
||||||
|
else
|
||||||
|
echo '0'
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
@@ -5,7 +5,7 @@
|
|||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>Default Site</title>
|
<title>Default Site</title>
|
||||||
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
|
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
|
||||||
<style>
|
<style>
|
||||||
.jumbotron { margin-top: 50px; }
|
.jumbotron { margin-top: 50px; }
|
||||||
</style>
|
</style>
|
||||||
|
@@ -8,7 +8,7 @@ BLUE='\E[1;34m'
|
|||||||
GREEN='\E[1;32m'
|
GREEN='\E[1;32m'
|
||||||
RESET='\E[0m'
|
RESET='\E[0m'
|
||||||
|
|
||||||
S6_OVERLAY_VERSION=3.1.5.0
|
S6_OVERLAY_VERSION=3.2.1.0
|
||||||
TARGETPLATFORM=${1:-linux/amd64}
|
TARGETPLATFORM=${1:-linux/amd64}
|
||||||
|
|
||||||
# Determine the correct binary file for the architecture given
|
# Determine the correct binary file for the architecture given
|
||||||
|
@@ -5,7 +5,7 @@
|
|||||||
"preview": "vitepress preview"
|
"preview": "vitepress preview"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"vitepress": "^1.1.4"
|
"vitepress": "^1.4.0"
|
||||||
},
|
},
|
||||||
"dependencies": {}
|
"dependencies": {}
|
||||||
}
|
}
|
||||||
|
@@ -50,7 +50,6 @@ networks:
|
|||||||
Let's look at a Portainer example:
|
Let's look at a Portainer example:
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
version: '3.8'
|
|
||||||
services:
|
services:
|
||||||
|
|
||||||
portainer:
|
portainer:
|
||||||
@@ -92,8 +91,6 @@ This image supports the use of Docker secrets to import from files and keep sens
|
|||||||
You can set any environment variable from a file by appending `__FILE` (double-underscore FILE) to the environmental variable name.
|
You can set any environment variable from a file by appending `__FILE` (double-underscore FILE) to the environmental variable name.
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
version: '3.8'
|
|
||||||
|
|
||||||
secrets:
|
secrets:
|
||||||
# Secrets are single-line text files where the sole content is the secret
|
# Secrets are single-line text files where the sole content is the secret
|
||||||
# Paths in this example assume that secrets are kept in local folder called ".secrets"
|
# Paths in this example assume that secrets are kept in local folder called ".secrets"
|
||||||
@@ -164,6 +161,14 @@ The easy fix is to add a Docker environment variable to the Nginx Proxy Manager
|
|||||||
DISABLE_IPV6: 'true'
|
DISABLE_IPV6: 'true'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Disabling IP Ranges Fetch
|
||||||
|
|
||||||
|
By default, NPM fetches IP ranges from CloudFront and Cloudflare during application startup. In environments with limited internet access or to speed up container startup, this fetch can be disabled:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
environment:
|
||||||
|
IP_RANGES_FETCH_ENABLED: 'false'
|
||||||
|
```
|
||||||
|
|
||||||
## Custom Nginx Configurations
|
## Custom Nginx Configurations
|
||||||
|
|
||||||
@@ -184,6 +189,7 @@ You can add your custom configuration snippet files at `/data/nginx/custom` as f
|
|||||||
- `/data/nginx/custom/server_stream.conf`: Included at the end of every stream server block
|
- `/data/nginx/custom/server_stream.conf`: Included at the end of every stream server block
|
||||||
- `/data/nginx/custom/server_stream_tcp.conf`: Included at the end of every TCP stream server block
|
- `/data/nginx/custom/server_stream_tcp.conf`: Included at the end of every TCP stream server block
|
||||||
- `/data/nginx/custom/server_stream_udp.conf`: Included at the end of every UDP stream server block
|
- `/data/nginx/custom/server_stream_udp.conf`: Included at the end of every UDP stream server block
|
||||||
|
- `/data/nginx/custom/server_dead.conf`: Included at the end of every 404 server block
|
||||||
|
|
||||||
Every file is optional.
|
Every file is optional.
|
||||||
|
|
||||||
|
@@ -62,7 +62,6 @@ I won't go in to too much detail here but here are the basics for someone new to
|
|||||||
2. Create a docker-compose.yml file similar to this:
|
2. Create a docker-compose.yml file similar to this:
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
version: '3.8'
|
|
||||||
services:
|
services:
|
||||||
app:
|
app:
|
||||||
image: 'jc21/nginx-proxy-manager:latest'
|
image: 'jc21/nginx-proxy-manager:latest'
|
||||||
|
@@ -9,7 +9,6 @@ outline: deep
|
|||||||
Create a `docker-compose.yml` file:
|
Create a `docker-compose.yml` file:
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
version: '3.8'
|
|
||||||
services:
|
services:
|
||||||
app:
|
app:
|
||||||
image: 'jc21/nginx-proxy-manager:latest'
|
image: 'jc21/nginx-proxy-manager:latest'
|
||||||
@@ -22,8 +21,7 @@ services:
|
|||||||
# Add any other Stream port you want to expose
|
# Add any other Stream port you want to expose
|
||||||
# - '21:21' # FTP
|
# - '21:21' # FTP
|
||||||
|
|
||||||
# Uncomment the next line if you uncomment anything in the section
|
#environment:
|
||||||
# environment:
|
|
||||||
# Uncomment this if you want to change the location of
|
# Uncomment this if you want to change the location of
|
||||||
# the SQLite DB file within the container
|
# the SQLite DB file within the container
|
||||||
# DB_SQLITE_FILE: "/data/database.sqlite"
|
# DB_SQLITE_FILE: "/data/database.sqlite"
|
||||||
@@ -55,7 +53,6 @@ are going to use.
|
|||||||
Here is an example of what your `docker-compose.yml` will look like when using a MariaDB container:
|
Here is an example of what your `docker-compose.yml` will look like when using a MariaDB container:
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
version: '3.8'
|
|
||||||
services:
|
services:
|
||||||
app:
|
app:
|
||||||
image: 'jc21/nginx-proxy-manager:latest'
|
image: 'jc21/nginx-proxy-manager:latest'
|
||||||
@@ -101,6 +98,53 @@ Please note, that `DB_MYSQL_*` environment variables will take precedent over `D
|
|||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
## Using Postgres database
|
||||||
|
|
||||||
|
Similar to the MySQL server setup:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
image: 'jc21/nginx-proxy-manager:latest'
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
# These ports are in format <host-port>:<container-port>
|
||||||
|
- '80:80' # Public HTTP Port
|
||||||
|
- '443:443' # Public HTTPS Port
|
||||||
|
- '81:81' # Admin Web Port
|
||||||
|
# Add any other Stream port you want to expose
|
||||||
|
# - '21:21' # FTP
|
||||||
|
environment:
|
||||||
|
# Postgres parameters:
|
||||||
|
DB_POSTGRES_HOST: 'db'
|
||||||
|
DB_POSTGRES_PORT: '5432'
|
||||||
|
DB_POSTGRES_USER: 'npm'
|
||||||
|
DB_POSTGRES_PASSWORD: 'npmpass'
|
||||||
|
DB_POSTGRES_NAME: 'npm'
|
||||||
|
# Uncomment this if IPv6 is not enabled on your host
|
||||||
|
# DISABLE_IPV6: 'true'
|
||||||
|
volumes:
|
||||||
|
- ./data:/data
|
||||||
|
- ./letsencrypt:/etc/letsencrypt
|
||||||
|
depends_on:
|
||||||
|
- db
|
||||||
|
|
||||||
|
db:
|
||||||
|
image: postgres:latest
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: 'npm'
|
||||||
|
POSTGRES_PASSWORD: 'npmpass'
|
||||||
|
POSTGRES_DB: 'npm'
|
||||||
|
volumes:
|
||||||
|
- ./postgres:/var/lib/postgresql/data
|
||||||
|
```
|
||||||
|
|
||||||
|
::: warning
|
||||||
|
|
||||||
|
Custom Postgres schema is not supported, as such `public` will be used.
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
## Running on Raspberry PI / ARM devices
|
## Running on Raspberry PI / ARM devices
|
||||||
|
|
||||||
The docker images support the following architectures:
|
The docker images support the following architectures:
|
||||||
@@ -137,5 +181,13 @@ Email: admin@example.com
|
|||||||
Password: changeme
|
Password: changeme
|
||||||
```
|
```
|
||||||
|
|
||||||
Immediately after logging in with this default user you will be asked to modify your details and change your password.
|
Immediately after logging in with this default user you will be asked to modify your details and change your password. You can change defaults with:
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
environment:
|
||||||
|
INITIAL_ADMIN_EMAIL: my@example.com
|
||||||
|
INITIAL_ADMIN_PASSWORD: mypassword1
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
1
docs/src/third-party/index.md
vendored
1
docs/src/third-party/index.md
vendored
@@ -12,6 +12,7 @@ Known integrations:
|
|||||||
- [HomeAssistant Hass.io plugin](https://github.com/hassio-addons/addon-nginx-proxy-manager)
|
- [HomeAssistant Hass.io plugin](https://github.com/hassio-addons/addon-nginx-proxy-manager)
|
||||||
- [UnRaid / Synology](https://github.com/jlesage/docker-nginx-proxy-manager)
|
- [UnRaid / Synology](https://github.com/jlesage/docker-nginx-proxy-manager)
|
||||||
- [Proxmox Scripts](https://github.com/ej52/proxmox-scripts/tree/main/apps/nginx-proxy-manager)
|
- [Proxmox Scripts](https://github.com/ej52/proxmox-scripts/tree/main/apps/nginx-proxy-manager)
|
||||||
|
- [Proxmox VE Helper-Scripts](https://community-scripts.github.io/ProxmoxVE/scripts?id=nginxproxymanager)
|
||||||
- [nginxproxymanagerGraf](https://github.com/ma-karai/nginxproxymanagerGraf)
|
- [nginxproxymanagerGraf](https://github.com/ma-karai/nginxproxymanagerGraf)
|
||||||
|
|
||||||
|
|
||||||
|
1188
docs/yarn.lock
1188
docs/yarn.lock
File diff suppressed because it is too large
Load Diff
@@ -4,444 +4,438 @@ const Tokens = require('./tokens');
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {String} route
|
* @param {String} route
|
||||||
* @param {Object} [options]
|
* @param {Object} [options]
|
||||||
* @returns {Boolean}
|
* @returns {Boolean}
|
||||||
*/
|
*/
|
||||||
navigate: function (route, options) {
|
navigate: function (route, options) {
|
||||||
options = options || {};
|
options = options || {};
|
||||||
Backbone.history.navigate(route.toString(), options);
|
Backbone.history.navigate(route.toString(), options);
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Login
|
* Login
|
||||||
*/
|
*/
|
||||||
showLogin: function () {
|
showLogin: function () {
|
||||||
window.location = '/login';
|
window.location = '/login';
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Users
|
* Users
|
||||||
*/
|
*/
|
||||||
showUsers: function () {
|
showUsers: function () {
|
||||||
let controller = this;
|
const controller = this;
|
||||||
if (Cache.User.isAdmin()) {
|
if (Cache.User.isAdmin()) {
|
||||||
require(['./main', './users/main'], (App, View) => {
|
require(['./main', './users/main'], (App, View) => {
|
||||||
controller.navigate('/users');
|
controller.navigate('/users');
|
||||||
App.UI.showAppContent(new View());
|
App.UI.showAppContent(new View());
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.showDashboard();
|
this.showDashboard();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User Form
|
* User Form
|
||||||
*
|
*
|
||||||
* @param [model]
|
* @param [model]
|
||||||
*/
|
*/
|
||||||
showUserForm: function (model) {
|
showUserForm: function (model) {
|
||||||
if (Cache.User.isAdmin()) {
|
if (Cache.User.isAdmin()) {
|
||||||
require(['./main', './user/form'], function (App, View) {
|
require(['./main', './user/form'], function (App, View) {
|
||||||
App.UI.showModalDialog(new View({model: model}));
|
App.UI.showModalDialog(new View({model: model}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User Permissions Form
|
* User Permissions Form
|
||||||
*
|
*
|
||||||
* @param model
|
* @param model
|
||||||
*/
|
*/
|
||||||
showUserPermissions: function (model) {
|
showUserPermissions: function (model) {
|
||||||
if (Cache.User.isAdmin()) {
|
if (Cache.User.isAdmin()) {
|
||||||
require(['./main', './user/permissions'], function (App, View) {
|
require(['./main', './user/permissions'], function (App, View) {
|
||||||
App.UI.showModalDialog(new View({model: model}));
|
App.UI.showModalDialog(new View({model: model}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User Password Form
|
* User Password Form
|
||||||
*
|
*
|
||||||
* @param model
|
* @param model
|
||||||
*/
|
*/
|
||||||
showUserPasswordForm: function (model) {
|
showUserPasswordForm: function (model) {
|
||||||
if (Cache.User.isAdmin() || model.get('id') === Cache.User.get('id')) {
|
if (Cache.User.isAdmin() || model.get('id') === Cache.User.get('id')) {
|
||||||
require(['./main', './user/password'], function (App, View) {
|
require(['./main', './user/password'], function (App, View) {
|
||||||
App.UI.showModalDialog(new View({model: model}));
|
App.UI.showModalDialog(new View({model: model}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User Delete Confirm
|
* User Delete Confirm
|
||||||
*
|
*
|
||||||
* @param model
|
* @param model
|
||||||
*/
|
*/
|
||||||
showUserDeleteConfirm: function (model) {
|
showUserDeleteConfirm: function (model) {
|
||||||
if (Cache.User.isAdmin() && model.get('id') !== Cache.User.get('id')) {
|
if (Cache.User.isAdmin() && model.get('id') !== Cache.User.get('id')) {
|
||||||
require(['./main', './user/delete'], function (App, View) {
|
require(['./main', './user/delete'], function (App, View) {
|
||||||
App.UI.showModalDialog(new View({model: model}));
|
App.UI.showModalDialog(new View({model: model}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dashboard
|
* Dashboard
|
||||||
*/
|
*/
|
||||||
showDashboard: function () {
|
showDashboard: function () {
|
||||||
let controller = this;
|
const controller = this;
|
||||||
|
require(['./main', './dashboard/main'], (App, View) => {
|
||||||
|
controller.navigate('/');
|
||||||
|
App.UI.showAppContent(new View());
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
require(['./main', './dashboard/main'], (App, View) => {
|
/**
|
||||||
controller.navigate('/');
|
* Nginx Proxy Hosts
|
||||||
App.UI.showAppContent(new View());
|
*/
|
||||||
});
|
showNginxProxy: function () {
|
||||||
},
|
if (Cache.User.isAdmin() || Cache.User.canView('proxy_hosts')) {
|
||||||
|
const controller = this;
|
||||||
|
|
||||||
/**
|
require(['./main', './nginx/proxy/main'], (App, View) => {
|
||||||
* Nginx Proxy Hosts
|
controller.navigate('/nginx/proxy');
|
||||||
*/
|
App.UI.showAppContent(new View());
|
||||||
showNginxProxy: function () {
|
});
|
||||||
if (Cache.User.isAdmin() || Cache.User.canView('proxy_hosts')) {
|
}
|
||||||
let controller = this;
|
},
|
||||||
|
|
||||||
require(['./main', './nginx/proxy/main'], (App, View) => {
|
/**
|
||||||
controller.navigate('/nginx/proxy');
|
* Nginx Proxy Host Form
|
||||||
App.UI.showAppContent(new View());
|
*
|
||||||
});
|
* @param [model]
|
||||||
}
|
*/
|
||||||
},
|
showNginxProxyForm: function (model) {
|
||||||
|
if (Cache.User.isAdmin() || Cache.User.canManage('proxy_hosts')) {
|
||||||
|
require(['./main', './nginx/proxy/form'], function (App, View) {
|
||||||
|
App.UI.showModalDialog(new View({model: model}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Nginx Proxy Host Form
|
* Proxy Host Delete Confirm
|
||||||
*
|
*
|
||||||
* @param [model]
|
* @param model
|
||||||
*/
|
*/
|
||||||
showNginxProxyForm: function (model) {
|
showNginxProxyDeleteConfirm: function (model) {
|
||||||
if (Cache.User.isAdmin() || Cache.User.canManage('proxy_hosts')) {
|
if (Cache.User.isAdmin() || Cache.User.canManage('proxy_hosts')) {
|
||||||
require(['./main', './nginx/proxy/form'], function (App, View) {
|
require(['./main', './nginx/proxy/delete'], function (App, View) {
|
||||||
App.UI.showModalDialog(new View({model: model}));
|
App.UI.showModalDialog(new View({model: model}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Proxy Host Delete Confirm
|
* Nginx Redirection Hosts
|
||||||
*
|
*/
|
||||||
* @param model
|
showNginxRedirection: function () {
|
||||||
*/
|
if (Cache.User.isAdmin() || Cache.User.canView('redirection_hosts')) {
|
||||||
showNginxProxyDeleteConfirm: function (model) {
|
const controller = this;
|
||||||
if (Cache.User.isAdmin() || Cache.User.canManage('proxy_hosts')) {
|
require(['./main', './nginx/redirection/main'], (App, View) => {
|
||||||
require(['./main', './nginx/proxy/delete'], function (App, View) {
|
controller.navigate('/nginx/redirection');
|
||||||
App.UI.showModalDialog(new View({model: model}));
|
App.UI.showAppContent(new View());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Nginx Redirection Hosts
|
* Nginx Redirection Host Form
|
||||||
*/
|
*
|
||||||
showNginxRedirection: function () {
|
* @param [model]
|
||||||
if (Cache.User.isAdmin() || Cache.User.canView('redirection_hosts')) {
|
*/
|
||||||
let controller = this;
|
showNginxRedirectionForm: function (model) {
|
||||||
|
if (Cache.User.isAdmin() || Cache.User.canManage('redirection_hosts')) {
|
||||||
|
require(['./main', './nginx/redirection/form'], function (App, View) {
|
||||||
|
App.UI.showModalDialog(new View({model: model}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
require(['./main', './nginx/redirection/main'], (App, View) => {
|
/**
|
||||||
controller.navigate('/nginx/redirection');
|
* Proxy Redirection Delete Confirm
|
||||||
App.UI.showAppContent(new View());
|
*
|
||||||
});
|
* @param model
|
||||||
}
|
*/
|
||||||
},
|
showNginxRedirectionDeleteConfirm: function (model) {
|
||||||
|
if (Cache.User.isAdmin() || Cache.User.canManage('redirection_hosts')) {
|
||||||
|
require(['./main', './nginx/redirection/delete'], function (App, View) {
|
||||||
|
App.UI.showModalDialog(new View({model: model}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Nginx Redirection Host Form
|
* Nginx Stream Hosts
|
||||||
*
|
*/
|
||||||
* @param [model]
|
showNginxStream: function () {
|
||||||
*/
|
if (Cache.User.isAdmin() || Cache.User.canView('streams')) {
|
||||||
showNginxRedirectionForm: function (model) {
|
const controller = this;
|
||||||
if (Cache.User.isAdmin() || Cache.User.canManage('redirection_hosts')) {
|
require(['./main', './nginx/stream/main'], (App, View) => {
|
||||||
require(['./main', './nginx/redirection/form'], function (App, View) {
|
controller.navigate('/nginx/stream');
|
||||||
App.UI.showModalDialog(new View({model: model}));
|
App.UI.showAppContent(new View());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Proxy Redirection Delete Confirm
|
* Stream Form
|
||||||
*
|
*
|
||||||
* @param model
|
* @param [model]
|
||||||
*/
|
*/
|
||||||
showNginxRedirectionDeleteConfirm: function (model) {
|
showNginxStreamForm: function (model) {
|
||||||
if (Cache.User.isAdmin() || Cache.User.canManage('redirection_hosts')) {
|
if (Cache.User.isAdmin() || Cache.User.canManage('streams')) {
|
||||||
require(['./main', './nginx/redirection/delete'], function (App, View) {
|
require(['./main', './nginx/stream/form'], function (App, View) {
|
||||||
App.UI.showModalDialog(new View({model: model}));
|
App.UI.showModalDialog(new View({model: model}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Nginx Stream Hosts
|
* Stream Delete Confirm
|
||||||
*/
|
*
|
||||||
showNginxStream: function () {
|
* @param model
|
||||||
if (Cache.User.isAdmin() || Cache.User.canView('streams')) {
|
*/
|
||||||
let controller = this;
|
showNginxStreamDeleteConfirm: function (model) {
|
||||||
|
if (Cache.User.isAdmin() || Cache.User.canManage('streams')) {
|
||||||
|
require(['./main', './nginx/stream/delete'], function (App, View) {
|
||||||
|
App.UI.showModalDialog(new View({model: model}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
require(['./main', './nginx/stream/main'], (App, View) => {
|
/**
|
||||||
controller.navigate('/nginx/stream');
|
* Nginx Dead Hosts
|
||||||
App.UI.showAppContent(new View());
|
*/
|
||||||
});
|
showNginxDead: function () {
|
||||||
}
|
if (Cache.User.isAdmin() || Cache.User.canView('dead_hosts')) {
|
||||||
},
|
const controller = this;
|
||||||
|
require(['./main', './nginx/dead/main'], (App, View) => {
|
||||||
|
controller.navigate('/nginx/404');
|
||||||
|
App.UI.showAppContent(new View());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stream Form
|
* Dead Host Form
|
||||||
*
|
*
|
||||||
* @param [model]
|
* @param [model]
|
||||||
*/
|
*/
|
||||||
showNginxStreamForm: function (model) {
|
showNginxDeadForm: function (model) {
|
||||||
if (Cache.User.isAdmin() || Cache.User.canManage('streams')) {
|
if (Cache.User.isAdmin() || Cache.User.canManage('dead_hosts')) {
|
||||||
require(['./main', './nginx/stream/form'], function (App, View) {
|
require(['./main', './nginx/dead/form'], function (App, View) {
|
||||||
App.UI.showModalDialog(new View({model: model}));
|
App.UI.showModalDialog(new View({model: model}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stream Delete Confirm
|
* Dead Host Delete Confirm
|
||||||
*
|
*
|
||||||
* @param model
|
* @param model
|
||||||
*/
|
*/
|
||||||
showNginxStreamDeleteConfirm: function (model) {
|
showNginxDeadDeleteConfirm: function (model) {
|
||||||
if (Cache.User.isAdmin() || Cache.User.canManage('streams')) {
|
if (Cache.User.isAdmin() || Cache.User.canManage('dead_hosts')) {
|
||||||
require(['./main', './nginx/stream/delete'], function (App, View) {
|
require(['./main', './nginx/dead/delete'], function (App, View) {
|
||||||
App.UI.showModalDialog(new View({model: model}));
|
App.UI.showModalDialog(new View({model: model}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Nginx Dead Hosts
|
* Help Dialog
|
||||||
*/
|
*
|
||||||
showNginxDead: function () {
|
* @param {String} title
|
||||||
if (Cache.User.isAdmin() || Cache.User.canView('dead_hosts')) {
|
* @param {String} content
|
||||||
let controller = this;
|
*/
|
||||||
|
showHelp: function (title, content) {
|
||||||
|
require(['./main', './help/main'], function (App, View) {
|
||||||
|
App.UI.showModalDialog(new View({title: title, content: content}));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
require(['./main', './nginx/dead/main'], (App, View) => {
|
/**
|
||||||
controller.navigate('/nginx/404');
|
* Nginx Access
|
||||||
App.UI.showAppContent(new View());
|
*/
|
||||||
});
|
showNginxAccess: function () {
|
||||||
}
|
if (Cache.User.isAdmin() || Cache.User.canView('access_lists')) {
|
||||||
},
|
const controller = this;
|
||||||
|
require(['./main', './nginx/access/main'], (App, View) => {
|
||||||
|
controller.navigate('/nginx/access');
|
||||||
|
App.UI.showAppContent(new View());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dead Host Form
|
* Nginx Access List Form
|
||||||
*
|
*
|
||||||
* @param [model]
|
* @param [model]
|
||||||
*/
|
*/
|
||||||
showNginxDeadForm: function (model) {
|
showNginxAccessListForm: function (model) {
|
||||||
if (Cache.User.isAdmin() || Cache.User.canManage('dead_hosts')) {
|
if (Cache.User.isAdmin() || Cache.User.canManage('access_lists')) {
|
||||||
require(['./main', './nginx/dead/form'], function (App, View) {
|
require(['./main', './nginx/access/form'], function (App, View) {
|
||||||
App.UI.showModalDialog(new View({model: model}));
|
App.UI.showModalDialog(new View({model: model}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dead Host Delete Confirm
|
* Access List Delete Confirm
|
||||||
*
|
*
|
||||||
* @param model
|
* @param model
|
||||||
*/
|
*/
|
||||||
showNginxDeadDeleteConfirm: function (model) {
|
showNginxAccessListDeleteConfirm: function (model) {
|
||||||
if (Cache.User.isAdmin() || Cache.User.canManage('dead_hosts')) {
|
if (Cache.User.isAdmin() || Cache.User.canManage('access_lists')) {
|
||||||
require(['./main', './nginx/dead/delete'], function (App, View) {
|
require(['./main', './nginx/access/delete'], function (App, View) {
|
||||||
App.UI.showModalDialog(new View({model: model}));
|
App.UI.showModalDialog(new View({model: model}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Help Dialog
|
* Nginx Certificates
|
||||||
*
|
*/
|
||||||
* @param {String} title
|
showNginxCertificates: function () {
|
||||||
* @param {String} content
|
if (Cache.User.isAdmin() || Cache.User.canView('certificates')) {
|
||||||
*/
|
const controller = this;
|
||||||
showHelp: function (title, content) {
|
require(['./main', './nginx/certificates/main'], (App, View) => {
|
||||||
require(['./main', './help/main'], function (App, View) {
|
controller.navigate('/nginx/certificates');
|
||||||
App.UI.showModalDialog(new View({title: title, content: content}));
|
App.UI.showAppContent(new View());
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Nginx Access
|
* Nginx Certificate Form
|
||||||
*/
|
*
|
||||||
showNginxAccess: function () {
|
* @param [model]
|
||||||
if (Cache.User.isAdmin() || Cache.User.canView('access_lists')) {
|
*/
|
||||||
let controller = this;
|
showNginxCertificateForm: function (model) {
|
||||||
|
if (Cache.User.isAdmin() || Cache.User.canManage('certificates')) {
|
||||||
|
require(['./main', './nginx/certificates/form'], function (App, View) {
|
||||||
|
App.UI.showModalDialog(new View({model: model}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
require(['./main', './nginx/access/main'], (App, View) => {
|
/**
|
||||||
controller.navigate('/nginx/access');
|
* Certificate Renew
|
||||||
App.UI.showAppContent(new View());
|
*
|
||||||
});
|
* @param model
|
||||||
}
|
*/
|
||||||
},
|
showNginxCertificateRenew: function (model) {
|
||||||
|
if (Cache.User.isAdmin() || Cache.User.canManage('certificates')) {
|
||||||
|
require(['./main', './nginx/certificates/renew'], function (App, View) {
|
||||||
|
App.UI.showModalDialog(new View({model: model}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Nginx Access List Form
|
* Certificate Delete Confirm
|
||||||
*
|
*
|
||||||
* @param [model]
|
* @param model
|
||||||
*/
|
*/
|
||||||
showNginxAccessListForm: function (model) {
|
showNginxCertificateDeleteConfirm: function (model) {
|
||||||
if (Cache.User.isAdmin() || Cache.User.canManage('access_lists')) {
|
if (Cache.User.isAdmin() || Cache.User.canManage('certificates')) {
|
||||||
require(['./main', './nginx/access/form'], function (App, View) {
|
require(['./main', './nginx/certificates/delete'], function (App, View) {
|
||||||
App.UI.showModalDialog(new View({model: model}));
|
App.UI.showModalDialog(new View({model: model}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Access List Delete Confirm
|
* Certificate Test Reachability
|
||||||
*
|
*
|
||||||
* @param model
|
* @param model
|
||||||
*/
|
*/
|
||||||
showNginxAccessListDeleteConfirm: function (model) {
|
showNginxCertificateTestReachability: function (model) {
|
||||||
if (Cache.User.isAdmin() || Cache.User.canManage('access_lists')) {
|
if (Cache.User.isAdmin() || Cache.User.canManage('certificates')) {
|
||||||
require(['./main', './nginx/access/delete'], function (App, View) {
|
require(['./main', './nginx/certificates/test'], function (App, View) {
|
||||||
App.UI.showModalDialog(new View({model: model}));
|
App.UI.showModalDialog(new View({model: model}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Nginx Certificates
|
* Audit Log
|
||||||
*/
|
*/
|
||||||
showNginxCertificates: function () {
|
showAuditLog: function () {
|
||||||
if (Cache.User.isAdmin() || Cache.User.canView('certificates')) {
|
const controller = this;
|
||||||
let controller = this;
|
if (Cache.User.isAdmin()) {
|
||||||
|
require(['./main', './audit-log/main'], (App, View) => {
|
||||||
|
controller.navigate('/audit-log');
|
||||||
|
App.UI.showAppContent(new View());
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.showDashboard();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
require(['./main', './nginx/certificates/main'], (App, View) => {
|
/**
|
||||||
controller.navigate('/nginx/certificates');
|
* Audit Log Metadata
|
||||||
App.UI.showAppContent(new View());
|
*
|
||||||
});
|
* @param model
|
||||||
}
|
*/
|
||||||
},
|
showAuditMeta: function (model) {
|
||||||
|
if (Cache.User.isAdmin()) {
|
||||||
|
require(['./main', './audit-log/meta'], function (App, View) {
|
||||||
|
App.UI.showModalDialog(new View({model: model}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Nginx Certificate Form
|
* Settings
|
||||||
*
|
*/
|
||||||
* @param [model]
|
showSettings: function () {
|
||||||
*/
|
const controller = this;
|
||||||
showNginxCertificateForm: function (model) {
|
if (Cache.User.isAdmin()) {
|
||||||
if (Cache.User.isAdmin() || Cache.User.canManage('certificates')) {
|
require(['./main', './settings/main'], (App, View) => {
|
||||||
require(['./main', './nginx/certificates/form'], function (App, View) {
|
controller.navigate('/settings');
|
||||||
App.UI.showModalDialog(new View({model: model}));
|
App.UI.showAppContent(new View());
|
||||||
});
|
});
|
||||||
}
|
} else {
|
||||||
},
|
this.showDashboard();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Certificate Renew
|
* Settings Item Form
|
||||||
*
|
*
|
||||||
* @param model
|
* @param model
|
||||||
*/
|
*/
|
||||||
showNginxCertificateRenew: function (model) {
|
showSettingForm: function (model) {
|
||||||
if (Cache.User.isAdmin() || Cache.User.canManage('certificates')) {
|
if (Cache.User.isAdmin()) {
|
||||||
require(['./main', './nginx/certificates/renew'], function (App, View) {
|
if (model.get('id') === 'default-site') {
|
||||||
App.UI.showModalDialog(new View({model: model}));
|
require(['./main', './settings/default-site/main'], function (App, View) {
|
||||||
});
|
App.UI.showModalDialog(new View({model: model}));
|
||||||
}
|
});
|
||||||
},
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Certificate Delete Confirm
|
* Logout
|
||||||
*
|
*/
|
||||||
* @param model
|
logout: function () {
|
||||||
*/
|
Tokens.dropTopToken();
|
||||||
showNginxCertificateDeleteConfirm: function (model) {
|
this.showLogin();
|
||||||
if (Cache.User.isAdmin() || Cache.User.canManage('certificates')) {
|
}
|
||||||
require(['./main', './nginx/certificates/delete'], function (App, View) {
|
|
||||||
App.UI.showModalDialog(new View({model: model}));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Certificate Test Reachability
|
|
||||||
*
|
|
||||||
* @param model
|
|
||||||
*/
|
|
||||||
showNginxCertificateTestReachability: function (model) {
|
|
||||||
if (Cache.User.isAdmin() || Cache.User.canManage('certificates')) {
|
|
||||||
require(['./main', './nginx/certificates/test'], function (App, View) {
|
|
||||||
App.UI.showModalDialog(new View({model: model}));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Audit Log
|
|
||||||
*/
|
|
||||||
showAuditLog: function () {
|
|
||||||
let controller = this;
|
|
||||||
if (Cache.User.isAdmin()) {
|
|
||||||
require(['./main', './audit-log/main'], (App, View) => {
|
|
||||||
controller.navigate('/audit-log');
|
|
||||||
App.UI.showAppContent(new View());
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.showDashboard();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Audit Log Metadata
|
|
||||||
*
|
|
||||||
* @param model
|
|
||||||
*/
|
|
||||||
showAuditMeta: function (model) {
|
|
||||||
if (Cache.User.isAdmin()) {
|
|
||||||
require(['./main', './audit-log/meta'], function (App, View) {
|
|
||||||
App.UI.showModalDialog(new View({model: model}));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Settings
|
|
||||||
*/
|
|
||||||
showSettings: function () {
|
|
||||||
let controller = this;
|
|
||||||
if (Cache.User.isAdmin()) {
|
|
||||||
require(['./main', './settings/main'], (App, View) => {
|
|
||||||
controller.navigate('/settings');
|
|
||||||
App.UI.showAppContent(new View());
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.showDashboard();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Settings Item Form
|
|
||||||
*
|
|
||||||
* @param model
|
|
||||||
*/
|
|
||||||
showSettingForm: function (model) {
|
|
||||||
if (Cache.User.isAdmin()) {
|
|
||||||
if (model.get('id') === 'default-site') {
|
|
||||||
require(['./main', './settings/default-site/main'], function (App, View) {
|
|
||||||
App.UI.showModalDialog(new View({model: model}));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Logout
|
|
||||||
*/
|
|
||||||
logout: function () {
|
|
||||||
Tokens.dropTopToken();
|
|
||||||
this.showLogin();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
@@ -6,87 +6,85 @@ const Helpers = require('../../lib/helpers');
|
|||||||
const template = require('./main.ejs');
|
const template = require('./main.ejs');
|
||||||
|
|
||||||
module.exports = Mn.View.extend({
|
module.exports = Mn.View.extend({
|
||||||
template: template,
|
template: template,
|
||||||
id: 'dashboard',
|
id: 'dashboard',
|
||||||
columns: 0,
|
columns: 0,
|
||||||
|
|
||||||
stats: {},
|
stats: {},
|
||||||
|
|
||||||
ui: {
|
ui: {
|
||||||
links: 'a'
|
links: 'a'
|
||||||
},
|
},
|
||||||
|
|
||||||
events: {
|
events: {
|
||||||
'click @ui.links': function (e) {
|
'click @ui.links': function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
Controller.navigate($(e.currentTarget).attr('href'), true);
|
Controller.navigate($(e.currentTarget).attr('href'), true);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
templateContext: function () {
|
templateContext: function () {
|
||||||
let view = this;
|
const view = this;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getUserName: function () {
|
getUserName: function () {
|
||||||
return Cache.User.get('nickname') || Cache.User.get('name');
|
return Cache.User.get('nickname') || Cache.User.get('name');
|
||||||
},
|
},
|
||||||
|
|
||||||
getHostStat: function (type) {
|
getHostStat: function (type) {
|
||||||
if (view.stats && typeof view.stats.hosts !== 'undefined' && typeof view.stats.hosts[type] !== 'undefined') {
|
if (view.stats && typeof view.stats.hosts !== 'undefined' && typeof view.stats.hosts[type] !== 'undefined') {
|
||||||
return Helpers.niceNumber(view.stats.hosts[type]);
|
return Helpers.niceNumber(view.stats.hosts[type]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return '-';
|
return '-';
|
||||||
},
|
},
|
||||||
|
|
||||||
canShow: function (perm) {
|
canShow: function (perm) {
|
||||||
return Cache.User.isAdmin() || Cache.User.canView(perm);
|
return Cache.User.isAdmin() || Cache.User.canView(perm);
|
||||||
},
|
},
|
||||||
|
|
||||||
columns: view.columns
|
columns: view.columns
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
onRender: function () {
|
onRender: function () {
|
||||||
let view = this;
|
const view = this;
|
||||||
|
if (typeof view.stats.hosts === 'undefined') {
|
||||||
|
Api.Reports.getHostStats()
|
||||||
|
.then(response => {
|
||||||
|
if (!view.isDestroyed()) {
|
||||||
|
view.stats.hosts = response;
|
||||||
|
view.render();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
if (typeof view.stats.hosts === 'undefined') {
|
/**
|
||||||
Api.Reports.getHostStats()
|
* @param {Object} [model]
|
||||||
.then(response => {
|
*/
|
||||||
if (!view.isDestroyed()) {
|
preRender: function (model) {
|
||||||
view.stats.hosts = response;
|
this.columns = 0;
|
||||||
view.render();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
console.log(err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
// calculate the available columns based on permissions for the objects
|
||||||
* @param {Object} [model]
|
// and store as a variable
|
||||||
*/
|
const perms = ['proxy_hosts', 'redirection_hosts', 'streams', 'dead_hosts'];
|
||||||
preRender: function (model) {
|
|
||||||
this.columns = 0;
|
|
||||||
|
|
||||||
// calculate the available columns based on permissions for the objects
|
perms.map(perm => {
|
||||||
// and store as a variable
|
this.columns += Cache.User.isAdmin() || Cache.User.canView(perm) ? 1 : 0;
|
||||||
//let view = this;
|
});
|
||||||
let perms = ['proxy_hosts', 'redirection_hosts', 'streams', 'dead_hosts'];
|
|
||||||
|
|
||||||
perms.map(perm => {
|
// Prevent double rendering on initial calls
|
||||||
this.columns += Cache.User.isAdmin() || Cache.User.canView(perm) ? 1 : 0;
|
if (typeof model !== 'undefined') {
|
||||||
});
|
this.render();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// Prevent double rendering on initial calls
|
initialize: function () {
|
||||||
if (typeof model !== 'undefined') {
|
this.preRender();
|
||||||
this.render();
|
this.listenTo(Cache.User, 'change', this.preRender);
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
initialize: function () {
|
|
||||||
this.preRender();
|
|
||||||
this.listenTo(Cache.User, 'change', this.preRender);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user