mirror of
https://github.com/NginxProxyManager/nginx-proxy-manager.git
synced 2026-09-24 19:00:20 +01:00
Merge branch 'develop' into develop
This commit is contained in:
+1
-1
@@ -62,7 +62,7 @@ app.use("/", mainRoutes);
|
||||
app.use((err, req, res, _) => {
|
||||
const payload = {
|
||||
error: {
|
||||
code: err.status,
|
||||
code: err.status || 500,
|
||||
message: err.public ? err.message : "Internal Error",
|
||||
},
|
||||
};
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"$schema": "https://biomejs.dev/schemas/2.4.5/schema.json",
|
||||
"$schema": "https://biomejs.dev/schemas/2.4.15/schema.json",
|
||||
"vcs": {
|
||||
"enabled": true,
|
||||
"clientKind": "git",
|
||||
|
||||
@@ -133,7 +133,7 @@
|
||||
"full_plugin_name": "dns-desec",
|
||||
"name": "deSEC",
|
||||
"package_name": "certbot-dns-desec",
|
||||
"version": "~=1.2.1"
|
||||
"version": "~=1.3.2"
|
||||
},
|
||||
"digitalocean": {
|
||||
"credentials": "dns_digitalocean_token = 0000111122223333444455556666777788889999aaaabbbbccccddddeeeeffff",
|
||||
@@ -232,12 +232,12 @@
|
||||
"version": "~=0.1.0"
|
||||
},
|
||||
"eurodns": {
|
||||
"credentials": "dns_eurodns_applicationId = myuser\ndns_eurodns_apiKey = mysecretpassword\ndns_eurodns_endpoint = https://rest-api.eurodns.com/user-api-gateway/proxy",
|
||||
"credentials": "dns_eurodns_applicationId = myuser\ndns_eurodns_apiKey = mysecretpassword\ndns_eurodns_endpoint = https://rest-api.eurodns.com/dns-zones/",
|
||||
"dependencies": "",
|
||||
"full_plugin_name": "dns-eurodns",
|
||||
"name": "EuroDNS",
|
||||
"package_name": "certbot-dns-eurodns",
|
||||
"version": "~=0.0.4"
|
||||
"version": "~=1.8.2"
|
||||
},
|
||||
"firstdomains": {
|
||||
"credentials": "dns_firstdomains_username = myremoteuser\ndns_firstdomains_password = verysecureremoteuserpassword",
|
||||
@@ -335,6 +335,14 @@
|
||||
"package_name": "certbot-dns-hetzner-cloud",
|
||||
"version": "~=1.0.4"
|
||||
},
|
||||
"hostinger": {
|
||||
"credentials": "dns_hostinger_api_token = 0123456789abcdef0123456789abcdef",
|
||||
"dependencies": "",
|
||||
"full_plugin_name": "dns-hostinger",
|
||||
"name": "Hostinger.com",
|
||||
"package_name": "certbot-dns-hostinger",
|
||||
"version": "~=0.1.3"
|
||||
},
|
||||
"hostingnl": {
|
||||
"credentials": "dns_hostingnl_api_key = 0123456789abcdef0123456789abcdef",
|
||||
"dependencies": "",
|
||||
@@ -351,6 +359,14 @@
|
||||
"package_name": "certbot-dns-hover",
|
||||
"version": "~=1.2.1"
|
||||
},
|
||||
"hosterby": {
|
||||
"credentials": "dns_hosterby_access_key = YOUR_ACCESS_KEY\ndns_hosterby_secret_key = YOUR_SECRET_KEY",
|
||||
"dependencies": "",
|
||||
"full_plugin_name": "dns-hosterby",
|
||||
"name": "hoster.by",
|
||||
"package_name": "certbot-dns-hosterby",
|
||||
"version": "~=0.1.0"
|
||||
},
|
||||
"infomaniak": {
|
||||
"credentials": "dns_infomaniak_token = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
|
||||
"dependencies": "",
|
||||
@@ -537,7 +553,7 @@
|
||||
},
|
||||
"powerdns": {
|
||||
"credentials": "dns_powerdns_api_url = https://api.mypowerdns.example.org\ndns_powerdns_api_key = AbCbASsd!@34",
|
||||
"dependencies": "PyYAML==5.3.1",
|
||||
"dependencies": "acme=={{certbot-version}}",
|
||||
"full_plugin_name": "dns-powerdns",
|
||||
"name": "PowerDNS",
|
||||
"package_name": "certbot-dns-powerdns",
|
||||
@@ -662,5 +678,13 @@
|
||||
"name": "ZoneEdit",
|
||||
"package_name": "certbot-dns-zoneedit",
|
||||
"version": "~=0.3.2"
|
||||
},
|
||||
"rcode0": {
|
||||
"credentials": "dns_rcode0_api_key = acme_0123456789abcdef0123456789abcdef01234567",
|
||||
"dependencies": "",
|
||||
"full_plugin_name": "dns-rcode0",
|
||||
"name": "RcodeZero",
|
||||
"package_name": "certbot-dns-rcode0",
|
||||
"version": "~=0.0.0.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,20 +48,17 @@ const internalAccessList = {
|
||||
return true;
|
||||
});
|
||||
|
||||
// Clients
|
||||
data.clients?.map((client) => {
|
||||
promises.push(
|
||||
accessListClientModel.query().insert({
|
||||
access_list_id: row.id,
|
||||
address: client.address,
|
||||
directive: client.directive,
|
||||
}),
|
||||
);
|
||||
return true;
|
||||
});
|
||||
|
||||
await Promise.all(promises);
|
||||
|
||||
// Clients
|
||||
for (const client of data.clients ?? []) {
|
||||
await accessListClientModel.query().insert({
|
||||
access_list_id: row.id,
|
||||
address: client.address,
|
||||
directive: client.directive,
|
||||
});
|
||||
}
|
||||
|
||||
// re-fetch with expansions
|
||||
const freshRow = await internalAccessList.get(
|
||||
access,
|
||||
@@ -154,25 +151,17 @@ const internalAccessList = {
|
||||
|
||||
// Check for clients and add/update/remove them
|
||||
if (typeof data.clients !== "undefined" && data.clients) {
|
||||
const clientPromises = [];
|
||||
data.clients.map((client) => {
|
||||
if (client.address) {
|
||||
clientPromises.push(
|
||||
accessListClientModel.query().insert({
|
||||
access_list_id: data.id,
|
||||
address: client.address,
|
||||
directive: client.directive,
|
||||
}),
|
||||
);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
const query = accessListClientModel.query().delete().where("access_list_id", data.id);
|
||||
await query;
|
||||
// Add new clitens
|
||||
if (clientPromises.length) {
|
||||
await Promise.all(clientPromises);
|
||||
|
||||
for (const client of data.clients) {
|
||||
if (client.address) {
|
||||
await accessListClientModel.query().insert({
|
||||
access_list_id: data.id,
|
||||
address: client.address,
|
||||
directive: client.directive,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,7 +230,7 @@ const internalAccessList = {
|
||||
|
||||
let row = await query.then(utils.omitRow(omissions()));
|
||||
|
||||
if (!row || !row.id) {
|
||||
if (!row?.id) {
|
||||
throw new errs.ItemNotFoundError(thisData.id);
|
||||
}
|
||||
if (!skipMasking && typeof row.items !== "undefined" && row.items) {
|
||||
@@ -268,7 +257,7 @@ const internalAccessList = {
|
||||
expand: ["proxy_hosts", "items", "clients"],
|
||||
});
|
||||
|
||||
if (!row || !row.id) {
|
||||
if (!row?.id) {
|
||||
throw new errs.ItemNotFoundError(data.id);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import fs from "node:fs";
|
||||
import https from "node:https";
|
||||
import path from "path";
|
||||
import archiver from "archiver";
|
||||
import { ZipArchive } from "archiver";
|
||||
import _ from "lodash";
|
||||
import moment from "moment";
|
||||
import { ProxyAgent } from "proxy-agent";
|
||||
@@ -66,7 +66,7 @@ const internalCertificate = {
|
||||
.andWhere("provider", "letsencrypt")
|
||||
.andWhere("expires_on", "<", expirationThreshold)
|
||||
.then((certificates) => {
|
||||
if (!certificates || !certificates.length) {
|
||||
if (!certificates?.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ const internalCertificate = {
|
||||
await internalCertificate.disableInUseHosts(inUseResult);
|
||||
|
||||
const user = await userModel.query().where("is_deleted", 0).andWhere("id", data.owner_user_id).first();
|
||||
if (!user || !user.email) {
|
||||
if (!user?.email) {
|
||||
throw new error.ValidationError(
|
||||
"A valid email address must be set on your user account to use Let's Encrypt",
|
||||
);
|
||||
@@ -305,7 +305,7 @@ const internalCertificate = {
|
||||
}
|
||||
|
||||
const row = await query.then(utils.omitRow(omissions()));
|
||||
if (!row || !row.id) {
|
||||
if (!row?.id) {
|
||||
throw new error.ItemNotFoundError(data.id);
|
||||
}
|
||||
// Custom omissions
|
||||
@@ -370,7 +370,7 @@ const internalCertificate = {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
zipFiles: async (source, out) => {
|
||||
const archive = archiver("zip", { zlib: { level: 9 } });
|
||||
const archive = new ZipArchive({ zlib: { level: 9 } });
|
||||
const stream = fs.createWriteStream(out);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -397,7 +397,7 @@ const internalCertificate = {
|
||||
await access.can("certificates:delete", data.id);
|
||||
const row = await internalCertificate.get(access, { id: data.id });
|
||||
|
||||
if (!row || !row.id) {
|
||||
if (!row?.id) {
|
||||
throw new error.ItemNotFoundError(data.id);
|
||||
}
|
||||
|
||||
@@ -614,7 +614,7 @@ const internalCertificate = {
|
||||
const certificate = await internalCertificate.update(access, {
|
||||
id: data.id,
|
||||
expires_on: moment(validations.certificate.dates.to, "X").format("YYYY-MM-DD HH:mm:ss"),
|
||||
domain_names: [validations.certificate.cn],
|
||||
domain_names: validations.certificate.cn ? [validations.certificate.cn] : [],
|
||||
meta: _.clone(row.meta), // Prevent the update method from changing this value that we'll use later
|
||||
});
|
||||
|
||||
@@ -683,13 +683,15 @@ const internalCertificate = {
|
||||
|
||||
try {
|
||||
const result = await utils.execFile("openssl", ["x509", "-in", certificateFile, "-subject", "-noout"]);
|
||||
|
||||
// Examples:
|
||||
// subject=CN = *.jc21.com
|
||||
// subject=CN = something.example.com
|
||||
const regex = /(?:subject=)?[^=]+=\s+(\S+)/gim;
|
||||
// subject=CN=*.jc21.com
|
||||
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].trim();
|
||||
}
|
||||
|
||||
const result2 = await utils.execFile("openssl", ["x509", "-in", certificateFile, "-issuer", "-noout"]);
|
||||
@@ -779,6 +781,7 @@ const internalCertificate = {
|
||||
|
||||
const args = [
|
||||
"certonly",
|
||||
"-n", // non-interactive
|
||||
"--config",
|
||||
letsencryptConfig,
|
||||
"--work-dir",
|
||||
@@ -834,6 +837,7 @@ const internalCertificate = {
|
||||
|
||||
const args = [
|
||||
"certonly",
|
||||
"-n", // non-interactive
|
||||
"--config",
|
||||
letsencryptConfig,
|
||||
"--work-dir",
|
||||
|
||||
@@ -206,7 +206,7 @@ const internalDeadHost = {
|
||||
}
|
||||
|
||||
const row = await query.then(utils.omitRow(omissions()));
|
||||
if (!row || !row.id) {
|
||||
if (!row?.id) {
|
||||
throw new errs.ItemNotFoundError(data.id);
|
||||
}
|
||||
// Custom omissions
|
||||
@@ -226,7 +226,7 @@ const internalDeadHost = {
|
||||
delete: async (access, data) => {
|
||||
await access.can("dead_hosts:delete", data.id)
|
||||
const row = await internalDeadHost.get(access, { id: data.id });
|
||||
if (!row || !row.id) {
|
||||
if (!row?.id) {
|
||||
throw new errs.ItemNotFoundError(data.id);
|
||||
}
|
||||
|
||||
@@ -264,7 +264,7 @@ const internalDeadHost = {
|
||||
id: data.id,
|
||||
expand: ["certificate", "owner"],
|
||||
});
|
||||
if (!row || !row.id) {
|
||||
if (!row?.id) {
|
||||
throw new errs.ItemNotFoundError(data.id);
|
||||
}
|
||||
if (row.enabled) {
|
||||
@@ -303,7 +303,7 @@ const internalDeadHost = {
|
||||
disable: async (access, data) => {
|
||||
await access.can("dead_hosts:update", data.id)
|
||||
const row = await internalDeadHost.get(access, { id: data.id });
|
||||
if (!row || !row.id) {
|
||||
if (!row?.id) {
|
||||
throw new errs.ItemNotFoundError(data.id);
|
||||
}
|
||||
if (!row.enabled) {
|
||||
|
||||
@@ -253,7 +253,7 @@ const internalProxyHost = {
|
||||
return query.then(utils.omitRow(omissions()));
|
||||
})
|
||||
.then((row) => {
|
||||
if (!row || !row.id) {
|
||||
if (!row?.id) {
|
||||
throw new errs.ItemNotFoundError(thisData.id);
|
||||
}
|
||||
const thisRow = internalHost.cleanRowCertificateMeta(row);
|
||||
@@ -279,7 +279,7 @@ const internalProxyHost = {
|
||||
return internalProxyHost.get(access, { id: data.id });
|
||||
})
|
||||
.then((row) => {
|
||||
if (!row || !row.id) {
|
||||
if (!row?.id) {
|
||||
throw new errs.ItemNotFoundError(data.id);
|
||||
}
|
||||
|
||||
@@ -327,7 +327,7 @@ const internalProxyHost = {
|
||||
});
|
||||
})
|
||||
.then((row) => {
|
||||
if (!row || !row.id) {
|
||||
if (!row?.id) {
|
||||
throw new errs.ItemNotFoundError(data.id);
|
||||
}
|
||||
if (row.enabled) {
|
||||
@@ -375,7 +375,7 @@ const internalProxyHost = {
|
||||
return internalProxyHost.get(access, { id: data.id });
|
||||
})
|
||||
.then((row) => {
|
||||
if (!row || !row.id) {
|
||||
if (!row?.id) {
|
||||
throw new errs.ItemNotFoundError(data.id);
|
||||
}
|
||||
if (!row.enabled) {
|
||||
|
||||
@@ -251,7 +251,7 @@ const internalRedirectionHost = {
|
||||
})
|
||||
.then((row) => {
|
||||
let thisRow = row;
|
||||
if (!thisRow || !thisRow.id) {
|
||||
if (!thisRow?.id) {
|
||||
throw new errs.ItemNotFoundError(thisData.id);
|
||||
}
|
||||
thisRow = internalHost.cleanRowCertificateMeta(thisRow);
|
||||
@@ -277,7 +277,7 @@ const internalRedirectionHost = {
|
||||
return internalRedirectionHost.get(access, { id: data.id });
|
||||
})
|
||||
.then((row) => {
|
||||
if (!row || !row.id) {
|
||||
if (!row?.id) {
|
||||
throw new errs.ItemNotFoundError(data.id);
|
||||
}
|
||||
|
||||
@@ -325,7 +325,7 @@ const internalRedirectionHost = {
|
||||
});
|
||||
})
|
||||
.then((row) => {
|
||||
if (!row || !row.id) {
|
||||
if (!row?.id) {
|
||||
throw new errs.ItemNotFoundError(data.id);
|
||||
}
|
||||
if (row.enabled) {
|
||||
@@ -373,7 +373,7 @@ const internalRedirectionHost = {
|
||||
return internalRedirectionHost.get(access, { id: data.id });
|
||||
})
|
||||
.then((row) => {
|
||||
if (!row || !row.id) {
|
||||
if (!row?.id) {
|
||||
throw new errs.ItemNotFoundError(data.id);
|
||||
}
|
||||
if (!row.enabled) {
|
||||
|
||||
@@ -200,7 +200,7 @@ const internalStream = {
|
||||
})
|
||||
.then((row) => {
|
||||
let thisRow = row;
|
||||
if (!thisRow || !thisRow.id) {
|
||||
if (!thisRow?.id) {
|
||||
throw new errs.ItemNotFoundError(thisData.id);
|
||||
}
|
||||
thisRow = internalHost.cleanRowCertificateMeta(thisRow);
|
||||
@@ -226,7 +226,7 @@ const internalStream = {
|
||||
return internalStream.get(access, { id: data.id });
|
||||
})
|
||||
.then((row) => {
|
||||
if (!row || !row.id) {
|
||||
if (!row?.id) {
|
||||
throw new errs.ItemNotFoundError(data.id);
|
||||
}
|
||||
|
||||
@@ -274,7 +274,7 @@ const internalStream = {
|
||||
});
|
||||
})
|
||||
.then((row) => {
|
||||
if (!row || !row.id) {
|
||||
if (!row?.id) {
|
||||
throw new errs.ItemNotFoundError(data.id);
|
||||
}
|
||||
if (row.enabled) {
|
||||
@@ -322,7 +322,7 @@ const internalStream = {
|
||||
return internalStream.get(access, { id: data.id });
|
||||
})
|
||||
.then((row) => {
|
||||
if (!row || !row.id) {
|
||||
if (!row?.id) {
|
||||
throw new errs.ItemNotFoundError(data.id);
|
||||
}
|
||||
if (!row.enabled) {
|
||||
|
||||
@@ -87,7 +87,13 @@ const internalUser = {
|
||||
}
|
||||
|
||||
return access
|
||||
.can("users:update", data.id)
|
||||
.can("users:permissions", data.id)
|
||||
.catch(() => {
|
||||
delete data.roles;
|
||||
})
|
||||
.then(() => {
|
||||
return access.can("users:update", data.id);
|
||||
})
|
||||
.then(() => {
|
||||
// Make sure that the user being updated doesn't change their email to another user that is already using it
|
||||
// 1. get user we want to update
|
||||
@@ -171,7 +177,7 @@ const internalUser = {
|
||||
return query.then(utils.omitRow(omissions()));
|
||||
})
|
||||
.then((row) => {
|
||||
if (!row || !row.id) {
|
||||
if (!row?.id) {
|
||||
throw new errs.ItemNotFoundError(thisData.id);
|
||||
}
|
||||
// Custom omissions
|
||||
|
||||
+17
-13
@@ -4,8 +4,6 @@ import { certbot as logger } from "../logger.js";
|
||||
import errs from "./error.js";
|
||||
import utils from "./utils.js";
|
||||
|
||||
const CERTBOT_VERSION_REPLACEMENT = "$(certbot --version | grep -Eo '[0-9](\\.[0-9]+)+')";
|
||||
|
||||
/**
|
||||
* Installs a cerbot plugin given the key for the object from
|
||||
* ../certbot/dns-plugins.json
|
||||
@@ -15,24 +13,32 @@ const CERTBOT_VERSION_REPLACEMENT = "$(certbot --version | grep -Eo '[0-9](\\.[0
|
||||
*/
|
||||
const installPlugin = async (pluginKey) => {
|
||||
if (typeof dnsPlugins[pluginKey] === "undefined") {
|
||||
// throw Error(`Certbot plugin ${pluginKey} not found`);
|
||||
throw new errs.ItemNotFoundError(pluginKey);
|
||||
}
|
||||
|
||||
const plugin = dnsPlugins[pluginKey];
|
||||
logger.start(`Installing ${pluginKey}...`);
|
||||
|
||||
plugin.version = plugin.version.replace(/{{certbot-version}}/g, CERTBOT_VERSION_REPLACEMENT);
|
||||
plugin.dependencies = plugin.dependencies.replace(/{{certbot-version}}/g, CERTBOT_VERSION_REPLACEMENT);
|
||||
plugin.version = plugin.version.replace(/{{certbot-version}}/g, process.env.CERTBOT_VERSION);
|
||||
plugin.dependencies = plugin.dependencies.replace(/{{certbot-version}}/g, process.env.CERTBOT_VERSION);
|
||||
|
||||
// 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" });
|
||||
// SETUPTOOLS_USE_DISTUTILS=local uses setuptools' own bundled distutils.
|
||||
// "stdlib" breaks Python 3.13+ where distutils was removed from the standard library.
|
||||
let env = Object.assign({}, process.env, { SETUPTOOLS_USE_DISTUTILS: "local" });
|
||||
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`;
|
||||
const quotedDeps = plugin.dependencies.trim()
|
||||
? plugin.dependencies
|
||||
.trim()
|
||||
.split(/\s+/)
|
||||
.filter(Boolean)
|
||||
.map((d) => `'${d}'`)
|
||||
.join(" ")
|
||||
: "";
|
||||
|
||||
const cmd = `. /opt/certbot/bin/activate && pip install --no-cache-dir ${quotedDeps} '${plugin.package_name}${plugin.version}' && deactivate`;
|
||||
return utils
|
||||
.exec(cmd, { env })
|
||||
.then((result) => {
|
||||
@@ -73,9 +79,7 @@ const installPlugins = async (pluginKeys) => {
|
||||
})
|
||||
.end(() => {
|
||||
if (hasErrors) {
|
||||
reject(
|
||||
new errs.CommandError("Some plugins failed to install. Please check the logs above", 1),
|
||||
);
|
||||
reject(new errs.CommandError("Some plugins failed to install. Please check the logs above", 1));
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
@@ -83,4 +87,4 @@ const installPlugins = async (pluginKeys) => {
|
||||
});
|
||||
};
|
||||
|
||||
export { installPlugins, installPlugin };
|
||||
export { installPlugin, installPlugins };
|
||||
|
||||
@@ -211,7 +211,7 @@ const isPostgres = () => {
|
||||
};
|
||||
|
||||
/**
|
||||
* Are we running in debug mdoe?
|
||||
* Are we running in debug mode?
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
|
||||
@@ -3,7 +3,7 @@ export default (req, res, next) => {
|
||||
res.set({
|
||||
"Access-Control-Allow-Origin": req.headers.origin,
|
||||
"Access-Control-Allow-Credentials": true,
|
||||
"Access-Control-Allow-Methods": "OPTIONS, GET, POST",
|
||||
"Access-Control-Allow-Methods": "OPTIONS, GET, POST, PUT, DELETE",
|
||||
"Access-Control-Allow-Headers":
|
||||
"Content-Type, Cache-Control, Pragma, Expires, Authorization, X-Dataset-Total, X-Dataset-Offset, X-Dataset-Limit",
|
||||
"Access-Control-Max-Age": 5 * 60,
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import chalk from "chalk";
|
||||
import { debug, express as logger } from "../../logger.js";
|
||||
|
||||
export default (req, _res, next) => {
|
||||
debug(logger, `[${chalk.yellow(req.method.toUpperCase())}] ${chalk.green(req.path)}`);
|
||||
next();
|
||||
};
|
||||
@@ -3,14 +3,12 @@ import { dirname } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { Liquid } from "liquidjs";
|
||||
import _ from "lodash";
|
||||
import { debug, global as logger } from "../logger.js";
|
||||
import errs from "./error.js";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
const exec = async (cmd, options = {}) => {
|
||||
debug(logger, "CMD:", cmd);
|
||||
const { stdout, stderr } = await new Promise((resolve, reject) => {
|
||||
const child = nodeExec(cmd, options, (isError, stdout, stderr) => {
|
||||
if (isError) {
|
||||
@@ -34,7 +32,6 @@ const exec = async (cmd, options = {}) => {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
const execFile = (cmd, args, options) => {
|
||||
debug(logger, `CMD: ${cmd} ${args ? args.join(" ") : ""}`);
|
||||
const opts = options || {};
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
@@ -10,7 +10,7 @@ const migrateName = "trust_forwarded_proto";
|
||||
* @param {Object} knex
|
||||
* @returns {Promise}
|
||||
*/
|
||||
const up = function (knex) {
|
||||
const up = (knex) => {
|
||||
logger.info(`[${migrateName}] Migrating Up...`);
|
||||
|
||||
return knex.schema
|
||||
@@ -28,7 +28,7 @@ const up = function (knex) {
|
||||
* @param {Object} knex
|
||||
* @returns {Promise}
|
||||
*/
|
||||
const down = function (knex) {
|
||||
const down = (knex) => {
|
||||
logger.info(`[${migrateName}] Migrating Down...`);
|
||||
|
||||
return knex.schema
|
||||
|
||||
@@ -15,6 +15,16 @@ Model.knex(db());
|
||||
|
||||
const boolFields = ["is_deleted"];
|
||||
|
||||
const cleanDomainNames = (domainNames) => {
|
||||
// Sort domain_names
|
||||
if (typeof domainNames !== "undefined") {
|
||||
const newDomainNames = domainNames.filter((name) => name != null);
|
||||
newDomainNames.sort();
|
||||
return newDomainNames;
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
class Certificate extends Model {
|
||||
$beforeInsert() {
|
||||
this.created_on = now();
|
||||
@@ -26,7 +36,9 @@ class Certificate extends Model {
|
||||
}
|
||||
|
||||
// Default for domain_names
|
||||
if (typeof this.domain_names === "undefined") {
|
||||
if (typeof this.domain_names !== "undefined") {
|
||||
this.domain_names = cleanDomainNames(this.domain_names);
|
||||
} else {
|
||||
this.domain_names = [];
|
||||
}
|
||||
|
||||
@@ -34,16 +46,12 @@ class Certificate extends Model {
|
||||
if (typeof this.meta === "undefined") {
|
||||
this.meta = {};
|
||||
}
|
||||
|
||||
this.domain_names.sort();
|
||||
}
|
||||
|
||||
$beforeUpdate() {
|
||||
this.modified_on = now();
|
||||
|
||||
// Sort domain_names
|
||||
if (typeof this.domain_names !== "undefined") {
|
||||
this.domain_names.sort();
|
||||
this.domain_names = cleanDomainNames(this.domain_names);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+14
-14
@@ -13,37 +13,37 @@
|
||||
"regenerate-config": "node scripts/regenerate-config"
|
||||
},
|
||||
"dependencies": {
|
||||
"@apidevtools/json-schema-ref-parser": "^15.3.1",
|
||||
"ajv": "^8.18.0",
|
||||
"archiver": "^7.0.1",
|
||||
"@apidevtools/json-schema-ref-parser": "^15.3.5",
|
||||
"ajv": "^8.20.0",
|
||||
"archiver": "^8.0.0",
|
||||
"batchflow": "^0.4.0",
|
||||
"bcrypt": "^6.0.0",
|
||||
"better-sqlite3": "^12.6.2",
|
||||
"better-sqlite3": "^12.10.0",
|
||||
"body-parser": "^2.2.2",
|
||||
"chalk": "5.6.2",
|
||||
"compression": "^1.8.1",
|
||||
"express": "^5.2.1",
|
||||
"express-fileupload": "^1.5.2",
|
||||
"gravatar": "^1.8.2",
|
||||
"jsonwebtoken": "^9.0.3",
|
||||
"knex": "3.1.0",
|
||||
"liquidjs": "10.24.0",
|
||||
"lodash": "^4.17.23",
|
||||
"knex": "3.2.10",
|
||||
"liquidjs": "10.27.0",
|
||||
"lodash": "^4.18.1",
|
||||
"moment": "^2.30.1",
|
||||
"mysql2": "^3.18.2",
|
||||
"mysql2": "^3.22.3",
|
||||
"node-rsa": "^1.1.1",
|
||||
"objection": "3.1.5",
|
||||
"otplib": "^13.3.0",
|
||||
"otplib": "^13.4.0",
|
||||
"path": "^0.12.7",
|
||||
"pg": "^8.19.0",
|
||||
"proxy-agent": "^6.5.0",
|
||||
"pg": "^8.21.0",
|
||||
"proxy-agent": "^8.0.1",
|
||||
"signale": "1.4.0",
|
||||
"sqlite3": "^5.1.7",
|
||||
"sqlite3": "^6.0.1",
|
||||
"temp-write": "^6.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@apidevtools/swagger-parser": "^12.1.0",
|
||||
"@biomejs/biome": "^2.4.5",
|
||||
"chalk": "5.6.2",
|
||||
"@biomejs/biome": "^2.4.15",
|
||||
"nodemon": "^3.1.14"
|
||||
},
|
||||
"signale": {
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import express from "express";
|
||||
import dnsPlugins from "../certbot/dns-plugins.json" with { type: "json" };
|
||||
import { installPlugin } from "../lib/certbot.js";
|
||||
import { debug, express as logger } from "../logger.js";
|
||||
|
||||
const router = express.Router({
|
||||
caseSensitive: true,
|
||||
strict: true,
|
||||
mergeParams: true,
|
||||
});
|
||||
|
||||
/**
|
||||
* ONLY AVAILABLE IN CI ENVIRONMENT!
|
||||
*/
|
||||
|
||||
/**
|
||||
* /api/ci/certbot-plugins
|
||||
*/
|
||||
router
|
||||
.route("/certbot-plugins")
|
||||
.options((_, res) => {
|
||||
res.sendStatus(204);
|
||||
})
|
||||
|
||||
// Return all certbot plugins
|
||||
.get(async (_req, res, _next) => {
|
||||
res.status(200).send(dnsPlugins);
|
||||
});
|
||||
|
||||
/**
|
||||
* /api/ci/certbot-plugins/{plugin}
|
||||
*/
|
||||
router
|
||||
.route("/certbot-plugins/:plugin")
|
||||
.options((_, res) => {
|
||||
res.sendStatus(204);
|
||||
})
|
||||
|
||||
// Install a certbot plugin
|
||||
.post(async (req, res, next) => {
|
||||
try {
|
||||
const pluginName = req.params.plugin;
|
||||
// check if plugin exists
|
||||
if (!dnsPlugins[pluginName]) {
|
||||
return res.status(404).send({
|
||||
error: "Plugin not found",
|
||||
});
|
||||
}
|
||||
|
||||
await installPlugin(pluginName);
|
||||
res.status(200).send(true);
|
||||
} catch (err) {
|
||||
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
|
||||
next(err);
|
||||
}
|
||||
return;
|
||||
});
|
||||
|
||||
export default router;
|
||||
@@ -1,8 +1,11 @@
|
||||
import express from "express";
|
||||
import { isCI } from "../lib/config.js";
|
||||
import errs from "../lib/error.js";
|
||||
import logRequest from "../lib/express/log-request.js";
|
||||
import pjson from "../package.json" with { type: "json" };
|
||||
import { isSetup } from "../setup.js";
|
||||
import auditLogRoutes from "./audit-log.js";
|
||||
import ciRoutes from "./ci.js";
|
||||
import accessListsRoutes from "./nginx/access_lists.js";
|
||||
import certificatesHostsRoutes from "./nginx/certificates.js";
|
||||
import deadHostsRoutes from "./nginx/dead_hosts.js";
|
||||
@@ -22,6 +25,8 @@ const router = express.Router({
|
||||
mergeParams: true,
|
||||
});
|
||||
|
||||
router.use(logRequest);
|
||||
|
||||
/**
|
||||
* Health Check
|
||||
* GET /api
|
||||
@@ -55,6 +60,11 @@ router.use("/nginx/streams", streamsRoutes);
|
||||
router.use("/nginx/access-lists", accessListsRoutes);
|
||||
router.use("/nginx/certificates", certificatesHostsRoutes);
|
||||
|
||||
// Only include CI routes if we're in a CI environment
|
||||
if (isCI()) {
|
||||
router.use("/ci", ciRoutes);
|
||||
}
|
||||
|
||||
/**
|
||||
* API 404 for all other routes
|
||||
*
|
||||
|
||||
@@ -18,7 +18,7 @@ router
|
||||
/**
|
||||
* GET /schema
|
||||
*/
|
||||
.get(async (req, res) => {
|
||||
.get(async (req, res, next) => {
|
||||
try {
|
||||
const swaggerJSON = await getCompiledSchema();
|
||||
|
||||
|
||||
@@ -10,11 +10,13 @@
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% if access_list.clients.length > 0 %}
|
||||
# Access Rules: {{ access_list.clients | size }} total
|
||||
{% for client in access_list.clients %}
|
||||
{{client | nginxAccessRule}}
|
||||
{% endfor %}
|
||||
deny all;
|
||||
{% endif %}
|
||||
|
||||
# Access checks must...
|
||||
{% if access_list.satisfy_any == 1 or access_list.satisfy_any == true %}
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
{% 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 %};
|
||||
listen {{ incoming_port }} reuseport {%- if certificate %} ssl {%- endif %};
|
||||
{% unless ipv6 -%} # {%- endunless -%} listen [::]:{{ incoming_port }} reuseport {%- if certificate %} ssl {%- endif %};
|
||||
|
||||
{%- include "_certificates_stream.conf" %}
|
||||
|
||||
@@ -23,8 +23,8 @@ server {
|
||||
|
||||
{% if udp_forwarding == 1 or udp_forwarding == true -%}
|
||||
server {
|
||||
listen {{ incoming_port }} udp;
|
||||
{% unless ipv6 -%} # {%- endunless -%} listen [::]:{{ incoming_port }} udp;
|
||||
listen {{ incoming_port }} udp reuseport;
|
||||
{% unless ipv6 -%} # {%- endunless -%} listen [::]:{{ incoming_port }} udp reuseport;
|
||||
|
||||
proxy_pass {{ forwarding_host }}:{{ forwarding_port }};
|
||||
|
||||
|
||||
+399
-976
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user