mirror of
https://github.com/NginxProxyManager/nginx-proxy-manager.git
synced 2025-06-17 17:56:27 +00:00
Compare commits
153 Commits
5aeb99b856
...
dependabot
Author | SHA1 | Date | |
---|---|---|---|
7580e65dd4 | |||
f327c1e825 | |||
6f539979ec | |||
3d8079a137 | |||
6d6d83c0d0 | |||
100a4888d0 | |||
34a46bd733 | |||
7f8adc7e50 | |||
98d118cb74 | |||
4fb93542c3 | |||
4fe305520a | |||
76be31cf76 | |||
55dadb2004 | |||
d9cdb3dc2c | |||
f5879dff6c | |||
5e66d677f1 | |||
18830f81b0 | |||
341ac65587 | |||
078baa255a | |||
bf9d9bd43b | |||
79d28f03d0 | |||
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 |
67
Jenkinsfile
vendored
67
Jenkinsfile
vendored
@ -43,7 +43,7 @@ pipeline {
|
||||
steps {
|
||||
script {
|
||||
// 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}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -127,6 +127,11 @@ pipeline {
|
||||
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('Test Mysql') {
|
||||
@ -155,6 +160,49 @@ pipeline {
|
||||
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('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') {
|
||||
@ -193,7 +241,13 @@ pipeline {
|
||||
}
|
||||
steps {
|
||||
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)
|
||||
as `nginxproxymanager/${IMAGE}-dev:${BRANCH_LOWER}`
|
||||
|
||||
**Note:** ensure you backup your NPM instance before testing this image! Especially if there are database changes
|
||||
**Note:** this is a different docker image namespace than the official image
|
||||
""", true)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -204,20 +258,13 @@ pipeline {
|
||||
always {
|
||||
sh 'echo Reverting ownership'
|
||||
sh 'docker run --rm -v "$(pwd):/data" jc21/ci-tools chown -R "$(id -u):$(id -g)" /data'
|
||||
}
|
||||
success {
|
||||
juxtapose event: 'success'
|
||||
sh 'figlet "SUCCESS"'
|
||||
printResult(true)
|
||||
}
|
||||
failure {
|
||||
archiveArtifacts(artifacts: 'debug/**/*.*', allowEmptyArchive: true)
|
||||
juxtapose event: 'failure'
|
||||
sh 'figlet "FAILURE"'
|
||||
}
|
||||
unstable {
|
||||
archiveArtifacts(artifacts: 'debug/**/*.*', allowEmptyArchive: true)
|
||||
juxtapose event: 'unstable'
|
||||
sh 'figlet "UNSTABLE"'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
<p align="center">
|
||||
<img src="https://nginxproxymanager.com/github.png">
|
||||
<br><br>
|
||||
<img src="https://img.shields.io/badge/version-2.12.0-green.svg?style=for-the-badge">
|
||||
<img src="https://img.shields.io/badge/version-2.12.3-green.svg?style=for-the-badge">
|
||||
<a href="https://hub.docker.com/repository/docker/jc21/nginx-proxy-manager">
|
||||
<img src="https://img.shields.io/docker/stars/jc21/nginx-proxy-manager.svg?style=for-the-badge">
|
||||
</a>
|
||||
|
@ -81,7 +81,7 @@ const internalAccessList = {
|
||||
|
||||
return internalAccessList.build(row)
|
||||
.then(() => {
|
||||
if (row.proxy_host_count) {
|
||||
if (parseInt(row.proxy_host_count, 10)) {
|
||||
return internalNginx.bulkGenerateConfigs('proxy_host', row.proxy_hosts);
|
||||
}
|
||||
})
|
||||
@ -223,7 +223,7 @@ const internalAccessList = {
|
||||
.then((row) => {
|
||||
return internalAccessList.build(row)
|
||||
.then(() => {
|
||||
if (row.proxy_host_count) {
|
||||
if (parseInt(row.proxy_host_count, 10)) {
|
||||
return internalNginx.bulkGenerateConfigs('proxy_host', row.proxy_hosts);
|
||||
}
|
||||
}).then(internalNginx.reload)
|
||||
@ -252,9 +252,13 @@ const internalAccessList = {
|
||||
let query = accessListModel
|
||||
.query()
|
||||
.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)
|
||||
.andWhere('access_list.id', data.id)
|
||||
.groupBy('access_list.id')
|
||||
.allowGraph('[owner,items,clients,proxy_hosts.[certificate,access_list.[clients,items]]]')
|
||||
.first();
|
||||
|
||||
@ -373,7 +377,10 @@ const internalAccessList = {
|
||||
let query = accessListModel
|
||||
.query()
|
||||
.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)
|
||||
.groupBy('access_list.id')
|
||||
.allowGraph('[owner,items,clients]')
|
||||
@ -501,8 +508,13 @@ const internalAccessList = {
|
||||
if (typeof item.password !== 'undefined' && item.password.length) {
|
||||
logger.info('Adding: ' + item.username);
|
||||
|
||||
utils.execFile('/usr/bin/htpasswd', ['-b', htpasswd_file, item.username, item.password])
|
||||
.then((/*result*/) => {
|
||||
utils.execFile('openssl', ['passwd', '-apr1', item.password])
|
||||
.then((res) => {
|
||||
try {
|
||||
fs.appendFileSync(htpasswd_file, item.username + ':' + res + '\n', {encoding: 'utf8'});
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
next();
|
||||
})
|
||||
.catch((err) => {
|
||||
|
@ -1,5 +1,6 @@
|
||||
const error = require('../lib/error');
|
||||
const auditLogModel = require('../models/audit-log');
|
||||
const error = require('../lib/error');
|
||||
const auditLogModel = require('../models/audit-log');
|
||||
const {castJsonIfNeed} = require('../lib/helpers');
|
||||
|
||||
const internalAuditLog = {
|
||||
|
||||
@ -22,9 +23,9 @@ const internalAuditLog = {
|
||||
.allowGraph('[user]');
|
||||
|
||||
// Query is used for searching
|
||||
if (typeof search_query === 'string') {
|
||||
if (typeof search_query === 'string' && search_query.length > 0) {
|
||||
query.where(function () {
|
||||
this.where('meta', 'like', '%' + search_query + '%');
|
||||
this.where(castJsonIfNeed('meta'), 'like', '%' + search_query + '%');
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -3,27 +3,29 @@ const fs = require('fs');
|
||||
const https = require('https');
|
||||
const tempWrite = require('temp-write');
|
||||
const moment = require('moment');
|
||||
const archiver = require('archiver');
|
||||
const path = require('path');
|
||||
const { isArray } = require('lodash');
|
||||
const logger = require('../logger').ssl;
|
||||
const config = require('../lib/config');
|
||||
const error = require('../lib/error');
|
||||
const utils = require('../lib/utils');
|
||||
const certbot = require('../lib/certbot');
|
||||
const certificateModel = require('../models/certificate');
|
||||
const tokenModel = require('../models/token');
|
||||
const dnsPlugins = require('../global/certbot-dns-plugins.json');
|
||||
const internalAuditLog = require('./audit-log');
|
||||
const internalNginx = require('./nginx');
|
||||
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 letsencryptServer = config.useLetsencryptServer();
|
||||
const letsencryptConfig = '/etc/letsencrypt.ini';
|
||||
const certbotCommand = 'certbot';
|
||||
|
||||
function omissions() {
|
||||
return ['is_deleted'];
|
||||
return ['is_deleted', 'owner.is_deleted'];
|
||||
}
|
||||
|
||||
const internalCertificate = {
|
||||
@ -207,6 +209,7 @@ const internalCertificate = {
|
||||
.patchAndFetchById(certificate.id, {
|
||||
expires_on: moment(cert_info.dates.to, 'X').format('YYYY-MM-DD HH:mm:ss')
|
||||
})
|
||||
.then(utils.omitRow(omissions()))
|
||||
.then((saved_row) => {
|
||||
// Add cert data for audit log
|
||||
saved_row.meta = _.assign({}, saved_row.meta, {
|
||||
@ -310,6 +313,9 @@ const internalCertificate = {
|
||||
.where('is_deleted', 0)
|
||||
.andWhere('id', data.id)
|
||||
.allowGraph('[owner]')
|
||||
.allowGraph('[proxy_hosts]')
|
||||
.allowGraph('[redirection_hosts]')
|
||||
.allowGraph('[dead_hosts]')
|
||||
.first();
|
||||
|
||||
if (access_data.permission_visibility !== 'all') {
|
||||
@ -461,6 +467,9 @@ const internalCertificate = {
|
||||
.where('is_deleted', 0)
|
||||
.groupBy('id')
|
||||
.allowGraph('[owner]')
|
||||
.allowGraph('[proxy_hosts]')
|
||||
.allowGraph('[redirection_hosts]')
|
||||
.allowGraph('[dead_hosts]')
|
||||
.orderBy('nice_name', 'ASC');
|
||||
|
||||
if (access_data.permission_visibility !== 'all') {
|
||||
@ -730,29 +739,29 @@ const internalCertificate = {
|
||||
|
||||
return utils.exec('openssl x509 -in ' + certificate_file + ' -subject -noout')
|
||||
.then((result) => {
|
||||
// Examples:
|
||||
// subject=CN = *.jc21.com
|
||||
// subject=CN = something.example.com
|
||||
const regex = /(?:subject=)?[^=]+=\s+(\S+)/gim;
|
||||
const match = regex.exec(result);
|
||||
|
||||
if (typeof match[1] === 'undefined') {
|
||||
throw new error.ValidationError('Could not determine subject from certificate: ' + result);
|
||||
if (match && typeof match[1] !== 'undefined') {
|
||||
certData['cn'] = match[1];
|
||||
}
|
||||
|
||||
certData['cn'] = match[1];
|
||||
})
|
||||
.then(() => {
|
||||
return utils.exec('openssl x509 -in ' + certificate_file + ' -issuer -noout');
|
||||
})
|
||||
|
||||
.then((result) => {
|
||||
// Examples:
|
||||
// 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 match = regex.exec(result);
|
||||
|
||||
if (typeof match[1] === 'undefined') {
|
||||
throw new error.ValidationError('Could not determine issuer from certificate: ' + result);
|
||||
if (match && typeof match[1] !== 'undefined') {
|
||||
certData['issuer'] = match[1];
|
||||
}
|
||||
|
||||
certData['issuer'] = match[1];
|
||||
})
|
||||
.then(() => {
|
||||
return utils.exec('openssl x509 -in ' + certificate_file + ' -dates -noout');
|
||||
@ -827,17 +836,18 @@ const internalCertificate = {
|
||||
requestLetsEncryptSsl: (certificate) => {
|
||||
logger.info('Requesting Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
|
||||
|
||||
const cmd = certbotCommand + ' certonly ' +
|
||||
'--config "' + letsencryptConfig + '" ' +
|
||||
const cmd = `${certbotCommand} certonly ` +
|
||||
`--config '${letsencryptConfig}' ` +
|
||||
'--work-dir "/tmp/letsencrypt-lib" ' +
|
||||
'--logs-dir "/tmp/letsencrypt-log" ' +
|
||||
'--cert-name "npm-' + certificate.id + '" ' +
|
||||
`--cert-name "npm-${certificate.id}" ` +
|
||||
'--agree-tos ' +
|
||||
'--authenticator webroot ' +
|
||||
'--email "' + certificate.meta.letsencrypt_email + '" ' +
|
||||
`--email '${certificate.meta.letsencrypt_email}' ` +
|
||||
'--preferred-challenges "dns,http" ' +
|
||||
'--domains "' + certificate.domain_names.join(',') + '" ' +
|
||||
(letsencryptStaging ? '--staging' : '');
|
||||
`--domains "${certificate.domain_names.join(',')}" ` +
|
||||
(letsencryptServer !== null ? `--server '${letsencryptServer}' ` : '') +
|
||||
(letsencryptStaging && letsencryptServer === null ? '--staging ' : '');
|
||||
|
||||
logger.info('Command:', cmd);
|
||||
|
||||
@ -868,25 +878,26 @@ const internalCertificate = {
|
||||
const hasConfigArg = certificate.meta.dns_provider !== 'route53';
|
||||
|
||||
let mainCmd = certbotCommand + ' certonly ' +
|
||||
'--config "' + letsencryptConfig + '" ' +
|
||||
`--config '${letsencryptConfig}' ` +
|
||||
'--work-dir "/tmp/letsencrypt-lib" ' +
|
||||
'--logs-dir "/tmp/letsencrypt-log" ' +
|
||||
'--cert-name "npm-' + certificate.id + '" ' +
|
||||
`--cert-name 'npm-${certificate.id}' ` +
|
||||
'--agree-tos ' +
|
||||
'--email "' + certificate.meta.letsencrypt_email + '" ' +
|
||||
'--domains "' + certificate.domain_names.join(',') + '" ' +
|
||||
'--authenticator ' + dnsPlugin.full_plugin_name + ' ' +
|
||||
`--email '${certificate.meta.letsencrypt_email}' ` +
|
||||
`--domains '${certificate.domain_names.join(',')}' ` +
|
||||
`--authenticator '${dnsPlugin.full_plugin_name}' ` +
|
||||
(
|
||||
hasConfigArg
|
||||
? '--' + dnsPlugin.full_plugin_name + '-credentials "' + credentialsLocation + '"'
|
||||
? `--${dnsPlugin.full_plugin_name}-credentials '${credentialsLocation}' `
|
||||
: ''
|
||||
) +
|
||||
(
|
||||
certificate.meta.propagation_seconds !== undefined
|
||||
? ' --' + dnsPlugin.full_plugin_name + '-propagation-seconds ' + certificate.meta.propagation_seconds
|
||||
? `--${dnsPlugin.full_plugin_name}-propagation-seconds '${certificate.meta.propagation_seconds}' `
|
||||
: ''
|
||||
) +
|
||||
(letsencryptStaging ? ' --staging' : '');
|
||||
(letsencryptServer !== null ? `--server '${letsencryptServer}' ` : '') +
|
||||
(letsencryptStaging && letsencryptServer === null ? '--staging ' : '');
|
||||
|
||||
// Prepend the path to the credentials file as an environment variable
|
||||
if (certificate.meta.dns_provider === 'route53') {
|
||||
@ -963,14 +974,15 @@ const internalCertificate = {
|
||||
logger.info('Renewing Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
|
||||
|
||||
const cmd = certbotCommand + ' renew --force-renewal ' +
|
||||
'--config "' + letsencryptConfig + '" ' +
|
||||
`--config '${letsencryptConfig}' ` +
|
||||
'--work-dir "/tmp/letsencrypt-lib" ' +
|
||||
'--logs-dir "/tmp/letsencrypt-log" ' +
|
||||
'--cert-name "npm-' + certificate.id + '" ' +
|
||||
`--cert-name 'npm-${certificate.id}' ` +
|
||||
'--preferred-challenges "dns,http" ' +
|
||||
'--no-random-sleep-on-renew ' +
|
||||
'--disable-hook-validation ' +
|
||||
(letsencryptStaging ? '--staging' : '');
|
||||
(letsencryptServer !== null ? `--server '${letsencryptServer}' ` : '') +
|
||||
(letsencryptStaging && letsencryptServer === null ? '--staging ' : '');
|
||||
|
||||
logger.info('Command:', cmd);
|
||||
|
||||
@ -995,13 +1007,14 @@ const internalCertificate = {
|
||||
logger.info(`Renewing Let'sEncrypt certificates via ${dnsPlugin.name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);
|
||||
|
||||
let mainCmd = certbotCommand + ' renew --force-renewal ' +
|
||||
'--config "' + letsencryptConfig + '" ' +
|
||||
`--config "${letsencryptConfig}" ` +
|
||||
'--work-dir "/tmp/letsencrypt-lib" ' +
|
||||
'--logs-dir "/tmp/letsencrypt-log" ' +
|
||||
'--cert-name "npm-' + certificate.id + '" ' +
|
||||
`--cert-name 'npm-${certificate.id}' ` +
|
||||
'--disable-hook-validation ' +
|
||||
'--no-random-sleep-on-renew ' +
|
||||
(letsencryptStaging ? ' --staging' : '');
|
||||
(letsencryptServer !== null ? `--server '${letsencryptServer}' ` : '') +
|
||||
(letsencryptStaging && letsencryptServer === null ? '--staging ' : '');
|
||||
|
||||
// Prepend the path to the credentials file as an environment variable
|
||||
if (certificate.meta.dns_provider === 'route53') {
|
||||
@ -1027,12 +1040,13 @@ const internalCertificate = {
|
||||
logger.info('Revoking Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
|
||||
|
||||
const mainCmd = certbotCommand + ' revoke ' +
|
||||
'--config "' + letsencryptConfig + '" ' +
|
||||
`--config '${letsencryptConfig}' ` +
|
||||
'--work-dir "/tmp/letsencrypt-lib" ' +
|
||||
'--logs-dir "/tmp/letsencrypt-log" ' +
|
||||
'--cert-path "/etc/letsencrypt/live/npm-' + certificate.id + '/fullchain.pem" ' +
|
||||
`--cert-path '/etc/letsencrypt/live/npm-${certificate.id}/fullchain.pem' ` +
|
||||
'--delete-after-revoke ' +
|
||||
(letsencryptStaging ? '--staging' : '');
|
||||
(letsencryptServer !== null ? `--server '${letsencryptServer}' ` : '') +
|
||||
(letsencryptStaging && letsencryptServer === null ? '--staging ' : '');
|
||||
|
||||
// Don't fail command if file does not exist
|
||||
const delete_credentialsCmd = `rm -f '/etc/letsencrypt/credentials/credentials-${certificate.id}' || true`;
|
||||
|
@ -6,6 +6,7 @@ const internalHost = require('./host');
|
||||
const internalNginx = require('./nginx');
|
||||
const internalAuditLog = require('./audit-log');
|
||||
const internalCertificate = require('./certificate');
|
||||
const {castJsonIfNeed} = require('../lib/helpers');
|
||||
|
||||
function omissions () {
|
||||
return ['is_deleted'];
|
||||
@ -409,16 +410,16 @@ const internalDeadHost = {
|
||||
.where('is_deleted', 0)
|
||||
.groupBy('id')
|
||||
.allowGraph('[owner,certificate]')
|
||||
.orderBy('domain_names', 'ASC');
|
||||
.orderBy(castJsonIfNeed('domain_names'), 'ASC');
|
||||
|
||||
if (access_data.permission_visibility !== 'all') {
|
||||
query.andWhere('owner_user_id', access.token.getUserId(1));
|
||||
}
|
||||
|
||||
// Query is used for searching
|
||||
if (typeof search_query === 'string') {
|
||||
if (typeof search_query === 'string' && search_query.length > 0) {
|
||||
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 redirectionHostModel = require('../models/redirection_host');
|
||||
const deadHostModel = require('../models/dead_host');
|
||||
const {castJsonIfNeed} = require('../lib/helpers');
|
||||
|
||||
const internalHost = {
|
||||
|
||||
@ -17,7 +18,7 @@ const internalHost = {
|
||||
cleanSslHstsData: function (data, 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) {
|
||||
combined_data.ssl_forced = false;
|
||||
@ -73,7 +74,7 @@ const internalHost = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getHostsWithDomains: function (domain_names) {
|
||||
let promises = [
|
||||
const promises = [
|
||||
proxyHostModel
|
||||
.query()
|
||||
.where('is_deleted', 0),
|
||||
@ -125,19 +126,19 @@ const internalHost = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
isHostnameTaken: function (hostname, ignore_type, ignore_id) {
|
||||
let promises = [
|
||||
const promises = [
|
||||
proxyHostModel
|
||||
.query()
|
||||
.where('is_deleted', 0)
|
||||
.andWhere('domain_names', 'like', '%' + hostname + '%'),
|
||||
.andWhere(castJsonIfNeed('domain_names'), 'like', '%' + hostname + '%'),
|
||||
redirectionHostModel
|
||||
.query()
|
||||
.where('is_deleted', 0)
|
||||
.andWhere('domain_names', 'like', '%' + hostname + '%'),
|
||||
.andWhere(castJsonIfNeed('domain_names'), 'like', '%' + hostname + '%'),
|
||||
deadHostModel
|
||||
.query()
|
||||
.where('is_deleted', 0)
|
||||
.andWhere('domain_names', 'like', '%' + hostname + '%')
|
||||
.andWhere(castJsonIfNeed('domain_names'), 'like', '%' + hostname + '%')
|
||||
];
|
||||
|
||||
return Promise.all(promises)
|
||||
|
@ -181,7 +181,9 @@ const internalNginx = {
|
||||
* @param {Object} host
|
||||
* @returns {Promise}
|
||||
*/
|
||||
generateConfig: (host_type, host) => {
|
||||
generateConfig: (host_type, host_row) => {
|
||||
// Prevent modifying the original object:
|
||||
let host = JSON.parse(JSON.stringify(host_row));
|
||||
const nice_host_type = internalNginx.getFileFriendlyHostType(host_type);
|
||||
|
||||
if (config.debug()) {
|
||||
|
@ -6,6 +6,7 @@ const internalHost = require('./host');
|
||||
const internalNginx = require('./nginx');
|
||||
const internalAuditLog = require('./audit-log');
|
||||
const internalCertificate = require('./certificate');
|
||||
const {castJsonIfNeed} = require('../lib/helpers');
|
||||
|
||||
function omissions () {
|
||||
return ['is_deleted', 'owner.is_deleted'];
|
||||
@ -416,16 +417,16 @@ const internalProxyHost = {
|
||||
.where('is_deleted', 0)
|
||||
.groupBy('id')
|
||||
.allowGraph('[owner,access_list,certificate]')
|
||||
.orderBy('domain_names', 'ASC');
|
||||
.orderBy(castJsonIfNeed('domain_names'), 'ASC');
|
||||
|
||||
if (access_data.permission_visibility !== 'all') {
|
||||
query.andWhere('owner_user_id', access.token.getUserId(1));
|
||||
}
|
||||
|
||||
// Query is used for searching
|
||||
if (typeof search_query === 'string') {
|
||||
if (typeof search_query === 'string' && search_query.length > 0) {
|
||||
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 internalAuditLog = require('./audit-log');
|
||||
const internalCertificate = require('./certificate');
|
||||
const {castJsonIfNeed} = require('../lib/helpers');
|
||||
|
||||
function omissions () {
|
||||
return ['is_deleted'];
|
||||
@ -409,16 +410,16 @@ const internalRedirectionHost = {
|
||||
.where('is_deleted', 0)
|
||||
.groupBy('id')
|
||||
.allowGraph('[owner,certificate]')
|
||||
.orderBy('domain_names', 'ASC');
|
||||
.orderBy(castJsonIfNeed('domain_names'), 'ASC');
|
||||
|
||||
if (access_data.permission_visibility !== 'all') {
|
||||
query.andWhere('owner_user_id', access.token.getUserId(1));
|
||||
}
|
||||
|
||||
// Query is used for searching
|
||||
if (typeof search_query === 'string') {
|
||||
if (typeof search_query === 'string' && search_query.length > 0) {
|
||||
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 error = require('../lib/error');
|
||||
const utils = require('../lib/utils');
|
||||
const streamModel = require('../models/stream');
|
||||
const internalNginx = require('./nginx');
|
||||
const internalAuditLog = require('./audit-log');
|
||||
const _ = require('lodash');
|
||||
const error = require('../lib/error');
|
||||
const utils = require('../lib/utils');
|
||||
const streamModel = require('../models/stream');
|
||||
const internalNginx = require('./nginx');
|
||||
const internalAuditLog = require('./audit-log');
|
||||
const internalCertificate = require('./certificate');
|
||||
const internalHost = require('./host');
|
||||
const {castJsonIfNeed} = require('../lib/helpers');
|
||||
|
||||
function omissions () {
|
||||
return ['is_deleted'];
|
||||
return ['is_deleted', 'owner.is_deleted', 'certificate.is_deleted'];
|
||||
}
|
||||
|
||||
const internalStream = {
|
||||
@ -17,6 +20,12 @@ const internalStream = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
create: (access, data) => {
|
||||
const create_certificate = data.certificate_id === 'new';
|
||||
|
||||
if (create_certificate) {
|
||||
delete data.certificate_id;
|
||||
}
|
||||
|
||||
return access.can('streams:create', data)
|
||||
.then((/*access_data*/) => {
|
||||
// TODO: At this point the existing ports should have been checked
|
||||
@ -26,16 +35,44 @@ const internalStream = {
|
||||
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
|
||||
.query()
|
||||
.insertAndFetch(data)
|
||||
.insertAndFetch(data_no_domains)
|
||||
.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) => {
|
||||
// Configure nginx
|
||||
return internalNginx.configure(streamModel, 'stream', row)
|
||||
.then(() => {
|
||||
return internalStream.get(access, {id: row.id, expand: ['owner']});
|
||||
return row;
|
||||
});
|
||||
})
|
||||
.then((row) => {
|
||||
@ -59,6 +96,12 @@ const internalStream = {
|
||||
* @return {Promise}
|
||||
*/
|
||||
update: (access, data) => {
|
||||
const create_certificate = data.certificate_id === 'new';
|
||||
|
||||
if (create_certificate) {
|
||||
delete data.certificate_id;
|
||||
}
|
||||
|
||||
return access.can('streams:update', data.id)
|
||||
.then((/*access_data*/) => {
|
||||
// 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);
|
||||
}
|
||||
|
||||
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
|
||||
.query()
|
||||
.patchAndFetchById(row.id, data)
|
||||
.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) => {
|
||||
// Add to audit log
|
||||
return internalAuditLog.add(access, {
|
||||
@ -92,6 +151,17 @@ const internalStream = {
|
||||
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()
|
||||
.where('is_deleted', 0)
|
||||
.andWhere('id', data.id)
|
||||
.allowGraph('[owner]')
|
||||
.allowGraph('[owner,certificate]')
|
||||
.first();
|
||||
|
||||
if (access_data.permission_visibility !== 'all') {
|
||||
@ -131,6 +201,7 @@ const internalStream = {
|
||||
if (!row || !row.id) {
|
||||
throw new error.ItemNotFoundError(data.id);
|
||||
}
|
||||
row = internalHost.cleanRowCertificateMeta(row);
|
||||
// Custom omissions
|
||||
if (typeof data.omit !== 'undefined' && data.omit !== null) {
|
||||
row = _.omit(row, data.omit);
|
||||
@ -196,14 +267,14 @@ const internalStream = {
|
||||
.then(() => {
|
||||
return internalStream.get(access, {
|
||||
id: data.id,
|
||||
expand: ['owner']
|
||||
expand: ['certificate', 'owner']
|
||||
});
|
||||
})
|
||||
.then((row) => {
|
||||
if (!row || !row.id) {
|
||||
throw new error.ItemNotFoundError(data.id);
|
||||
} else if (row.enabled) {
|
||||
throw new error.ValidationError('Host is already enabled');
|
||||
throw new error.ValidationError('Stream is already enabled');
|
||||
}
|
||||
|
||||
row.enabled = 1;
|
||||
@ -249,7 +320,7 @@ const internalStream = {
|
||||
if (!row || !row.id) {
|
||||
throw new error.ItemNotFoundError(data.id);
|
||||
} else if (!row.enabled) {
|
||||
throw new error.ValidationError('Host is already disabled');
|
||||
throw new error.ValidationError('Stream is already disabled');
|
||||
}
|
||||
|
||||
row.enabled = 0;
|
||||
@ -293,21 +364,21 @@ const internalStream = {
|
||||
getAll: (access, expand, search_query) => {
|
||||
return access.can('streams:list')
|
||||
.then((access_data) => {
|
||||
let query = streamModel
|
||||
const query = streamModel
|
||||
.query()
|
||||
.where('is_deleted', 0)
|
||||
.groupBy('id')
|
||||
.allowGraph('[owner]')
|
||||
.orderBy('incoming_port', 'ASC');
|
||||
.allowGraph('[owner,certificate]')
|
||||
.orderByRaw('CAST(incoming_port AS INTEGER) ASC');
|
||||
|
||||
if (access_data.permission_visibility !== 'all') {
|
||||
query.andWhere('owner_user_id', access.token.getUserId(1));
|
||||
}
|
||||
|
||||
// Query is used for searching
|
||||
if (typeof search_query === 'string') {
|
||||
if (typeof search_query === 'string' && search_query.length > 0) {
|
||||
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()));
|
||||
})
|
||||
.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}
|
||||
*/
|
||||
getCount: (user_id, visibility) => {
|
||||
let query = streamModel
|
||||
const query = streamModel
|
||||
.query()
|
||||
.count('id as count')
|
||||
.count('id AS count')
|
||||
.where('is_deleted', 0);
|
||||
|
||||
if (visibility !== 'all') {
|
||||
|
@ -5,6 +5,8 @@ const authModel = require('../models/auth');
|
||||
const helpers = require('../lib/helpers');
|
||||
const TokenModel = require('../models/token');
|
||||
|
||||
const ERROR_MESSAGE_INVALID_AUTH = 'Invalid email or password';
|
||||
|
||||
module.exports = {
|
||||
|
||||
/**
|
||||
@ -69,15 +71,15 @@ module.exports = {
|
||||
};
|
||||
});
|
||||
} else {
|
||||
throw new error.AuthError('Invalid password');
|
||||
throw new error.AuthError(ERROR_MESSAGE_INVALID_AUTH);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
throw new error.AuthError('No password auth for user');
|
||||
throw new error.AuthError(ERROR_MESSAGE_INVALID_AUTH);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
throw new error.AuthError('No relevant user found');
|
||||
throw new error.AuthError(ERROR_MESSAGE_INVALID_AUTH);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
@ -2,7 +2,10 @@ const fs = require('fs');
|
||||
const NodeRSA = require('node-rsa');
|
||||
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;
|
||||
|
||||
@ -14,7 +17,7 @@ const configure = () => {
|
||||
let configData;
|
||||
try {
|
||||
configData = require(filename);
|
||||
} catch (err) {
|
||||
} catch (_) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@ -34,7 +37,7 @@ const configure = () => {
|
||||
logger.info('Using MySQL configuration');
|
||||
instance = {
|
||||
database: {
|
||||
engine: 'mysql2',
|
||||
engine: mysqlEngine,
|
||||
host: envMysqlHost,
|
||||
port: process.env.DB_MYSQL_PORT || 3306,
|
||||
user: envMysqlUser,
|
||||
@ -46,13 +49,33 @@ const configure = () => {
|
||||
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';
|
||||
logger.info(`Using Sqlite: ${envSqliteFile}`);
|
||||
instance = {
|
||||
database: {
|
||||
engine: 'knex-native',
|
||||
knex: {
|
||||
client: 'sqlite3',
|
||||
client: sqliteClientName,
|
||||
connection: {
|
||||
filename: envSqliteFile
|
||||
},
|
||||
@ -143,7 +166,27 @@ module.exports = {
|
||||
*/
|
||||
isSqlite: function () {
|
||||
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 () {
|
||||
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 = {
|
||||
|
||||
@ -45,6 +47,16 @@ module.exports = {
|
||||
}
|
||||
});
|
||||
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;
|
||||
}
|
||||
|
||||
};
|
||||
|
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 helpers = require('../lib/helpers');
|
||||
const Model = require('objection').Model;
|
||||
const User = require('./user');
|
||||
const now = require('./now_helper');
|
||||
|
||||
Model.knex(db);
|
||||
@ -68,6 +67,11 @@ class Certificate extends Model {
|
||||
}
|
||||
|
||||
static get relationMappings () {
|
||||
const ProxyHost = require('./proxy_host');
|
||||
const DeadHost = require('./dead_host');
|
||||
const User = require('./user');
|
||||
const RedirectionHost = require('./redirection_host');
|
||||
|
||||
return {
|
||||
owner: {
|
||||
relation: Model.HasOneRelation,
|
||||
@ -79,6 +83,39 @@ class Certificate extends Model {
|
||||
modify: function (qb) {
|
||||
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 = [
|
||||
'is_deleted',
|
||||
'ssl_forced',
|
||||
'http2_support',
|
||||
'enabled',
|
||||
'hsts_enabled',
|
||||
'hsts_subdomains',
|
||||
];
|
||||
|
||||
class DeadHost extends Model {
|
||||
|
@ -17,6 +17,9 @@ const boolFields = [
|
||||
'preserve_path',
|
||||
'ssl_forced',
|
||||
'block_exploits',
|
||||
'hsts_enabled',
|
||||
'hsts_subdomains',
|
||||
'http2_support',
|
||||
];
|
||||
|
||||
class RedirectionHost extends Model {
|
||||
|
@ -1,16 +1,15 @@
|
||||
// Objection Docs:
|
||||
// http://vincit.github.io/objection.js/
|
||||
|
||||
const db = require('../db');
|
||||
const helpers = require('../lib/helpers');
|
||||
const Model = require('objection').Model;
|
||||
const User = require('./user');
|
||||
const now = require('./now_helper');
|
||||
const Model = require('objection').Model;
|
||||
const db = require('../db');
|
||||
const helpers = require('../lib/helpers');
|
||||
const User = require('./user');
|
||||
const Certificate = require('./certificate');
|
||||
const now = require('./now_helper');
|
||||
|
||||
Model.knex(db);
|
||||
|
||||
const boolFields = [
|
||||
'is_deleted',
|
||||
'enabled',
|
||||
'tcp_forwarding',
|
||||
'udp_forwarding',
|
||||
];
|
||||
@ -64,6 +63,17 @@ class Stream extends Model {
|
||||
modify: function (qb) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -23,6 +23,7 @@
|
||||
"node-rsa": "^1.0.8",
|
||||
"objection": "3.0.1",
|
||||
"path": "^0.12.7",
|
||||
"pg": "^8.13.1",
|
||||
"signale": "1.4.0",
|
||||
"sqlite3": "5.1.6",
|
||||
"temp-write": "^4.0.0"
|
||||
|
@ -24,22 +24,34 @@
|
||||
"description": "Nice Name for the custom certificate"
|
||||
},
|
||||
"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": {
|
||||
"description": "Date and time of expiration",
|
||||
"readOnly": true,
|
||||
"type": "string"
|
||||
},
|
||||
"owner": {
|
||||
"$ref": "./user-object.json"
|
||||
},
|
||||
"meta": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"letsencrypt_email": {
|
||||
"type": "string"
|
||||
"certificate": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"letsencrypt_agree": {
|
||||
"type": "boolean"
|
||||
"certificate_key": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"dns_challenge": {
|
||||
"type": "boolean"
|
||||
@ -50,13 +62,18 @@
|
||||
"dns_provider_credentials": {
|
||||
"type": "string"
|
||||
},
|
||||
"letsencrypt_agree": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"letsencrypt_certificate": {
|
||||
"type": "object"
|
||||
},
|
||||
"letsencrypt_email": {
|
||||
"type": "string"
|
||||
},
|
||||
"propagation_seconds": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
}
|
||||
]
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,10 +22,7 @@
|
||||
"enabled",
|
||||
"locations",
|
||||
"hsts_enabled",
|
||||
"hsts_subdomains",
|
||||
"certificate",
|
||||
"use_default_location",
|
||||
"ipv6"
|
||||
"hsts_subdomains"
|
||||
],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
@ -151,12 +148,6 @@
|
||||
"$ref": "./access-list-object.json"
|
||||
}
|
||||
]
|
||||
},
|
||||
"use_default_location": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"ipv6": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,7 @@
|
||||
},
|
||||
"forward_scheme": {
|
||||
"type": "string",
|
||||
"enum": ["http", "https"]
|
||||
"enum": ["auto", "http", "https"]
|
||||
},
|
||||
"forward_domain_name": {
|
||||
"description": "Domain Name",
|
||||
|
@ -25,7 +25,7 @@
|
||||
"value": {
|
||||
"description": "Value in almost any form",
|
||||
"example": "congratulations",
|
||||
"oneOf": [
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
@ -46,7 +46,10 @@
|
||||
},
|
||||
"meta": {
|
||||
"description": "Extra metadata",
|
||||
"example": {},
|
||||
"example": {
|
||||
"redirect": "http://example.com",
|
||||
"html": "<h1>404</h1>"
|
||||
},
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
|
@ -53,8 +53,24 @@
|
||||
"enabled": {
|
||||
"$ref": "../common.json#/properties/enabled"
|
||||
},
|
||||
"certificate_id": {
|
||||
"$ref": "../common.json#/properties/certificate_id"
|
||||
},
|
||||
"meta": {
|
||||
"type": "object"
|
||||
},
|
||||
"owner": {
|
||||
"$ref": "./user-object.json"
|
||||
},
|
||||
"certificate": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "null"
|
||||
},
|
||||
{
|
||||
"$ref": "./certificate-object.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,10 +5,9 @@
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"expires": {
|
||||
"description": "Token Expiry Unix Time",
|
||||
"example": 1566540249,
|
||||
"minimum": 1,
|
||||
"type": "number"
|
||||
"description": "Token Expiry ISO Time String",
|
||||
"example": "2025-02-04T20:40:46.340Z",
|
||||
"type": "string"
|
||||
},
|
||||
"token": {
|
||||
"description": "JWT Token",
|
||||
|
@ -49,8 +49,7 @@
|
||||
"minLength": 1
|
||||
},
|
||||
"password": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
},
|
||||
"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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -94,9 +94,7 @@
|
||||
"avatar": "",
|
||||
"roles": ["admin"]
|
||||
},
|
||||
"certificate": null,
|
||||
"use_default_location": true,
|
||||
"ipv6": true
|
||||
"certificate": null
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -79,9 +79,7 @@
|
||||
"nickname": "Admin",
|
||||
"avatar": "",
|
||||
"roles": ["admin"]
|
||||
},
|
||||
"use_default_location": true,
|
||||
"ipv6": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -129,9 +129,7 @@
|
||||
"roles": ["admin"]
|
||||
},
|
||||
"certificate": null,
|
||||
"access_list": null,
|
||||
"use_default_location": true,
|
||||
"ipv6": true
|
||||
"access_list": null
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -114,9 +114,7 @@
|
||||
"avatar": "//www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?default=mm",
|
||||
"roles": ["admin"]
|
||||
},
|
||||
"access_list": null,
|
||||
"use_default_location": true,
|
||||
"ipv6": true
|
||||
"access_list": null
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -114,9 +114,7 @@
|
||||
"avatar": "",
|
||||
"roles": ["admin"]
|
||||
},
|
||||
"certificate": null,
|
||||
"use_default_location": true,
|
||||
"ipv6": true
|
||||
"certificate": null
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -99,9 +99,7 @@
|
||||
"nickname": "Admin",
|
||||
"avatar": "",
|
||||
"roles": ["admin"]
|
||||
},
|
||||
"use_default_location": true,
|
||||
"ipv6": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -14,7 +14,7 @@
|
||||
"description": "Expansions",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"enum": ["access_list", "owner", "certificate"]
|
||||
"enum": ["owner", "certificate"]
|
||||
}
|
||||
}
|
||||
],
|
||||
@ -40,7 +40,8 @@
|
||||
"nginx_online": true,
|
||||
"nginx_err": null
|
||||
},
|
||||
"enabled": true
|
||||
"enabled": true,
|
||||
"certificate_id": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -32,6 +32,9 @@
|
||||
"udp_forwarding": {
|
||||
"$ref": "../../../components/stream-object.json#/properties/udp_forwarding"
|
||||
},
|
||||
"certificate_id": {
|
||||
"$ref": "../../../components/stream-object.json#/properties/certificate_id"
|
||||
},
|
||||
"meta": {
|
||||
"$ref": "../../../components/stream-object.json#/properties/meta"
|
||||
}
|
||||
@ -73,7 +76,8 @@
|
||||
"nickname": "Admin",
|
||||
"avatar": "",
|
||||
"roles": ["admin"]
|
||||
}
|
||||
},
|
||||
"certificate_id": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -40,7 +40,8 @@
|
||||
"nginx_online": true,
|
||||
"nginx_err": null
|
||||
},
|
||||
"enabled": true
|
||||
"enabled": true,
|
||||
"certificate_id": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -29,56 +29,26 @@
|
||||
"additionalProperties": false,
|
||||
"minProperties": 1,
|
||||
"properties": {
|
||||
"domain_names": {
|
||||
"$ref": "../../../../components/proxy-host-object.json#/properties/domain_names"
|
||||
"incoming_port": {
|
||||
"$ref": "../../../../components/stream-object.json#/properties/incoming_port"
|
||||
},
|
||||
"forward_scheme": {
|
||||
"$ref": "../../../../components/proxy-host-object.json#/properties/forward_scheme"
|
||||
"forwarding_host": {
|
||||
"$ref": "../../../../components/stream-object.json#/properties/forwarding_host"
|
||||
},
|
||||
"forward_host": {
|
||||
"$ref": "../../../../components/proxy-host-object.json#/properties/forward_host"
|
||||
"forwarding_port": {
|
||||
"$ref": "../../../../components/stream-object.json#/properties/forwarding_port"
|
||||
},
|
||||
"forward_port": {
|
||||
"$ref": "../../../../components/proxy-host-object.json#/properties/forward_port"
|
||||
"tcp_forwarding": {
|
||||
"$ref": "../../../../components/stream-object.json#/properties/tcp_forwarding"
|
||||
},
|
||||
"udp_forwarding": {
|
||||
"$ref": "../../../../components/stream-object.json#/properties/udp_forwarding"
|
||||
},
|
||||
"certificate_id": {
|
||||
"$ref": "../../../../components/proxy-host-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"
|
||||
"$ref": "../../../../components/stream-object.json#/properties/certificate_id"
|
||||
},
|
||||
"meta": {
|
||||
"$ref": "../../../../components/proxy-host-object.json#/properties/meta"
|
||||
},
|
||||
"locations": {
|
||||
"$ref": "../../../../components/proxy-host-object.json#/properties/locations"
|
||||
"$ref": "../../../../components/stream-object.json#/properties/meta"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -94,44 +64,32 @@
|
||||
"default": {
|
||||
"value": {
|
||||
"id": 1,
|
||||
"created_on": "2024-10-08T23:23:03.000Z",
|
||||
"modified_on": "2024-10-08T23:26:37.000Z",
|
||||
"created_on": "2024-10-09T02:33:45.000Z",
|
||||
"modified_on": "2024-10-09T02:33:45.000Z",
|
||||
"owner_user_id": 1,
|
||||
"domain_names": ["test.example.com"],
|
||||
"forward_host": "192.168.0.10",
|
||||
"forward_port": 8989,
|
||||
"access_list_id": 0,
|
||||
"certificate_id": 0,
|
||||
"ssl_forced": false,
|
||||
"caching_enabled": false,
|
||||
"block_exploits": false,
|
||||
"advanced_config": "",
|
||||
"incoming_port": 9090,
|
||||
"forwarding_host": "router.internal",
|
||||
"forwarding_port": 80,
|
||||
"tcp_forwarding": true,
|
||||
"udp_forwarding": false,
|
||||
"meta": {
|
||||
"nginx_online": true,
|
||||
"nginx_err": null
|
||||
},
|
||||
"allow_websocket_upgrade": false,
|
||||
"http2_support": false,
|
||||
"forward_scheme": "http",
|
||||
"enabled": true,
|
||||
"hsts_enabled": false,
|
||||
"hsts_subdomains": false,
|
||||
"owner": {
|
||||
"id": 1,
|
||||
"created_on": "2024-10-07T22:43:55.000Z",
|
||||
"modified_on": "2024-10-08T12:52:54.000Z",
|
||||
"created_on": "2024-10-09T02:33:16.000Z",
|
||||
"modified_on": "2024-10-09T02:33:16.000Z",
|
||||
"is_deleted": false,
|
||||
"is_disabled": false,
|
||||
"email": "admin@example.com",
|
||||
"name": "Administrator",
|
||||
"nickname": "some guy",
|
||||
"avatar": "//www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?default=mm",
|
||||
"nickname": "Admin",
|
||||
"avatar": "",
|
||||
"roles": ["admin"]
|
||||
},
|
||||
"certificate": null,
|
||||
"access_list": null,
|
||||
"use_default_location": true,
|
||||
"ipv6": true
|
||||
"certificate_id": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -13,7 +13,8 @@
|
||||
"name": "settingID",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
"minLength": 1,
|
||||
"enum": ["default-site"]
|
||||
},
|
||||
"required": true,
|
||||
"description": "Setting ID",
|
||||
@ -31,10 +32,21 @@
|
||||
"minProperties": 1,
|
||||
"properties": {
|
||||
"value": {
|
||||
"$ref": "../../../components/setting-object.json#/properties/value"
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"enum": ["congratulations", "404", "444", "redirect", "html"]
|
||||
},
|
||||
"meta": {
|
||||
"$ref": "../../../components/setting-object.json#/properties/meta"
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"redirect": {
|
||||
"type": "string"
|
||||
},
|
||||
"html": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
"examples": {
|
||||
"default": {
|
||||
"value": {
|
||||
"expires": 1566540510,
|
||||
"expires": "2025-02-04T20:40:46.340Z",
|
||||
"token": "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.ey...xaHKYr3Kk6MvkUjcC4"
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,7 @@
|
||||
"default": {
|
||||
"value": {
|
||||
"result": {
|
||||
"expires": 1566540510,
|
||||
"expires": "2025-02-04T20:40:46.340Z",
|
||||
"token": "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.ey...xaHKYr3Kk6MvkUjcC4"
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,15 @@
|
||||
"url": "http://127.0.0.1:81/api"
|
||||
}
|
||||
],
|
||||
"components": {
|
||||
"securitySchemes": {
|
||||
"bearerAuth": {
|
||||
"type": "http",
|
||||
"scheme": "bearer",
|
||||
"bearerFormat": "JWT"
|
||||
}
|
||||
}
|
||||
},
|
||||
"paths": {
|
||||
"/": {
|
||||
"get": {
|
||||
|
@ -15,18 +15,18 @@ const certbot = require('./lib/certbot');
|
||||
const setupDefaultUser = () => {
|
||||
return userModel
|
||||
.query()
|
||||
.select(userModel.raw('COUNT(`id`) as `count`'))
|
||||
.select('id', )
|
||||
.where('is_deleted', 0)
|
||||
.first()
|
||||
.then((row) => {
|
||||
if (!row.count) {
|
||||
if (!row || !row.id) {
|
||||
// Create a new user and set password
|
||||
let email = process.env.INITIAL_ADMIN_EMAIL || 'admin@example.com';
|
||||
let password = process.env.INITIAL_ADMIN_PASSWORD || 'changeme';
|
||||
|
||||
const email = process.env.INITIAL_ADMIN_EMAIL || 'admin@example.com';
|
||||
const password = process.env.INITIAL_ADMIN_PASSWORD || 'changeme';
|
||||
|
||||
logger.info('Creating a new user: ' + email + ' with password: ' + password);
|
||||
|
||||
let data = {
|
||||
const data = {
|
||||
is_deleted: 0,
|
||||
email: email,
|
||||
name: 'Administrator',
|
||||
@ -77,11 +77,11 @@ const setupDefaultUser = () => {
|
||||
const setupDefaultSettings = () => {
|
||||
return settingModel
|
||||
.query()
|
||||
.select(settingModel.raw('COUNT(`id`) as `count`'))
|
||||
.select('id')
|
||||
.where({id: 'default-site'})
|
||||
.first()
|
||||
.then((row) => {
|
||||
if (!row.count) {
|
||||
if (!row || !row.id) {
|
||||
settingModel
|
||||
.query()
|
||||
.insert({
|
||||
|
@ -4,7 +4,7 @@
|
||||
auth_basic "Authorization required";
|
||||
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 "";
|
||||
{% endif %}
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
deny all;
|
||||
|
||||
# Access checks must...
|
||||
{% if access_list.satisfy_any == 1 %}
|
||||
{% if access_list.satisfy_any == 1 or access_list.satisfy_any == true %}
|
||||
satisfy any;
|
||||
{% else %}
|
||||
satisfy all;
|
||||
|
@ -2,6 +2,7 @@
|
||||
{% if certificate.provider == "letsencrypt" %}
|
||||
# Let's Encrypt SSL
|
||||
include conf.d/include/letsencrypt-acme-challenge.conf;
|
||||
include conf.d/include/ssl-cache.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;
|
||||
|
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;
|
||||
{% endif %}
|
||||
{% if certificate -%}
|
||||
listen 443 ssl{% if http2_support == 1 or http2_support == true %} http2{% endif %};
|
||||
listen 443 ssl;
|
||||
{% if ipv6 -%}
|
||||
listen [::]:443 ssl{% if http2_support == 1 or http2_support == true %} http2{% endif %};
|
||||
listen [::]:443 ssl;
|
||||
{% else -%}
|
||||
#listen [::]:443;
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
server_name {{ domain_names | join: " " }};
|
||||
{% if http2_support == 1 or http2_support == true %}
|
||||
http2 on;
|
||||
{% else -%}
|
||||
http2 off;
|
||||
{% endif %}
|
@ -7,11 +7,7 @@
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
|
||||
set $proxy_forward_scheme {{ forward_scheme }};
|
||||
set $proxy_server "{{ forward_host }}";
|
||||
set $proxy_port {{ forward_port }};
|
||||
|
||||
proxy_pass $proxy_forward_scheme://$proxy_server:$proxy_port{{ forward_path }};
|
||||
proxy_pass {{ forward_scheme }}://{{ forward_host }}:{{ forward_port }}{{ forward_path }};
|
||||
|
||||
{% include "_access.conf" %}
|
||||
{% include "_assets.conf" %}
|
||||
|
@ -22,5 +22,7 @@ server {
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
# Custom
|
||||
include /data/nginx/custom/server_dead[.]conf;
|
||||
}
|
||||
{% endif %}
|
||||
|
@ -5,12 +5,10 @@
|
||||
{% if enabled %}
|
||||
{% if tcp_forwarding == 1 or tcp_forwarding == true -%}
|
||||
server {
|
||||
listen {{ incoming_port }};
|
||||
{% if ipv6 -%}
|
||||
listen [::]:{{ incoming_port }};
|
||||
{% else -%}
|
||||
#listen [::]:{{ incoming_port }};
|
||||
{% endif %}
|
||||
listen {{ incoming_port }} {%- if certificate %} ssl {%- endif %};
|
||||
{% unless ipv6 -%} # {%- endunless -%} listen [::]:{{ incoming_port }} {%- if certificate %} ssl {%- endif %};
|
||||
|
||||
{%- include "_certificates_stream.conf" %}
|
||||
|
||||
proxy_pass {{ forwarding_host }}:{{ forwarding_port }};
|
||||
|
||||
@ -19,14 +17,12 @@ server {
|
||||
include /data/nginx/custom/server_stream_tcp[.]conf;
|
||||
}
|
||||
{% endif %}
|
||||
{% if udp_forwarding == 1 or udp_forwarding == true %}
|
||||
|
||||
{% if udp_forwarding == 1 or udp_forwarding == true -%}
|
||||
server {
|
||||
listen {{ incoming_port }} udp;
|
||||
{% if ipv6 -%}
|
||||
listen [::]:{{ incoming_port }} udp;
|
||||
{% else -%}
|
||||
#listen [::]:{{ incoming_port }} udp;
|
||||
{% endif %}
|
||||
{% unless ipv6 -%} # {%- endunless -%} listen [::]:{{ incoming_port }} udp;
|
||||
|
||||
proxy_pass {{ forwarding_host }}:{{ forwarding_port }};
|
||||
|
||||
# Custom
|
||||
|
@ -830,9 +830,9 @@ crc32-stream@^4.0.2:
|
||||
readable-stream "^3.4.0"
|
||||
|
||||
cross-spawn@^7.0.2:
|
||||
version "7.0.3"
|
||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
|
||||
integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
|
||||
version "7.0.6"
|
||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f"
|
||||
integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==
|
||||
dependencies:
|
||||
path-key "^3.1.0"
|
||||
shebang-command "^2.0.0"
|
||||
@ -2735,11 +2735,67 @@ path@^0.12.7:
|
||||
process "^0.11.1"
|
||||
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:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.5.0.tgz#538cadd0f7e603fc09a12590f3b8a452c2c0cf34"
|
||||
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:
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad"
|
||||
@ -2758,6 +2814,28 @@ pkg-conf@^2.1.0:
|
||||
find-up "^2.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:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
|
||||
@ -3194,6 +3272,11 @@ socks@^2.6.2:
|
||||
ip "^2.0.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:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
|
||||
@ -3665,6 +3748,11 @@ xdg-basedir@^4.0.0:
|
||||
resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13"
|
||||
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:
|
||||
version "4.0.1"
|
||||
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
|
||||
|
||||
FROM nginxproxymanager/testca AS testca
|
||||
FROM letsencrypt/pebble AS pebbleca
|
||||
FROM nginxproxymanager/nginx-full:certbot-node
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
@ -45,6 +47,8 @@ RUN yarn install \
|
||||
|
||||
# add late to limit cache-busting by modifications
|
||||
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
|
||||
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
|
||||
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 \
|
||||
S6_BEHAVIOUR_IF_STAGE2_FAILS=1 \
|
||||
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/*
|
||||
|
||||
# Task
|
||||
RUN cd /usr \
|
||||
&& curl -sL https://taskfile.dev/install.sh | sh \
|
||||
&& cd /root
|
||||
WORKDIR /usr
|
||||
RUN curl -sL https://taskfile.dev/install.sh | sh
|
||||
WORKDIR /root
|
||||
|
||||
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
|
||||
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
|
||||
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_USER: 'npm'
|
||||
MYSQL_PASSWORD: 'npmpass'
|
||||
MARIADB_AUTO_UPGRADE: '1'
|
||||
volumes:
|
||||
- mysql_vol:/var/lib/mysql
|
||||
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:
|
||||
DEBUG: 'true'
|
||||
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:
|
||||
- 'npm_data_ci:/data'
|
||||
- 'npm_le_ci:/etc/letsencrypt'
|
||||
@ -19,6 +22,10 @@ services:
|
||||
test: ["CMD", "/usr/bin/check-health"]
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
expose:
|
||||
- '80-81/tcp'
|
||||
- '443/tcp'
|
||||
- '1500-1503/tcp'
|
||||
networks:
|
||||
fulltest:
|
||||
aliases:
|
||||
@ -37,7 +44,7 @@ services:
|
||||
- ca.internal
|
||||
|
||||
pdns:
|
||||
image: pschiffe/pdns-mysql
|
||||
image: pschiffe/pdns-mysql:4.8
|
||||
volumes:
|
||||
- '/etc/localtime:/etc/localtime:ro'
|
||||
environment:
|
||||
@ -91,14 +98,25 @@ services:
|
||||
context: ../
|
||||
dockerfile: test/cypress/Dockerfile
|
||||
environment:
|
||||
CYPRESS_baseUrl: 'http://fullstack:81'
|
||||
HTTP_PROXY: 'squid:3128'
|
||||
HTTPS_PROXY: 'squid:3128'
|
||||
volumes:
|
||||
- 'cypress_logs:/results'
|
||||
- 'cypress_logs:/test/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:
|
||||
- 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:
|
||||
cypress_logs:
|
||||
npm_data_ci:
|
||||
|
@ -1,9 +1,9 @@
|
||||
# WARNING: This is a DEVELOPMENT docker-compose file, it should not be used for production.
|
||||
services:
|
||||
|
||||
npm:
|
||||
image: nginxproxymanager:dev
|
||||
container_name: npm_core
|
||||
fullstack:
|
||||
image: npm2dev:core
|
||||
container_name: npm2dev.core
|
||||
build:
|
||||
context: ./
|
||||
dockerfile: ./dev/Dockerfile
|
||||
@ -12,7 +12,11 @@ services:
|
||||
- 3081:81
|
||||
- 3443:443
|
||||
networks:
|
||||
- nginx_proxy_manager
|
||||
nginx_proxy_manager:
|
||||
aliases:
|
||||
- website1.example.com
|
||||
- website2.example.com
|
||||
- website3.example.com
|
||||
environment:
|
||||
PUID: 1000
|
||||
PGID: 1000
|
||||
@ -22,26 +26,44 @@ services:
|
||||
DEVELOPMENT: 'true'
|
||||
LE_STAGING: 'true'
|
||||
# db:
|
||||
DB_MYSQL_HOST: 'db'
|
||||
DB_MYSQL_PORT: '3306'
|
||||
DB_MYSQL_USER: 'npm'
|
||||
DB_MYSQL_PASSWORD: 'npm'
|
||||
DB_MYSQL_NAME: 'npm'
|
||||
# DB_MYSQL_HOST: 'db'
|
||||
# DB_MYSQL_PORT: '3306'
|
||||
# DB_MYSQL_USER: 'npm'
|
||||
# DB_MYSQL_PASSWORD: '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"
|
||||
# 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:
|
||||
- npm_data:/data
|
||||
- le_data:/etc/letsencrypt
|
||||
- './dev/resolv.conf:/etc/resolv.conf:ro'
|
||||
- ../backend:/app
|
||||
- ../frontend:/app/frontend
|
||||
- ../global:/app/global
|
||||
healthcheck:
|
||||
test: ["CMD", "/usr/bin/check-health"]
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
depends_on:
|
||||
- db
|
||||
- db-postgres
|
||||
- authentik
|
||||
- authentik-worker
|
||||
- authentik-ldap
|
||||
working_dir: /app
|
||||
|
||||
db:
|
||||
image: jc21/mariadb-aria
|
||||
container_name: npm_db
|
||||
container_name: npm2dev.db
|
||||
ports:
|
||||
- 33306:3306
|
||||
networks:
|
||||
@ -54,25 +76,193 @@ services:
|
||||
volumes:
|
||||
- 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:
|
||||
image: swaggerapi/swagger-ui:latest
|
||||
container_name: npm_swagger
|
||||
container_name: npm2dev.swagger
|
||||
ports:
|
||||
- 3082:80
|
||||
environment:
|
||||
URL: "http://npm:81/api/schema"
|
||||
PORT: '80'
|
||||
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:
|
||||
npm_data:
|
||||
name: npm_core_data
|
||||
name: npm2dev_core_data
|
||||
le_data:
|
||||
name: npm_le_data
|
||||
name: npm2dev_le_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:
|
||||
nginx_proxy_manager:
|
||||
name: npm_network
|
||||
name: npm2dev_network
|
||||
|
@ -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;
|
||||
|
||||
# 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.
|
||||
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';
|
||||
|
@ -8,7 +8,7 @@ BLUE='\E[1;34m'
|
||||
GREEN='\E[1;32m'
|
||||
RESET='\E[0m'
|
||||
|
||||
S6_OVERLAY_VERSION=3.1.5.0
|
||||
S6_OVERLAY_VERSION=3.2.0.2
|
||||
TARGETPLATFORM=${1:-linux/amd64}
|
||||
|
||||
# Determine the correct binary file for the architecture given
|
||||
|
@ -50,7 +50,6 @@ networks:
|
||||
Let's look at a Portainer example:
|
||||
|
||||
```yml
|
||||
version: '3.8'
|
||||
services:
|
||||
|
||||
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.
|
||||
|
||||
```yml
|
||||
version: '3.8'
|
||||
|
||||
secrets:
|
||||
# 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"
|
||||
@ -184,6 +181,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_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_dead.conf`: Included at the end of every 404 server block
|
||||
|
||||
Every file is optional.
|
||||
|
||||
|
@ -9,7 +9,6 @@ outline: deep
|
||||
Create a `docker-compose.yml` file:
|
||||
|
||||
```yml
|
||||
version: '3.8'
|
||||
services:
|
||||
app:
|
||||
image: 'jc21/nginx-proxy-manager:latest'
|
||||
@ -22,8 +21,7 @@ services:
|
||||
# Add any other Stream port you want to expose
|
||||
# - '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
|
||||
# the SQLite DB file within the container
|
||||
# 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:
|
||||
|
||||
```yml
|
||||
version: '3.8'
|
||||
services:
|
||||
app:
|
||||
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
|
||||
|
||||
The docker images support the following architectures:
|
||||
@ -137,5 +181,13 @@ Email: admin@example.com
|
||||
Password: changeme
|
||||
```
|
||||
|
||||
Immediately after logging in with this default user you will be asked to modify your details and change your password.
|
||||
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)
|
||||
- [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 VE Helper-Scripts](https://community-scripts.github.io/ProxmoxVE/scripts?id=nginxproxymanager)
|
||||
- [nginxproxymanagerGraf](https://github.com/ma-karai/nginxproxymanagerGraf)
|
||||
|
||||
|
||||
|
@ -873,9 +873,9 @@ mitt@^3.0.1:
|
||||
integrity sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==
|
||||
|
||||
nanoid@^3.3.7:
|
||||
version "3.3.7"
|
||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8"
|
||||
integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==
|
||||
version "3.3.8"
|
||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.8.tgz#b1be3030bee36aaff18bacb375e5cce521684baf"
|
||||
integrity sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==
|
||||
|
||||
oniguruma-to-js@0.4.3:
|
||||
version "0.4.3"
|
||||
@ -1065,9 +1065,9 @@ vfile@^6.0.0:
|
||||
vfile-message "^4.0.0"
|
||||
|
||||
vite@^5.4.8:
|
||||
version "5.4.8"
|
||||
resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.8.tgz#af548ce1c211b2785478d3ba3e8da51e39a287e8"
|
||||
integrity sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==
|
||||
version "5.4.14"
|
||||
resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.14.tgz#ff8255edb02134df180dcfca1916c37a6abe8408"
|
||||
integrity sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==
|
||||
dependencies:
|
||||
esbuild "^0.21.3"
|
||||
postcss "^8.4.43"
|
||||
|
@ -4,444 +4,438 @@ const Tokens = require('./tokens');
|
||||
|
||||
module.exports = {
|
||||
|
||||
/**
|
||||
* @param {String} route
|
||||
* @param {Object} [options]
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
navigate: function (route, options) {
|
||||
options = options || {};
|
||||
Backbone.history.navigate(route.toString(), options);
|
||||
return true;
|
||||
},
|
||||
/**
|
||||
* @param {String} route
|
||||
* @param {Object} [options]
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
navigate: function (route, options) {
|
||||
options = options || {};
|
||||
Backbone.history.navigate(route.toString(), options);
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Login
|
||||
*/
|
||||
showLogin: function () {
|
||||
window.location = '/login';
|
||||
},
|
||||
/**
|
||||
* Login
|
||||
*/
|
||||
showLogin: function () {
|
||||
window.location = '/login';
|
||||
},
|
||||
|
||||
/**
|
||||
* Users
|
||||
*/
|
||||
showUsers: function () {
|
||||
let controller = this;
|
||||
if (Cache.User.isAdmin()) {
|
||||
require(['./main', './users/main'], (App, View) => {
|
||||
controller.navigate('/users');
|
||||
App.UI.showAppContent(new View());
|
||||
});
|
||||
} else {
|
||||
this.showDashboard();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Users
|
||||
*/
|
||||
showUsers: function () {
|
||||
const controller = this;
|
||||
if (Cache.User.isAdmin()) {
|
||||
require(['./main', './users/main'], (App, View) => {
|
||||
controller.navigate('/users');
|
||||
App.UI.showAppContent(new View());
|
||||
});
|
||||
} else {
|
||||
this.showDashboard();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* User Form
|
||||
*
|
||||
* @param [model]
|
||||
*/
|
||||
showUserForm: function (model) {
|
||||
if (Cache.User.isAdmin()) {
|
||||
require(['./main', './user/form'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* User Form
|
||||
*
|
||||
* @param [model]
|
||||
*/
|
||||
showUserForm: function (model) {
|
||||
if (Cache.User.isAdmin()) {
|
||||
require(['./main', './user/form'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* User Permissions Form
|
||||
*
|
||||
* @param model
|
||||
*/
|
||||
showUserPermissions: function (model) {
|
||||
if (Cache.User.isAdmin()) {
|
||||
require(['./main', './user/permissions'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* User Permissions Form
|
||||
*
|
||||
* @param model
|
||||
*/
|
||||
showUserPermissions: function (model) {
|
||||
if (Cache.User.isAdmin()) {
|
||||
require(['./main', './user/permissions'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* User Password Form
|
||||
*
|
||||
* @param model
|
||||
*/
|
||||
showUserPasswordForm: function (model) {
|
||||
if (Cache.User.isAdmin() || model.get('id') === Cache.User.get('id')) {
|
||||
require(['./main', './user/password'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* User Password Form
|
||||
*
|
||||
* @param model
|
||||
*/
|
||||
showUserPasswordForm: function (model) {
|
||||
if (Cache.User.isAdmin() || model.get('id') === Cache.User.get('id')) {
|
||||
require(['./main', './user/password'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* User Delete Confirm
|
||||
*
|
||||
* @param model
|
||||
*/
|
||||
showUserDeleteConfirm: function (model) {
|
||||
if (Cache.User.isAdmin() && model.get('id') !== Cache.User.get('id')) {
|
||||
require(['./main', './user/delete'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* User Delete Confirm
|
||||
*
|
||||
* @param model
|
||||
*/
|
||||
showUserDeleteConfirm: function (model) {
|
||||
if (Cache.User.isAdmin() && model.get('id') !== Cache.User.get('id')) {
|
||||
require(['./main', './user/delete'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Dashboard
|
||||
*/
|
||||
showDashboard: function () {
|
||||
let controller = this;
|
||||
/**
|
||||
* Dashboard
|
||||
*/
|
||||
showDashboard: function () {
|
||||
const controller = this;
|
||||
require(['./main', './dashboard/main'], (App, View) => {
|
||||
controller.navigate('/');
|
||||
App.UI.showAppContent(new View());
|
||||
});
|
||||
},
|
||||
|
||||
require(['./main', './dashboard/main'], (App, View) => {
|
||||
controller.navigate('/');
|
||||
App.UI.showAppContent(new View());
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Nginx Proxy Hosts
|
||||
*/
|
||||
showNginxProxy: function () {
|
||||
if (Cache.User.isAdmin() || Cache.User.canView('proxy_hosts')) {
|
||||
const controller = this;
|
||||
|
||||
/**
|
||||
* Nginx Proxy Hosts
|
||||
*/
|
||||
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');
|
||||
App.UI.showAppContent(new View());
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
require(['./main', './nginx/proxy/main'], (App, View) => {
|
||||
controller.navigate('/nginx/proxy');
|
||||
App.UI.showAppContent(new View());
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Nginx Proxy Host Form
|
||||
*
|
||||
* @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
|
||||
*
|
||||
* @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}));
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Proxy Host Delete Confirm
|
||||
*
|
||||
* @param model
|
||||
*/
|
||||
showNginxProxyDeleteConfirm: function (model) {
|
||||
if (Cache.User.isAdmin() || Cache.User.canManage('proxy_hosts')) {
|
||||
require(['./main', './nginx/proxy/delete'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Proxy Host Delete Confirm
|
||||
*
|
||||
* @param model
|
||||
*/
|
||||
showNginxProxyDeleteConfirm: function (model) {
|
||||
if (Cache.User.isAdmin() || Cache.User.canManage('proxy_hosts')) {
|
||||
require(['./main', './nginx/proxy/delete'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Nginx Redirection Hosts
|
||||
*/
|
||||
showNginxRedirection: function () {
|
||||
if (Cache.User.isAdmin() || Cache.User.canView('redirection_hosts')) {
|
||||
const controller = this;
|
||||
require(['./main', './nginx/redirection/main'], (App, View) => {
|
||||
controller.navigate('/nginx/redirection');
|
||||
App.UI.showAppContent(new View());
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Nginx Redirection Hosts
|
||||
*/
|
||||
showNginxRedirection: function () {
|
||||
if (Cache.User.isAdmin() || Cache.User.canView('redirection_hosts')) {
|
||||
let controller = this;
|
||||
/**
|
||||
* Nginx Redirection Host Form
|
||||
*
|
||||
* @param [model]
|
||||
*/
|
||||
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');
|
||||
App.UI.showAppContent(new View());
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Proxy Redirection Delete Confirm
|
||||
*
|
||||
* @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
|
||||
*
|
||||
* @param [model]
|
||||
*/
|
||||
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}));
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Nginx Stream Hosts
|
||||
*/
|
||||
showNginxStream: function () {
|
||||
if (Cache.User.isAdmin() || Cache.User.canView('streams')) {
|
||||
const controller = this;
|
||||
require(['./main', './nginx/stream/main'], (App, View) => {
|
||||
controller.navigate('/nginx/stream');
|
||||
App.UI.showAppContent(new View());
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Proxy Redirection Delete Confirm
|
||||
*
|
||||
* @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}));
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Stream Form
|
||||
*
|
||||
* @param [model]
|
||||
*/
|
||||
showNginxStreamForm: function (model) {
|
||||
if (Cache.User.isAdmin() || Cache.User.canManage('streams')) {
|
||||
require(['./main', './nginx/stream/form'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Nginx Stream Hosts
|
||||
*/
|
||||
showNginxStream: function () {
|
||||
if (Cache.User.isAdmin() || Cache.User.canView('streams')) {
|
||||
let controller = this;
|
||||
/**
|
||||
* Stream Delete Confirm
|
||||
*
|
||||
* @param model
|
||||
*/
|
||||
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');
|
||||
App.UI.showAppContent(new View());
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Nginx Dead Hosts
|
||||
*/
|
||||
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
|
||||
*
|
||||
* @param [model]
|
||||
*/
|
||||
showNginxStreamForm: function (model) {
|
||||
if (Cache.User.isAdmin() || Cache.User.canManage('streams')) {
|
||||
require(['./main', './nginx/stream/form'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Dead Host Form
|
||||
*
|
||||
* @param [model]
|
||||
*/
|
||||
showNginxDeadForm: function (model) {
|
||||
if (Cache.User.isAdmin() || Cache.User.canManage('dead_hosts')) {
|
||||
require(['./main', './nginx/dead/form'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Stream Delete Confirm
|
||||
*
|
||||
* @param model
|
||||
*/
|
||||
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}));
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Dead Host Delete Confirm
|
||||
*
|
||||
* @param model
|
||||
*/
|
||||
showNginxDeadDeleteConfirm: function (model) {
|
||||
if (Cache.User.isAdmin() || Cache.User.canManage('dead_hosts')) {
|
||||
require(['./main', './nginx/dead/delete'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Nginx Dead Hosts
|
||||
*/
|
||||
showNginxDead: function () {
|
||||
if (Cache.User.isAdmin() || Cache.User.canView('dead_hosts')) {
|
||||
let controller = this;
|
||||
/**
|
||||
* Help Dialog
|
||||
*
|
||||
* @param {String} title
|
||||
* @param {String} content
|
||||
*/
|
||||
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');
|
||||
App.UI.showAppContent(new View());
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Nginx Access
|
||||
*/
|
||||
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
|
||||
*
|
||||
* @param [model]
|
||||
*/
|
||||
showNginxDeadForm: function (model) {
|
||||
if (Cache.User.isAdmin() || Cache.User.canManage('dead_hosts')) {
|
||||
require(['./main', './nginx/dead/form'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Nginx Access List Form
|
||||
*
|
||||
* @param [model]
|
||||
*/
|
||||
showNginxAccessListForm: function (model) {
|
||||
if (Cache.User.isAdmin() || Cache.User.canManage('access_lists')) {
|
||||
require(['./main', './nginx/access/form'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Dead Host Delete Confirm
|
||||
*
|
||||
* @param model
|
||||
*/
|
||||
showNginxDeadDeleteConfirm: function (model) {
|
||||
if (Cache.User.isAdmin() || Cache.User.canManage('dead_hosts')) {
|
||||
require(['./main', './nginx/dead/delete'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Access List Delete Confirm
|
||||
*
|
||||
* @param model
|
||||
*/
|
||||
showNginxAccessListDeleteConfirm: function (model) {
|
||||
if (Cache.User.isAdmin() || Cache.User.canManage('access_lists')) {
|
||||
require(['./main', './nginx/access/delete'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Help Dialog
|
||||
*
|
||||
* @param {String} title
|
||||
* @param {String} content
|
||||
*/
|
||||
showHelp: function (title, content) {
|
||||
require(['./main', './help/main'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({title: title, content: content}));
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Nginx Certificates
|
||||
*/
|
||||
showNginxCertificates: function () {
|
||||
if (Cache.User.isAdmin() || Cache.User.canView('certificates')) {
|
||||
const controller = this;
|
||||
require(['./main', './nginx/certificates/main'], (App, View) => {
|
||||
controller.navigate('/nginx/certificates');
|
||||
App.UI.showAppContent(new View());
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Nginx Access
|
||||
*/
|
||||
showNginxAccess: function () {
|
||||
if (Cache.User.isAdmin() || Cache.User.canView('access_lists')) {
|
||||
let controller = this;
|
||||
/**
|
||||
* Nginx Certificate Form
|
||||
*
|
||||
* @param [model]
|
||||
*/
|
||||
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');
|
||||
App.UI.showAppContent(new View());
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Certificate Renew
|
||||
*
|
||||
* @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
|
||||
*
|
||||
* @param [model]
|
||||
*/
|
||||
showNginxAccessListForm: function (model) {
|
||||
if (Cache.User.isAdmin() || Cache.User.canManage('access_lists')) {
|
||||
require(['./main', './nginx/access/form'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Certificate Delete Confirm
|
||||
*
|
||||
* @param model
|
||||
*/
|
||||
showNginxCertificateDeleteConfirm: function (model) {
|
||||
if (Cache.User.isAdmin() || Cache.User.canManage('certificates')) {
|
||||
require(['./main', './nginx/certificates/delete'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Access List Delete Confirm
|
||||
*
|
||||
* @param model
|
||||
*/
|
||||
showNginxAccessListDeleteConfirm: function (model) {
|
||||
if (Cache.User.isAdmin() || Cache.User.canManage('access_lists')) {
|
||||
require(['./main', './nginx/access/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}));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Nginx Certificates
|
||||
*/
|
||||
showNginxCertificates: function () {
|
||||
if (Cache.User.isAdmin() || Cache.User.canView('certificates')) {
|
||||
let controller = this;
|
||||
/**
|
||||
* Audit Log
|
||||
*/
|
||||
showAuditLog: function () {
|
||||
const 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');
|
||||
App.UI.showAppContent(new View());
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 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}));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Nginx Certificate Form
|
||||
*
|
||||
* @param [model]
|
||||
*/
|
||||
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}));
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Settings
|
||||
*/
|
||||
showSettings: function () {
|
||||
const controller = this;
|
||||
if (Cache.User.isAdmin()) {
|
||||
require(['./main', './settings/main'], (App, View) => {
|
||||
controller.navigate('/settings');
|
||||
App.UI.showAppContent(new View());
|
||||
});
|
||||
} else {
|
||||
this.showDashboard();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Certificate Renew
|
||||
*
|
||||
* @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}));
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 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}));
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Certificate Delete Confirm
|
||||
*
|
||||
* @param model
|
||||
*/
|
||||
showNginxCertificateDeleteConfirm: function (model) {
|
||||
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();
|
||||
}
|
||||
/**
|
||||
* Logout
|
||||
*/
|
||||
logout: function () {
|
||||
Tokens.dropTopToken();
|
||||
this.showLogin();
|
||||
}
|
||||
};
|
||||
|
@ -6,87 +6,85 @@ const Helpers = require('../../lib/helpers');
|
||||
const template = require('./main.ejs');
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
template: template,
|
||||
id: 'dashboard',
|
||||
columns: 0,
|
||||
template: template,
|
||||
id: 'dashboard',
|
||||
columns: 0,
|
||||
|
||||
stats: {},
|
||||
stats: {},
|
||||
|
||||
ui: {
|
||||
links: 'a'
|
||||
},
|
||||
ui: {
|
||||
links: 'a'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click @ui.links': function (e) {
|
||||
e.preventDefault();
|
||||
Controller.navigate($(e.currentTarget).attr('href'), true);
|
||||
}
|
||||
},
|
||||
events: {
|
||||
'click @ui.links': function (e) {
|
||||
e.preventDefault();
|
||||
Controller.navigate($(e.currentTarget).attr('href'), true);
|
||||
}
|
||||
},
|
||||
|
||||
templateContext: function () {
|
||||
let view = this;
|
||||
templateContext: function () {
|
||||
const view = this;
|
||||
|
||||
return {
|
||||
getUserName: function () {
|
||||
return Cache.User.get('nickname') || Cache.User.get('name');
|
||||
},
|
||||
return {
|
||||
getUserName: function () {
|
||||
return Cache.User.get('nickname') || Cache.User.get('name');
|
||||
},
|
||||
|
||||
getHostStat: function (type) {
|
||||
if (view.stats && typeof view.stats.hosts !== 'undefined' && typeof view.stats.hosts[type] !== 'undefined') {
|
||||
return Helpers.niceNumber(view.stats.hosts[type]);
|
||||
}
|
||||
getHostStat: function (type) {
|
||||
if (view.stats && typeof view.stats.hosts !== 'undefined' && typeof view.stats.hosts[type] !== 'undefined') {
|
||||
return Helpers.niceNumber(view.stats.hosts[type]);
|
||||
}
|
||||
|
||||
return '-';
|
||||
},
|
||||
return '-';
|
||||
},
|
||||
|
||||
canShow: function (perm) {
|
||||
return Cache.User.isAdmin() || Cache.User.canView(perm);
|
||||
},
|
||||
canShow: function (perm) {
|
||||
return Cache.User.isAdmin() || Cache.User.canView(perm);
|
||||
},
|
||||
|
||||
columns: view.columns
|
||||
};
|
||||
},
|
||||
columns: view.columns
|
||||
};
|
||||
},
|
||||
|
||||
onRender: function () {
|
||||
let view = this;
|
||||
onRender: function () {
|
||||
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()
|
||||
.then(response => {
|
||||
if (!view.isDestroyed()) {
|
||||
view.stats.hosts = response;
|
||||
view.render();
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @param {Object} [model]
|
||||
*/
|
||||
preRender: function (model) {
|
||||
this.columns = 0;
|
||||
|
||||
/**
|
||||
* @param {Object} [model]
|
||||
*/
|
||||
preRender: function (model) {
|
||||
this.columns = 0;
|
||||
// calculate the available columns based on permissions for the objects
|
||||
// and store as a variable
|
||||
const perms = ['proxy_hosts', 'redirection_hosts', 'streams', 'dead_hosts'];
|
||||
|
||||
// calculate the available columns based on permissions for the objects
|
||||
// and store as a variable
|
||||
//let view = this;
|
||||
let perms = ['proxy_hosts', 'redirection_hosts', 'streams', 'dead_hosts'];
|
||||
perms.map(perm => {
|
||||
this.columns += Cache.User.isAdmin() || Cache.User.canView(perm) ? 1 : 0;
|
||||
});
|
||||
|
||||
perms.map(perm => {
|
||||
this.columns += Cache.User.isAdmin() || Cache.User.canView(perm) ? 1 : 0;
|
||||
});
|
||||
// Prevent double rendering on initial calls
|
||||
if (typeof model !== 'undefined') {
|
||||
this.render();
|
||||
}
|
||||
},
|
||||
|
||||
// Prevent double rendering on initial calls
|
||||
if (typeof model !== 'undefined') {
|
||||
this.render();
|
||||
}
|
||||
},
|
||||
|
||||
initialize: function () {
|
||||
this.preRender();
|
||||
this.listenTo(Cache.User, 'change', this.preRender);
|
||||
}
|
||||
initialize: function () {
|
||||
this.preRender();
|
||||
this.listenTo(Cache.User, 'change', this.preRender);
|
||||
}
|
||||
});
|
||||
|
@ -1,6 +1,6 @@
|
||||
<td class="text-center">
|
||||
<div class="avatar d-block" style="background-image: url(<%- owner.avatar || '/images/default-avatar.jpg' %>)" title="Owned by <%- owner.name %>">
|
||||
<span class="avatar-status <%- owner.is_disabled ? 'bg-red' : 'bg-green' %>"></span>
|
||||
<div class="avatar d-block" style="background-image: url(<%- (owner && owner.avatar) || '/images/default-avatar.jpg' %>)" title="Owned by <%- (owner && owner.name) || 'a deleted user' %>">
|
||||
<span class="avatar-status <%- owner && !owner.is_disabled ? 'bg-green' : 'bg-red' %>"></span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<td class="text-center">
|
||||
<div class="avatar d-block" style="background-image: url(<%- owner.avatar || '/images/default-avatar.jpg' %>)" title="Owned by <%- owner.name %>">
|
||||
<span class="avatar-status <%- owner.is_disabled ? 'bg-red' : 'bg-green' %>"></span>
|
||||
<div class="avatar d-block" style="background-image: url(<%- (owner && owner.avatar) || '/images/default-avatar.jpg' %>)" title="Owned by <%- (owner && owner.name) || 'a deleted user' %>">
|
||||
<span class="avatar-status <%- owner && !owner.is_disabled ? 'bg-green' : 'bg-red' %>"></span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
@ -33,6 +33,13 @@
|
||||
<td class="<%- isExpired() ? 'text-danger' : '' %>">
|
||||
<%- formatDbDate(expires_on, 'Do MMMM YYYY, h:mm a') %>
|
||||
</td>
|
||||
<td>
|
||||
<% if (active_domain_names().length > 0) { %>
|
||||
<span class="status-icon bg-success"></span> <%- i18n('certificates', 'in-use') %>
|
||||
<% } else { %>
|
||||
<span class="status-icon bg-danger"></span> <%- i18n('certificates', 'inactive') %>
|
||||
<% } %>
|
||||
</td>
|
||||
<% if (canManage) { %>
|
||||
<td class="text-right">
|
||||
<div class="item-action dropdown">
|
||||
@ -48,7 +55,14 @@
|
||||
<div class="dropdown-divider"></div>
|
||||
<% } %>
|
||||
<a href="#" class="delete dropdown-item"><i class="dropdown-icon fe fe-trash-2"></i> <%- i18n('str', 'delete') %></a>
|
||||
<% if (active_domain_names().length > 0) { %>
|
||||
<div class="dropdown-divider"></div>
|
||||
<span class="dropdown-header"><%- i18n('certificates', 'active-domain_names') %></span>
|
||||
<% active_domain_names().forEach(function(host) { %>
|
||||
<a href="https://<%- host %>" class="dropdown-item" target="_blank"><%- host %></a>
|
||||
<% }); %>
|
||||
<% } %>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<% } %>
|
||||
<% } %>
|
@ -44,14 +44,24 @@ module.exports = Mn.View.extend({
|
||||
},
|
||||
},
|
||||
|
||||
templateContext: {
|
||||
canManage: App.Cache.User.canManage('certificates'),
|
||||
isExpired: function () {
|
||||
return moment(this.expires_on).isBefore(moment());
|
||||
},
|
||||
dns_providers: dns_providers
|
||||
templateContext: function () {
|
||||
return {
|
||||
canManage: App.Cache.User.canManage('certificates'),
|
||||
isExpired: function () {
|
||||
return moment(this.expires_on).isBefore(moment());
|
||||
},
|
||||
dns_providers: dns_providers,
|
||||
active_domain_names: function () {
|
||||
const { proxy_hosts = [], redirect_hosts = [], dead_hosts = [] } = this;
|
||||
return [...proxy_hosts, ...redirect_hosts, ...dead_hosts].reduce((acc, host) => {
|
||||
acc.push(...(host.domain_names || []));
|
||||
return acc;
|
||||
}, []);
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
initialize: function () {
|
||||
this.listenTo(this.model, 'change', this.render);
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
<th><%- i18n('str', 'name') %></th>
|
||||
<th><%- i18n('all-hosts', 'cert-provider') %></th>
|
||||
<th><%- i18n('str', 'expires') %></th>
|
||||
<th><%- i18n('str', 'status') %></th>
|
||||
<% if (canManage) { %>
|
||||
<th> </th>
|
||||
<% } %>
|
||||
|
@ -74,7 +74,7 @@ module.exports = Mn.View.extend({
|
||||
e.preventDefault();
|
||||
let query = this.ui.query.val();
|
||||
|
||||
this.fetch(['owner'], query)
|
||||
this.fetch(['owner','proxy_hosts', 'dead_hosts', 'redirection_hosts'], query)
|
||||
.then(response => this.showData(response))
|
||||
.catch(err => {
|
||||
this.showError(err);
|
||||
@ -89,7 +89,7 @@ module.exports = Mn.View.extend({
|
||||
onRender: function () {
|
||||
let view = this;
|
||||
|
||||
view.fetch(['owner'])
|
||||
view.fetch(['owner','proxy_hosts', 'dead_hosts', 'redirection_hosts'])
|
||||
.then(response => {
|
||||
if (!view.isDestroyed()) {
|
||||
if (response && response.length) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
<td class="text-center">
|
||||
<div class="avatar d-block" style="background-image: url(<%- owner.avatar || '/images/default-avatar.jpg' %>)" title="Owned by <%- owner.name %>">
|
||||
<span class="avatar-status <%- owner.is_disabled ? 'bg-red' : 'bg-green' %>"></span>
|
||||
<div class="avatar d-block" style="background-image: url(<%- (owner && owner.avatar) || '/images/default-avatar.jpg' %>)" title="Owned by <%- (owner && owner.name) || 'a deleted user' %>">
|
||||
<span class="avatar-status <%- owner && !owner.is_disabled ? 'bg-green' : 'bg-red' %>"></span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<td class="text-center">
|
||||
<div class="avatar d-block" style="background-image: url(<%- owner.avatar || '/images/default-avatar.jpg' %>)" title="Owned by <%- owner.name %>">
|
||||
<span class="avatar-status <%- owner.is_disabled ? 'bg-red' : 'bg-green' %>"></span>
|
||||
<div class="avatar d-block" style="background-image: url(<%- (owner && owner.avatar) || '/images/default-avatar.jpg' %>)" title="Owned by <%- (owner && owner.name) || 'a deleted user' %>">
|
||||
<span class="avatar-status <%- owner && !owner.is_disabled ? 'bg-green' : 'bg-red' %>"></span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<td class="text-center">
|
||||
<div class="avatar d-block" style="background-image: url(<%- owner.avatar || '/images/default-avatar.jpg' %>)" title="Owned by <%- owner.name %>">
|
||||
<span class="avatar-status <%- owner.is_disabled ? 'bg-red' : 'bg-green' %>"></span>
|
||||
<div class="avatar d-block" style="background-image: url(<%- (owner && owner.avatar) || '/images/default-avatar.jpg' %>)" title="Owned by <%- (owner && owner.name) || 'a deleted user' %>">
|
||||
<span class="avatar-status <%- owner && !owner.is_disabled ? 'bg-green' : 'bg-red' %>"></span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
|
@ -3,48 +3,187 @@
|
||||
<h5 class="modal-title"><%- i18n('streams', 'form-title', {id: id}) %></h5>
|
||||
<button type="button" class="close cancel" aria-label="Close" data-dismiss="modal"> </button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="modal-body has-tabs">
|
||||
<div class="alert alert-danger mb-0 rounded-0" id="le-error-info" role="alert"></div>
|
||||
<form>
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('streams', 'incoming-port') %> <span class="form-required">*</span></label>
|
||||
<input name="incoming_port" type="number" class="form-control text-monospace" placeholder="eg: 8080" min="1" max="65535" value="<%- incoming_port %>" required>
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
<li role="presentation" class="nav-item"><a href="#details" aria-controls="tab1" role="tab" data-toggle="tab" class="nav-link active"><i class="fe fe-zap"></i> <%- i18n('all-hosts', 'details') %></a></li>
|
||||
<li role="presentation" class="nav-item"><a href="#ssl-options" aria-controls="tab2" role="tab" data-toggle="tab" class="nav-link"><i class="fe fe-shield"></i> <%- i18n('str', 'ssl') %></a></li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<!-- Details -->
|
||||
<div role="tabpanel" class="tab-pane active" id="details">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('streams', 'incoming-port') %> <span class="form-required">*</span></label>
|
||||
<input name="incoming_port" type="number" class="form-control text-monospace" placeholder="eg: 8080" min="1" max="65535" value="<%- incoming_port %>" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-8 col-md-8">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('streams', 'forwarding-host') %><span class="form-required">*</span></label>
|
||||
<input type="text" name="forwarding_host" class="form-control text-monospace" placeholder="example.com or 10.0.0.1 or 2001:db8:3333:4444:5555:6666:7777:8888" value="<%- forwarding_host %>" autocomplete="off" maxlength="255" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-4 col-md-4">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('streams', 'forwarding-port') %> <span class="form-required">*</span></label>
|
||||
<input name="forwarding_port" type="number" class="form-control text-monospace" placeholder="eg: 80" min="1" max="65535" value="<%- forwarding_port %>" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="custom-switch">
|
||||
<input type="checkbox" class="custom-switch-input" name="tcp_forwarding" value="1"<%- tcp_forwarding ? ' checked' : '' %>>
|
||||
<span class="custom-switch-indicator"></span>
|
||||
<span class="custom-switch-description"><%- i18n('streams', 'tcp-forwarding') %></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="custom-switch">
|
||||
<input type="checkbox" class="custom-switch-input" name="udp_forwarding" value="1"<%- udp_forwarding ? ' checked' : '' %>>
|
||||
<span class="custom-switch-indicator"></span>
|
||||
<span class="custom-switch-description"><%- i18n('streams', 'udp-forwarding') %></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="forward-type-error invalid-feedback"><%- i18n('streams', 'forward-type-error') %></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-8 col-md-8">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('streams', 'forwarding-host') %><span class="form-required">*</span></label>
|
||||
<input type="text" name="forwarding_host" class="form-control text-monospace" placeholder="example.com or 10.0.0.1 or 2001:db8:3333:4444:5555:6666:7777:8888" value="<%- forwarding_host %>" autocomplete="off" maxlength="255" required>
|
||||
|
||||
<!-- SSL -->
|
||||
<div role="tabpanel" class="tab-pane" id="ssl-options">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('streams', 'ssl-certificate') %></label>
|
||||
<select name="certificate_id" class="form-control custom-select" placeholder="<%- i18n('all-hosts', 'none') %>">
|
||||
<option selected value="0" data-data="{"id":0}" <%- certificate_id ? '' : 'selected' %>><%- i18n('all-hosts', 'none') %></option>
|
||||
<option selected value="new" data-data="{"id":"new"}"><%- i18n('all-hosts', 'new-cert') %></option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- DNS challenge -->
|
||||
<div class="col-sm-12 col-md-12 letsencrypt">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('all-hosts', 'domain-names') %> <span class="form-required">*</span></label>
|
||||
<input type="text" name="domain_names" class="form-control" id="input-domains" value="<%- domain_names.join(',') %>">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="custom-switch">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="custom-switch-input"
|
||||
name="meta[dns_challenge]"
|
||||
value="1"
|
||||
checked
|
||||
disabled
|
||||
>
|
||||
<span class="custom-switch-indicator"></span>
|
||||
<span class="custom-switch-description"><%= i18n('ssl', 'dns-challenge') %></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-12 letsencrypt">
|
||||
<fieldset class="form-fieldset dns-challenge">
|
||||
<div class="text-red mb-4"><i class="fe fe-alert-triangle"></i> <%= i18n('ssl', 'certbot-warning') %></div>
|
||||
|
||||
<!-- Certbot DNS plugin selection -->
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('ssl', 'dns-provider') %> <span class="form-required">*</span></label>
|
||||
<select
|
||||
name="meta[dns_provider]"
|
||||
id="dns_provider"
|
||||
class="form-control custom-select"
|
||||
>
|
||||
<option
|
||||
value=""
|
||||
disabled
|
||||
hidden
|
||||
<%- getDnsProvider() === null ? 'selected' : '' %>
|
||||
>Please Choose...</option>
|
||||
<% _.each(dns_plugins, function(plugin_info, plugin_name){ %>
|
||||
<option
|
||||
value="<%- plugin_name %>"
|
||||
<%- getDnsProvider() === plugin_name ? 'selected' : '' %>
|
||||
><%- plugin_info.name %></option>
|
||||
<% }); %>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Certbot credentials file content -->
|
||||
<div class="row credentials-file-content">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('ssl', 'credentials-file-content') %> <span class="form-required">*</span></label>
|
||||
<textarea
|
||||
name="meta[dns_provider_credentials]"
|
||||
class="form-control text-monospace"
|
||||
id="dns_provider_credentials"
|
||||
><%- getDnsProviderCredentials() %></textarea>
|
||||
<div class="text-secondary small">
|
||||
<i class="fe fe-info"></i>
|
||||
<%= i18n('ssl', 'credentials-file-content-info') %>
|
||||
</div>
|
||||
<div class="text-red small">
|
||||
<i class="fe fe-alert-triangle"></i>
|
||||
<%= i18n('ssl', 'stored-as-plaintext-info') %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- DNS propagation delay -->
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="form-group mb-0">
|
||||
<label class="form-label"><%- i18n('ssl', 'propagation-seconds') %></label>
|
||||
<input
|
||||
type="number"
|
||||
min="0"
|
||||
name="meta[propagation_seconds]"
|
||||
class="form-control"
|
||||
id="propagation_seconds"
|
||||
value="<%- getPropagationSeconds() %>"
|
||||
>
|
||||
<div class="text-secondary small">
|
||||
<i class="fe fe-info"></i>
|
||||
<%= i18n('ssl', 'propagation-seconds-info') %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<!-- Lets encrypt -->
|
||||
<div class="col-sm-12 col-md-12 letsencrypt">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('ssl', 'letsencrypt-email') %> <span class="form-required">*</span></label>
|
||||
<input name="meta[letsencrypt_email]" type="email" class="form-control" placeholder="" value="<%- getLetsencryptEmail() %>" required disabled>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-12 letsencrypt">
|
||||
<div class="form-group">
|
||||
<label class="custom-switch">
|
||||
<input type="checkbox" class="custom-switch-input" name="meta[letsencrypt_agree]" value="1" required disabled>
|
||||
<span class="custom-switch-indicator"></span>
|
||||
<span class="custom-switch-description"><%= i18n('ssl', 'letsencrypt-agree', {url: 'https://letsencrypt.org/repository/'}) %> <span class="form-required">*</span></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-4 col-md-4">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('streams', 'forwarding-port') %> <span class="form-required">*</span></label>
|
||||
<input name="forwarding_port" type="number" class="form-control text-monospace" placeholder="eg: 80" min="1" max="65535" value="<%- forwarding_port %>" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="custom-switch">
|
||||
<input type="checkbox" class="custom-switch-input" name="tcp_forwarding" value="1"<%- tcp_forwarding ? ' checked' : '' %>>
|
||||
<span class="custom-switch-indicator"></span>
|
||||
<span class="custom-switch-description"><%- i18n('streams', 'tcp-forwarding') %></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="custom-switch">
|
||||
<input type="checkbox" class="custom-switch-input" name="udp_forwarding" value="1"<%- udp_forwarding ? ' checked' : '' %>>
|
||||
<span class="custom-switch-indicator"></span>
|
||||
<span class="custom-switch-description"><%- i18n('streams', 'udp-forwarding') %></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="forward-type-error invalid-feedback"><%- i18n('streams', 'forward-type-error') %></div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
@ -1,24 +1,38 @@
|
||||
const Mn = require('backbone.marionette');
|
||||
const App = require('../../main');
|
||||
const StreamModel = require('../../../models/stream');
|
||||
const template = require('./form.ejs');
|
||||
const Mn = require('backbone.marionette');
|
||||
const App = require('../../main');
|
||||
const StreamModel = require('../../../models/stream');
|
||||
const template = require('./form.ejs');
|
||||
const dns_providers = require('../../../../../global/certbot-dns-plugins');
|
||||
|
||||
require('jquery-serializejson');
|
||||
require('jquery-mask-plugin');
|
||||
require('selectize');
|
||||
const Helpers = require("../../../lib/helpers");
|
||||
const certListItemTemplate = require("../certificates-list-item.ejs");
|
||||
const i18n = require("../../i18n");
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
template: template,
|
||||
className: 'modal-dialog',
|
||||
|
||||
ui: {
|
||||
form: 'form',
|
||||
forwarding_host: 'input[name="forwarding_host"]',
|
||||
type_error: '.forward-type-error',
|
||||
buttons: '.modal-footer button',
|
||||
switches: '.custom-switch-input',
|
||||
cancel: 'button.cancel',
|
||||
save: 'button.save'
|
||||
form: 'form',
|
||||
forwarding_host: 'input[name="forwarding_host"]',
|
||||
type_error: '.forward-type-error',
|
||||
buttons: '.modal-footer button',
|
||||
switches: '.custom-switch-input',
|
||||
cancel: 'button.cancel',
|
||||
save: 'button.save',
|
||||
le_error_info: '#le-error-info',
|
||||
certificate_select: 'select[name="certificate_id"]',
|
||||
domain_names: 'input[name="domain_names"]',
|
||||
dns_challenge_switch: 'input[name="meta[dns_challenge]"]',
|
||||
dns_challenge_content: '.dns-challenge',
|
||||
dns_provider: 'select[name="meta[dns_provider]"]',
|
||||
credentials_file_content: '.credentials-file-content',
|
||||
dns_provider_credentials: 'textarea[name="meta[dns_provider_credentials]"]',
|
||||
propagation_seconds: 'input[name="meta[propagation_seconds]"]',
|
||||
letsencrypt: '.letsencrypt'
|
||||
},
|
||||
|
||||
events: {
|
||||
@ -48,6 +62,35 @@ module.exports = Mn.View.extend({
|
||||
data.tcp_forwarding = !!data.tcp_forwarding;
|
||||
data.udp_forwarding = !!data.udp_forwarding;
|
||||
|
||||
if (typeof data.meta === 'undefined') data.meta = {};
|
||||
data.meta.letsencrypt_agree = data.meta.letsencrypt_agree == 1;
|
||||
data.meta.dns_challenge = true;
|
||||
|
||||
if (data.meta.propagation_seconds === '') data.meta.propagation_seconds = undefined;
|
||||
|
||||
if (typeof data.domain_names === 'string' && data.domain_names) {
|
||||
data.domain_names = data.domain_names.split(',');
|
||||
}
|
||||
|
||||
// Check for any domain names containing wildcards, which are not allowed with letsencrypt
|
||||
if (data.certificate_id === 'new') {
|
||||
let domain_err = false;
|
||||
if (!data.meta.dns_challenge) {
|
||||
data.domain_names.map(function (name) {
|
||||
if (name.match(/\*/im)) {
|
||||
domain_err = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (domain_err) {
|
||||
alert(i18n('ssl', 'no-wildcard-without-dns'));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
data.certificate_id = parseInt(data.certificate_id, 10);
|
||||
}
|
||||
|
||||
let method = App.Api.Nginx.Streams.create;
|
||||
let is_new = true;
|
||||
|
||||
@ -70,10 +113,108 @@ module.exports = Mn.View.extend({
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
alert(err.message);
|
||||
let more_info = '';
|
||||
if (err.code === 500 && err.debug) {
|
||||
try {
|
||||
more_info = JSON.parse(err.debug).debug.stack.join("\n");
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
this.ui.le_error_info[0].innerHTML = `${err.message}${more_info !== '' ? `<pre class="mt-3">${more_info}</pre>` : ''}`;
|
||||
this.ui.le_error_info.show();
|
||||
this.ui.le_error_info[0].scrollIntoView();
|
||||
this.ui.buttons.prop('disabled', false).removeClass('btn-disabled');
|
||||
this.ui.save.removeClass('btn-loading');
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
'change @ui.certificate_select': function () {
|
||||
let id = this.ui.certificate_select.val();
|
||||
if (id === 'new') {
|
||||
this.ui.letsencrypt.show().find('input').prop('disabled', false);
|
||||
this.ui.domain_names.prop('required', 'required');
|
||||
|
||||
this.ui.dns_challenge_switch
|
||||
.prop('disabled', true)
|
||||
.parents('.form-group')
|
||||
.css('opacity', 0.5);
|
||||
|
||||
this.ui.dns_provider.prop('required', 'required');
|
||||
const selected_provider = this.ui.dns_provider[0].options[this.ui.dns_provider[0].selectedIndex].value;
|
||||
if (selected_provider != '' && dns_providers[selected_provider].credentials !== false) {
|
||||
this.ui.dns_provider_credentials.prop('required', 'required');
|
||||
}
|
||||
this.ui.dns_challenge_content.show();
|
||||
} else {
|
||||
this.ui.letsencrypt.hide().find('input').prop('disabled', true);
|
||||
}
|
||||
},
|
||||
|
||||
'change @ui.dns_provider': function () {
|
||||
const selected_provider = this.ui.dns_provider[0].options[this.ui.dns_provider[0].selectedIndex].value;
|
||||
if (selected_provider != '' && dns_providers[selected_provider].credentials !== false) {
|
||||
this.ui.dns_provider_credentials.prop('required', 'required');
|
||||
this.ui.dns_provider_credentials[0].value = dns_providers[selected_provider].credentials;
|
||||
this.ui.credentials_file_content.show();
|
||||
} else {
|
||||
this.ui.dns_provider_credentials.prop('required', false);
|
||||
this.ui.credentials_file_content.hide();
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
templateContext: {
|
||||
getLetsencryptEmail: function () {
|
||||
return App.Cache.User.get('email');
|
||||
},
|
||||
getDnsProvider: function () {
|
||||
return typeof this.meta.dns_provider !== 'undefined' && this.meta.dns_provider != '' ? this.meta.dns_provider : null;
|
||||
},
|
||||
getDnsProviderCredentials: function () {
|
||||
return typeof this.meta.dns_provider_credentials !== 'undefined' ? this.meta.dns_provider_credentials : '';
|
||||
},
|
||||
getPropagationSeconds: function () {
|
||||
return typeof this.meta.propagation_seconds !== 'undefined' ? this.meta.propagation_seconds : '';
|
||||
},
|
||||
dns_plugins: dns_providers,
|
||||
},
|
||||
|
||||
onRender: function () {
|
||||
let view = this;
|
||||
|
||||
// Certificates
|
||||
this.ui.le_error_info.hide();
|
||||
this.ui.dns_challenge_content.hide();
|
||||
this.ui.credentials_file_content.hide();
|
||||
this.ui.letsencrypt.hide();
|
||||
this.ui.certificate_select.selectize({
|
||||
valueField: 'id',
|
||||
labelField: 'nice_name',
|
||||
searchField: ['nice_name', 'domain_names'],
|
||||
create: false,
|
||||
preload: true,
|
||||
allowEmptyOption: true,
|
||||
render: {
|
||||
option: function (item) {
|
||||
item.i18n = App.i18n;
|
||||
item.formatDbDate = Helpers.formatDbDate;
|
||||
return certListItemTemplate(item);
|
||||
}
|
||||
},
|
||||
load: function (query, callback) {
|
||||
App.Api.Nginx.Certificates.getAll()
|
||||
.then(rows => {
|
||||
callback(rows);
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
callback();
|
||||
});
|
||||
},
|
||||
onLoad: function () {
|
||||
view.ui.certificate_select[0].selectize.setValue(view.model.get('certificate_id'));
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
initialize: function (options) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
<td class="text-center">
|
||||
<div class="avatar d-block" style="background-image: url(<%- owner.avatar || '/images/default-avatar.jpg' %>)" title="Owned by <%- owner.name %>">
|
||||
<span class="avatar-status <%- owner.is_disabled ? 'bg-red' : 'bg-green' %>"></span>
|
||||
<div class="avatar d-block" style="background-image: url(<%- (owner && owner.avatar) || '/images/default-avatar.jpg' %>)" title="Owned by <%- (owner && owner.name) || 'a deleted user' %>">
|
||||
<span class="avatar-status <%- owner && !owner.is_disabled ? 'bg-green' : 'bg-red' %>"></span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
@ -16,7 +16,10 @@
|
||||
</td>
|
||||
<td>
|
||||
<div>
|
||||
<% if (tcp_forwarding) { %>
|
||||
<% if (certificate) { %>
|
||||
<span class="tag"><%- i18n('streams', 'tcp+ssl') %></span>
|
||||
<% }
|
||||
else if (tcp_forwarding) { %>
|
||||
<span class="tag"><%- i18n('streams', 'tcp') %></span>
|
||||
<% }
|
||||
if (udp_forwarding) { %>
|
||||
@ -24,6 +27,9 @@
|
||||
<% } %>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div><%- certificate && certificate_id ? i18n('ssl', certificate.provider) : i18n('all-hosts', 'none') %></div>
|
||||
</td>
|
||||
<td>
|
||||
<%
|
||||
var o = isOnline();
|
||||
|
@ -3,6 +3,7 @@
|
||||
<th><%- i18n('streams', 'incoming-port') %></th>
|
||||
<th><%- i18n('str', 'destination') %></th>
|
||||
<th><%- i18n('streams', 'protocol') %></th>
|
||||
<th><%- i18n('str', 'ssl') %></th>
|
||||
<th><%- i18n('str', 'status') %></th>
|
||||
<% if (canManage) { %>
|
||||
<th> </th>
|
||||
|
@ -88,7 +88,7 @@ module.exports = Mn.View.extend({
|
||||
onRender: function () {
|
||||
let view = this;
|
||||
|
||||
view.fetch(['owner'])
|
||||
view.fetch(['owner', 'certificate'])
|
||||
.then(response => {
|
||||
if (!view.isDestroyed()) {
|
||||
if (response && response.length) {
|
||||
|
@ -1,10 +1,10 @@
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><%- i18n('users', 'form-title', {id: id}) %></h5>
|
||||
<button type="button" class="close cancel" aria-label="Close" data-dismiss="modal"> </button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form>
|
||||
<form>
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><%- i18n('users', 'form-title', {id: id}) %></h5>
|
||||
<button type="button" class="close cancel" aria-label="Close" data-dismiss="modal"> </button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="row">
|
||||
<div class="col-sm-6 col-md-6">
|
||||
<div class="form-group">
|
||||
@ -49,10 +49,10 @@
|
||||
</div>
|
||||
<% } %>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary cancel" data-dismiss="modal"><%- i18n('str', 'cancel') %></button>
|
||||
<button type="button" class="btn btn-teal save"><%- i18n('str', 'save') %></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary cancel" data-dismiss="modal"><%- i18n('str', 'cancel') %></button>
|
||||
<button type="submit" class="btn btn-teal save"><%- i18n('str', 'save') %></button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
@ -19,7 +19,7 @@ module.exports = Mn.View.extend({
|
||||
|
||||
events: {
|
||||
|
||||
'click @ui.save': function (e) {
|
||||
'submit @ui.form': function (e) {
|
||||
e.preventDefault();
|
||||
this.ui.error.hide();
|
||||
let view = this;
|
||||
|
@ -179,7 +179,9 @@
|
||||
"delete-confirm": "Are you sure you want to delete this Stream?",
|
||||
"help-title": "What is a Stream?",
|
||||
"help-content": "A relatively new feature for Nginx, a Stream will serve to forward TCP/UDP traffic directly to another computer on the network.\nIf you're running game servers, FTP or SSH servers this can come in handy.",
|
||||
"search": "Search Incoming Port…"
|
||||
"search": "Search Incoming Port…",
|
||||
"ssl-certificate": "SSL Certificate for TCP Forwarding",
|
||||
"tcp+ssl": "TCP+SSL"
|
||||
},
|
||||
"certificates": {
|
||||
"title": "SSL Certificates",
|
||||
@ -206,7 +208,10 @@
|
||||
"reachability-other": "There is a server found at this domain but it returned an unexpected status code {code}. Is it the NPM server? Please make sure your domain points to the IP where your NPM instance is running.",
|
||||
"download": "Download",
|
||||
"renew-title": "Renew Let's Encrypt Certificate",
|
||||
"search": "Search Certificate…"
|
||||
"search": "Search Certificate…",
|
||||
"in-use" : "In use",
|
||||
"inactive": "Inactive",
|
||||
"active-domain_names": "Active domain names"
|
||||
},
|
||||
"access-lists": {
|
||||
"title": "Access Lists",
|
||||
|
@ -15,8 +15,11 @@ const model = Backbone.Model.extend({
|
||||
udp_forwarding: false,
|
||||
enabled: true,
|
||||
meta: {},
|
||||
certificate_id: 0,
|
||||
domain_names: [],
|
||||
// The following are expansions:
|
||||
owner: null
|
||||
owner: null,
|
||||
certificate: null
|
||||
};
|
||||
}
|
||||
});
|
||||
|
@ -2648,9 +2648,9 @@ electron-to-chromium@^1.3.47:
|
||||
integrity sha512-67V62Z4CFOiAtox+o+tosGfVk0QX4DJgH609tjT8QymbJZVAI/jWnAthnr8c5hnRNziIRwkc9EMQYejiVz3/9Q==
|
||||
|
||||
elliptic@^6.5.3, elliptic@^6.5.4:
|
||||
version "6.5.7"
|
||||
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.7.tgz#8ec4da2cb2939926a1b9a73619d768207e647c8b"
|
||||
integrity sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q==
|
||||
version "6.6.0"
|
||||
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.6.0.tgz#5919ec723286c1edf28685aa89261d4761afa210"
|
||||
integrity sha512-dpwoQcLc/2WLQvJvLRHKZ+f9FgOdjnq11rurqwekGQygGPsYSK29OMMD2WalatiqQ+XGFDglTNixpPfI+lpaAA==
|
||||
dependencies:
|
||||
bn.js "^4.11.9"
|
||||
brorand "^1.1.0"
|
||||
|
@ -7,18 +7,18 @@
|
||||
"credentials": "dns_acmedns_api_url = http://acmedns-server/\ndns_acmedns_registration_file = /data/acme-registration.json",
|
||||
"full_plugin_name": "dns-acmedns"
|
||||
},
|
||||
"active24":{
|
||||
"active24": {
|
||||
"name": "Active24",
|
||||
"package_name": "certbot-dns-active24",
|
||||
"version": "~=1.5.1",
|
||||
"version": "~=2.0.0",
|
||||
"dependencies": "",
|
||||
"credentials": "dns_active24_token=\"TOKEN\"",
|
||||
"credentials": "dns_active24_api_key = <identifier>\ndns_active24_secret = <secret>",
|
||||
"full_plugin_name": "dns-active24"
|
||||
},
|
||||
"aliyun": {
|
||||
"name": "Aliyun",
|
||||
"package_name": "certbot-dns-aliyun",
|
||||
"version": "~=0.38.1",
|
||||
"version": "~=2.0.0",
|
||||
"dependencies": "",
|
||||
"credentials": "dns_aliyun_access_key = 12345678\ndns_aliyun_access_key_secret = 1234567890abcdef1234567890abcdef",
|
||||
"full_plugin_name": "dns-aliyun"
|
||||
@ -31,6 +31,22 @@
|
||||
"credentials": "# This plugin supported API authentication using either Service Principals or utilizing a Managed Identity assigned to the virtual machine.\n# Regardless which authentication method used, the identity will need the “DNS Zone Contributor” role assigned to it.\n# As multiple Azure DNS Zones in multiple resource groups can exist, the config file needs a mapping of zone to resource group ID. Multiple zones -> ID mappings can be listed by using the key dns_azure_zoneX where X is a unique number. At least 1 zone mapping is required.\n\n# Using a service principal (option 1)\ndns_azure_sp_client_id = 912ce44a-0156-4669-ae22-c16a17d34ca5\ndns_azure_sp_client_secret = E-xqXU83Y-jzTI6xe9fs2YC~mck3ZzUih9\ndns_azure_tenant_id = ed1090f3-ab18-4b12-816c-599af8a88cf7\n\n# Using used assigned MSI (option 2)\n# dns_azure_msi_client_id = 912ce44a-0156-4669-ae22-c16a17d34ca5\n\n# Using system assigned MSI (option 3)\n# dns_azure_msi_system_assigned = true\n\n# Zones (at least one always required)\ndns_azure_zone1 = example.com:/subscriptions/c135abce-d87d-48df-936c-15596c6968a5/resourceGroups/dns1\ndns_azure_zone2 = example.org:/subscriptions/99800903-fb14-4992-9aff-12eaf2744622/resourceGroups/dns2",
|
||||
"full_plugin_name": "dns-azure"
|
||||
},
|
||||
"baidu": {
|
||||
"name": "baidu",
|
||||
"package_name": "certbot-dns-baidu",
|
||||
"version": "~=0.1.1",
|
||||
"dependencies": "",
|
||||
"credentials": "dns_baidu_access_key = 12345678\ndns_baidu_secret_key = 1234567890abcdef1234567890abcdef",
|
||||
"full_plugin_name": "dns-baidu"
|
||||
},
|
||||
"beget": {
|
||||
"name":"Beget",
|
||||
"package_name": "certbot-beget-plugin",
|
||||
"version": "~=1.0.0.dev9",
|
||||
"dependencies": "",
|
||||
"credentials": "# Beget API credentials used by Certbot\nbeget_plugin_username = username\nbeget_plugin_password = password",
|
||||
"full_plugin_name": "beget-plugin"
|
||||
},
|
||||
"bunny": {
|
||||
"name": "bunny.net",
|
||||
"package_name": "certbot-dns-bunny",
|
||||
@ -43,7 +59,7 @@
|
||||
"name": "Cloudflare",
|
||||
"package_name": "certbot-dns-cloudflare",
|
||||
"version": "=={{certbot-version}}",
|
||||
"dependencies": "cloudflare==2.19.* acme=={{certbot-version}}",
|
||||
"dependencies": "cloudflare==4.0.* acme=={{certbot-version}}",
|
||||
"credentials": "# Cloudflare API token\ndns_cloudflare_api_token=0123456789abcdef0123456789abcdef01234567",
|
||||
"full_plugin_name": "dns-cloudflare"
|
||||
},
|
||||
@ -84,9 +100,17 @@
|
||||
"package_name": "certbot-dns-cpanel",
|
||||
"version": "~=0.2.2",
|
||||
"dependencies": "",
|
||||
"credentials": "cpanel_url = https://cpanel.example.com:2083\ncpanel_username = user\ncpanel_password = hunter2",
|
||||
"credentials": "cpanel_url = https://cpanel.example.com:2083\ncpanel_username = user\ncpanel_password = hunter2\ncpanel_api_token = token",
|
||||
"full_plugin_name": "cpanel"
|
||||
},
|
||||
"ddnss": {
|
||||
"name": "DDNSS",
|
||||
"package_name": "certbot-dns-ddnss",
|
||||
"version": "~=1.1.0",
|
||||
"dependencies": "",
|
||||
"credentials": "dns_ddnss_token = YOUR_DDNSS_API_TOKEN",
|
||||
"full_plugin_name": "dns-ddnss"
|
||||
},
|
||||
"desec": {
|
||||
"name": "deSEC",
|
||||
"package_name": "certbot-dns-desec",
|
||||
@ -153,11 +177,11 @@
|
||||
},
|
||||
"domainoffensive": {
|
||||
"name": "DomainOffensive (do.de)",
|
||||
"package_name": "certbot-dns-do",
|
||||
"version": "~=0.31.0",
|
||||
"package_name": "certbot-dns-domainoffensive",
|
||||
"version": "~=2.0.0",
|
||||
"dependencies": "",
|
||||
"credentials": "dns_do_api_token = YOUR_DO_DE_AUTH_TOKEN",
|
||||
"full_plugin_name": "dns-do"
|
||||
"credentials": "dns_domainoffensive_api_token = YOUR_DO_DE_AUTH_TOKEN",
|
||||
"full_plugin_name": "dns-domainoffensive"
|
||||
},
|
||||
"domeneshop": {
|
||||
"name": "Domeneshop",
|
||||
@ -191,6 +215,14 @@
|
||||
"credentials": "dns_eurodns_applicationId = myuser\ndns_eurodns_apiKey = mysecretpassword\ndns_eurodns_endpoint = https://rest-api.eurodns.com/user-api-gateway/proxy",
|
||||
"full_plugin_name": "dns-eurodns"
|
||||
},
|
||||
"firstdomains": {
|
||||
"name": "First Domains",
|
||||
"package_name": "certbot-dns-firstdomains",
|
||||
"version": ">=1.0",
|
||||
"dependencies": "",
|
||||
"credentials": "dns_firstdomains_username = myremoteuser\ndns_firstdomains_password = verysecureremoteuserpassword",
|
||||
"full_plugin_name": "dns-firstdomains"
|
||||
},
|
||||
"freedns": {
|
||||
"name": "FreeDNS",
|
||||
"package_name": "certbot-dns-freedns",
|
||||
@ -201,12 +233,20 @@
|
||||
},
|
||||
"gandi": {
|
||||
"name": "Gandi Live DNS",
|
||||
"package_name": "certbot_plugin_gandi",
|
||||
"version": "~=1.5.0",
|
||||
"package_name": "certbot-dns-gandi",
|
||||
"version": "~=1.6.1",
|
||||
"dependencies": "",
|
||||
"credentials": "# Gandi personal access token\ndns_gandi_token=PERSONAL_ACCESS_TOKEN",
|
||||
"full_plugin_name": "dns-gandi"
|
||||
},
|
||||
"gcore": {
|
||||
"name": "Gcore DNS",
|
||||
"package_name": "certbot-dns-gcore",
|
||||
"version": "~=0.1.8",
|
||||
"dependencies": "",
|
||||
"credentials": "dns_gcore_apitoken = 0123456789abcdef0123456789abcdef01234567",
|
||||
"full_plugin_name": "dns-gcore"
|
||||
},
|
||||
"godaddy": {
|
||||
"name": "GoDaddy",
|
||||
"package_name": "certbot-dns-godaddy",
|
||||
@ -247,6 +287,14 @@
|
||||
"credentials": "dns_hetzner_api_token = 0123456789abcdef0123456789abcdef",
|
||||
"full_plugin_name": "dns-hetzner"
|
||||
},
|
||||
"hostingnl": {
|
||||
"name": "Hosting.nl",
|
||||
"package_name": "certbot-dns-hostingnl",
|
||||
"version": "~=0.1.5",
|
||||
"dependencies": "",
|
||||
"credentials": "dns_hostingnl_api_key = 0123456789abcdef0123456789abcdef",
|
||||
"full_plugin_name": "dns-hostingnl"
|
||||
},
|
||||
"hover": {
|
||||
"name": "Hover",
|
||||
"package_name": "certbot-dns-hover",
|
||||
@ -303,6 +351,14 @@
|
||||
"credentials": "dns_joker_username = <Dynamic DNS Authentication Username>\ndns_joker_password = <Dynamic DNS Authentication Password>\ndns_joker_domain = <Dynamic DNS Domain>",
|
||||
"full_plugin_name": "dns-joker"
|
||||
},
|
||||
"leaseweb": {
|
||||
"name": "LeaseWeb",
|
||||
"package_name": "certbot-dns-leaseweb",
|
||||
"version": "~=1.0.1",
|
||||
"dependencies": "",
|
||||
"credentials": "dns_leaseweb_api_token = 01234556789",
|
||||
"full_plugin_name": "dns-leaseweb"
|
||||
},
|
||||
"linode": {
|
||||
"name": "Linode",
|
||||
"package_name": "certbot-dns-linode",
|
||||
@ -332,7 +388,7 @@
|
||||
"package_name": "certbot-dns-mijn-host",
|
||||
"version": "~=0.0.4",
|
||||
"dependencies": "",
|
||||
"credentials": "dns-mijn-host-credentials = /etc/letsencrypt/mijnhost-credentials.ini",
|
||||
"credentials": "dns_mijn_host_api_key=0123456789abcdef0123456789abcdef",
|
||||
"full_plugin_name": "dns-mijn-host"
|
||||
},
|
||||
"namecheap": {
|
||||
@ -351,6 +407,14 @@
|
||||
"credentials": "dns_netcup_customer_id = 123456\ndns_netcup_api_key = 0123456789abcdef0123456789abcdef01234567\ndns_netcup_api_password = abcdef0123456789abcdef01234567abcdef0123",
|
||||
"full_plugin_name": "dns-netcup"
|
||||
},
|
||||
"nicru": {
|
||||
"name": "nic.ru",
|
||||
"package_name": "certbot-dns-nicru",
|
||||
"version": "~=1.0.3",
|
||||
"dependencies": "",
|
||||
"credentials": "dns_nicru_client_id = application-id\ndns_nicru_client_secret = application-token\ndns_nicru_username = 0001110/NIC-D\ndns_nicru_password = password\ndns_nicru_scope = .+:.+/zones/example.com(/.+)?\ndns_nicru_service = DNS_SERVICE_NAME\ndns_nicru_zone = example.com",
|
||||
"full_plugin_name": "dns-nicru"
|
||||
},
|
||||
"njalla": {
|
||||
"name": "Njalla",
|
||||
"package_name": "certbot-dns-njalla",
|
||||
@ -394,7 +458,7 @@
|
||||
"porkbun": {
|
||||
"name": "Porkbun",
|
||||
"package_name": "certbot-dns-porkbun",
|
||||
"version": "~=0.2",
|
||||
"version": "~=0.9",
|
||||
"dependencies": "",
|
||||
"credentials": "dns_porkbun_key=your-porkbun-api-key\ndns_porkbun_secret=your-porkbun-api-secret",
|
||||
"full_plugin_name": "dns-porkbun"
|
||||
@ -424,13 +488,13 @@
|
||||
"full_plugin_name": "dns-rfc2136"
|
||||
},
|
||||
"rockenstein": {
|
||||
"name": "rockenstein AG",
|
||||
"package_name": "certbot-dns-rockenstein",
|
||||
"version": "~=1.0.0",
|
||||
"dependencies": "",
|
||||
"credentials": "dns_rockenstein_token=<token>",
|
||||
"full_plugin_name": "dns-rockenstein"
|
||||
},
|
||||
"name": "rockenstein AG",
|
||||
"package_name": "certbot-dns-rockenstein",
|
||||
"version": "~=1.0.0",
|
||||
"dependencies": "",
|
||||
"credentials": "dns_rockenstein_token=<token>",
|
||||
"full_plugin_name": "dns-rockenstein"
|
||||
},
|
||||
"route53": {
|
||||
"name": "Route 53 (Amazon)",
|
||||
"package_name": "certbot-dns-route53",
|
||||
@ -487,12 +551,12 @@
|
||||
"credentials": "dns_websupport_identifier = <api_key>\ndns_websupport_secret_key = <secret>",
|
||||
"full_plugin_name": "dns-websupport"
|
||||
},
|
||||
"wedos":{
|
||||
"wedos": {
|
||||
"name": "Wedos",
|
||||
"package_name": "certbot-dns-wedos",
|
||||
"version": "~=2.2",
|
||||
"dependencies": "",
|
||||
"credentials": "dns_wedos_user = <wedos_registration>\ndns_wedos_auth = <wapi_sha256_password>",
|
||||
"credentials": "dns_wedos_user = <wedos_registration>\ndns_wedos_auth = <wapi_password>",
|
||||
"full_plugin_name": "dns-wedos"
|
||||
},
|
||||
"edgedns": {
|
||||
@ -502,5 +566,13 @@
|
||||
"dependencies": "",
|
||||
"credentials": "edgedns_client_secret = as3d1asd5d1a32sdfsdfs2d1asd5=\nedgedns_host = sdflskjdf-dfsdfsdf-sdfsdfsdf.luna.akamaiapis.net\nedgedns_access_token = kjdsi3-34rfsdfsdf-234234fsdfsdf\nedgedns_client_token = dkfjdf-342fsdfsd-23fsdfsdfsdf",
|
||||
"full_plugin_name": "edgedns"
|
||||
}
|
||||
},
|
||||
"zoneedit": {
|
||||
"name": "ZoneEdit",
|
||||
"package_name": "certbot-dns-zoneedit",
|
||||
"version": "~=0.3.2",
|
||||
"dependencies": "--no-deps dnspython",
|
||||
"credentials": "dns_zoneedit_user = <login-user-id>\ndns_zoneedit_token = <dyn-authentication-token>",
|
||||
"full_plugin_name": "dns-zoneedit"
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,17 @@ YELLOW='\E[1;33m'
|
||||
export BLUE CYAN GREEN RED RESET YELLOW
|
||||
|
||||
# Docker Compose
|
||||
COMPOSE_PROJECT_NAME="npmdev"
|
||||
COMPOSE_PROJECT_NAME="npm2dev"
|
||||
COMPOSE_FILE="docker/docker-compose.dev.yml"
|
||||
|
||||
export COMPOSE_FILE COMPOSE_PROJECT_NAME
|
||||
|
||||
# $1: container_name
|
||||
get_container_ip () {
|
||||
local container_name=$1
|
||||
local container
|
||||
local ip
|
||||
container=$(docker-compose ps --all -q "${container_name}" | tail -n1)
|
||||
ip=$(docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$container")
|
||||
echo "$ip"
|
||||
}
|
||||
|
@ -65,8 +65,10 @@ rm -rf "${LOCAL_RESOLVE}"
|
||||
printf "nameserver %s\noptions ndots:0" "${DNSROUTER_IP}" > "${LOCAL_RESOLVE}"
|
||||
|
||||
# bring up all remaining containers, except cypress!
|
||||
docker-compose up -d --remove-orphans stepca
|
||||
docker-compose up -d --remove-orphans stepca squid
|
||||
docker-compose pull db-mysql || true # ok to fail
|
||||
docker-compose pull db-postgres || true # ok to fail
|
||||
docker-compose pull authentik authentik-redis authentik-ldap || true # ok to fail
|
||||
docker-compose up -d --remove-orphans --pull=never fullstack
|
||||
|
||||
# wait for main container to be healthy
|
||||
|
13
scripts/cypress-dev
Executable file
13
scripts/cypress-dev
Executable file
@ -0,0 +1,13 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
. "$DIR/.common.sh"
|
||||
|
||||
# Ensure docker-compose exists
|
||||
if hash docker-compose 2>/dev/null; then
|
||||
cd "${DIR}/.."
|
||||
rm -rf "$DIR/../test/results"
|
||||
docker-compose up --build cypress
|
||||
else
|
||||
echo -e "${RED}❯ docker-compose command is not available${RESET}"
|
||||
fi
|
@ -7,8 +7,42 @@ DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
if hash docker-compose 2>/dev/null; then
|
||||
cd "${DIR}/.."
|
||||
echo -e "${BLUE}❯ ${CYAN}Starting Dev Stack ...${RESET}"
|
||||
echo -e "${BLUE}❯ $(docker-compose config)${RESET}"
|
||||
|
||||
docker-compose up -d --remove-orphans --force-recreate --build
|
||||
# Bring up a stack, in steps so we can inject IPs everywhere
|
||||
docker-compose up -d pdns pdns-db
|
||||
PDNS_IP=$(get_container_ip "pdns")
|
||||
echo -e "${BLUE}❯ ${YELLOW}PDNS IP is ${PDNS_IP}${RESET}"
|
||||
|
||||
# adjust the dnsrouter config
|
||||
LOCAL_DNSROUTER_CONFIG="$DIR/../docker/dev/dnsrouter-config.json"
|
||||
rm -rf "$LOCAL_DNSROUTER_CONFIG.tmp"
|
||||
# IMPORTANT: changes to dnsrouter-config.json will affect this line:
|
||||
jq --arg a "$PDNS_IP" '.servers[0].upstreams[1].upstream = $a' "$LOCAL_DNSROUTER_CONFIG" > "$LOCAL_DNSROUTER_CONFIG.tmp"
|
||||
|
||||
docker-compose up -d dnsrouter
|
||||
DNSROUTER_IP=$(get_container_ip "dnsrouter")
|
||||
echo -e "${BLUE}❯ ${YELLOW}DNS Router IP is ${DNSROUTER_IP}"
|
||||
|
||||
if [ "${DNSROUTER_IP:-}" = "" ]; then
|
||||
echo -e "${RED}❯ ERROR: DNS Router IP is not set${RESET}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# mount the resolver
|
||||
LOCAL_RESOLVE="$DIR/../docker/dev/resolv.conf"
|
||||
rm -rf "${LOCAL_RESOLVE}"
|
||||
printf "nameserver %s\noptions ndots:0" "${DNSROUTER_IP}" > "${LOCAL_RESOLVE}"
|
||||
|
||||
# bring up all remaining containers, except cypress!
|
||||
docker-compose up -d --remove-orphans stepca squid
|
||||
docker-compose pull db db-postgres authentik-redis authentik authentik-worker authentik-ldap
|
||||
docker-compose build --pull --parallel fullstack
|
||||
docker-compose up -d --remove-orphans fullstack
|
||||
docker-compose up -d --remove-orphans swagger
|
||||
|
||||
# wait for main container to be healthy
|
||||
bash "$DIR/wait-healthy" "$(docker-compose ps --all -q fullstack)" 120
|
||||
|
||||
echo ""
|
||||
echo -e "${CYAN}Admin UI: http://127.0.0.1:3081${RESET}"
|
||||
@ -18,10 +52,10 @@ if hash docker-compose 2>/dev/null; then
|
||||
|
||||
if [ "$1" == "-f" ]; then
|
||||
echo -e "${BLUE}❯ ${YELLOW}Following Backend Container:${RESET}"
|
||||
docker logs -f npm_core
|
||||
docker logs -f npm2dev.core
|
||||
else
|
||||
echo -e "${YELLOW}Hint:${RESET} You can follow the output of some of the containers with:"
|
||||
echo " docker logs -f npm_core"
|
||||
echo " docker logs -f npm2dev.core"
|
||||
fi
|
||||
else
|
||||
echo -e "${RED}❯ docker-compose command is not available${RESET}"
|
||||
|
@ -1,11 +1,22 @@
|
||||
FROM cypress/included:13.9.0
|
||||
|
||||
COPY --chown=1000 ./test /test
|
||||
FROM cypress/included:14.0.1
|
||||
|
||||
# Disable Cypress CLI colors
|
||||
ENV FORCE_COLOR=0
|
||||
ENV NO_COLOR=1
|
||||
|
||||
# testssl.sh and mkcert
|
||||
RUN wget "https://github.com/testssl/testssl.sh/archive/refs/tags/v3.2rc4.tar.gz" -O /tmp/testssl.tgz -q \
|
||||
&& tar -xzf /tmp/testssl.tgz -C /tmp \
|
||||
&& mv /tmp/testssl.sh-3.2rc4 /testssl \
|
||||
&& rm /tmp/testssl.tgz \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y bsdmainutils curl dnsutils \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& wget "https://github.com/FiloSottile/mkcert/releases/download/v1.4.4/mkcert-v1.4.4-linux-amd64" -O /bin/mkcert \
|
||||
&& chmod +x /bin/mkcert
|
||||
|
||||
COPY --chown=1000 ./test /test
|
||||
WORKDIR /test
|
||||
RUN yarn install && yarn cache clean
|
||||
ENTRYPOINT []
|
||||
|
@ -15,8 +15,8 @@ module.exports = defineConfig({
|
||||
return require("../plugins/index.js")(on, config);
|
||||
},
|
||||
env: {
|
||||
swaggerBase: '{{baseUrl}}/api/schema',
|
||||
swaggerBase: '{{baseUrl}}/api/schema?ts=' + Date.now(),
|
||||
},
|
||||
baseUrl: 'http://localhost:1234',
|
||||
baseUrl: 'http://fullstack:81',
|
||||
}
|
||||
});
|
||||
|
@ -1,22 +0,0 @@
|
||||
const { defineConfig } = require('cypress');
|
||||
|
||||
module.exports = defineConfig({
|
||||
requestTimeout: 30000,
|
||||
defaultCommandTimeout: 20000,
|
||||
reporter: 'cypress-multi-reporters',
|
||||
reporterOptions: {
|
||||
configFile: 'multi-reporter.json'
|
||||
},
|
||||
video: false,
|
||||
videosFolder: 'results/videos',
|
||||
screenshotsFolder: 'results/screenshots',
|
||||
e2e: {
|
||||
setupNodeEvents(on, config) {
|
||||
return require("../plugins/index.js")(on, config);
|
||||
},
|
||||
env: {
|
||||
swaggerBase: '{{baseUrl}}/api/schema',
|
||||
},
|
||||
baseUrl: 'http://localhost:1234',
|
||||
}
|
||||
});
|
@ -1,7 +1,8 @@
|
||||
/// <reference types="Cypress" />
|
||||
/// <reference types="cypress" />
|
||||
|
||||
describe('Certificates endpoints', () => {
|
||||
let token;
|
||||
let certID;
|
||||
|
||||
before(() => {
|
||||
cy.getToken().then((tok) => {
|
||||
@ -24,6 +25,54 @@ describe('Certificates endpoints', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('Custom certificate lifecycle', function() {
|
||||
// Create custom cert
|
||||
cy.task('backendApiPost', {
|
||||
token: token,
|
||||
path: '/api/nginx/certificates',
|
||||
data: {
|
||||
provider: "other",
|
||||
nice_name: "Test Certificate",
|
||||
},
|
||||
}).then((data) => {
|
||||
cy.validateSwaggerSchema('post', 201, '/nginx/certificates', data);
|
||||
expect(data).to.have.property('id');
|
||||
certID = data.id;
|
||||
|
||||
// Upload files
|
||||
cy.task('backendApiPostFiles', {
|
||||
token: token,
|
||||
path: `/api/nginx/certificates/${certID}/upload`,
|
||||
files: {
|
||||
certificate: 'test.example.com.pem',
|
||||
certificate_key: 'test.example.com-key.pem',
|
||||
},
|
||||
}).then((data) => {
|
||||
cy.validateSwaggerSchema('post', 200, '/nginx/certificates/{certID}/upload', data);
|
||||
expect(data).to.have.property('certificate');
|
||||
expect(data).to.have.property('certificate_key');
|
||||
|
||||
// Get all certs
|
||||
cy.task('backendApiGet', {
|
||||
token: token,
|
||||
path: '/api/nginx/certificates?expand=owner'
|
||||
}).then((data) => {
|
||||
cy.validateSwaggerSchema('get', 200, '/nginx/certificates', data);
|
||||
expect(data.length).to.be.greaterThan(0);
|
||||
|
||||
// Delete cert
|
||||
cy.task('backendApiDelete', {
|
||||
token: token,
|
||||
path: `/api/nginx/certificates/${certID}`
|
||||
}).then((data) => {
|
||||
cy.validateSwaggerSchema('delete', 200, '/nginx/certificates/{certID}', data);
|
||||
expect(data).to.be.equal(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Request Certificate - CVE-2024-46256/CVE-2024-46257', function() {
|
||||
cy.task('backendApiPost', {
|
||||
token: token,
|
||||
|
25
test/cypress/e2e/api/Dashboard.cy.js
Normal file
25
test/cypress/e2e/api/Dashboard.cy.js
Normal file
@ -0,0 +1,25 @@
|
||||
/// <reference types="cypress" />
|
||||
|
||||
describe('Dashboard endpoints', () => {
|
||||
let token;
|
||||
|
||||
before(() => {
|
||||
cy.getToken().then((tok) => {
|
||||
token = tok;
|
||||
});
|
||||
});
|
||||
|
||||
it('Should be able to get host counts', function() {
|
||||
cy.task('backendApiGet', {
|
||||
token: token,
|
||||
path: '/api/reports/hosts'
|
||||
}).then((data) => {
|
||||
cy.validateSwaggerSchema('get', 200, '/reports/hosts', data);
|
||||
expect(data).to.have.property('dead');
|
||||
expect(data).to.have.property('proxy');
|
||||
expect(data).to.have.property('redirection');
|
||||
expect(data).to.have.property('stream');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user