mirror of
https://github.com/NginxProxyManager/nginx-proxy-manager.git
synced 2025-10-06 21:00:10 +00:00
Compare commits
7 Commits
dependabot
...
7912119d14
Author | SHA1 | Date | |
---|---|---|---|
|
7912119d14 | ||
|
671817d640 | ||
|
c39e580e81 | ||
|
da29cd2e0e | ||
|
72abe50799 | ||
|
a92624d086 | ||
|
c6465a5090 |
57
Jenkinsfile
vendored
57
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/nginxproxymanager/${IMAGE}-dev:${BRANCH_LOWER}"
|
||||
buildxPushTags = "-t docker.io/jc21/${IMAGE}:github-${BRANCH_LOWER}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -128,7 +128,7 @@ pipeline {
|
||||
sh 'docker-compose down --remove-orphans --volumes -t 30 || true'
|
||||
}
|
||||
unstable {
|
||||
dir(path: 'test/results') {
|
||||
dir(path: 'testing/results') {
|
||||
archiveArtifacts(allowEmptyArchive: true, artifacts: '**/*', excludes: '**/*.xml')
|
||||
}
|
||||
}
|
||||
@@ -161,45 +161,7 @@ pipeline {
|
||||
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') {
|
||||
dir(path: 'testing/results') {
|
||||
archiveArtifacts(allowEmptyArchive: true, artifacts: '**/*', excludes: '**/*.xml')
|
||||
}
|
||||
}
|
||||
@@ -241,18 +203,7 @@ pipeline {
|
||||
}
|
||||
steps {
|
||||
script {
|
||||
npmGithubPrComment("""Docker Image for build ${BUILD_NUMBER} is available on [DockerHub](https://cloud.docker.com/repository/docker/nginxproxymanager/${IMAGE}-dev):
|
||||
```
|
||||
nginxproxymanager/${IMAGE}-dev:${BRANCH_LOWER}
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> Ensure you backup your NPM instance before testing this image! Especially if there are database changes.
|
||||
> This is a different docker image namespace than the official image.
|
||||
|
||||
> [!WARNING]
|
||||
> Changes and additions to DNS Providers require verification by at least 2 members of the community!
|
||||
""", true)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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.6-green.svg?style=for-the-badge">
|
||||
<img src="https://img.shields.io/badge/version-2.12.1-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>
|
||||
@@ -101,6 +101,11 @@ Immediately after logging in with this default user you will be asked to modify
|
||||
|
||||
All are welcome to create pull requests for this project, against the `develop` branch. Official releases are created from the `master` branch.
|
||||
|
||||
Run the following commands to test locally:
|
||||
|
||||
- `cd backend && npm i && node_modules/eslint/bin/eslint.js .`
|
||||
- `cd test && npm i && npm run cypress`
|
||||
|
||||
CI is used in this project. All PR's must pass before being considered. After passing,
|
||||
docker builds for PR's are available on dockerhub for manual verifications.
|
||||
|
||||
|
@@ -3,8 +3,6 @@
|
||||
const schema = require('./schema');
|
||||
const logger = require('./logger').global;
|
||||
|
||||
const IP_RANGES_FETCH_ENABLED = process.env.IP_RANGES_FETCH_ENABLED !== 'false';
|
||||
|
||||
async function appStart () {
|
||||
const migrate = require('./migrate');
|
||||
const setup = require('./setup');
|
||||
@@ -15,16 +13,7 @@ async function appStart () {
|
||||
return migrate.latest()
|
||||
.then(setup)
|
||||
.then(schema.getCompiledSchema)
|
||||
.then(() => {
|
||||
if (IP_RANGES_FETCH_ENABLED) {
|
||||
logger.info('IP Ranges fetch is enabled');
|
||||
return internalIpRanges.fetch().catch((err) => {
|
||||
logger.error('IP Ranges fetch failed, continuing anyway:', err.message);
|
||||
});
|
||||
} else {
|
||||
logger.info('IP Ranges fetch is disabled by environment variable');
|
||||
}
|
||||
})
|
||||
.then(internalIpRanges.fetch)
|
||||
.then(() => {
|
||||
internalCertificate.initTimer();
|
||||
internalIpRanges.initTimer();
|
||||
|
@@ -1,5 +1,5 @@
|
||||
const _ = require('lodash');
|
||||
const fs = require('node:fs');
|
||||
const fs = require('fs');
|
||||
const batchflow = require('batchflow');
|
||||
const logger = require('../logger').access;
|
||||
const error = require('../lib/error');
|
||||
@@ -38,7 +38,7 @@ const internalAccessList = {
|
||||
.then((row) => {
|
||||
data.id = row.id;
|
||||
|
||||
const promises = [];
|
||||
let promises = [];
|
||||
|
||||
// Now add the items
|
||||
data.items.map((item) => {
|
||||
@@ -81,7 +81,7 @@ const internalAccessList = {
|
||||
|
||||
return internalAccessList.build(row)
|
||||
.then(() => {
|
||||
if (parseInt(row.proxy_host_count, 10)) {
|
||||
if (row.proxy_host_count) {
|
||||
return internalNginx.bulkGenerateConfigs('proxy_host', row.proxy_hosts);
|
||||
}
|
||||
})
|
||||
@@ -116,7 +116,7 @@ const internalAccessList = {
|
||||
.then((row) => {
|
||||
if (row.id !== data.id) {
|
||||
// Sanity check that something crazy hasn't happened
|
||||
throw new error.InternalValidationError(`Access List could not be updated, IDs do not match: ${row.id} !== ${data.id}`);
|
||||
throw new error.InternalValidationError('Access List could not be updated, IDs do not match: ' + row.id + ' !== ' + data.id);
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
@@ -135,10 +135,10 @@ const internalAccessList = {
|
||||
.then(() => {
|
||||
// Check for items and add/update/remove them
|
||||
if (typeof data.items !== 'undefined' && data.items) {
|
||||
const promises = [];
|
||||
const items_to_keep = [];
|
||||
let promises = [];
|
||||
let items_to_keep = [];
|
||||
|
||||
data.items.map((item) => {
|
||||
data.items.map(function (item) {
|
||||
if (item.password) {
|
||||
promises.push(accessListAuthModel
|
||||
.query()
|
||||
@@ -154,7 +154,7 @@ const internalAccessList = {
|
||||
}
|
||||
});
|
||||
|
||||
const query = accessListAuthModel
|
||||
let query = accessListAuthModel
|
||||
.query()
|
||||
.delete()
|
||||
.where('access_list_id', data.id);
|
||||
@@ -175,9 +175,9 @@ const internalAccessList = {
|
||||
.then(() => {
|
||||
// Check for clients and add/update/remove them
|
||||
if (typeof data.clients !== 'undefined' && data.clients) {
|
||||
const promises = [];
|
||||
let promises = [];
|
||||
|
||||
data.clients.map((client) => {
|
||||
data.clients.map(function (client) {
|
||||
if (client.address) {
|
||||
promises.push(accessListClientModel
|
||||
.query()
|
||||
@@ -190,7 +190,7 @@ const internalAccessList = {
|
||||
}
|
||||
});
|
||||
|
||||
const query = accessListClientModel
|
||||
let query = accessListClientModel
|
||||
.query()
|
||||
.delete()
|
||||
.where('access_list_id', data.id);
|
||||
@@ -223,7 +223,7 @@ const internalAccessList = {
|
||||
.then((row) => {
|
||||
return internalAccessList.build(row)
|
||||
.then(() => {
|
||||
if (parseInt(row.proxy_host_count, 10)) {
|
||||
if (row.proxy_host_count) {
|
||||
return internalNginx.bulkGenerateConfigs('proxy_host', row.proxy_hosts);
|
||||
}
|
||||
}).then(internalNginx.reload)
|
||||
@@ -249,16 +249,12 @@ const internalAccessList = {
|
||||
|
||||
return access.can('access_lists:get', data.id)
|
||||
.then((access_data) => {
|
||||
const query = accessListModel
|
||||
let query = accessListModel
|
||||
.query()
|
||||
.select('access_list.*', accessListModel.raw('COUNT(proxy_host.id) as proxy_host_count'))
|
||||
.leftJoin('proxy_host', function() {
|
||||
this.on('proxy_host.access_list_id', '=', 'access_list.id')
|
||||
.andOn('proxy_host.is_deleted', '=', 0);
|
||||
})
|
||||
.joinRaw('LEFT JOIN `proxy_host` ON `proxy_host`.`access_list_id` = `access_list`.`id` AND `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();
|
||||
|
||||
@@ -267,7 +263,7 @@ const internalAccessList = {
|
||||
}
|
||||
|
||||
if (typeof data.expand !== 'undefined' && data.expand !== null) {
|
||||
query.withGraphFetched(`[${data.expand.join(', ')}]`);
|
||||
query.withGraphFetched('[' + data.expand.join(', ') + ']');
|
||||
}
|
||||
|
||||
return query.then(utils.omitRow(omissions()));
|
||||
@@ -327,7 +323,7 @@ const internalAccessList = {
|
||||
// 3. reconfigure those hosts, then reload nginx
|
||||
|
||||
// set the access_list_id to zero for these items
|
||||
row.proxy_hosts.map((_val, idx) => {
|
||||
row.proxy_hosts.map(function (val, idx) {
|
||||
row.proxy_hosts[idx].access_list_id = 0;
|
||||
});
|
||||
|
||||
@@ -340,11 +336,11 @@ const internalAccessList = {
|
||||
})
|
||||
.then(() => {
|
||||
// delete the htpasswd file
|
||||
const htpasswd_file = internalAccessList.getFilename(row);
|
||||
let htpasswd_file = internalAccessList.getFilename(row);
|
||||
|
||||
try {
|
||||
fs.unlinkSync(htpasswd_file);
|
||||
} catch (_err) {
|
||||
} catch (err) {
|
||||
// do nothing
|
||||
}
|
||||
})
|
||||
@@ -374,13 +370,10 @@ const internalAccessList = {
|
||||
getAll: (access, expand, search_query) => {
|
||||
return access.can('access_lists:list')
|
||||
.then((access_data) => {
|
||||
const query = accessListModel
|
||||
let query = accessListModel
|
||||
.query()
|
||||
.select('access_list.*', accessListModel.raw('COUNT(proxy_host.id) as proxy_host_count'))
|
||||
.leftJoin('proxy_host', function() {
|
||||
this.on('proxy_host.access_list_id', '=', 'access_list.id')
|
||||
.andOn('proxy_host.is_deleted', '=', 0);
|
||||
})
|
||||
.joinRaw('LEFT JOIN `proxy_host` ON `proxy_host`.`access_list_id` = `access_list`.`id` AND `proxy_host`.`is_deleted` = 0')
|
||||
.where('access_list.is_deleted', 0)
|
||||
.groupBy('access_list.id')
|
||||
.allowGraph('[owner,items,clients]')
|
||||
@@ -393,19 +386,19 @@ const internalAccessList = {
|
||||
// Query is used for searching
|
||||
if (typeof search_query === 'string') {
|
||||
query.where(function () {
|
||||
this.where('name', 'like', `%${search_query}%`);
|
||||
this.where('name', 'like', '%' + search_query + '%');
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof expand !== 'undefined' && expand !== null) {
|
||||
query.withGraphFetched(`[${expand.join(', ')}]`);
|
||||
query.withGraphFetched('[' + expand.join(', ') + ']');
|
||||
}
|
||||
|
||||
return query.then(utils.omitRows(omissions()));
|
||||
})
|
||||
.then((rows) => {
|
||||
if (rows) {
|
||||
rows.map((row, idx) => {
|
||||
rows.map(function (row, idx) {
|
||||
if (typeof row.items !== 'undefined' && row.items) {
|
||||
rows[idx] = internalAccessList.maskItems(row);
|
||||
}
|
||||
@@ -424,7 +417,7 @@ const internalAccessList = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getCount: (user_id, visibility) => {
|
||||
const query = accessListModel
|
||||
let query = accessListModel
|
||||
.query()
|
||||
.count('id as count')
|
||||
.where('is_deleted', 0);
|
||||
@@ -445,7 +438,7 @@ const internalAccessList = {
|
||||
*/
|
||||
maskItems: (list) => {
|
||||
if (list && typeof list.items !== 'undefined') {
|
||||
list.items.map((val, idx) => {
|
||||
list.items.map(function (val, idx) {
|
||||
let repeat_for = 8;
|
||||
let first_char = '*';
|
||||
|
||||
@@ -468,7 +461,7 @@ const internalAccessList = {
|
||||
* @returns {String}
|
||||
*/
|
||||
getFilename: (list) => {
|
||||
return `/data/access/${list.id}`;
|
||||
return '/data/access/' + list.id;
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -479,15 +472,15 @@ const internalAccessList = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
build: (list) => {
|
||||
logger.info(`Building Access file #${list.id} for: ${list.name}`);
|
||||
logger.info('Building Access file #' + list.id + ' for: ' + list.name);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const htpasswd_file = internalAccessList.getFilename(list);
|
||||
let htpasswd_file = internalAccessList.getFilename(list);
|
||||
|
||||
// 1. remove any existing access file
|
||||
try {
|
||||
fs.unlinkSync(htpasswd_file);
|
||||
} catch (_err) {
|
||||
} catch (err) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@@ -504,17 +497,12 @@ const internalAccessList = {
|
||||
if (list.items.length) {
|
||||
return new Promise((resolve, reject) => {
|
||||
batchflow(list.items).sequential()
|
||||
.each((_i, item, next) => {
|
||||
.each((i, item, next) => {
|
||||
if (typeof item.password !== 'undefined' && item.password.length) {
|
||||
logger.info(`Adding: ${item.username}`);
|
||||
logger.info('Adding: ' + item.username);
|
||||
|
||||
utils.execFile('openssl', ['passwd', '-apr1', item.password])
|
||||
.then((res) => {
|
||||
try {
|
||||
fs.appendFileSync(htpasswd_file, `${item.username}:${res}\n`, {encoding: 'utf8'});
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
utils.execFile('/usr/bin/htpasswd', ['-b', htpasswd_file, item.username, item.password])
|
||||
.then((/*result*/) => {
|
||||
next();
|
||||
})
|
||||
.catch((err) => {
|
||||
@@ -528,7 +516,7 @@ const internalAccessList = {
|
||||
reject(err);
|
||||
})
|
||||
.end((results) => {
|
||||
logger.success(`Built Access file #${list.id} for: ${list.name}`);
|
||||
logger.success('Built Access file #' + list.id + ' for: ' + list.name);
|
||||
resolve(results);
|
||||
});
|
||||
});
|
||||
|
@@ -1,6 +1,5 @@
|
||||
const error = require('../lib/error');
|
||||
const auditLogModel = require('../models/audit-log');
|
||||
const {castJsonIfNeed} = require('../lib/helpers');
|
||||
const error = require('../lib/error');
|
||||
const auditLogModel = require('../models/audit-log');
|
||||
|
||||
const internalAuditLog = {
|
||||
|
||||
@@ -23,9 +22,9 @@ const internalAuditLog = {
|
||||
.allowGraph('[user]');
|
||||
|
||||
// Query is used for searching
|
||||
if (typeof search_query === 'string' && search_query.length > 0) {
|
||||
if (typeof search_query === 'string') {
|
||||
query.where(function () {
|
||||
this.where(castJsonIfNeed('meta'), 'like', '%' + search_query + '%');
|
||||
this.where('meta', 'like', '%' + search_query + '%');
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
const _ = require('lodash');
|
||||
const fs = require('node:fs');
|
||||
const https = require('node:https');
|
||||
const fs = require('fs');
|
||||
const https = require('https');
|
||||
const tempWrite = require('temp-write');
|
||||
const moment = require('moment');
|
||||
const archiver = require('archiver');
|
||||
@@ -49,7 +49,7 @@ const internalCertificate = {
|
||||
processExpiringHosts: () => {
|
||||
if (!internalCertificate.intervalProcessing) {
|
||||
internalCertificate.intervalProcessing = true;
|
||||
logger.info(`Renewing SSL certs expiring within ${internalCertificate.renewBeforeExpirationBy[0]} ${internalCertificate.renewBeforeExpirationBy[1]} ...`);
|
||||
logger.info('Renewing SSL certs expiring within ' + internalCertificate.renewBeforeExpirationBy[0] + ' ' + internalCertificate.renewBeforeExpirationBy[1] + ' ...');
|
||||
|
||||
const expirationThreshold = moment().add(internalCertificate.renewBeforeExpirationBy[0], internalCertificate.renewBeforeExpirationBy[1]).format('YYYY-MM-DD HH:mm:ss');
|
||||
|
||||
@@ -70,7 +70,7 @@ const internalCertificate = {
|
||||
*/
|
||||
let sequence = Promise.resolve();
|
||||
|
||||
certificates.forEach((certificate) => {
|
||||
certificates.forEach(function (certificate) {
|
||||
sequence = sequence.then(() =>
|
||||
internalCertificate
|
||||
.renew(
|
||||
@@ -202,7 +202,7 @@ const internalCertificate = {
|
||||
.then(() => {
|
||||
// At this point, the letsencrypt cert should exist on disk.
|
||||
// Lets get the expiry date from the file and update the row silently
|
||||
return internalCertificate.getCertificateInfoFromFile(`${internalCertificate.getLiveCertPath(certificate.id)}/fullchain.pem`)
|
||||
return internalCertificate.getCertificateInfoFromFile('/etc/letsencrypt/live/npm-' + certificate.id + '/fullchain.pem')
|
||||
.then((cert_info) => {
|
||||
return certificateModel
|
||||
.query()
|
||||
@@ -263,7 +263,7 @@ const internalCertificate = {
|
||||
.then((row) => {
|
||||
if (row.id !== data.id) {
|
||||
// Sanity check that something crazy hasn't happened
|
||||
throw new error.InternalValidationError(`Certificate could not be updated, IDs do not match: ${row.id} !== ${data.id}`);
|
||||
throw new error.InternalValidationError('Certificate could not be updated, IDs do not match: ' + row.id + ' !== ' + data.id);
|
||||
}
|
||||
|
||||
return certificateModel
|
||||
@@ -308,14 +308,11 @@ const internalCertificate = {
|
||||
|
||||
return access.can('certificates:get', data.id)
|
||||
.then((access_data) => {
|
||||
const query = certificateModel
|
||||
let query = certificateModel
|
||||
.query()
|
||||
.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') {
|
||||
@@ -323,7 +320,7 @@ const internalCertificate = {
|
||||
}
|
||||
|
||||
if (typeof data.expand !== 'undefined' && data.expand !== null) {
|
||||
query.withGraphFetched(`[${data.expand.join(', ')}]`);
|
||||
query.withGraphFetched('[' + data.expand.join(', ') + ']');
|
||||
}
|
||||
|
||||
return query.then(utils.omitRow(omissions()));
|
||||
@@ -354,17 +351,17 @@ const internalCertificate = {
|
||||
})
|
||||
.then((certificate) => {
|
||||
if (certificate.provider === 'letsencrypt') {
|
||||
const zipDirectory = internalCertificate.getLiveCertPath(data.id);
|
||||
const zipDirectory = '/etc/letsencrypt/live/npm-' + data.id;
|
||||
|
||||
if (!fs.existsSync(zipDirectory)) {
|
||||
throw new error.ItemNotFoundError(`Certificate ${certificate.nice_name} does not exists`);
|
||||
throw new error.ItemNotFoundError('Certificate ' + certificate.nice_name + ' does not exists');
|
||||
}
|
||||
|
||||
const certFiles = fs.readdirSync(zipDirectory)
|
||||
let certFiles = fs.readdirSync(zipDirectory)
|
||||
.filter((fn) => fn.endsWith('.pem'))
|
||||
.map((fn) => fs.realpathSync(path.join(zipDirectory, fn)));
|
||||
const downloadName = `npm-${data.id}-${Date.now()}.zip`;
|
||||
const opName = `/tmp/${downloadName}`;
|
||||
const downloadName = 'npm-' + data.id + '-' + `${Date.now()}.zip`;
|
||||
const opName = '/tmp/' + downloadName;
|
||||
internalCertificate.zipFiles(certFiles, opName)
|
||||
.then(() => {
|
||||
logger.debug('zip completed : ', opName);
|
||||
@@ -392,7 +389,7 @@ const internalCertificate = {
|
||||
return new Promise((resolve, reject) => {
|
||||
source
|
||||
.map((fl) => {
|
||||
const fileName = path.basename(fl);
|
||||
let fileName = path.basename(fl);
|
||||
logger.debug(fl, 'added to certificate zip');
|
||||
archive.file(fl, { name: fileName });
|
||||
});
|
||||
@@ -462,14 +459,11 @@ const internalCertificate = {
|
||||
getAll: (access, expand, search_query) => {
|
||||
return access.can('certificates:list')
|
||||
.then((access_data) => {
|
||||
const query = certificateModel
|
||||
let query = certificateModel
|
||||
.query()
|
||||
.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') {
|
||||
@@ -479,12 +473,12 @@ const internalCertificate = {
|
||||
// Query is used for searching
|
||||
if (typeof search_query === 'string') {
|
||||
query.where(function () {
|
||||
this.where('nice_name', 'like', `%${search_query}%`);
|
||||
this.where('nice_name', 'like', '%' + search_query + '%');
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof expand !== 'undefined' && expand !== null) {
|
||||
query.withGraphFetched(`[${expand.join(', ')}]`);
|
||||
query.withGraphFetched('[' + expand.join(', ') + ']');
|
||||
}
|
||||
|
||||
return query.then(utils.omitRows(omissions()));
|
||||
@@ -499,7 +493,7 @@ const internalCertificate = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getCount: (user_id, visibility) => {
|
||||
const query = certificateModel
|
||||
let query = certificateModel
|
||||
.query()
|
||||
.count('id as count')
|
||||
.where('is_deleted', 0);
|
||||
@@ -521,7 +515,7 @@ const internalCertificate = {
|
||||
writeCustomCert: (certificate) => {
|
||||
logger.info('Writing Custom Certificate:', certificate);
|
||||
|
||||
const dir = `/data/custom_ssl/npm-${certificate.id}`;
|
||||
const dir = '/data/custom_ssl/npm-' + certificate.id;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
if (certificate.provider === 'letsencrypt') {
|
||||
@@ -531,7 +525,7 @@ const internalCertificate = {
|
||||
|
||||
let certData = certificate.meta.certificate;
|
||||
if (typeof certificate.meta.intermediate_certificate !== 'undefined') {
|
||||
certData = `${certData}\n${certificate.meta.intermediate_certificate}`;
|
||||
certData = certData + '\n' + certificate.meta.intermediate_certificate;
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -543,7 +537,7 @@ const internalCertificate = {
|
||||
return;
|
||||
}
|
||||
|
||||
fs.writeFile(`${dir}/fullchain.pem`, certData, (err) => {
|
||||
fs.writeFile(dir + '/fullchain.pem', certData, function (err) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
@@ -553,7 +547,7 @@ const internalCertificate = {
|
||||
})
|
||||
.then(() => {
|
||||
return new Promise((resolve, reject) => {
|
||||
fs.writeFile(`${dir}/privkey.pem`, certificate.meta.certificate_key, (err) => {
|
||||
fs.writeFile(dir + '/privkey.pem', certificate.meta.certificate_key, function (err) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
@@ -591,7 +585,7 @@ const internalCertificate = {
|
||||
validate: (data) => {
|
||||
return new Promise((resolve) => {
|
||||
// Put file contents into an object
|
||||
const files = {};
|
||||
let files = {};
|
||||
_.map(data.files, (file, name) => {
|
||||
if (internalCertificate.allowedSslFiles.indexOf(name) !== -1) {
|
||||
files[name] = file.data.toString();
|
||||
@@ -603,7 +597,7 @@ const internalCertificate = {
|
||||
.then((files) => {
|
||||
// For each file, create a temp file and write the contents to it
|
||||
// Then test it depending on the file type
|
||||
const promises = [];
|
||||
let promises = [];
|
||||
_.map(files, (content, type) => {
|
||||
promises.push(new Promise((resolve) => {
|
||||
if (type === 'certificate_key') {
|
||||
@@ -688,11 +682,11 @@ const internalCertificate = {
|
||||
reject(new error.ValidationError('Result Validation Error: Validation timed out. This could be due to the key being passphrase-protected.'));
|
||||
}, 10000);
|
||||
utils
|
||||
.exec(`openssl pkey -in ${filepath} -check -noout 2>&1 `)
|
||||
.exec('openssl pkey -in ' + filepath + ' -check -noout 2>&1 ')
|
||||
.then((result) => {
|
||||
clearTimeout(failTimeout);
|
||||
if (!result.toLowerCase().includes('key is valid')) {
|
||||
reject(new error.ValidationError(`Result Validation Error: ${result}`));
|
||||
reject(new error.ValidationError('Result Validation Error: ' + result));
|
||||
}
|
||||
fs.unlinkSync(filepath);
|
||||
resolve(true);
|
||||
@@ -700,7 +694,7 @@ const internalCertificate = {
|
||||
.catch((err) => {
|
||||
clearTimeout(failTimeout);
|
||||
fs.unlinkSync(filepath);
|
||||
reject(new error.ValidationError(`Certificate Key is not valid (${err.message})`, err));
|
||||
reject(new error.ValidationError('Certificate Key is not valid (' + err.message + ')', err));
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -735,9 +729,9 @@ const internalCertificate = {
|
||||
* @param {Boolean} [throw_expired] Throw when the certificate is out of date
|
||||
*/
|
||||
getCertificateInfoFromFile: (certificate_file, throw_expired) => {
|
||||
const certData = {};
|
||||
let certData = {};
|
||||
|
||||
return utils.execFile('openssl', ['x509', '-in', certificate_file, '-subject', '-noout'])
|
||||
return utils.exec('openssl x509 -in ' + certificate_file + ' -subject -noout')
|
||||
.then((result) => {
|
||||
// Examples:
|
||||
// subject=CN = *.jc21.com
|
||||
@@ -745,11 +739,11 @@ const internalCertificate = {
|
||||
const regex = /(?:subject=)?[^=]+=\s+(\S+)/gim;
|
||||
const match = regex.exec(result);
|
||||
if (match && typeof match[1] !== 'undefined') {
|
||||
certData.cn = match[1];
|
||||
certData['cn'] = match[1];
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
return utils.execFile('openssl', ['x509', '-in', certificate_file, '-issuer', '-noout']);
|
||||
return utils.exec('openssl x509 -in ' + certificate_file + ' -issuer -noout');
|
||||
})
|
||||
|
||||
.then((result) => {
|
||||
@@ -760,11 +754,11 @@ const internalCertificate = {
|
||||
const regex = /^(?:issuer=)?(.*)$/gim;
|
||||
const match = regex.exec(result);
|
||||
if (match && typeof match[1] !== 'undefined') {
|
||||
certData.issuer = match[1];
|
||||
certData['issuer'] = match[1];
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
return utils.execFile('openssl', ['x509', '-in', certificate_file, '-dates', '-noout']);
|
||||
return utils.exec('openssl x509 -in ' + certificate_file + ' -dates -noout');
|
||||
})
|
||||
.then((result) => {
|
||||
// notBefore=Jul 14 04:04:29 2018 GMT
|
||||
@@ -773,7 +767,7 @@ const internalCertificate = {
|
||||
let validTo = null;
|
||||
|
||||
const lines = result.split('\n');
|
||||
lines.map((str) => {
|
||||
lines.map(function (str) {
|
||||
const regex = /^(\S+)=(.*)$/gim;
|
||||
const match = regex.exec(str.trim());
|
||||
|
||||
@@ -789,21 +783,21 @@ const internalCertificate = {
|
||||
});
|
||||
|
||||
if (!validFrom || !validTo) {
|
||||
throw new error.ValidationError(`Could not determine dates from certificate: ${result}`);
|
||||
throw new error.ValidationError('Could not determine dates from certificate: ' + result);
|
||||
}
|
||||
|
||||
if (throw_expired && validTo < parseInt(moment().format('X'), 10)) {
|
||||
throw new error.ValidationError('Certificate has expired');
|
||||
}
|
||||
|
||||
certData.dates = {
|
||||
certData['dates'] = {
|
||||
from: validFrom,
|
||||
to: validTo
|
||||
};
|
||||
|
||||
return certData;
|
||||
}).catch((err) => {
|
||||
throw new error.ValidationError(`Certificate is not valid (${err.message})`, err);
|
||||
throw new error.ValidationError('Certificate is not valid (' + err.message + ')', err);
|
||||
});
|
||||
},
|
||||
|
||||
@@ -814,7 +808,7 @@ const internalCertificate = {
|
||||
* @param {Boolean} [remove]
|
||||
* @returns {Object}
|
||||
*/
|
||||
cleanMeta: (meta, remove) => {
|
||||
cleanMeta: function (meta, remove) {
|
||||
internalCertificate.allowedSslFiles.map((key) => {
|
||||
if (typeof meta[key] !== 'undefined' && meta[key]) {
|
||||
if (remove) {
|
||||
@@ -834,35 +828,24 @@ const internalCertificate = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
requestLetsEncryptSsl: (certificate) => {
|
||||
logger.info(`Requesting LetsEncrypt certificates for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);
|
||||
logger.info('Requesting Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
|
||||
|
||||
const args = [
|
||||
'certonly',
|
||||
'--config',
|
||||
letsencryptConfig,
|
||||
'--work-dir',
|
||||
'/tmp/letsencrypt-lib',
|
||||
'--logs-dir',
|
||||
'/tmp/letsencrypt-log',
|
||||
'--cert-name',
|
||||
`npm-${certificate.id}`,
|
||||
'--agree-tos',
|
||||
'--authenticator',
|
||||
'webroot',
|
||||
'--email',
|
||||
certificate.meta.letsencrypt_email,
|
||||
'--preferred-challenges',
|
||||
'dns,http',
|
||||
'--domains',
|
||||
certificate.domain_names.join(','),
|
||||
];
|
||||
const cmd = `${certbotCommand} certonly ` +
|
||||
`--config '${letsencryptConfig}' ` +
|
||||
'--work-dir "/tmp/letsencrypt-lib" ' +
|
||||
'--logs-dir "/tmp/letsencrypt-log" ' +
|
||||
`--cert-name "npm-${certificate.id}" ` +
|
||||
'--agree-tos ' +
|
||||
'--authenticator webroot ' +
|
||||
`--email '${certificate.meta.letsencrypt_email}' ` +
|
||||
'--preferred-challenges "dns,http" ' +
|
||||
`--domains "${certificate.domain_names.join(',')}" ` +
|
||||
(letsencryptServer !== null ? `--server '${letsencryptServer}' ` : '') +
|
||||
(letsencryptStaging && letsencryptServer === null ? '--staging ' : '');
|
||||
|
||||
const adds = internalCertificate.getAdditionalCertbotArgs(certificate.id);
|
||||
args.push(...adds.args);
|
||||
logger.info('Command:', cmd);
|
||||
|
||||
logger.info(`Command: ${certbotCommand} ${args ? args.join(' ') : ''}`);
|
||||
|
||||
return utils.execFile(certbotCommand, args, adds.opts)
|
||||
return utils.exec(cmd)
|
||||
.then((result) => {
|
||||
logger.success(result);
|
||||
return result;
|
||||
@@ -879,48 +862,50 @@ const internalCertificate = {
|
||||
requestLetsEncryptSslWithDnsChallenge: async (certificate) => {
|
||||
await certbot.installPlugin(certificate.meta.dns_provider);
|
||||
const dnsPlugin = dnsPlugins[certificate.meta.dns_provider];
|
||||
logger.info(`Requesting LetsEncrypt certificates via ${dnsPlugin.name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);
|
||||
logger.info(`Requesting Let'sEncrypt certificates via ${dnsPlugin.name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);
|
||||
|
||||
const credentialsLocation = `/etc/letsencrypt/credentials/credentials-${certificate.id}`;
|
||||
const credentialsLocation = '/etc/letsencrypt/credentials/credentials-' + certificate.id;
|
||||
fs.mkdirSync('/etc/letsencrypt/credentials', { recursive: true });
|
||||
fs.writeFileSync(credentialsLocation, certificate.meta.dns_provider_credentials, {mode: 0o600});
|
||||
|
||||
// Whether the plugin has a --<name>-credentials argument
|
||||
const hasConfigArg = certificate.meta.dns_provider !== 'route53';
|
||||
|
||||
const args = [
|
||||
'certonly',
|
||||
'--config',
|
||||
letsencryptConfig,
|
||||
'--work-dir',
|
||||
'/tmp/letsencrypt-lib',
|
||||
'--logs-dir',
|
||||
'/tmp/letsencrypt-log',
|
||||
'--cert-name',
|
||||
`npm-${certificate.id}`,
|
||||
'--agree-tos',
|
||||
'--email',
|
||||
certificate.meta.letsencrypt_email,
|
||||
'--domains',
|
||||
certificate.domain_names.join(','),
|
||||
'--authenticator',
|
||||
dnsPlugin.full_plugin_name,
|
||||
];
|
||||
let mainCmd = certbotCommand + ' certonly ' +
|
||||
`--config '${letsencryptConfig}' ` +
|
||||
'--work-dir "/tmp/letsencrypt-lib" ' +
|
||||
'--logs-dir "/tmp/letsencrypt-log" ' +
|
||||
`--cert-name 'npm-${certificate.id}' ` +
|
||||
'--agree-tos ' +
|
||||
`--email '${certificate.meta.letsencrypt_email}' ` +
|
||||
`--domains '${certificate.domain_names.join(',')}' ` +
|
||||
`--authenticator '${dnsPlugin.full_plugin_name}' ` +
|
||||
(
|
||||
hasConfigArg
|
||||
? `--${dnsPlugin.full_plugin_name}-credentials '${credentialsLocation}' `
|
||||
: ''
|
||||
) +
|
||||
(
|
||||
certificate.meta.propagation_seconds !== undefined
|
||||
? `--${dnsPlugin.full_plugin_name}-propagation-seconds '${certificate.meta.propagation_seconds}' `
|
||||
: ''
|
||||
) +
|
||||
(letsencryptServer !== null ? `--server '${letsencryptServer}' ` : '') +
|
||||
(letsencryptStaging && letsencryptServer === null ? '--staging ' : '');
|
||||
|
||||
if (hasConfigArg) {
|
||||
args.push(`--${dnsPlugin.full_plugin_name}-credentials`, credentialsLocation);
|
||||
}
|
||||
if (certificate.meta.propagation_seconds !== undefined) {
|
||||
args.push(`--${dnsPlugin.full_plugin_name}-propagation-seconds`, certificate.meta.propagation_seconds.toString());
|
||||
// Prepend the path to the credentials file as an environment variable
|
||||
if (certificate.meta.dns_provider === 'route53') {
|
||||
mainCmd = 'AWS_CONFIG_FILE=\'' + credentialsLocation + '\' ' + mainCmd;
|
||||
}
|
||||
|
||||
const adds = internalCertificate.getAdditionalCertbotArgs(certificate.id, certificate.meta.dns_provider);
|
||||
args.push(...adds.args);
|
||||
if (certificate.meta.dns_provider === 'duckdns') {
|
||||
mainCmd = mainCmd + ' --dns-duckdns-no-txt-restore';
|
||||
}
|
||||
|
||||
logger.info(`Command: ${certbotCommand} ${args ? args.join(' ') : ''}`);
|
||||
logger.info('Command:', mainCmd);
|
||||
|
||||
try {
|
||||
const result = await utils.execFile(certbotCommand, args, adds.opts);
|
||||
const result = await utils.exec(mainCmd);
|
||||
logger.info(result);
|
||||
return result;
|
||||
} catch (err) {
|
||||
@@ -948,7 +933,7 @@ const internalCertificate = {
|
||||
|
||||
return renewMethod(certificate)
|
||||
.then(() => {
|
||||
return internalCertificate.getCertificateInfoFromFile(`${internalCertificate.getLiveCertPath(certificate.id)}/fullchain.pem`);
|
||||
return internalCertificate.getCertificateInfoFromFile('/etc/letsencrypt/live/npm-' + certificate.id + '/fullchain.pem');
|
||||
})
|
||||
.then((cert_info) => {
|
||||
return certificateModel
|
||||
@@ -980,31 +965,22 @@ const internalCertificate = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
renewLetsEncryptSsl: (certificate) => {
|
||||
logger.info(`Renewing LetsEncrypt certificates for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);
|
||||
logger.info('Renewing Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
|
||||
|
||||
const args = [
|
||||
'renew',
|
||||
'--force-renewal',
|
||||
'--config',
|
||||
letsencryptConfig,
|
||||
'--work-dir',
|
||||
'/tmp/letsencrypt-lib',
|
||||
'--logs-dir',
|
||||
'/tmp/letsencrypt-log',
|
||||
'--cert-name',
|
||||
`npm-${certificate.id}`,
|
||||
'--preferred-challenges',
|
||||
'dns,http',
|
||||
'--no-random-sleep-on-renew',
|
||||
'--disable-hook-validation',
|
||||
];
|
||||
const cmd = certbotCommand + ' renew --force-renewal ' +
|
||||
`--config '${letsencryptConfig}' ` +
|
||||
'--work-dir "/tmp/letsencrypt-lib" ' +
|
||||
'--logs-dir "/tmp/letsencrypt-log" ' +
|
||||
`--cert-name 'npm-${certificate.id}' ` +
|
||||
'--preferred-challenges "dns,http" ' +
|
||||
'--no-random-sleep-on-renew ' +
|
||||
'--disable-hook-validation ' +
|
||||
(letsencryptServer !== null ? `--server '${letsencryptServer}' ` : '') +
|
||||
(letsencryptStaging && letsencryptServer === null ? '--staging ' : '');
|
||||
|
||||
const adds = internalCertificate.getAdditionalCertbotArgs(certificate.id, certificate.meta.dns_provider);
|
||||
args.push(...adds.args);
|
||||
logger.info('Command:', cmd);
|
||||
|
||||
logger.info(`Command: ${certbotCommand} ${args ? args.join(' ') : ''}`);
|
||||
|
||||
return utils.execFile(certbotCommand, args, adds.opts)
|
||||
return utils.exec(cmd)
|
||||
.then((result) => {
|
||||
logger.info(result);
|
||||
return result;
|
||||
@@ -1022,29 +998,27 @@ const internalCertificate = {
|
||||
throw Error(`Unknown DNS provider '${certificate.meta.dns_provider}'`);
|
||||
}
|
||||
|
||||
logger.info(`Renewing LetsEncrypt certificates via ${dnsPlugin.name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);
|
||||
logger.info(`Renewing Let'sEncrypt certificates via ${dnsPlugin.name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);
|
||||
|
||||
const args = [
|
||||
'renew',
|
||||
'--force-renewal',
|
||||
'--config',
|
||||
letsencryptConfig,
|
||||
'--work-dir',
|
||||
'/tmp/letsencrypt-lib',
|
||||
'--logs-dir',
|
||||
'/tmp/letsencrypt-log',
|
||||
'--cert-name',
|
||||
`npm-${certificate.id}`,
|
||||
'--disable-hook-validation',
|
||||
'--no-random-sleep-on-renew',
|
||||
];
|
||||
let mainCmd = certbotCommand + ' renew --force-renewal ' +
|
||||
`--config "${letsencryptConfig}" ` +
|
||||
'--work-dir "/tmp/letsencrypt-lib" ' +
|
||||
'--logs-dir "/tmp/letsencrypt-log" ' +
|
||||
`--cert-name 'npm-${certificate.id}' ` +
|
||||
'--disable-hook-validation ' +
|
||||
'--no-random-sleep-on-renew ' +
|
||||
(letsencryptServer !== null ? `--server '${letsencryptServer}' ` : '') +
|
||||
(letsencryptStaging && letsencryptServer === null ? '--staging ' : '');
|
||||
|
||||
const adds = internalCertificate.getAdditionalCertbotArgs(certificate.id, certificate.meta.dns_provider);
|
||||
args.push(...adds.args);
|
||||
// Prepend the path to the credentials file as an environment variable
|
||||
if (certificate.meta.dns_provider === 'route53') {
|
||||
const credentialsLocation = '/etc/letsencrypt/credentials/credentials-' + certificate.id;
|
||||
mainCmd = 'AWS_CONFIG_FILE=\'' + credentialsLocation + '\' ' + mainCmd;
|
||||
}
|
||||
|
||||
logger.info(`Command: ${certbotCommand} ${args ? args.join(' ') : ''}`);
|
||||
logger.info('Command:', mainCmd);
|
||||
|
||||
return utils.execFile(certbotCommand, args, adds.opts)
|
||||
return utils.exec(mainCmd)
|
||||
.then(async (result) => {
|
||||
logger.info(result);
|
||||
return result;
|
||||
@@ -1057,29 +1031,25 @@ const internalCertificate = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
revokeLetsEncryptSsl: (certificate, throw_errors) => {
|
||||
logger.info(`Revoking LetsEncrypt certificates for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);
|
||||
logger.info('Revoking Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
|
||||
|
||||
const args = [
|
||||
'revoke',
|
||||
'--config',
|
||||
letsencryptConfig,
|
||||
'--work-dir',
|
||||
'/tmp/letsencrypt-lib',
|
||||
'--logs-dir',
|
||||
'/tmp/letsencrypt-log',
|
||||
'--cert-path',
|
||||
`${internalCertificate.getLiveCertPath(certificate.id)}/fullchain.pem`,
|
||||
'--delete-after-revoke',
|
||||
];
|
||||
const mainCmd = certbotCommand + ' revoke ' +
|
||||
`--config '${letsencryptConfig}' ` +
|
||||
'--work-dir "/tmp/letsencrypt-lib" ' +
|
||||
'--logs-dir "/tmp/letsencrypt-log" ' +
|
||||
`--cert-path '/etc/letsencrypt/live/npm-${certificate.id}/fullchain.pem' ` +
|
||||
'--delete-after-revoke ' +
|
||||
(letsencryptServer !== null ? `--server '${letsencryptServer}' ` : '') +
|
||||
(letsencryptStaging && letsencryptServer === null ? '--staging ' : '');
|
||||
|
||||
const adds = internalCertificate.getAdditionalCertbotArgs(certificate.id);
|
||||
args.push(...adds.args);
|
||||
// Don't fail command if file does not exist
|
||||
const delete_credentialsCmd = `rm -f '/etc/letsencrypt/credentials/credentials-${certificate.id}' || true`;
|
||||
|
||||
logger.info(`Command: ${certbotCommand} ${args ? args.join(' ') : ''}`);
|
||||
logger.info('Command:', mainCmd + '; ' + delete_credentialsCmd);
|
||||
|
||||
return utils.execFile(certbotCommand, args, adds.opts)
|
||||
return utils.exec(mainCmd)
|
||||
.then(async (result) => {
|
||||
await utils.exec(`rm -f '/etc/letsencrypt/credentials/credentials-${certificate.id}' || true`);
|
||||
await utils.exec(delete_credentialsCmd);
|
||||
logger.info(result);
|
||||
return result;
|
||||
})
|
||||
@@ -1097,8 +1067,9 @@ const internalCertificate = {
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
hasLetsEncryptSslCerts: (certificate) => {
|
||||
const letsencryptPath = internalCertificate.getLiveCertPath(certificate.id);
|
||||
return fs.existsSync(`${letsencryptPath}/fullchain.pem`) && fs.existsSync(`${letsencryptPath}/privkey.pem`);
|
||||
const letsencryptPath = '/etc/letsencrypt/live/npm-' + certificate.id;
|
||||
|
||||
return fs.existsSync(letsencryptPath + '/fullchain.pem') && fs.existsSync(letsencryptPath + '/privkey.pem');
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -1110,7 +1081,7 @@ const internalCertificate = {
|
||||
*/
|
||||
disableInUseHosts: (in_use_result) => {
|
||||
if (in_use_result.total_count) {
|
||||
const promises = [];
|
||||
let promises = [];
|
||||
|
||||
if (in_use_result.proxy_hosts.length) {
|
||||
promises.push(internalNginx.bulkDeleteConfigs('proxy_host', in_use_result.proxy_hosts));
|
||||
@@ -1140,7 +1111,7 @@ const internalCertificate = {
|
||||
*/
|
||||
enableInUseHosts: (in_use_result) => {
|
||||
if (in_use_result.total_count) {
|
||||
const promises = [];
|
||||
let promises = [];
|
||||
|
||||
if (in_use_result.proxy_hosts.length) {
|
||||
promises.push(internalNginx.bulkGenerateConfigs('proxy_host', in_use_result.proxy_hosts));
|
||||
@@ -1173,12 +1144,12 @@ const internalCertificate = {
|
||||
|
||||
// Create a test challenge file
|
||||
const testChallengeDir = '/data/letsencrypt-acme-challenge/.well-known/acme-challenge';
|
||||
const testChallengeFile = `${testChallengeDir}/test-challenge`;
|
||||
const testChallengeFile = testChallengeDir + '/test-challenge';
|
||||
fs.mkdirSync(testChallengeDir, {recursive: true});
|
||||
fs.writeFileSync(testChallengeFile, 'Success', {encoding: 'utf8'});
|
||||
|
||||
async function performTestForDomain (domain) {
|
||||
logger.info(`Testing http challenge for ${domain}`);
|
||||
logger.info('Testing http challenge for ' + domain);
|
||||
const url = `http://${domain}/.well-known/acme-challenge/test-challenge`;
|
||||
const formBody = `method=G&url=${encodeURI(url)}&bodytype=T&requestbody=&headername=User-Agent&headervalue=None&locationid=1&ch=false&cc=false`;
|
||||
const options = {
|
||||
@@ -1192,16 +1163,13 @@ const internalCertificate = {
|
||||
|
||||
const result = await new Promise((resolve) => {
|
||||
|
||||
const req = https.request('https://www.site24x7.com/tools/restapi-tester', options, (res) => {
|
||||
const req = https.request('https://www.site24x7.com/tools/restapi-tester', options, function (res) {
|
||||
let responseBody = '';
|
||||
|
||||
res.on('data', (chunk) => {
|
||||
responseBody = responseBody + chunk;
|
||||
});
|
||||
|
||||
res.on('end', () => {
|
||||
res.on('data', (chunk) => responseBody = responseBody + chunk);
|
||||
res.on('end', function () {
|
||||
try {
|
||||
const parsedBody = JSON.parse(`${responseBody}`);
|
||||
const parsedBody = JSON.parse(responseBody + '');
|
||||
if (res.statusCode !== 200) {
|
||||
logger.warn(`Failed to test HTTP challenge for domain ${domain} because HTTP status code ${res.statusCode} was returned: ${parsedBody.message}`);
|
||||
resolve(undefined);
|
||||
@@ -1222,7 +1190,7 @@ const internalCertificate = {
|
||||
// Make sure to write the request body.
|
||||
req.write(formBody);
|
||||
req.end();
|
||||
req.on('error', (e) => { logger.warn(`Failed to test HTTP challenge for domain ${domain}`, e);
|
||||
req.on('error', function (e) { logger.warn(`Failed to test HTTP challenge for domain ${domain}`, e);
|
||||
resolve(undefined); });
|
||||
});
|
||||
|
||||
@@ -1264,34 +1232,6 @@ const internalCertificate = {
|
||||
fs.unlinkSync(testChallengeFile);
|
||||
|
||||
return results;
|
||||
},
|
||||
|
||||
getAdditionalCertbotArgs: (certificate_id, dns_provider) => {
|
||||
const args = [];
|
||||
if (letsencryptServer !== null) {
|
||||
args.push('--server', letsencryptServer);
|
||||
}
|
||||
if (letsencryptStaging && letsencryptServer === null) {
|
||||
args.push('--staging');
|
||||
}
|
||||
|
||||
// For route53, add the credentials file as an environment variable,
|
||||
// inheriting the process env
|
||||
const opts = {};
|
||||
if (certificate_id && dns_provider === 'route53') {
|
||||
opts.env = process.env;
|
||||
opts.env.AWS_CONFIG_FILE = `/etc/letsencrypt/credentials/credentials-${certificate_id}`;
|
||||
}
|
||||
|
||||
if (dns_provider === 'duckdns') {
|
||||
args.push('--dns-duckdns-no-txt-restore');
|
||||
}
|
||||
|
||||
return {args: args, opts: opts};
|
||||
},
|
||||
|
||||
getLiveCertPath: (certificate_id) => {
|
||||
return `/etc/letsencrypt/live/npm-${certificate_id}`;
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -6,7 +6,6 @@ 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'];
|
||||
@@ -410,16 +409,16 @@ const internalDeadHost = {
|
||||
.where('is_deleted', 0)
|
||||
.groupBy('id')
|
||||
.allowGraph('[owner,certificate]')
|
||||
.orderBy(castJsonIfNeed('domain_names'), 'ASC');
|
||||
.orderBy('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' && search_query.length > 0) {
|
||||
if (typeof search_query === 'string') {
|
||||
query.where(function () {
|
||||
this.where(castJsonIfNeed('domain_names'), 'like', '%' + search_query + '%');
|
||||
this.where('domain_names', 'like', '%' + search_query + '%');
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -2,7 +2,6 @@ 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 = {
|
||||
|
||||
@@ -18,7 +17,7 @@ const internalHost = {
|
||||
cleanSslHstsData: function (data, existing_data) {
|
||||
existing_data = existing_data === undefined ? {} : existing_data;
|
||||
|
||||
const combined_data = _.assign({}, existing_data, data);
|
||||
let combined_data = _.assign({}, existing_data, data);
|
||||
|
||||
if (!combined_data.certificate_id) {
|
||||
combined_data.ssl_forced = false;
|
||||
@@ -74,7 +73,7 @@ const internalHost = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getHostsWithDomains: function (domain_names) {
|
||||
const promises = [
|
||||
let promises = [
|
||||
proxyHostModel
|
||||
.query()
|
||||
.where('is_deleted', 0),
|
||||
@@ -126,19 +125,19 @@ const internalHost = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
isHostnameTaken: function (hostname, ignore_type, ignore_id) {
|
||||
const promises = [
|
||||
let promises = [
|
||||
proxyHostModel
|
||||
.query()
|
||||
.where('is_deleted', 0)
|
||||
.andWhere(castJsonIfNeed('domain_names'), 'like', '%' + hostname + '%'),
|
||||
.andWhere('domain_names', 'like', '%' + hostname + '%'),
|
||||
redirectionHostModel
|
||||
.query()
|
||||
.where('is_deleted', 0)
|
||||
.andWhere(castJsonIfNeed('domain_names'), 'like', '%' + hostname + '%'),
|
||||
.andWhere('domain_names', 'like', '%' + hostname + '%'),
|
||||
deadHostModel
|
||||
.query()
|
||||
.where('is_deleted', 0)
|
||||
.andWhere(castJsonIfNeed('domain_names'), 'like', '%' + hostname + '%')
|
||||
.andWhere('domain_names', 'like', '%' + hostname + '%')
|
||||
];
|
||||
|
||||
return Promise.all(promises)
|
||||
|
@@ -1,5 +1,5 @@
|
||||
const _ = require('lodash');
|
||||
const fs = require('node:fs');
|
||||
const fs = require('fs');
|
||||
const logger = require('../logger').nginx;
|
||||
const config = require('../lib/config');
|
||||
const utils = require('../lib/utils');
|
||||
@@ -57,9 +57,9 @@ const internalNginx = {
|
||||
// It will always look like this:
|
||||
// nginx: [alert] could not open error log file: open() "/var/log/nginx/error.log" failed (6: No such device or address)
|
||||
|
||||
const valid_lines = [];
|
||||
const err_lines = err.message.split('\n');
|
||||
err_lines.map((line) => {
|
||||
let valid_lines = [];
|
||||
let err_lines = err.message.split('\n');
|
||||
err_lines.map(function (line) {
|
||||
if (line.indexOf('/var/log/nginx/error.log') === -1) {
|
||||
valid_lines.push(line);
|
||||
}
|
||||
@@ -105,7 +105,7 @@ const internalNginx = {
|
||||
logger.info('Testing Nginx configuration');
|
||||
}
|
||||
|
||||
return utils.execFile('/usr/sbin/nginx', ['-t', '-g', 'error_log off;']);
|
||||
return utils.exec('/usr/sbin/nginx -t -g "error_log off;"');
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -115,7 +115,7 @@ const internalNginx = {
|
||||
return internalNginx.test()
|
||||
.then(() => {
|
||||
logger.info('Reloading Nginx');
|
||||
return utils.execFile('/usr/sbin/nginx', ['-s', 'reload']);
|
||||
return utils.exec('/usr/sbin/nginx -s reload');
|
||||
});
|
||||
},
|
||||
|
||||
@@ -128,7 +128,7 @@ const internalNginx = {
|
||||
if (host_type === 'default') {
|
||||
return '/data/nginx/default_host/site.conf';
|
||||
}
|
||||
return `/data/nginx/${internalNginx.getFileFriendlyHostType(host_type)}/${host_id}.conf`;
|
||||
return '/data/nginx/' + internalNginx.getFileFriendlyHostType(host_type) + '/' + host_id + '.conf';
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -141,7 +141,7 @@ const internalNginx = {
|
||||
let template;
|
||||
|
||||
try {
|
||||
template = fs.readFileSync(`${__dirname}/../templates/_location.conf`, {encoding: 'utf8'});
|
||||
template = fs.readFileSync(__dirname + '/../templates/_location.conf', {encoding: 'utf8'});
|
||||
} catch (err) {
|
||||
reject(new error.ConfigurationError(err.message));
|
||||
return;
|
||||
@@ -152,9 +152,10 @@ const internalNginx = {
|
||||
|
||||
const locationRendering = async () => {
|
||||
for (let i = 0; i < host.locations.length; i++) {
|
||||
const locationCopy = Object.assign({}, {access_list_id: host.access_list_id}, {certificate_id: host.certificate_id},
|
||||
let locationCopy = Object.assign({}, {access_list_id: host.access_list_id}, {certificate_id: host.certificate_id},
|
||||
{ssl_forced: host.ssl_forced}, {caching_enabled: host.caching_enabled}, {block_exploits: host.block_exploits},
|
||||
{allow_websocket_upgrade: host.allow_websocket_upgrade}, {http2_support: host.http2_support},
|
||||
{enable_proxy_protocol: host.enable_proxy_protocol}, {load_balancer_ip: host.load_balancer_ip},
|
||||
{hsts_enabled: host.hsts_enabled}, {hsts_subdomains: host.hsts_subdomains}, {access_list: host.access_list},
|
||||
{certificate: host.certificate}, host.locations[i]);
|
||||
|
||||
@@ -183,21 +184,21 @@ const internalNginx = {
|
||||
*/
|
||||
generateConfig: (host_type, host_row) => {
|
||||
// Prevent modifying the original object:
|
||||
const host = JSON.parse(JSON.stringify(host_row));
|
||||
let host = JSON.parse(JSON.stringify(host_row));
|
||||
const nice_host_type = internalNginx.getFileFriendlyHostType(host_type);
|
||||
|
||||
if (config.debug()) {
|
||||
logger.info(`Generating ${nice_host_type} Config:`, JSON.stringify(host, null, 2));
|
||||
logger.info('Generating ' + nice_host_type + ' Config:', JSON.stringify(host, null, 2));
|
||||
}
|
||||
|
||||
const renderEngine = utils.getRenderEngine();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let template = null;
|
||||
const filename = internalNginx.getConfigName(nice_host_type, host.id);
|
||||
let template = null;
|
||||
let filename = internalNginx.getConfigName(nice_host_type, host.id);
|
||||
|
||||
try {
|
||||
template = fs.readFileSync(`${__dirname}/../templates/${nice_host_type}.conf`, {encoding: 'utf8'});
|
||||
template = fs.readFileSync(__dirname + '/../templates/' + nice_host_type + '.conf', {encoding: 'utf8'});
|
||||
} catch (err) {
|
||||
reject(new error.ConfigurationError(err.message));
|
||||
return;
|
||||
@@ -252,7 +253,7 @@ const internalNginx = {
|
||||
})
|
||||
.catch((err) => {
|
||||
if (config.debug()) {
|
||||
logger.warn(`Could not write ${filename}:`, err.message);
|
||||
logger.warn('Could not write ' + filename + ':', err.message);
|
||||
}
|
||||
|
||||
reject(new error.ConfigurationError(err.message));
|
||||
@@ -277,11 +278,11 @@ const internalNginx = {
|
||||
const renderEngine = utils.getRenderEngine();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let template = null;
|
||||
const filename = `/data/nginx/temp/letsencrypt_${certificate.id}.conf`;
|
||||
let template = null;
|
||||
let filename = '/data/nginx/temp/letsencrypt_' + certificate.id + '.conf';
|
||||
|
||||
try {
|
||||
template = fs.readFileSync(`${__dirname}/../templates/letsencrypt-request.conf`, {encoding: 'utf8'});
|
||||
template = fs.readFileSync(__dirname + '/../templates/letsencrypt-request.conf', {encoding: 'utf8'});
|
||||
} catch (err) {
|
||||
reject(new error.ConfigurationError(err.message));
|
||||
return;
|
||||
@@ -302,7 +303,7 @@ const internalNginx = {
|
||||
})
|
||||
.catch((err) => {
|
||||
if (config.debug()) {
|
||||
logger.warn(`Could not write ${filename}:`, err.message);
|
||||
logger.warn('Could not write ' + filename + ':', err.message);
|
||||
}
|
||||
|
||||
reject(new error.ConfigurationError(err.message));
|
||||
@@ -316,7 +317,7 @@ const internalNginx = {
|
||||
* @param {String} filename
|
||||
*/
|
||||
deleteFile: (filename) => {
|
||||
logger.debug(`Deleting file: ${filename}`);
|
||||
logger.debug('Deleting file: ' + filename);
|
||||
try {
|
||||
fs.unlinkSync(filename);
|
||||
} catch (err) {
|
||||
@@ -330,7 +331,7 @@ const internalNginx = {
|
||||
* @returns String
|
||||
*/
|
||||
getFileFriendlyHostType: (host_type) => {
|
||||
return host_type.replace(/-/g, '_');
|
||||
return host_type.replace(new RegExp('-', 'g'), '_');
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -340,7 +341,7 @@ const internalNginx = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
deleteLetsEncryptRequestConfig: (certificate) => {
|
||||
const config_file = `/data/nginx/temp/letsencrypt_${certificate.id}.conf`;
|
||||
const config_file = '/data/nginx/temp/letsencrypt_' + certificate.id + '.conf';
|
||||
return new Promise((resolve/*, reject*/) => {
|
||||
internalNginx.deleteFile(config_file);
|
||||
resolve();
|
||||
@@ -355,7 +356,7 @@ const internalNginx = {
|
||||
*/
|
||||
deleteConfig: (host_type, host, delete_err_file) => {
|
||||
const config_file = internalNginx.getConfigName(internalNginx.getFileFriendlyHostType(host_type), typeof host === 'undefined' ? 0 : host.id);
|
||||
const config_file_err = `${config_file}.err`;
|
||||
const config_file_err = config_file + '.err';
|
||||
|
||||
return new Promise((resolve/*, reject*/) => {
|
||||
internalNginx.deleteFile(config_file);
|
||||
@@ -373,7 +374,7 @@ const internalNginx = {
|
||||
*/
|
||||
renameConfigAsError: (host_type, host) => {
|
||||
const config_file = internalNginx.getConfigName(internalNginx.getFileFriendlyHostType(host_type), typeof host === 'undefined' ? 0 : host.id);
|
||||
const config_file_err = `${config_file}.err`;
|
||||
const config_file_err = config_file + '.err';
|
||||
|
||||
return new Promise((resolve/*, reject*/) => {
|
||||
fs.unlink(config_file, () => {
|
||||
@@ -392,8 +393,8 @@ const internalNginx = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
bulkGenerateConfigs: (host_type, hosts) => {
|
||||
const promises = [];
|
||||
hosts.map((host) => {
|
||||
let promises = [];
|
||||
hosts.map(function (host) {
|
||||
promises.push(internalNginx.generateConfig(host_type, host));
|
||||
});
|
||||
|
||||
@@ -406,8 +407,8 @@ const internalNginx = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
bulkDeleteConfigs: (host_type, hosts) => {
|
||||
const promises = [];
|
||||
hosts.map((host) => {
|
||||
let promises = [];
|
||||
hosts.map(function (host) {
|
||||
promises.push(internalNginx.deleteConfig(host_type, host, true));
|
||||
});
|
||||
|
||||
@@ -418,12 +419,14 @@ const internalNginx = {
|
||||
* @param {string} config
|
||||
* @returns {boolean}
|
||||
*/
|
||||
advancedConfigHasDefaultLocation: (cfg) => !!cfg.match(/^(?:.*;)?\s*?location\s*?\/\s*?{/im),
|
||||
advancedConfigHasDefaultLocation: function (cfg) {
|
||||
return !!cfg.match(/^(?:.*;)?\s*?location\s*?\/\s*?{/im);
|
||||
},
|
||||
|
||||
/**
|
||||
* @returns {boolean}
|
||||
*/
|
||||
ipv6Enabled: () => {
|
||||
ipv6Enabled: function () {
|
||||
if (typeof process.env.DISABLE_IPV6 !== 'undefined') {
|
||||
const disabled = process.env.DISABLE_IPV6.toLowerCase();
|
||||
return !(disabled === 'on' || disabled === 'true' || disabled === '1' || disabled === 'yes');
|
||||
|
@@ -6,7 +6,6 @@ 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'];
|
||||
@@ -417,16 +416,16 @@ const internalProxyHost = {
|
||||
.where('is_deleted', 0)
|
||||
.groupBy('id')
|
||||
.allowGraph('[owner,access_list,certificate]')
|
||||
.orderBy(castJsonIfNeed('domain_names'), 'ASC');
|
||||
.orderBy('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' && search_query.length > 0) {
|
||||
if (typeof search_query === 'string') {
|
||||
query.where(function () {
|
||||
this.where(castJsonIfNeed('domain_names'), 'like', `%${search_query}%`);
|
||||
this.where('domain_names', 'like', '%' + search_query + '%');
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -6,7 +6,6 @@ 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'];
|
||||
@@ -410,16 +409,16 @@ const internalRedirectionHost = {
|
||||
.where('is_deleted', 0)
|
||||
.groupBy('id')
|
||||
.allowGraph('[owner,certificate]')
|
||||
.orderBy(castJsonIfNeed('domain_names'), 'ASC');
|
||||
.orderBy('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' && search_query.length > 0) {
|
||||
if (typeof search_query === 'string') {
|
||||
query.where(function () {
|
||||
this.where(castJsonIfNeed('domain_names'), 'like', `%${search_query}%`);
|
||||
this.where('domain_names', 'like', '%' + search_query + '%');
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -1,15 +1,12 @@
|
||||
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');
|
||||
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');
|
||||
|
||||
function omissions () {
|
||||
return ['is_deleted', 'owner.is_deleted', 'certificate.is_deleted'];
|
||||
return ['is_deleted'];
|
||||
}
|
||||
|
||||
const internalStream = {
|
||||
@@ -20,12 +17,6 @@ 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
|
||||
@@ -35,44 +26,16 @@ 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_no_domains)
|
||||
.insertAndFetch(data)
|
||||
.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 row;
|
||||
return internalStream.get(access, {id: row.id, expand: ['owner']});
|
||||
});
|
||||
})
|
||||
.then((row) => {
|
||||
@@ -96,12 +59,6 @@ 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
|
||||
@@ -113,32 +70,16 @@ 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, {
|
||||
@@ -151,17 +92,6 @@ 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());
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
@@ -184,7 +114,7 @@ const internalStream = {
|
||||
.query()
|
||||
.where('is_deleted', 0)
|
||||
.andWhere('id', data.id)
|
||||
.allowGraph('[owner,certificate]')
|
||||
.allowGraph('[owner]')
|
||||
.first();
|
||||
|
||||
if (access_data.permission_visibility !== 'all') {
|
||||
@@ -201,7 +131,6 @@ 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);
|
||||
@@ -267,14 +196,14 @@ const internalStream = {
|
||||
.then(() => {
|
||||
return internalStream.get(access, {
|
||||
id: data.id,
|
||||
expand: ['certificate', 'owner']
|
||||
expand: ['owner']
|
||||
});
|
||||
})
|
||||
.then((row) => {
|
||||
if (!row || !row.id) {
|
||||
throw new error.ItemNotFoundError(data.id);
|
||||
} else if (row.enabled) {
|
||||
throw new error.ValidationError('Stream is already enabled');
|
||||
throw new error.ValidationError('Host is already enabled');
|
||||
}
|
||||
|
||||
row.enabled = 1;
|
||||
@@ -320,7 +249,7 @@ const internalStream = {
|
||||
if (!row || !row.id) {
|
||||
throw new error.ItemNotFoundError(data.id);
|
||||
} else if (!row.enabled) {
|
||||
throw new error.ValidationError('Stream is already disabled');
|
||||
throw new error.ValidationError('Host is already disabled');
|
||||
}
|
||||
|
||||
row.enabled = 0;
|
||||
@@ -364,11 +293,11 @@ const internalStream = {
|
||||
getAll: (access, expand, search_query) => {
|
||||
return access.can('streams:list')
|
||||
.then((access_data) => {
|
||||
const query = streamModel
|
||||
let query = streamModel
|
||||
.query()
|
||||
.where('is_deleted', 0)
|
||||
.groupBy('id')
|
||||
.allowGraph('[owner,certificate]')
|
||||
.allowGraph('[owner]')
|
||||
.orderBy('incoming_port', 'ASC');
|
||||
|
||||
if (access_data.permission_visibility !== 'all') {
|
||||
@@ -376,9 +305,9 @@ const internalStream = {
|
||||
}
|
||||
|
||||
// Query is used for searching
|
||||
if (typeof search_query === 'string' && search_query.length > 0) {
|
||||
if (typeof search_query === 'string') {
|
||||
query.where(function () {
|
||||
this.where(castJsonIfNeed('incoming_port'), 'like', `%${search_query}%`);
|
||||
this.where('incoming_port', 'like', '%' + search_query + '%');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -387,13 +316,6 @@ 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;
|
||||
});
|
||||
},
|
||||
|
||||
@@ -405,9 +327,9 @@ const internalStream = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getCount: (user_id, visibility) => {
|
||||
const query = streamModel
|
||||
let query = streamModel
|
||||
.query()
|
||||
.count('id AS count')
|
||||
.count('id as count')
|
||||
.where('is_deleted', 0);
|
||||
|
||||
if (visibility !== 'all') {
|
||||
|
@@ -5,8 +5,6 @@ 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 = {
|
||||
|
||||
/**
|
||||
@@ -71,15 +69,15 @@ module.exports = {
|
||||
};
|
||||
});
|
||||
} else {
|
||||
throw new error.AuthError(ERROR_MESSAGE_INVALID_AUTH);
|
||||
throw new error.AuthError('Invalid password');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
throw new error.AuthError(ERROR_MESSAGE_INVALID_AUTH);
|
||||
throw new error.AuthError('No password auth for user');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
throw new error.AuthError(ERROR_MESSAGE_INVALID_AUTH);
|
||||
throw new error.AuthError('No relevant user found');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
@@ -11,7 +11,7 @@ const certbot = {
|
||||
/**
|
||||
* @param {array} pluginKeys
|
||||
*/
|
||||
installPlugins: async (pluginKeys) => {
|
||||
installPlugins: async function (pluginKeys) {
|
||||
let hasErrors = false;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -21,7 +21,7 @@ const certbot = {
|
||||
}
|
||||
|
||||
batchflow(pluginKeys).sequential()
|
||||
.each((_i, pluginKey, next) => {
|
||||
.each((i, pluginKey, next) => {
|
||||
certbot.installPlugin(pluginKey)
|
||||
.then(() => {
|
||||
next();
|
||||
@@ -51,7 +51,7 @@ const certbot = {
|
||||
* @param {string} pluginKey
|
||||
* @returns {Object}
|
||||
*/
|
||||
installPlugin: async (pluginKey) => {
|
||||
installPlugin: async function (pluginKey) {
|
||||
if (typeof dnsPlugins[pluginKey] === 'undefined') {
|
||||
// throw Error(`Certbot plugin ${pluginKey} not found`);
|
||||
throw new error.ItemNotFoundError(pluginKey);
|
||||
@@ -63,15 +63,8 @@ const certbot = {
|
||||
plugin.version = plugin.version.replace(/{{certbot-version}}/g, CERTBOT_VERSION_REPLACEMENT);
|
||||
plugin.dependencies = plugin.dependencies.replace(/{{certbot-version}}/g, CERTBOT_VERSION_REPLACEMENT);
|
||||
|
||||
// SETUPTOOLS_USE_DISTUTILS is required for certbot plugins to install correctly
|
||||
// in new versions of Python
|
||||
let env = Object.assign({}, process.env, {SETUPTOOLS_USE_DISTUTILS: 'stdlib'});
|
||||
if (typeof plugin.env === 'object') {
|
||||
env = Object.assign(env, plugin.env);
|
||||
}
|
||||
|
||||
const cmd = `. /opt/certbot/bin/activate && pip install --no-cache-dir ${plugin.dependencies} ${plugin.package_name}${plugin.version} && deactivate`;
|
||||
return utils.exec(cmd, {env})
|
||||
const cmd = '. /opt/certbot/bin/activate && pip install --no-cache-dir ' + plugin.dependencies + ' ' + plugin.package_name + plugin.version + ' ' + ' && deactivate';
|
||||
return utils.exec(cmd)
|
||||
.then((result) => {
|
||||
logger.complete(`Installed ${pluginKey}`);
|
||||
return result;
|
||||
|
@@ -2,10 +2,7 @@ const fs = require('fs');
|
||||
const NodeRSA = require('node-rsa');
|
||||
const logger = require('../logger').global;
|
||||
|
||||
const keysFile = '/data/keys.json';
|
||||
const mysqlEngine = 'mysql2';
|
||||
const postgresEngine = 'pg';
|
||||
const sqliteClientName = 'sqlite3';
|
||||
const keysFile = '/data/keys.json';
|
||||
|
||||
let instance = null;
|
||||
|
||||
@@ -17,7 +14,7 @@ const configure = () => {
|
||||
let configData;
|
||||
try {
|
||||
configData = require(filename);
|
||||
} catch (_) {
|
||||
} catch (err) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@@ -37,7 +34,7 @@ const configure = () => {
|
||||
logger.info('Using MySQL configuration');
|
||||
instance = {
|
||||
database: {
|
||||
engine: mysqlEngine,
|
||||
engine: 'mysql2',
|
||||
host: envMysqlHost,
|
||||
port: process.env.DB_MYSQL_PORT || 3306,
|
||||
user: envMysqlUser,
|
||||
@@ -49,33 +46,13 @@ 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: sqliteClientName,
|
||||
client: 'sqlite3',
|
||||
connection: {
|
||||
filename: envSqliteFile
|
||||
},
|
||||
@@ -166,27 +143,7 @@ module.exports = {
|
||||
*/
|
||||
isSqlite: function () {
|
||||
instance === null && configure();
|
||||
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;
|
||||
return instance.database.knex && instance.database.knex.client === 'sqlite3';
|
||||
},
|
||||
|
||||
/**
|
||||
|
@@ -1,6 +1,4 @@
|
||||
const moment = require('moment');
|
||||
const {isPostgres} = require('./config');
|
||||
const {ref} = require('objection');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = {
|
||||
|
||||
@@ -47,16 +45,6 @@ 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;
|
||||
}
|
||||
|
||||
};
|
||||
|
@@ -1,13 +1,13 @@
|
||||
const _ = require('lodash');
|
||||
const exec = require('node:child_process').exec;
|
||||
const execFile = require('node:child_process').execFile;
|
||||
const exec = require('child_process').exec;
|
||||
const execFile = require('child_process').execFile;
|
||||
const { Liquid } = require('liquidjs');
|
||||
const logger = require('../logger').global;
|
||||
const error = require('./error');
|
||||
|
||||
module.exports = {
|
||||
|
||||
exec: async (cmd, options = {}) => {
|
||||
exec: async function(cmd, options = {}) {
|
||||
logger.debug('CMD:', cmd);
|
||||
|
||||
const { stdout, stderr } = await new Promise((resolve, reject) => {
|
||||
@@ -29,19 +29,15 @@ module.exports = {
|
||||
/**
|
||||
* @param {String} cmd
|
||||
* @param {Array} args
|
||||
* @param {Object|undefined} options
|
||||
* @returns {Promise}
|
||||
*/
|
||||
execFile: (cmd, args, options) => {
|
||||
logger.debug(`CMD: ${cmd} ${args ? args.join(' ') : ''}`);
|
||||
if (typeof options === 'undefined') {
|
||||
options = {};
|
||||
}
|
||||
execFile: function (cmd, args) {
|
||||
// logger.debug('CMD: ' + cmd + ' ' + (args ? args.join(' ') : ''));
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
execFile(cmd, args, options, (err, stdout, stderr) => {
|
||||
execFile(cmd, args, function (err, stdout, /*stderr*/) {
|
||||
if (err && typeof err === 'object') {
|
||||
reject(new error.CommandError(stderr, 1, err));
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(stdout.trim());
|
||||
}
|
||||
@@ -55,7 +51,7 @@ module.exports = {
|
||||
* @param {Array} omissions
|
||||
* @returns {Function}
|
||||
*/
|
||||
omitRow: (omissions) => {
|
||||
omitRow: function (omissions) {
|
||||
/**
|
||||
* @param {Object} row
|
||||
* @returns {Object}
|
||||
@@ -71,7 +67,7 @@ module.exports = {
|
||||
* @param {Array} omissions
|
||||
* @returns {Function}
|
||||
*/
|
||||
omitRows: (omissions) => {
|
||||
omitRows: function (omissions) {
|
||||
/**
|
||||
* @param {Array} rows
|
||||
* @returns {Object}
|
||||
@@ -87,9 +83,9 @@ module.exports = {
|
||||
/**
|
||||
* @returns {Object} Liquid render engine
|
||||
*/
|
||||
getRenderEngine: () => {
|
||||
getRenderEngine: function () {
|
||||
const renderEngine = new Liquid({
|
||||
root: `${__dirname}/../templates/`
|
||||
root: __dirname + '/../templates/'
|
||||
});
|
||||
|
||||
/**
|
||||
|
41
backend/migrations/20240310085523_proxy_protocol.js
Normal file
41
backend/migrations/20240310085523_proxy_protocol.js
Normal file
@@ -0,0 +1,41 @@
|
||||
const migrate_name = 'proxy_protocol';
|
||||
const logger = require('../logger').migrate;
|
||||
|
||||
/**
|
||||
* Migrate
|
||||
*
|
||||
* @see http://knexjs.org/#Schema
|
||||
*
|
||||
* @param {Object} knex
|
||||
* @param {Promise} Promise
|
||||
* @returns {Promise}
|
||||
*/
|
||||
exports.up = function (knex/*, Promise*/) {
|
||||
logger.info('[' + migrate_name + '] Migrating Up...');
|
||||
|
||||
return knex.schema.table('proxy_host', function (proxy_host) {
|
||||
proxy_host.integer('enable_proxy_protocol').notNull().unsigned().defaultTo(0);
|
||||
proxy_host.string('load_balancer_ip').notNull().defaultTo('');
|
||||
})
|
||||
.then(() => {
|
||||
logger.info('[' + migrate_name + '] proxy_host Table altered');
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Undo Migrate
|
||||
*
|
||||
* @param {Object} knex
|
||||
* @param {Promise} Promise
|
||||
* @returns {Promise}
|
||||
*/
|
||||
exports.down = function (knex/*, Promise*/) {
|
||||
return knex.schema.table('proxy_host', function (proxy_host) {
|
||||
proxy_host.dropColumn('enable_proxy_protocol');
|
||||
proxy_host.dropColumn('load_balancer_ip');
|
||||
})
|
||||
.then(function () {
|
||||
logger.info('[' + migrate_name + '] proxy_host Table altered');
|
||||
});
|
||||
};
|
41
backend/migrations/20240310100432_proxy_protocol_streams.js
Normal file
41
backend/migrations/20240310100432_proxy_protocol_streams.js
Normal file
@@ -0,0 +1,41 @@
|
||||
const migrate_name = 'proxy_protocol_streams';
|
||||
const logger = require('../logger').migrate;
|
||||
|
||||
/**
|
||||
* Migrate
|
||||
*
|
||||
* @see http://knexjs.org/#Schema
|
||||
*
|
||||
* @param {Object} knex
|
||||
* @param {Promise} Promise
|
||||
* @returns {Promise}
|
||||
*/
|
||||
exports.up = function (knex/*, Promise*/) {
|
||||
logger.info('[' + migrate_name + '] Migrating Up...');
|
||||
|
||||
return knex.schema.table('stream', function (stream) {
|
||||
stream.integer('enable_proxy_protocol').notNull().unsigned().defaultTo(0);
|
||||
stream.string('load_balancer_ip').notNull().defaultTo('');
|
||||
})
|
||||
.then(() => {
|
||||
logger.info('[' + migrate_name + '] stream Table altered');
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Undo Migrate
|
||||
*
|
||||
* @param {Object} knex
|
||||
* @param {Promise} Promise
|
||||
* @returns {Promise}
|
||||
*/
|
||||
exports.down = function (knex/*, Promise*/) {
|
||||
return knex.schema.table('stream', function (stream) {
|
||||
stream.dropColumn('enable_proxy_protocol');
|
||||
stream.dropColumn('load_balancer_ip');
|
||||
})
|
||||
.then(function () {
|
||||
logger.info('[' + migrate_name + '] stream Table altered');
|
||||
});
|
||||
};
|
@@ -1,38 +0,0 @@
|
||||
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,6 +4,7 @@
|
||||
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);
|
||||
@@ -67,11 +68,6 @@ 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,
|
||||
@@ -83,39 +79,6 @@ 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,11 +12,7 @@ Model.knex(db);
|
||||
|
||||
const boolFields = [
|
||||
'is_deleted',
|
||||
'ssl_forced',
|
||||
'http2_support',
|
||||
'enabled',
|
||||
'hsts_enabled',
|
||||
'hsts_subdomains',
|
||||
];
|
||||
|
||||
class DeadHost extends Model {
|
||||
|
@@ -17,9 +17,6 @@ const boolFields = [
|
||||
'preserve_path',
|
||||
'ssl_forced',
|
||||
'block_exploits',
|
||||
'hsts_enabled',
|
||||
'hsts_subdomains',
|
||||
'http2_support',
|
||||
];
|
||||
|
||||
class RedirectionHost extends Model {
|
||||
|
@@ -1,15 +1,16 @@
|
||||
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');
|
||||
// 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');
|
||||
|
||||
Model.knex(db);
|
||||
|
||||
const boolFields = [
|
||||
'is_deleted',
|
||||
'enabled',
|
||||
'tcp_forwarding',
|
||||
'udp_forwarding',
|
||||
];
|
||||
@@ -63,17 +64,6 @@ 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,7 +23,6 @@
|
||||
"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"
|
||||
|
@@ -6,7 +6,7 @@ const apiValidator = require('../../lib/validator/api');
|
||||
const internalCertificate = require('../../internal/certificate');
|
||||
const schema = require('../../schema');
|
||||
|
||||
const router = express.Router({
|
||||
let router = express.Router({
|
||||
caseSensitive: true,
|
||||
strict: true,
|
||||
mergeParams: true
|
||||
@@ -231,7 +231,7 @@ router
|
||||
*/
|
||||
router
|
||||
.route('/:certificate_id/download')
|
||||
.options((_req, res) => {
|
||||
.options((req, res) => {
|
||||
res.sendStatus(204);
|
||||
})
|
||||
.all(jwtdecode())
|
||||
|
@@ -181,7 +181,7 @@ router
|
||||
return internalUser.setPassword(res.locals.access, payload);
|
||||
})
|
||||
.then((result) => {
|
||||
res.status(200)
|
||||
res.status(201)
|
||||
.send(result);
|
||||
})
|
||||
.catch(next);
|
||||
@@ -212,7 +212,7 @@ router
|
||||
return internalUser.setPermissions(res.locals.access, payload);
|
||||
})
|
||||
.then((result) => {
|
||||
res.status(200)
|
||||
res.status(201)
|
||||
.send(result);
|
||||
})
|
||||
.catch(next);
|
||||
@@ -238,7 +238,7 @@ router
|
||||
.post((req, res, next) => {
|
||||
internalUser.loginAs(res.locals.access, {id: parseInt(req.params.user_id, 10)})
|
||||
.then((result) => {
|
||||
res.status(200)
|
||||
res.status(201)
|
||||
.send(result);
|
||||
})
|
||||
.catch(next);
|
||||
|
@@ -110,11 +110,6 @@
|
||||
"caching_enabled": {
|
||||
"description": "Should we cache assets",
|
||||
"type": "boolean"
|
||||
},
|
||||
"email": {
|
||||
"description": "Email address",
|
||||
"type": "string",
|
||||
"pattern": "^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -69,7 +69,7 @@
|
||||
"type": "object"
|
||||
},
|
||||
"letsencrypt_email": {
|
||||
"$ref": "../common.json#/properties/email"
|
||||
"type": "string"
|
||||
},
|
||||
"propagation_seconds": {
|
||||
"type": "integer",
|
||||
|
@@ -22,7 +22,8 @@
|
||||
"enabled",
|
||||
"locations",
|
||||
"hsts_enabled",
|
||||
"hsts_subdomains"
|
||||
"hsts_subdomains",
|
||||
"certificate"
|
||||
],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"type": "array",
|
||||
"description": "Streams list",
|
||||
"description": "Proxy Hosts list",
|
||||
"items": {
|
||||
"$ref": "./stream-object.json"
|
||||
"$ref": "./proxy-host-object.json"
|
||||
}
|
||||
}
|
||||
|
@@ -53,24 +53,8 @@
|
||||
"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,9 +5,10 @@
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"expires": {
|
||||
"description": "Token Expiry ISO Time String",
|
||||
"example": "2025-02-04T20:40:46.340Z",
|
||||
"type": "string"
|
||||
"description": "Token Expiry Unix Time",
|
||||
"example": 1566540249,
|
||||
"minimum": 1,
|
||||
"type": "number"
|
||||
},
|
||||
"token": {
|
||||
"description": "JWT Token",
|
||||
|
@@ -49,7 +49,8 @@
|
||||
"minLength": 1
|
||||
},
|
||||
"password": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -14,7 +14,7 @@
|
||||
"description": "Expansions",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"enum": ["owner", "certificate"]
|
||||
"enum": ["access_list", "owner", "certificate"]
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -40,8 +40,7 @@
|
||||
"nginx_online": true,
|
||||
"nginx_err": null
|
||||
},
|
||||
"enabled": true,
|
||||
"certificate_id": 0
|
||||
"enabled": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@@ -32,9 +32,6 @@
|
||||
"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"
|
||||
}
|
||||
@@ -76,8 +73,7 @@
|
||||
"nickname": "Admin",
|
||||
"avatar": "",
|
||||
"roles": ["admin"]
|
||||
},
|
||||
"certificate_id": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@@ -40,8 +40,7 @@
|
||||
"nginx_online": true,
|
||||
"nginx_err": null
|
||||
},
|
||||
"enabled": true,
|
||||
"certificate_id": 0
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@@ -29,26 +29,56 @@
|
||||
"additionalProperties": false,
|
||||
"minProperties": 1,
|
||||
"properties": {
|
||||
"incoming_port": {
|
||||
"$ref": "../../../../components/stream-object.json#/properties/incoming_port"
|
||||
"domain_names": {
|
||||
"$ref": "../../../../components/proxy-host-object.json#/properties/domain_names"
|
||||
},
|
||||
"forwarding_host": {
|
||||
"$ref": "../../../../components/stream-object.json#/properties/forwarding_host"
|
||||
"forward_scheme": {
|
||||
"$ref": "../../../../components/proxy-host-object.json#/properties/forward_scheme"
|
||||
},
|
||||
"forwarding_port": {
|
||||
"$ref": "../../../../components/stream-object.json#/properties/forwarding_port"
|
||||
"forward_host": {
|
||||
"$ref": "../../../../components/proxy-host-object.json#/properties/forward_host"
|
||||
},
|
||||
"tcp_forwarding": {
|
||||
"$ref": "../../../../components/stream-object.json#/properties/tcp_forwarding"
|
||||
},
|
||||
"udp_forwarding": {
|
||||
"$ref": "../../../../components/stream-object.json#/properties/udp_forwarding"
|
||||
"forward_port": {
|
||||
"$ref": "../../../../components/proxy-host-object.json#/properties/forward_port"
|
||||
},
|
||||
"certificate_id": {
|
||||
"$ref": "../../../../components/stream-object.json#/properties/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"
|
||||
},
|
||||
"meta": {
|
||||
"$ref": "../../../../components/stream-object.json#/properties/meta"
|
||||
"$ref": "../../../../components/proxy-host-object.json#/properties/meta"
|
||||
},
|
||||
"locations": {
|
||||
"$ref": "../../../../components/proxy-host-object.json#/properties/locations"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -64,32 +94,42 @@
|
||||
"default": {
|
||||
"value": {
|
||||
"id": 1,
|
||||
"created_on": "2024-10-09T02:33:45.000Z",
|
||||
"modified_on": "2024-10-09T02:33:45.000Z",
|
||||
"created_on": "2024-10-08T23:23:03.000Z",
|
||||
"modified_on": "2024-10-08T23:26:37.000Z",
|
||||
"owner_user_id": 1,
|
||||
"incoming_port": 9090,
|
||||
"forwarding_host": "router.internal",
|
||||
"forwarding_port": 80,
|
||||
"tcp_forwarding": true,
|
||||
"udp_forwarding": false,
|
||||
"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": "",
|
||||
"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-09T02:33:16.000Z",
|
||||
"modified_on": "2024-10-09T02:33:16.000Z",
|
||||
"created_on": "2024-10-07T22:43:55.000Z",
|
||||
"modified_on": "2024-10-08T12:52:54.000Z",
|
||||
"is_deleted": false,
|
||||
"is_disabled": false,
|
||||
"email": "admin@example.com",
|
||||
"name": "Administrator",
|
||||
"nickname": "Admin",
|
||||
"avatar": "",
|
||||
"nickname": "some guy",
|
||||
"avatar": "//www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?default=mm",
|
||||
"roles": ["admin"]
|
||||
},
|
||||
"certificate_id": 0
|
||||
"certificate": null,
|
||||
"access_list": null
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@@ -15,7 +15,7 @@
|
||||
"examples": {
|
||||
"default": {
|
||||
"value": {
|
||||
"expires": "2025-02-04T20:40:46.340Z",
|
||||
"expires": 1566540510,
|
||||
"token": "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.ey...xaHKYr3Kk6MvkUjcC4"
|
||||
}
|
||||
}
|
||||
|
@@ -38,7 +38,7 @@
|
||||
"default": {
|
||||
"value": {
|
||||
"result": {
|
||||
"expires": "2025-02-04T20:40:46.340Z",
|
||||
"expires": 1566540510,
|
||||
"token": "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.ey...xaHKYr3Kk6MvkUjcC4"
|
||||
}
|
||||
}
|
||||
|
@@ -9,15 +9,6 @@
|
||||
"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('id', )
|
||||
.select(userModel.raw('COUNT(`id`) as `count`'))
|
||||
.where('is_deleted', 0)
|
||||
.first()
|
||||
.then((row) => {
|
||||
if (!row || !row.id) {
|
||||
if (!row.count) {
|
||||
// Create a new user and set password
|
||||
const email = (process.env.INITIAL_ADMIN_EMAIL || 'admin@example.com').toLowerCase();
|
||||
const password = process.env.INITIAL_ADMIN_PASSWORD || 'changeme';
|
||||
let email = process.env.INITIAL_ADMIN_EMAIL || 'admin@example.com';
|
||||
let password = process.env.INITIAL_ADMIN_PASSWORD || 'changeme';
|
||||
|
||||
logger.info('Creating a new user: ' + email + ' with password: ' + password);
|
||||
|
||||
logger.info(`Creating a new user: ${email} with password: ${password}`);
|
||||
|
||||
const data = {
|
||||
let data = {
|
||||
is_deleted: 0,
|
||||
email: email,
|
||||
name: 'Administrator',
|
||||
@@ -77,11 +77,11 @@ const setupDefaultUser = () => {
|
||||
const setupDefaultSettings = () => {
|
||||
return settingModel
|
||||
.query()
|
||||
.select('id')
|
||||
.select(settingModel.raw('COUNT(`id`) as `count`'))
|
||||
.where({id: 'default-site'})
|
||||
.first()
|
||||
.then((row) => {
|
||||
if (!row || !row.id) {
|
||||
if (!row.count) {
|
||||
settingModel
|
||||
.query()
|
||||
.insert({
|
||||
@@ -113,20 +113,20 @@ const setupCertbotPlugins = () => {
|
||||
.andWhere('provider', 'letsencrypt')
|
||||
.then((certificates) => {
|
||||
if (certificates && certificates.length) {
|
||||
const plugins = [];
|
||||
const promises = [];
|
||||
let plugins = [];
|
||||
let promises = [];
|
||||
|
||||
certificates.map((certificate) => {
|
||||
certificates.map(function (certificate) {
|
||||
if (certificate.meta && certificate.meta.dns_challenge === true) {
|
||||
if (plugins.indexOf(certificate.meta.dns_provider) === -1) {
|
||||
plugins.push(certificate.meta.dns_provider);
|
||||
}
|
||||
|
||||
// Make sure credentials file exists
|
||||
const credentials_loc = `/etc/letsencrypt/credentials/credentials-${certificate.id}`;
|
||||
const credentials_loc = '/etc/letsencrypt/credentials/credentials-' + certificate.id;
|
||||
// Escape single quotes and backslashes
|
||||
const escapedCredentials = certificate.meta.dns_provider_credentials.replaceAll('\'', '\\\'').replaceAll('\\', '\\\\');
|
||||
const credentials_cmd = `[ -f '${credentials_loc}' ] || { mkdir -p /etc/letsencrypt/credentials 2> /dev/null; echo '${escapedCredentials}' > '${credentials_loc}' && chmod 600 '${credentials_loc}'; }`;
|
||||
const credentials_cmd = '[ -f \'' + credentials_loc + '\' ] || { mkdir -p /etc/letsencrypt/credentials 2> /dev/null; echo \'' + escapedCredentials + '\' > \'' + credentials_loc + '\' && chmod 600 \'' + credentials_loc + '\'; }';
|
||||
promises.push(utils.exec(credentials_cmd));
|
||||
}
|
||||
});
|
||||
@@ -136,7 +136,7 @@ const setupCertbotPlugins = () => {
|
||||
if (promises.length) {
|
||||
return Promise.all(promises)
|
||||
.then(() => {
|
||||
logger.info(`Added Certbot plugins ${plugins.join(', ')}`);
|
||||
logger.info('Added Certbot plugins ' + plugins.join(', '));
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -165,7 +165,9 @@ const setupLogrotation = () => {
|
||||
return runLogrotate();
|
||||
};
|
||||
|
||||
module.exports = () => setupDefaultUser()
|
||||
.then(setupDefaultSettings)
|
||||
.then(setupCertbotPlugins)
|
||||
.then(setupLogrotation);
|
||||
module.exports = function () {
|
||||
return setupDefaultUser()
|
||||
.then(setupDefaultSettings)
|
||||
.then(setupCertbotPlugins)
|
||||
.then(setupLogrotation);
|
||||
};
|
||||
|
@@ -4,7 +4,7 @@
|
||||
auth_basic "Authorization required";
|
||||
auth_basic_user_file /data/access/{{ access_list_id }};
|
||||
|
||||
{% if access_list.pass_auth == 0 or access_list.pass_auth == true %}
|
||||
{% if access_list.pass_auth == 0 %}
|
||||
proxy_set_header Authorization "";
|
||||
{% endif %}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
deny all;
|
||||
|
||||
# Access checks must...
|
||||
{% if access_list.satisfy_any == 1 or access_list.satisfy_any == true %}
|
||||
{% if access_list.satisfy_any == 1 %}
|
||||
satisfy any;
|
||||
{% else %}
|
||||
satisfy all;
|
||||
|
@@ -2,7 +2,6 @@
|
||||
{% 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;
|
||||
|
@@ -1,13 +0,0 @@
|
||||
{% 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 -%}
|
@@ -1,20 +1,24 @@
|
||||
listen 80;
|
||||
{% if ipv6 -%}
|
||||
listen [::]:80;
|
||||
{% if enable_proxy_protocol == 1 or enable_proxy_protocol == true -%}
|
||||
{% assign port_number_http = "88" -%}
|
||||
{% assign port_number_https = "444" -%}
|
||||
{% assign listen_extra_args = "proxy_protocol" -%}
|
||||
{% else -%}
|
||||
#listen [::]:80;
|
||||
{% endif %}
|
||||
{% assign port_number_http = "80" -%}
|
||||
{% assign port_number_https = "443" -%}
|
||||
{% assign listen_extra_args = "" -%}
|
||||
{% endif -%}
|
||||
|
||||
listen {{ port_number_http }} {{ listen_extra_args }};
|
||||
{% if ipv6 -%}
|
||||
listen [::]:{{ port_number_http }} {{ listen_extra_args }};
|
||||
{% endif -%}
|
||||
|
||||
{% if certificate -%}
|
||||
listen 443 ssl;
|
||||
{% capture listen_extra_args_https %}ssl{% if http2_support %} http2{% endif %} {{ listen_extra_args }}{% endcapture -%}
|
||||
listen {{ port_number_https }} {{ listen_extra_args_https }};
|
||||
{% if ipv6 -%}
|
||||
listen [::]:443 ssl;
|
||||
{% else -%}
|
||||
#listen [::]:443;
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
listen [::]:{{ port_number_https }} {{ listen_extra_args_https }};
|
||||
{% endif -%}
|
||||
{% endif -%}
|
||||
|
||||
server_name {{ domain_names | join: " " }};
|
||||
{% if http2_support == 1 or http2_support == true %}
|
||||
http2 on;
|
||||
{% else -%}
|
||||
http2 off;
|
||||
{% endif %}
|
@@ -7,7 +7,11 @@
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
|
||||
proxy_pass {{ forward_scheme }}://{{ forward_host }}:{{ forward_port }}{{ forward_path }};
|
||||
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 }};
|
||||
|
||||
{% include "_access.conf" %}
|
||||
{% include "_assets.conf" %}
|
||||
|
6
backend/templates/_proxy_protocol.conf
Normal file
6
backend/templates/_proxy_protocol.conf
Normal file
@@ -0,0 +1,6 @@
|
||||
{% if enable_proxy_protocol == 1 or enable_proxy_protocol == true %}
|
||||
{% if load_balancer_ip != '' %}
|
||||
set_real_ip_from {{ load_balancer_ip }};
|
||||
real_ip_header proxy_protocol;
|
||||
{% endif %}
|
||||
{% endif %}
|
@@ -22,7 +22,5 @@ server {
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
# Custom
|
||||
include /data/nginx/custom/server_dead[.]conf;
|
||||
}
|
||||
{% endif %}
|
||||
|
@@ -15,6 +15,7 @@ server {
|
||||
{% include "_exploits.conf" %}
|
||||
{% include "_hsts.conf" %}
|
||||
{% include "_forced_ssl.conf" %}
|
||||
{% include "_proxy_protocol.conf" %}
|
||||
|
||||
{% if allow_websocket_upgrade == 1 or allow_websocket_upgrade == true %}
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
|
@@ -1,28 +1,39 @@
|
||||
# ------------------------------------------------------------
|
||||
# {{ incoming_port }} TCP: {{ tcp_forwarding }} UDP: {{ udp_forwarding }}
|
||||
# ------------------------------------------------------------
|
||||
{% if enable_proxy_protocol == 1 or enable_proxy_protocol == true -%}
|
||||
{% capture listen_extra_args %}proxy_protocol{% endcapture -%}
|
||||
{% endif -%}
|
||||
|
||||
{% if enabled %}
|
||||
{% if tcp_forwarding == 1 or tcp_forwarding == true -%}
|
||||
server {
|
||||
listen {{ incoming_port }} {%- if certificate %} ssl {%- endif %};
|
||||
{% unless ipv6 -%} # {%- endunless -%} listen [::]:{{ incoming_port }} {%- if certificate %} ssl {%- endif %};
|
||||
|
||||
{%- include "_certificates_stream.conf" %}
|
||||
listen {{ incoming_port }} {{ listen_extra_args }};
|
||||
{% if ipv6 -%}
|
||||
listen [::]:{{ incoming_port }} {{ listen_extra_args }};
|
||||
{% else -%}
|
||||
#listen [::]:{{ incoming_port }} {{ listen_extra_args }};
|
||||
{% endif %}
|
||||
|
||||
proxy_pass {{ forwarding_host }}:{{ forwarding_port }};
|
||||
|
||||
{% include '_proxy_protocol.conf' %}
|
||||
|
||||
# Custom
|
||||
include /data/nginx/custom/server_stream[.]conf;
|
||||
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 %}
|
||||
{% # Proxy Protocol is not supported for UDP %}
|
||||
{% assign listen_extra_args = "" %}
|
||||
server {
|
||||
listen {{ incoming_port }} udp;
|
||||
{% unless ipv6 -%} # {%- endunless -%} listen [::]:{{ incoming_port }} udp;
|
||||
|
||||
listen {{ incoming_port }} udp {{ listen_extra_args }};
|
||||
{% if ipv6 -%}
|
||||
listen [::]:{{ incoming_port }} udp {{ listen_extra_args }};
|
||||
{% else -%}
|
||||
#listen [::]:{{ incoming_port }} udp {{ listen_extra_args }};
|
||||
{% endif %}
|
||||
proxy_pass {{ forwarding_host }}:{{ forwarding_port }};
|
||||
|
||||
# Custom
|
||||
|
@@ -492,9 +492,9 @@ boxen@^4.2.0:
|
||||
widest-line "^3.1.0"
|
||||
|
||||
brace-expansion@^1.1.7:
|
||||
version "1.1.12"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.12.tgz#ab9b454466e5a8cc3a187beaad580412a9c5b843"
|
||||
integrity sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==
|
||||
version "1.1.11"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
|
||||
integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
|
||||
dependencies:
|
||||
balanced-match "^1.0.0"
|
||||
concat-map "0.0.1"
|
||||
@@ -830,9 +830,9 @@ crc32-stream@^4.0.2:
|
||||
readable-stream "^3.4.0"
|
||||
|
||||
cross-spawn@^7.0.2:
|
||||
version "7.0.6"
|
||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f"
|
||||
integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==
|
||||
version "7.0.3"
|
||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
|
||||
integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
|
||||
dependencies:
|
||||
path-key "^3.1.0"
|
||||
shebang-command "^2.0.0"
|
||||
@@ -2735,67 +2735,11 @@ 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"
|
||||
@@ -2814,28 +2758,6 @@ 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"
|
||||
@@ -3272,11 +3194,6 @@ 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"
|
||||
@@ -3748,11 +3665,6 @@ 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"
|
||||
|
@@ -35,7 +35,7 @@ RUN echo "fs.file-max = 65535" > /etc/sysctl.conf \
|
||||
COPY docker/scripts/install-s6 /tmp/install-s6
|
||||
RUN /tmp/install-s6 "${TARGETPLATFORM}" && rm -f /tmp/install-s6
|
||||
|
||||
EXPOSE 80 81 443
|
||||
EXPOSE 80 81 88 443 444
|
||||
|
||||
COPY backend /app
|
||||
COPY frontend/dist /app/frontend
|
||||
|
@@ -1,8 +0,0 @@
|
||||
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
|
Binary file not shown.
@@ -29,12 +29,11 @@ COPY scripts/install-s6 /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
|
||||
&& rm -f /tmp/install-s6
|
||||
|
||||
# Certs for testing purposes
|
||||
COPY --from=pebbleca /test/certs/pebble.minica.pem /etc/ssl/certs/pebble.minica.pem
|
||||
COPY --from=testca /home/step/certs/root_ca.crt /etc/ssl/certs/NginxProxyManager.crt
|
||||
|
||||
EXPOSE 80 81 443
|
||||
EXPOSE 80 81 88 443 444
|
||||
ENTRYPOINT [ "/init" ]
|
||||
|
@@ -18,7 +18,6 @@ services:
|
||||
MYSQL_DATABASE: 'npm'
|
||||
MYSQL_USER: 'npm'
|
||||
MYSQL_PASSWORD: 'npmpass'
|
||||
MARIADB_AUTO_UPGRADE: '1'
|
||||
volumes:
|
||||
- mysql_vol:/var/lib/mysql
|
||||
networks:
|
||||
|
@@ -1,78 +0,0 @@
|
||||
# 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:
|
@@ -22,10 +22,6 @@ services:
|
||||
test: ["CMD", "/usr/bin/check-health"]
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
expose:
|
||||
- '80-81/tcp'
|
||||
- '443/tcp'
|
||||
- '1500-1503/tcp'
|
||||
networks:
|
||||
fulltest:
|
||||
aliases:
|
||||
@@ -44,7 +40,7 @@ services:
|
||||
- ca.internal
|
||||
|
||||
pdns:
|
||||
image: pschiffe/pdns-mysql:4.8
|
||||
image: pschiffe/pdns-mysql
|
||||
volumes:
|
||||
- '/etc/localtime:/etc/localtime:ro'
|
||||
environment:
|
||||
@@ -101,7 +97,7 @@ services:
|
||||
HTTP_PROXY: 'squid:3128'
|
||||
HTTPS_PROXY: 'squid:3128'
|
||||
volumes:
|
||||
- 'cypress_logs:/test/results'
|
||||
- 'cypress_logs:/results'
|
||||
- './dev/resolv.conf:/etc/resolv.conf:ro'
|
||||
- '/etc/localtime:/etc/localtime:ro'
|
||||
command: cypress run --browser chrome --config-file=cypress/config/ci.js
|
||||
|
@@ -2,15 +2,17 @@
|
||||
services:
|
||||
|
||||
fullstack:
|
||||
image: npm2dev:core
|
||||
container_name: npm2dev.core
|
||||
image: nginxproxymanager:dev
|
||||
container_name: npm_core
|
||||
build:
|
||||
context: ./
|
||||
dockerfile: ./dev/Dockerfile
|
||||
ports:
|
||||
- 3080:80
|
||||
- 3081:81
|
||||
- 3088:88
|
||||
- 3443:443
|
||||
- 3444:444
|
||||
networks:
|
||||
nginx_proxy_manager:
|
||||
aliases:
|
||||
@@ -26,17 +28,11 @@ 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-postgres:
|
||||
DB_POSTGRES_HOST: 'db-postgres'
|
||||
DB_POSTGRES_PORT: '5432'
|
||||
DB_POSTGRES_USER: 'npm'
|
||||
DB_POSTGRES_PASSWORD: 'npmpass'
|
||||
DB_POSTGRES_NAME: 'npm'
|
||||
DB_MYSQL_HOST: 'db'
|
||||
DB_MYSQL_PORT: '3306'
|
||||
DB_MYSQL_USER: 'npm'
|
||||
DB_MYSQL_PASSWORD: 'npm'
|
||||
DB_MYSQL_NAME: 'npm'
|
||||
# DB_SQLITE_FILE: "/data/database.sqlite"
|
||||
# DISABLE_IPV6: "true"
|
||||
# Required for DNS Certificate provisioning testing:
|
||||
@@ -55,15 +51,11 @@ services:
|
||||
timeout: 3s
|
||||
depends_on:
|
||||
- db
|
||||
- db-postgres
|
||||
- authentik
|
||||
- authentik-worker
|
||||
- authentik-ldap
|
||||
working_dir: /app
|
||||
|
||||
db:
|
||||
image: jc21/mariadb-aria
|
||||
container_name: npm2dev.db
|
||||
container_name: npm_db
|
||||
ports:
|
||||
- 33306:3306
|
||||
networks:
|
||||
@@ -76,22 +68,8 @@ 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'
|
||||
@@ -102,7 +80,6 @@ services:
|
||||
|
||||
dnsrouter:
|
||||
image: jc21/dnsrouter
|
||||
container_name: npm2dev.dnsrouter
|
||||
volumes:
|
||||
- ./dev/dnsrouter-config.json.tmp:/dnsrouter-config.json:ro
|
||||
networks:
|
||||
@@ -110,7 +87,7 @@ services:
|
||||
|
||||
swagger:
|
||||
image: swaggerapi/swagger-ui:latest
|
||||
container_name: npm2dev.swagger
|
||||
container_name: npm_swagger
|
||||
ports:
|
||||
- 3082:80
|
||||
environment:
|
||||
@@ -121,7 +98,7 @@ services:
|
||||
|
||||
squid:
|
||||
image: ubuntu/squid
|
||||
container_name: npm2dev.squid
|
||||
container_name: npm_squid
|
||||
volumes:
|
||||
- './dev/squid.conf:/etc/squid/squid.conf:ro'
|
||||
- './dev/resolv.conf:/etc/resolv.conf:ro'
|
||||
@@ -132,8 +109,7 @@ services:
|
||||
- 8128:3128
|
||||
|
||||
pdns:
|
||||
image: pschiffe/pdns-mysql:4.8
|
||||
container_name: npm2dev.pdns
|
||||
image: pschiffe/pdns-mysql
|
||||
volumes:
|
||||
- '/etc/localtime:/etc/localtime:ro'
|
||||
environment:
|
||||
@@ -162,7 +138,6 @@ services:
|
||||
|
||||
pdns-db:
|
||||
image: mariadb
|
||||
container_name: npm2dev.pdns-db
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: 'pdns'
|
||||
MYSQL_DATABASE: 'pdns'
|
||||
@@ -176,8 +151,7 @@ services:
|
||||
- nginx_proxy_manager
|
||||
|
||||
cypress:
|
||||
image: npm2dev:cypress
|
||||
container_name: npm2dev.cypress
|
||||
image: "npm_dev_cypress"
|
||||
build:
|
||||
context: ../
|
||||
dockerfile: test/cypress/Dockerfile
|
||||
@@ -192,77 +166,16 @@ services:
|
||||
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: npm2dev_core_data
|
||||
name: npm_core_data
|
||||
le_data:
|
||||
name: npm2dev_le_data
|
||||
name: npm_le_data
|
||||
db_data:
|
||||
name: npm2dev_db_data
|
||||
name: npm_db_data
|
||||
pdns_mysql:
|
||||
name: npnpm2dev_pdns_mysql
|
||||
psql_data:
|
||||
name: npm2dev_psql_data
|
||||
redis_data:
|
||||
name: npm2dev_redis_data
|
||||
name: npm_pdns_mysql
|
||||
|
||||
networks:
|
||||
nginx_proxy_manager:
|
||||
name: npm2dev_network
|
||||
name: npm_network
|
||||
|
@@ -1,4 +1,4 @@
|
||||
location ~* ^.*\.(css|js|jpe?g|gif|png|webp|woff|woff2|eot|ttf|svg|ico|css\.map|js\.map)$ {
|
||||
location ~* ^.*\.(css|js|jpe?g|gif|png|webp|woff|eot|ttf|svg|ico|css\.map|js\.map)$ {
|
||||
if_modified_since off;
|
||||
|
||||
# use the public cache
|
||||
|
@@ -1,2 +0,0 @@
|
||||
ssl_session_timeout 5m;
|
||||
ssl_session_cache shared:SSL_stream:50m;
|
@@ -1,2 +0,0 @@
|
||||
ssl_session_timeout 5m;
|
||||
ssl_session_cache shared:SSL:50m;
|
@@ -1,3 +1,6 @@
|
||||
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,53 +8,21 @@ log_info 'Setting ownership ...'
|
||||
# root
|
||||
chown root /tmp/nginx
|
||||
|
||||
locations=(
|
||||
"/data"
|
||||
"/etc/letsencrypt"
|
||||
"/run/nginx"
|
||||
"/tmp/nginx"
|
||||
"/var/cache/nginx"
|
||||
"/var/lib/logrotate"
|
||||
"/var/lib/nginx"
|
||||
"/var/log/nginx"
|
||||
"/etc/nginx/nginx"
|
||||
"/etc/nginx/nginx.conf"
|
||||
"/etc/nginx/conf.d"
|
||||
)
|
||||
# npm user and group
|
||||
chown -R "$PUID:$PGID" /data
|
||||
chown -R "$PUID:$PGID" /etc/letsencrypt
|
||||
chown -R "$PUID:$PGID" /run/nginx
|
||||
chown -R "$PUID:$PGID" /tmp/nginx
|
||||
chown -R "$PUID:$PGID" /var/cache/nginx
|
||||
chown -R "$PUID:$PGID" /var/lib/logrotate
|
||||
chown -R "$PUID:$PGID" /var/lib/nginx
|
||||
chown -R "$PUID:$PGID" /var/log/nginx
|
||||
|
||||
chownit() {
|
||||
local dir="$1"
|
||||
local recursive="${2:-true}"
|
||||
# Don't chown entire /etc/nginx folder as this causes crashes on some systems
|
||||
chown -R "$PUID:$PGID" /etc/nginx/nginx
|
||||
chown -R "$PUID:$PGID" /etc/nginx/nginx.conf
|
||||
chown -R "$PUID:$PGID" /etc/nginx/conf.d
|
||||
|
||||
local have
|
||||
have="$(stat -c '%u:%g' "$dir")"
|
||||
echo "- $dir ... "
|
||||
|
||||
if [ "$have" != "$PUID:$PGID" ]; then
|
||||
if [ "$recursive" = 'true' ] && [ -d "$dir" ]; then
|
||||
chown -R "$PUID:$PGID" "$dir"
|
||||
else
|
||||
chown "$PUID:$PGID" "$dir"
|
||||
fi
|
||||
echo " DONE"
|
||||
else
|
||||
echo " SKIPPED"
|
||||
fi
|
||||
}
|
||||
|
||||
for loc in "${locations[@]}"; do
|
||||
chownit "$loc"
|
||||
done
|
||||
|
||||
if [ "$(is_true "${SKIP_CERTBOT_OWNERSHIP:-}")" = '1' ]; then
|
||||
log_info 'Skipping ownership change of certbot directories'
|
||||
else
|
||||
log_info 'Changing ownership of certbot directories, this may take some time ...'
|
||||
chownit "/opt/certbot" false
|
||||
chownit "/opt/certbot/bin" false
|
||||
|
||||
# Handle all site-packages directories efficiently
|
||||
find /opt/certbot/lib -type d -name "site-packages" | while read -r SITE_PACKAGES_DIR; do
|
||||
chownit "$SITE_PACKAGES_DIR"
|
||||
done
|
||||
fi
|
||||
# Prevents errors when installing python certbot plugins when non-root
|
||||
chown "$PUID:$PGID" /opt/certbot /opt/certbot/bin
|
||||
find /opt/certbot/lib/python*/site-packages -not -user "$PUID" -execdir chown "$PUID:$PGID" {} \+
|
||||
|
@@ -5,9 +5,12 @@ set -e
|
||||
|
||||
log_info 'Dynamic resolvers ...'
|
||||
|
||||
DISABLE_IPV6=$(echo "${DISABLE_IPV6:-}" | tr '[:upper:]' '[:lower:]')
|
||||
|
||||
# Dynamically generate resolvers file, if resolver is IPv6, enclose in `[]`
|
||||
# thanks @tfmm
|
||||
if [ "$(is_true "$DISABLE_IPV6")" = '1' ]; then
|
||||
if [ "$DISABLE_IPV6" == "true" ] || [ "$DISABLE_IPV6" == "on" ] || [ "$DISABLE_IPV6" == "1" ] || [ "$DISABLE_IPV6" == "yes" ];
|
||||
then
|
||||
echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" { sub(/%.*$/,"",$2); print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf) ipv6=off valid=10s;" > /etc/nginx/conf.d/include/resolvers.conf
|
||||
else
|
||||
echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" { sub(/%.*$/,"",$2); print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf) valid=10s;" > /etc/nginx/conf.d/include/resolvers.conf
|
||||
|
@@ -8,11 +8,14 @@ set -e
|
||||
|
||||
log_info 'IPv6 ...'
|
||||
|
||||
# Lowercase
|
||||
DISABLE_IPV6=$(echo "${DISABLE_IPV6:-}" | tr '[:upper:]' '[:lower:]')
|
||||
|
||||
process_folder () {
|
||||
FILES=$(find "$1" -type f -name "*.conf")
|
||||
SED_REGEX=
|
||||
|
||||
if [ "$(is_true "$DISABLE_IPV6")" = '1' ]; then
|
||||
if [ "$DISABLE_IPV6" == "true" ] || [ "$DISABLE_IPV6" == "on" ] || [ "$DISABLE_IPV6" == "1" ] || [ "$DISABLE_IPV6" == "yes" ]; then
|
||||
# IPV6 is disabled
|
||||
echo "Disabling IPV6 in hosts in: $1"
|
||||
SED_REGEX='s/^([^#]*)listen \[::\]/\1#listen [::]/g'
|
||||
|
@@ -56,13 +56,3 @@ get_group_id () {
|
||||
getent group "$1" | cut -d: -f3
|
||||
fi
|
||||
}
|
||||
|
||||
# param $1: value
|
||||
is_true () {
|
||||
VAL=$(echo "${1:-}" | tr '[:upper:]' '[:lower:]')
|
||||
if [ "$VAL" == 'true' ] || [ "$VAL" == 'on' ] || [ "$VAL" == '1' ] || [ "$VAL" == 'yes' ]; then
|
||||
echo '1'
|
||||
else
|
||||
echo '0'
|
||||
fi
|
||||
}
|
||||
|
@@ -8,7 +8,7 @@ BLUE='\E[1;34m'
|
||||
GREEN='\E[1;32m'
|
||||
RESET='\E[0m'
|
||||
|
||||
S6_OVERLAY_VERSION=3.2.1.0
|
||||
S6_OVERLAY_VERSION=3.1.5.0
|
||||
TARGETPLATFORM=${1:-linux/amd64}
|
||||
|
||||
# Determine the correct binary file for the architecture given
|
||||
|
@@ -50,6 +50,7 @@ networks:
|
||||
Let's look at a Portainer example:
|
||||
|
||||
```yml
|
||||
version: '3.8'
|
||||
services:
|
||||
|
||||
portainer:
|
||||
@@ -91,6 +92,8 @@ 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"
|
||||
@@ -161,14 +164,6 @@ The easy fix is to add a Docker environment variable to the Nginx Proxy Manager
|
||||
DISABLE_IPV6: 'true'
|
||||
```
|
||||
|
||||
## Disabling IP Ranges Fetch
|
||||
|
||||
By default, NPM fetches IP ranges from CloudFront and Cloudflare during application startup. In environments with limited internet access or to speed up container startup, this fetch can be disabled:
|
||||
|
||||
```yml
|
||||
environment:
|
||||
IP_RANGES_FETCH_ENABLED: 'false'
|
||||
```
|
||||
|
||||
## Custom Nginx Configurations
|
||||
|
||||
@@ -189,7 +184,6 @@ 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.
|
||||
|
||||
@@ -220,6 +214,29 @@ You can customise the logrotate configuration through a mount (if your custom co
|
||||
|
||||
For reference, the default configuration can be found [here](https://github.com/NginxProxyManager/nginx-proxy-manager/blob/develop/docker/rootfs/etc/logrotate.d/nginx-proxy-manager).
|
||||
|
||||
## Enabling PROXY protocol for Proxy Hosts
|
||||
|
||||
When running NPM behind a load balancer, you might want to use the [PROXY procotol](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) to receive client information such as the source IP address (useful for banning IPs).
|
||||
|
||||
When configuring the PROXY protocol for proxy hosts, NPM uses the ports 88 for http and 444 for https traffic to allow you to decide on a per host basis whether to use the PROSY protocol.
|
||||
|
||||
To enable the PROXY protocol for your hosts you need to perform the following steps:
|
||||
|
||||
1. Expose the ports `88` (and `444` is applicable) by adjusting your `docker-compose.yml`
|
||||
2. Edit your proxy hosts to enable the PROXY protocol
|
||||
3. Edit your upstream load balancer to redirect traffic to the port `88`/`444` and enable the PROXY protocol
|
||||
|
||||
## Enabling PROXY protocol for Streams
|
||||
|
||||
When running NPM behind a load balancer, you might want to use the [PROXY procotol](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) to receive client information such as the source IP address (useful for banning IPs).
|
||||
|
||||
Keep in mind that the PROXY procotol cannot be enabled for udp endpoints.
|
||||
|
||||
To enable the PROXY protocol for streams:
|
||||
|
||||
1. Expose the desired port by adjusting you `docker-compose.yml`
|
||||
2. Edit the Stream to enable the PROXY protocol
|
||||
3. Edit your upstream load balancer to enable the PROXY protocol
|
||||
## Enabling the geoip2 module
|
||||
|
||||
To enable the geoip2 module, you can create the custom configuration file `/data/nginx/custom/root_top.conf` and include the following snippet:
|
||||
|
@@ -9,6 +9,7 @@ outline: deep
|
||||
Create a `docker-compose.yml` file:
|
||||
|
||||
```yml
|
||||
version: '3.8'
|
||||
services:
|
||||
app:
|
||||
image: 'jc21/nginx-proxy-manager:latest'
|
||||
@@ -21,7 +22,8 @@ services:
|
||||
# Add any other Stream port you want to expose
|
||||
# - '21:21' # FTP
|
||||
|
||||
#environment:
|
||||
# Uncomment the next line if you uncomment anything in the section
|
||||
# environment:
|
||||
# Uncomment this if you want to change the location of
|
||||
# the SQLite DB file within the container
|
||||
# DB_SQLITE_FILE: "/data/database.sqlite"
|
||||
@@ -53,6 +55,7 @@ are going to use.
|
||||
Here is an example of what your `docker-compose.yml` will look like when using a MariaDB container:
|
||||
|
||||
```yml
|
||||
version: '3.8'
|
||||
services:
|
||||
app:
|
||||
image: 'jc21/nginx-proxy-manager:latest'
|
||||
@@ -62,6 +65,8 @@ services:
|
||||
- '80:80' # Public HTTP Port
|
||||
- '443:443' # Public HTTPS Port
|
||||
- '81:81' # Admin Web Port
|
||||
# - '88:88' # Public HTTP Port with proxy_protocol enabled
|
||||
# - '444:444' # Public HTTPS Port with proxy_protocol enabled
|
||||
# Add any other Stream port you want to expose
|
||||
# - '21:21' # FTP
|
||||
environment:
|
||||
@@ -98,53 +103,6 @@ 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:
|
||||
@@ -181,13 +139,5 @@ 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. You can change defaults with:
|
||||
|
||||
|
||||
```
|
||||
environment:
|
||||
INITIAL_ADMIN_EMAIL: my@example.com
|
||||
INITIAL_ADMIN_PASSWORD: mypassword1
|
||||
```
|
||||
|
||||
Immediately after logging in with this default user you will be asked to modify your details and change your password.
|
||||
|
||||
|
1
docs/src/third-party/index.md
vendored
1
docs/src/third-party/index.md
vendored
@@ -12,7 +12,6 @@ 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.8"
|
||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.8.tgz#b1be3030bee36aaff18bacb375e5cce521684baf"
|
||||
integrity sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==
|
||||
version "3.3.7"
|
||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8"
|
||||
integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==
|
||||
|
||||
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.19"
|
||||
resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.19.tgz#20efd060410044b3ed555049418a5e7d1998f959"
|
||||
integrity sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==
|
||||
version "5.4.8"
|
||||
resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.8.tgz#af548ce1c211b2785478d3ba3e8da51e39a287e8"
|
||||
integrity sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==
|
||||
dependencies:
|
||||
esbuild "^0.21.3"
|
||||
postcss "^8.4.43"
|
||||
|
@@ -4,438 +4,444 @@ 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 () {
|
||||
const 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 () {
|
||||
let 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 () {
|
||||
const controller = this;
|
||||
require(['./main', './dashboard/main'], (App, View) => {
|
||||
controller.navigate('/');
|
||||
App.UI.showAppContent(new View());
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Dashboard
|
||||
*/
|
||||
showDashboard: function () {
|
||||
let controller = this;
|
||||
|
||||
/**
|
||||
* Nginx Proxy Hosts
|
||||
*/
|
||||
showNginxProxy: function () {
|
||||
if (Cache.User.isAdmin() || Cache.User.canView('proxy_hosts')) {
|
||||
const controller = this;
|
||||
require(['./main', './dashboard/main'], (App, View) => {
|
||||
controller.navigate('/');
|
||||
App.UI.showAppContent(new View());
|
||||
});
|
||||
},
|
||||
|
||||
require(['./main', './nginx/proxy/main'], (App, View) => {
|
||||
controller.navigate('/nginx/proxy');
|
||||
App.UI.showAppContent(new View());
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Nginx Proxy Hosts
|
||||
*/
|
||||
showNginxProxy: function () {
|
||||
if (Cache.User.isAdmin() || Cache.User.canView('proxy_hosts')) {
|
||||
let controller = this;
|
||||
|
||||
/**
|
||||
* 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}));
|
||||
});
|
||||
}
|
||||
},
|
||||
require(['./main', './nginx/proxy/main'], (App, View) => {
|
||||
controller.navigate('/nginx/proxy');
|
||||
App.UI.showAppContent(new View());
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 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 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 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());
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 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 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 Redirection Hosts
|
||||
*/
|
||||
showNginxRedirection: function () {
|
||||
if (Cache.User.isAdmin() || Cache.User.canView('redirection_hosts')) {
|
||||
let controller = this;
|
||||
|
||||
/**
|
||||
* 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}));
|
||||
});
|
||||
}
|
||||
},
|
||||
require(['./main', './nginx/redirection/main'], (App, View) => {
|
||||
controller.navigate('/nginx/redirection');
|
||||
App.UI.showAppContent(new View());
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 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());
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 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}));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 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}));
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 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 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}));
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Nginx Stream Hosts
|
||||
*/
|
||||
showNginxStream: function () {
|
||||
if (Cache.User.isAdmin() || Cache.User.canView('streams')) {
|
||||
let controller = this;
|
||||
|
||||
/**
|
||||
* 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());
|
||||
});
|
||||
}
|
||||
},
|
||||
require(['./main', './nginx/stream/main'], (App, View) => {
|
||||
controller.navigate('/nginx/stream');
|
||||
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}));
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 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 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}));
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 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}));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 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 Dead Hosts
|
||||
*/
|
||||
showNginxDead: function () {
|
||||
if (Cache.User.isAdmin() || Cache.User.canView('dead_hosts')) {
|
||||
let controller = this;
|
||||
|
||||
/**
|
||||
* 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());
|
||||
});
|
||||
}
|
||||
},
|
||||
require(['./main', './nginx/dead/main'], (App, View) => {
|
||||
controller.navigate('/nginx/404');
|
||||
App.UI.showAppContent(new View());
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 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 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}));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 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}));
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 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 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());
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 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 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}));
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Nginx Access
|
||||
*/
|
||||
showNginxAccess: function () {
|
||||
if (Cache.User.isAdmin() || Cache.User.canView('access_lists')) {
|
||||
let controller = this;
|
||||
|
||||
/**
|
||||
* 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}));
|
||||
});
|
||||
}
|
||||
},
|
||||
require(['./main', './nginx/access/main'], (App, View) => {
|
||||
controller.navigate('/nginx/access');
|
||||
App.UI.showAppContent(new View());
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 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}));
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 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 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}));
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 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}));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Nginx Certificates
|
||||
*/
|
||||
showNginxCertificates: function () {
|
||||
if (Cache.User.isAdmin() || Cache.User.canView('certificates')) {
|
||||
let controller = this;
|
||||
|
||||
/**
|
||||
* 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}));
|
||||
});
|
||||
}
|
||||
},
|
||||
require(['./main', './nginx/certificates/main'], (App, View) => {
|
||||
controller.navigate('/nginx/certificates');
|
||||
App.UI.showAppContent(new View());
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 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 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 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}));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Logout
|
||||
*/
|
||||
logout: function () {
|
||||
Tokens.dropTopToken();
|
||||
this.showLogin();
|
||||
}
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
};
|
||||
|
@@ -6,85 +6,87 @@ 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 () {
|
||||
const view = this;
|
||||
templateContext: function () {
|
||||
let 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 () {
|
||||
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);
|
||||
});
|
||||
}
|
||||
},
|
||||
onRender: function () {
|
||||
let view = this;
|
||||
|
||||
/**
|
||||
* @param {Object} [model]
|
||||
*/
|
||||
preRender: function (model) {
|
||||
this.columns = 0;
|
||||
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);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// calculate the available columns based on permissions for the objects
|
||||
// and store as a variable
|
||||
const perms = ['proxy_hosts', 'redirection_hosts', 'streams', 'dead_hosts'];
|
||||
/**
|
||||
* @param {Object} [model]
|
||||
*/
|
||||
preRender: function (model) {
|
||||
this.columns = 0;
|
||||
|
||||
perms.map(perm => {
|
||||
this.columns += Cache.User.isAdmin() || Cache.User.canView(perm) ? 1 : 0;
|
||||
});
|
||||
// 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'];
|
||||
|
||||
// Prevent double rendering on initial calls
|
||||
if (typeof model !== 'undefined') {
|
||||
this.render();
|
||||
}
|
||||
},
|
||||
perms.map(perm => {
|
||||
this.columns += Cache.User.isAdmin() || Cache.User.canView(perm) ? 1 : 0;
|
||||
});
|
||||
|
||||
initialize: function () {
|
||||
this.preRender();
|
||||
this.listenTo(Cache.User, 'change', this.preRender);
|
||||
}
|
||||
// Prevent double rendering on initial calls
|
||||
if (typeof model !== 'undefined') {
|
||||
this.render();
|
||||
}
|
||||
},
|
||||
|
||||
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 && 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 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>
|
||||
</td>
|
||||
<td>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<td class="text-center">
|
||||
<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 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>
|
||||
</td>
|
||||
<td>
|
||||
@@ -33,13 +33,6 @@
|
||||
<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">
|
||||
@@ -55,14 +48,7 @@
|
||||
<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,24 +44,14 @@ module.exports = Mn.View.extend({
|
||||
},
|
||||
},
|
||||
|
||||
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;
|
||||
}, []);
|
||||
}
|
||||
};
|
||||
templateContext: {
|
||||
canManage: App.Cache.User.canManage('certificates'),
|
||||
isExpired: function () {
|
||||
return moment(this.expires_on).isBefore(moment());
|
||||
},
|
||||
dns_providers: dns_providers
|
||||
},
|
||||
|
||||
|
||||
initialize: function () {
|
||||
this.listenTo(this.model, 'change', this.render);
|
||||
}
|
||||
|
@@ -3,7 +3,6 @@
|
||||
<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','proxy_hosts', 'dead_hosts', 'redirection_hosts'], query)
|
||||
this.fetch(['owner'], 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','proxy_hosts', 'dead_hosts', 'redirection_hosts'])
|
||||
view.fetch(['owner'])
|
||||
.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 && 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 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>
|
||||
</td>
|
||||
<td>
|
||||
|
@@ -72,7 +72,7 @@
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="col-sm-6 col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="custom-switch">
|
||||
<input type="checkbox" class="custom-switch-input" name="allow_websocket_upgrade" value="1"<%- allow_websocket_upgrade ? ' checked' : '' %>>
|
||||
@@ -81,6 +81,22 @@
|
||||
</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="enable_proxy_protocol" value="1"<%- enable_proxy_protocol ? ' checked' : '' %>>
|
||||
<span class="custom-switch-indicator"></span>
|
||||
<span class="custom-switch-description"><%- i18n('proxy-hosts', 'enable-proxy-protocol') %><a href="https://docs.nginx.com/nginx/admin-guide/load-balancer/using-proxy-protocol/#introduction" target="_blank"><i class="fe fe-help-circle"></i></a></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('proxy-hosts', 'load-balancer-ip') %> <a href="https://docs.nginx.com/nginx/admin-guide/load-balancer/using-proxy-protocol/#changing-the-load-balancers-ip-address-to-the-client-ip-address" target="_blank"><i class="fe fe-help-circle"></i></a></label>
|
||||
<input type="text" name="load_balancer_ip" class="form-control text-monospace" placeholder="" value="<%- load_balancer_ip %>" autocomplete="off" maxlength="255" <%- enable_proxy_protocol ? '' : ' disabled' %>>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="form-group">
|
||||
|
@@ -43,6 +43,8 @@ module.exports = Mn.View.extend({
|
||||
dns_provider_credentials: 'textarea[name="meta[dns_provider_credentials]"]',
|
||||
propagation_seconds: 'input[name="meta[propagation_seconds]"]',
|
||||
forward_scheme: 'select[name="forward_scheme"]',
|
||||
enable_proxy_protocol: 'input[name="enable_proxy_protocol"]',
|
||||
load_balancer_ip: 'input[name="load_balancer_ip"]',
|
||||
letsencrypt: '.letsencrypt'
|
||||
},
|
||||
|
||||
@@ -51,6 +53,13 @@ module.exports = Mn.View.extend({
|
||||
},
|
||||
|
||||
events: {
|
||||
'change @ui.enable_proxy_protocol': function () {
|
||||
let checked = this.ui.enable_proxy_protocol.prop('checked');
|
||||
this.ui.load_balancer_ip
|
||||
.prop('disabled', !checked)
|
||||
.parents('.form-group')
|
||||
.css('opacity', checked ? 1 : 0.5);
|
||||
},
|
||||
'change @ui.certificate_select': function () {
|
||||
let id = this.ui.certificate_select.val();
|
||||
if (id === 'new') {
|
||||
@@ -163,6 +172,7 @@ module.exports = Mn.View.extend({
|
||||
data.block_exploits = !!data.block_exploits;
|
||||
data.caching_enabled = !!data.caching_enabled;
|
||||
data.allow_websocket_upgrade = !!data.allow_websocket_upgrade;
|
||||
data.enable_proxy_protocol = !!data.enable_proxy_protocol;
|
||||
data.http2_support = !!data.http2_support;
|
||||
data.hsts_enabled = !!data.hsts_enabled;
|
||||
data.hsts_subdomains = !!data.hsts_subdomains;
|
||||
@@ -264,6 +274,7 @@ module.exports = Mn.View.extend({
|
||||
onRender: function () {
|
||||
let view = this;
|
||||
|
||||
this.ui.enable_proxy_protocol.trigger('change');
|
||||
this.ui.ssl_forced.trigger('change');
|
||||
this.ui.hsts_enabled.trigger('change');
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<td class="text-center">
|
||||
<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 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>
|
||||
</td>
|
||||
<td>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<td class="text-center">
|
||||
<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 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>
|
||||
</td>
|
||||
<td>
|
||||
|
@@ -3,187 +3,64 @@
|
||||
<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 has-tabs">
|
||||
<div class="alert alert-danger mb-0 rounded-0" id="le-error-info" role="alert"></div>
|
||||
<div class="modal-body">
|
||||
<form>
|
||||
<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 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-6 col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="custom-switch">
|
||||
<input type="checkbox" class="custom-switch-input" name="enable_proxy_protocol" value="1"<%- enable_proxy_protocol ? ' checked' : '' %>>
|
||||
<span class="custom-switch-indicator"></span>
|
||||
<span class="custom-switch-description"><%- i18n('streams', 'enable-proxy-protocol') %><a href="https://docs.nginx.com/nginx/admin-guide/load-balancer/using-proxy-protocol/#introduction" target="_blank"><i class="fe fe-help-circle"></i></a></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 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 class="col-sm-12 col-md-12">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><%- i18n('streams', 'load-balancer-ip') %><a href="https://docs.nginx.com/nginx/admin-guide/load-balancer/using-proxy-protocol/#changing-the-load-balancers-ip-address-to-the-client-ip-address" target="_blank"><i class="fe fe-help-circle"></i></a></label>
|
||||
<input type="text" name="load_balancer_ip" class="form-control text-monospace" placeholder="" value="<%- load_balancer_ip %>" autocomplete="off" maxlength="255" <%- enable_proxy_protocol ? '' : ' disabled' %>>
|
||||
</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,41 +1,36 @@
|
||||
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');
|
||||
const Mn = require('backbone.marionette');
|
||||
const App = require('../../main');
|
||||
const StreamModel = require('../../../models/stream');
|
||||
const template = require('./form.ejs');
|
||||
|
||||
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',
|
||||
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'
|
||||
form: 'form',
|
||||
forwarding_host: 'input[name="forwarding_host"]',
|
||||
enable_proxy_protocol: 'input[name="enable_proxy_protocol"]',
|
||||
load_balancer_ip: 'input[name="load_balancer_ip"]',
|
||||
type_error: '.forward-type-error',
|
||||
buttons: '.modal-footer button',
|
||||
switches: '.custom-switch-input',
|
||||
cancel: 'button.cancel',
|
||||
save: 'button.save'
|
||||
},
|
||||
|
||||
events: {
|
||||
'change @ui.enable_proxy_protocol': function () {
|
||||
let checked = this.ui.enable_proxy_protocol.prop('checked');
|
||||
this.ui.load_balancer_ip
|
||||
.prop('disabled', !checked)
|
||||
.parents('.form-group')
|
||||
.css('opacity', checked ? 1 : 0.5);
|
||||
},
|
||||
'change @ui.switches': function () {
|
||||
this.ui.type_error.hide();
|
||||
},
|
||||
@@ -61,35 +56,7 @@ module.exports = Mn.View.extend({
|
||||
data.forwarding_port = parseInt(data.forwarding_port, 10);
|
||||
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);
|
||||
}
|
||||
data.enable_proxy_protocol = !!data.enable_proxy_protocol;
|
||||
|
||||
let method = App.Api.Nginx.Streams.create;
|
||||
let is_new = true;
|
||||
@@ -113,108 +80,14 @@ module.exports = Mn.View.extend({
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
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();
|
||||
alert(err.message);
|
||||
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'));
|
||||
}
|
||||
});
|
||||
this.ui.enable_proxy_protocol.trigger('change');
|
||||
},
|
||||
|
||||
initialize: function (options) {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<td class="text-center">
|
||||
<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 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>
|
||||
</td>
|
||||
<td>
|
||||
@@ -16,10 +16,7 @@
|
||||
</td>
|
||||
<td>
|
||||
<div>
|
||||
<% if (certificate) { %>
|
||||
<span class="tag"><%- i18n('streams', 'tcp+ssl') %></span>
|
||||
<% }
|
||||
else if (tcp_forwarding) { %>
|
||||
<% if (tcp_forwarding) { %>
|
||||
<span class="tag"><%- i18n('streams', 'tcp') %></span>
|
||||
<% }
|
||||
if (udp_forwarding) { %>
|
||||
@@ -27,9 +24,6 @@
|
||||
<% } %>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div><%- certificate && certificate_id ? i18n('ssl', certificate.provider) : i18n('all-hosts', 'none') %></div>
|
||||
</td>
|
||||
<td>
|
||||
<%
|
||||
var o = isOnline();
|
||||
|
@@ -3,7 +3,6 @@
|
||||
<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', 'certificate'])
|
||||
view.fetch(['owner'])
|
||||
.then(response => {
|
||||
if (!view.isDestroyed()) {
|
||||
if (response && response.length) {
|
||||
|
@@ -1,10 +1,10 @@
|
||||
<div class="modal-content">
|
||||
<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="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>
|
||||
<div class="row">
|
||||
<div class="col-sm-6 col-md-6">
|
||||
<div class="form-group">
|
||||
@@ -49,10 +49,10 @@
|
||||
</div>
|
||||
<% } %>
|
||||
</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>
|
||||
</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>
|
||||
|
@@ -19,7 +19,7 @@ module.exports = Mn.View.extend({
|
||||
|
||||
events: {
|
||||
|
||||
'submit @ui.form': function (e) {
|
||||
'click @ui.save': function (e) {
|
||||
e.preventDefault();
|
||||
this.ui.error.hide();
|
||||
let view = this;
|
||||
|
@@ -60,7 +60,7 @@
|
||||
},
|
||||
"footer": {
|
||||
"fork-me": "Fork me on Github",
|
||||
"copy": "© 2025 <a href=\"{url}\" target=\"_blank\">jc21.com</a>.",
|
||||
"copy": "© 2024 <a href=\"{url}\" target=\"_blank\">jc21.com</a>.",
|
||||
"theme": "Theme by <a href=\"{url}\" target=\"_blank\">Tabler</a>"
|
||||
},
|
||||
"dashboard": {
|
||||
@@ -131,6 +131,8 @@
|
||||
"help-content": "A Proxy Host is the incoming endpoint for a web service that you want to forward.\nIt provides optional SSL termination for your service that might not have SSL support built in.\nProxy Hosts are the most common use for the Nginx Proxy Manager.",
|
||||
"access-list": "Access List",
|
||||
"allow-websocket-upgrade": "Websockets Support",
|
||||
"enable-proxy-protocol": "Enable Proxy Protocol",
|
||||
"load-balancer-ip": "Load balancer or TCP proxy IP / CIDR range",
|
||||
"ignore-invalid-upstream-ssl": "Ignore Invalid SSL",
|
||||
"custom-forward-host-help": "Add a path for sub-folder forwarding.\nExample: 203.0.113.25/path/",
|
||||
"search": "Search Host…"
|
||||
@@ -175,13 +177,13 @@
|
||||
"protocol": "Protocol",
|
||||
"tcp": "TCP",
|
||||
"udp": "UDP",
|
||||
"enable-proxy-protocol": "Enable Proxy Protocol",
|
||||
"load-balancer-ip": "Load balancer or TCP proxy IP / CIDR range",
|
||||
"delete": "Delete Stream",
|
||||
"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…",
|
||||
"ssl-certificate": "SSL Certificate for TCP Forwarding",
|
||||
"tcp+ssl": "TCP+SSL"
|
||||
"search": "Search Incoming Port…"
|
||||
},
|
||||
"certificates": {
|
||||
"title": "SSL Certificates",
|
||||
@@ -208,10 +210,7 @@
|
||||
"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…",
|
||||
"in-use" : "In use",
|
||||
"inactive": "Inactive",
|
||||
"active-domain_names": "Active domain names"
|
||||
"search": "Search Certificate…"
|
||||
},
|
||||
"access-lists": {
|
||||
"title": "Access Lists",
|
||||
|
@@ -19,6 +19,8 @@ const model = Backbone.Model.extend({
|
||||
hsts_subdomains: false,
|
||||
caching_enabled: false,
|
||||
allow_websocket_upgrade: false,
|
||||
enable_proxy_protocol: false,
|
||||
load_balancer_ip: '',
|
||||
block_exploits: false,
|
||||
http2_support: false,
|
||||
advanced_config: '',
|
||||
|
@@ -5,21 +5,20 @@ const model = Backbone.Model.extend({
|
||||
|
||||
defaults: function () {
|
||||
return {
|
||||
id: undefined,
|
||||
created_on: null,
|
||||
modified_on: null,
|
||||
incoming_port: null,
|
||||
forwarding_host: null,
|
||||
forwarding_port: null,
|
||||
tcp_forwarding: true,
|
||||
udp_forwarding: false,
|
||||
enabled: true,
|
||||
meta: {},
|
||||
certificate_id: 0,
|
||||
domain_names: [],
|
||||
id: undefined,
|
||||
created_on: null,
|
||||
modified_on: null,
|
||||
incoming_port: null,
|
||||
forwarding_host: null,
|
||||
forwarding_port: null,
|
||||
tcp_forwarding: true,
|
||||
udp_forwarding: false,
|
||||
enable_proxy_protocol: false,
|
||||
load_balancer_ip: "",
|
||||
enabled: true,
|
||||
meta: {},
|
||||
// The following are expansions:
|
||||
owner: null,
|
||||
certificate: null
|
||||
owner: null
|
||||
};
|
||||
}
|
||||
});
|
||||
|
@@ -26,8 +26,8 @@
|
||||
"messageformat": "^2.3.0",
|
||||
"messageformat-loader": "^0.8.1",
|
||||
"mini-css-extract-plugin": "^0.9.0",
|
||||
"moment": "^2.30.1",
|
||||
"sass": "^1.92.1",
|
||||
"moment": "^2.29.4",
|
||||
"node-sass": "^9.0.0",
|
||||
"nodemon": "^2.0.2",
|
||||
"numeral": "^2.0.6",
|
||||
"sass-loader": "^10.0.0",
|
||||
|
@@ -167,5 +167,4 @@ $pink: #f66d9b;
|
||||
|
||||
textarea.form-control.text-monospace {
|
||||
font-size: 12px;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
1497
frontend/yarn.lock
1497
frontend/yarn.lock
File diff suppressed because it is too large
Load Diff
@@ -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": "~=2.0.0",
|
||||
"version": "~=1.5.1",
|
||||
"dependencies": "",
|
||||
"credentials": "dns_active24_api_key = <identifier>\ndns_active24_secret = <secret>",
|
||||
"credentials": "dns_active24_token=\"TOKEN\"",
|
||||
"full_plugin_name": "dns-active24"
|
||||
},
|
||||
"aliyun": {
|
||||
"name": "Aliyun",
|
||||
"package_name": "certbot-dns-aliyun",
|
||||
"version": "~=2.0.0",
|
||||
"version": "~=0.38.1",
|
||||
"dependencies": "",
|
||||
"credentials": "dns_aliyun_access_key = 12345678\ndns_aliyun_access_key_secret = 1234567890abcdef1234567890abcdef",
|
||||
"full_plugin_name": "dns-aliyun"
|
||||
@@ -31,22 +31,6 @@
|
||||
"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",
|
||||
@@ -55,19 +39,11 @@
|
||||
"credentials": "# Bunny API token used by Certbot (see https://dash.bunny.net/account/settings)\ndns_bunny_api_key = xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx",
|
||||
"full_plugin_name": "dns-bunny"
|
||||
},
|
||||
"cdmon": {
|
||||
"name": "cdmon",
|
||||
"package_name": "certbot-dns-cdmon",
|
||||
"version": "~=0.4.1",
|
||||
"dependencies": "",
|
||||
"credentials": "dns_cdmon_api_key=your-cdmon-api-token\ndns_cdmon_domain=your_domain_is_optional",
|
||||
"full_plugin_name": "dns-cdmon"
|
||||
},
|
||||
"cloudflare": {
|
||||
"name": "Cloudflare",
|
||||
"package_name": "certbot-dns-cloudflare",
|
||||
"version": "=={{certbot-version}}",
|
||||
"dependencies": "acme=={{certbot-version}}",
|
||||
"dependencies": "cloudflare==2.19.* acme=={{certbot-version}}",
|
||||
"credentials": "# Cloudflare API token\ndns_cloudflare_api_token=0123456789abcdef0123456789abcdef01234567",
|
||||
"full_plugin_name": "dns-cloudflare"
|
||||
},
|
||||
@@ -106,19 +82,11 @@
|
||||
"cpanel": {
|
||||
"name": "cPanel",
|
||||
"package_name": "certbot-dns-cpanel",
|
||||
"version": "~=0.4.0",
|
||||
"version": "~=0.2.2",
|
||||
"dependencies": "",
|
||||
"credentials": "cpanel_url = https://cpanel.example.com:2083\ncpanel_username = your_username\ncpanel_password = your_password\ncpanel_token = your_api_token",
|
||||
"credentials": "cpanel_url = https://cpanel.example.com:2083\ncpanel_username = user\ncpanel_password = hunter2",
|
||||
"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",
|
||||
@@ -185,11 +153,11 @@
|
||||
},
|
||||
"domainoffensive": {
|
||||
"name": "DomainOffensive (do.de)",
|
||||
"package_name": "certbot-dns-domainoffensive",
|
||||
"version": "~=2.0.0",
|
||||
"package_name": "certbot-dns-do",
|
||||
"version": "~=0.31.0",
|
||||
"dependencies": "",
|
||||
"credentials": "dns_domainoffensive_api_token = YOUR_DO_DE_AUTH_TOKEN",
|
||||
"full_plugin_name": "dns-domainoffensive"
|
||||
"credentials": "dns_do_api_token = YOUR_DO_DE_AUTH_TOKEN",
|
||||
"full_plugin_name": "dns-do"
|
||||
},
|
||||
"domeneshop": {
|
||||
"name": "Domeneshop",
|
||||
@@ -223,14 +191,6 @@
|
||||
"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",
|
||||
@@ -241,20 +201,12 @@
|
||||
},
|
||||
"gandi": {
|
||||
"name": "Gandi Live DNS",
|
||||
"package_name": "certbot-dns-gandi",
|
||||
"version": "~=1.6.1",
|
||||
"package_name": "certbot_plugin_gandi",
|
||||
"version": "~=1.5.0",
|
||||
"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",
|
||||
@@ -295,14 +247,6 @@
|
||||
"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",
|
||||
@@ -359,14 +303,6 @@
|
||||
"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",
|
||||
@@ -396,7 +332,7 @@
|
||||
"package_name": "certbot-dns-mijn-host",
|
||||
"version": "~=0.0.4",
|
||||
"dependencies": "",
|
||||
"credentials": "dns_mijn_host_api_key=0123456789abcdef0123456789abcdef",
|
||||
"credentials": "dns-mijn-host-credentials = /etc/letsencrypt/mijnhost-credentials.ini",
|
||||
"full_plugin_name": "dns-mijn-host"
|
||||
},
|
||||
"namecheap": {
|
||||
@@ -415,14 +351,6 @@
|
||||
"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",
|
||||
@@ -466,7 +394,7 @@
|
||||
"porkbun": {
|
||||
"name": "Porkbun",
|
||||
"package_name": "certbot-dns-porkbun",
|
||||
"version": "~=0.9",
|
||||
"version": "~=0.2",
|
||||
"dependencies": "",
|
||||
"credentials": "dns_porkbun_key=your-porkbun-api-key\ndns_porkbun_secret=your-porkbun-api-secret",
|
||||
"full_plugin_name": "dns-porkbun"
|
||||
@@ -496,13 +424,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",
|
||||
@@ -511,30 +439,14 @@
|
||||
"credentials": "[default]\naws_access_key_id=AKIAIOSFODNN7EXAMPLE\naws_secret_access_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
|
||||
"full_plugin_name": "dns-route53"
|
||||
},
|
||||
"spaceship": {
|
||||
"name": "Spaceship",
|
||||
"package_name": "certbot-dns-spaceship",
|
||||
"version": "~=1.0.4",
|
||||
"dependencies": "",
|
||||
"credentials": "[spaceship]\napi_key=your_api_key\napi_secret=your_api_secret",
|
||||
"full_plugin_name": "dns-spaceship"
|
||||
},
|
||||
"strato": {
|
||||
"name": "Strato",
|
||||
"package_name": "certbot-dns-strato",
|
||||
"version": "~=0.2.2",
|
||||
"version": "~=0.2.1",
|
||||
"dependencies": "",
|
||||
"credentials": "dns_strato_username = user\ndns_strato_password = pass\n# uncomment if youre using two factor authentication:\n# dns_strato_totp_devicename = 2fa_device\n# dns_strato_totp_secret = 2fa_secret\n#\n# uncomment if domain name contains special characters\n# insert domain display name as seen on your account page here\n# dns_strato_domain_display_name = my-punicode-url.de\n#\n# if youre not using strato.de or another special endpoint you can customise it below\n# you will probably only need to adjust the host, but you can also change the complete endpoint url\n# dns_strato_custom_api_scheme = https\n# dns_strato_custom_api_host = www.strato.de\n# dns_strato_custom_api_port = 443\n# dns_strato_custom_api_path = \"/apps/CustomerService\"",
|
||||
"full_plugin_name": "dns-strato"
|
||||
},
|
||||
"selectelv2": {
|
||||
"name": "Selectel api v2",
|
||||
"package_name": "certbot-dns-selectel-api-v2",
|
||||
"version": "~=0.3.0",
|
||||
"dependencies": "",
|
||||
"credentials": "dns_selectel_api_v2_account_id = your_account_id\ndns_selectel_api_v2_project_name = your_project\ndns_selectel_api_v2_username = your_username\ndns_selectel_api_v2_password = your_password",
|
||||
"full_plugin_name": "dns-selectel-api-v2"
|
||||
},
|
||||
"timeweb": {
|
||||
"name": "Timeweb Cloud",
|
||||
"package_name": "certbot-dns-timeweb",
|
||||
@@ -575,7 +487,7 @@
|
||||
"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",
|
||||
@@ -590,13 +502,5 @@
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user