Compare commits

...

23 Commits

Author SHA1 Message Date
Jamie Curnow
32208f3864 More Persian lang updates 2025-11-03 08:12:52 +10:00
Jamie Curnow
52ab4844dc Persian Locale 2025-11-02 22:52:43 +10:00
jc21
24216f1f2f Merge pull request #4785 from NginxProxyManager/react
v2.13.0 React UI
2025-11-02 22:48:16 +10:00
Jamie Curnow
52e528f217 Remove incomplete languages and cleanup 2025-11-02 21:28:25 +10:00
Jamie Curnow
4709f9826c Permissions polish for restricted users 2025-10-31 12:50:54 +10:00
Jamie Curnow
74a8c5d806 Fix app crash when do unautorized things 2025-10-30 15:03:01 +10:00
Jamie Curnow
82a1a86c3a Log in as user support 2025-10-30 14:45:22 +10:00
Jamie Curnow
95957a192c Re-add dns_provider_credentials to swagger schema 2025-10-30 12:24:17 +10:00
Jamie Curnow
906ce8ced2 Swagger/openapi schema mega fixes and Cypress validation/enforcement 2025-10-30 11:50:51 +10:00
Jamie Curnow
89abb9d559 Fix bugs from feedback 2025-10-29 08:48:29 +10:00
Jamie Curnow
5d6916dcf0 Tidy up
- Add help docs for most sections
- Add translations documentation
- Fix up todos
- Remove german translation
2025-10-28 15:41:11 +10:00
Jamie Curnow
0f718570d6 Use status components for true/false things 2025-10-28 14:18:52 +10:00
Jamie Curnow
fac5f2cbc5 Cert column provider tweaks 2025-10-28 11:51:27 +10:00
Jamie Curnow
3b9beaeae5 Various tweaks and backend improvements 2025-10-28 11:38:26 +10:00
Jamie Curnow
7331cb3675 Audit log tweaks for certificates 2025-10-28 10:38:05 +10:00
Jamie Curnow
678593111e Settings polish 2025-10-28 08:53:01 +10:00
Jamie Curnow
c08b1be3cb Use code edit for dns provider config dialog 2025-10-27 19:42:58 +10:00
Jamie Curnow
ca3c9aa39a Show cert expiry date in yellow when < 30 days 2025-10-27 19:34:25 +10:00
Jamie Curnow
e4e5fb3b58 Update biome 2025-10-27 19:29:14 +10:00
Jamie Curnow
83a2c79e16 Custom certificate upload 2025-10-27 19:26:33 +10:00
Jamie Curnow
0de26f2950 Certificates react work
- renewal and download
- table columns rendering
- searching
- deleting
2025-10-27 18:08:37 +10:00
Jamie Curnow
7b5c70ed35 Fix cert renewal backend bug after refactor 2025-10-27 18:04:58 +10:00
Jamie Curnow
e4d9f48870 Fix creating wrong cert type when trying dns 2025-10-27 18:04:29 +10:00
193 changed files with 5576 additions and 1657 deletions

View File

@@ -1,5 +1,5 @@
{ {
"$schema": "https://biomejs.dev/schemas/2.2.4/schema.json", "$schema": "https://biomejs.dev/schemas/2.3.1/schema.json",
"vcs": { "vcs": {
"enabled": true, "enabled": true,
"clientKind": "git", "clientKind": "git",

View File

@@ -195,11 +195,11 @@ const internalAccessList = {
); );
await internalAccessList.build(freshRow) await internalAccessList.build(freshRow)
if (Number.parseInt(row.proxy_host_count, 10)) { if (Number.parseInt(freshRow.proxy_host_count, 10)) {
await internalNginx.bulkGenerateConfigs("proxy_host", row.proxy_hosts); await internalNginx.bulkGenerateConfigs("proxy_host", freshRow.proxy_hosts);
} }
await internalNginx.reload(); await internalNginx.reload();
return internalAccessList.maskItems(row); return internalAccessList.maskItems(freshRow);
}, },
/** /**

View File

@@ -1,9 +1,9 @@
import fs from "node:fs"; import fs from "node:fs";
import https from "node:https"; import https from "node:https";
import path from "path";
import archiver from "archiver"; import archiver from "archiver";
import _ from "lodash"; import _ from "lodash";
import moment from "moment"; import moment from "moment";
import path from "path";
import tempWrite from "temp-write"; import tempWrite from "temp-write";
import dnsPlugins from "../certbot/dns-plugins.json" with { type: "json" }; import dnsPlugins from "../certbot/dns-plugins.json" with { type: "json" };
import { installPlugin } from "../lib/certbot.js"; import { installPlugin } from "../lib/certbot.js";
@@ -20,17 +20,15 @@ import internalNginx from "./nginx.js";
const letsencryptConfig = "/etc/letsencrypt.ini"; const letsencryptConfig = "/etc/letsencrypt.ini";
const certbotCommand = "certbot"; const certbotCommand = "certbot";
const certbotLogsDir = "/data/logs";
const certbotWorkDir = "/tmp/letsencrypt-lib";
const omissions = () => { const omissions = () => {
return ["is_deleted", "owner.is_deleted"]; return ["is_deleted", "owner.is_deleted", "meta.dns_provider_credentials"];
}; };
const internalCertificate = { const internalCertificate = {
allowedSslFiles: [ allowedSslFiles: ["certificate", "certificate_key", "intermediate_certificate"],
"certificate",
"certificate_key",
"intermediate_certificate",
],
intervalTimeout: 1000 * 60 * 60, // 1 hour intervalTimeout: 1000 * 60 * 60, // 1 hour
interval: null, interval: null,
intervalProcessing: false, intervalProcessing: false,
@@ -57,10 +55,7 @@ const internalCertificate = {
); );
const expirationThreshold = moment() const expirationThreshold = moment()
.add( .add(internalCertificate.renewBeforeExpirationBy[0], internalCertificate.renewBeforeExpirationBy[1])
internalCertificate.renewBeforeExpirationBy[0],
internalCertificate.renewBeforeExpirationBy[1],
)
.format("YYYY-MM-DD HH:mm:ss"); .format("YYYY-MM-DD HH:mm:ss");
// Fetch all the letsencrypt certs from the db that will expire within the configured threshold // Fetch all the letsencrypt certs from the db that will expire within the configured threshold
@@ -127,10 +122,7 @@ const internalCertificate = {
} }
// this command really should clean up and delete the cert if it can't fully succeed // this command really should clean up and delete the cert if it can't fully succeed
const certificate = await certificateModel const certificate = await certificateModel.query().insertAndFetch(data);
.query()
.insertAndFetch(data)
.then(utils.omitRow(omissions()));
try { try {
if (certificate.provider === "letsencrypt") { if (certificate.provider === "letsencrypt") {
@@ -144,18 +136,12 @@ const internalCertificate = {
// 6. Re-instate previously disabled hosts // 6. Re-instate previously disabled hosts
// 1. Find out any hosts that are using any of the hostnames in this cert // 1. Find out any hosts that are using any of the hostnames in this cert
const inUseResult = await internalHost.getHostsWithDomains( const inUseResult = await internalHost.getHostsWithDomains(certificate.domain_names);
certificate.domain_names,
);
// 2. Disable them in nginx temporarily // 2. Disable them in nginx temporarily
await internalCertificate.disableInUseHosts(inUseResult); await internalCertificate.disableInUseHosts(inUseResult);
const user = await userModel const user = await userModel.query().where("is_deleted", 0).andWhere("id", data.owner_user_id).first();
.query()
.where("is_deleted", 0)
.andWhere("id", data.owner_user_id)
.first();
if (!user || !user.email) { if (!user || !user.email) {
throw new error.ValidationError( throw new error.ValidationError(
"A valid email address must be set on your user account to use Let's Encrypt", "A valid email address must be set on your user account to use Let's Encrypt",
@@ -167,10 +153,7 @@ const internalCertificate = {
try { try {
await internalNginx.reload(); await internalNginx.reload();
// 4. Request cert // 4. Request cert
await internalCertificate.requestLetsEncryptSslWithDnsChallenge( await internalCertificate.requestLetsEncryptSslWithDnsChallenge(certificate, user.email);
certificate,
user.email,
);
await internalNginx.reload(); await internalNginx.reload();
// 6. Re-instate previously disabled hosts // 6. Re-instate previously disabled hosts
await internalCertificate.enableInUseHosts(inUseResult); await internalCertificate.enableInUseHosts(inUseResult);
@@ -187,10 +170,7 @@ const internalCertificate = {
await internalNginx.reload(); await internalNginx.reload();
setTimeout(() => {}, 5000); setTimeout(() => {}, 5000);
// 4. Request cert // 4. Request cert
await internalCertificate.requestLetsEncryptSsl( await internalCertificate.requestLetsEncryptSsl(certificate, user.email);
certificate,
user.email,
);
// 5. Remove LE config // 5. Remove LE config
await internalNginx.deleteLetsEncryptRequestConfig(certificate); await internalNginx.deleteLetsEncryptRequestConfig(certificate);
await internalNginx.reload(); await internalNginx.reload();
@@ -214,9 +194,7 @@ const internalCertificate = {
const savedRow = await certificateModel const savedRow = await certificateModel
.query() .query()
.patchAndFetchById(certificate.id, { .patchAndFetchById(certificate.id, {
expires_on: moment(certInfo.dates.to, "X").format( expires_on: moment(certInfo.dates.to, "X").format("YYYY-MM-DD HH:mm:ss"),
"YYYY-MM-DD HH:mm:ss",
),
}) })
.then(utils.omitRow(omissions())); .then(utils.omitRow(omissions()));
@@ -224,6 +202,9 @@ const internalCertificate = {
savedRow.meta = _.assign({}, savedRow.meta, { savedRow.meta = _.assign({}, savedRow.meta, {
letsencrypt_certificate: certInfo, letsencrypt_certificate: certInfo,
}); });
await internalCertificate.addCreatedAuditLog(access, certificate.id, savedRow);
return savedRow; return savedRow;
} catch (err) { } catch (err) {
// Delete the certificate from the database if it was not created successfully // Delete the certificate from the database if it was not created successfully
@@ -240,14 +221,18 @@ const internalCertificate = {
data.meta = _.assign({}, data.meta || {}, certificate.meta); data.meta = _.assign({}, data.meta || {}, certificate.meta);
// Add to audit log // Add to audit log
await internalCertificate.addCreatedAuditLog(access, certificate.id, utils.omitRow(omissions())(data));
return utils.omitRow(omissions())(certificate);
},
addCreatedAuditLog: async (access, certificate_id, meta) => {
await internalAuditLog.add(access, { await internalAuditLog.add(access, {
action: "created", action: "created",
object_type: "certificate", object_type: "certificate",
object_id: certificate.id, object_id: certificate_id,
meta: data, meta: meta,
}); });
return certificate;
}, },
/** /**
@@ -307,10 +292,7 @@ const internalCertificate = {
.query() .query()
.where("is_deleted", 0) .where("is_deleted", 0)
.andWhere("id", data.id) .andWhere("id", data.id)
.allowGraph("[owner]") .allowGraph("[owner,proxy_hosts,redirection_hosts,dead_hosts,streams]")
.allowGraph("[proxy_hosts]")
.allowGraph("[redirection_hosts]")
.allowGraph("[dead_hosts]")
.first(); .first();
if (accessData.permission_visibility !== "all") { if (accessData.permission_visibility !== "all") {
@@ -327,7 +309,24 @@ const internalCertificate = {
} }
// Custom omissions // Custom omissions
if (typeof data.omit !== "undefined" && data.omit !== null) { if (typeof data.omit !== "undefined" && data.omit !== null) {
return _.omit(row, data.omit); return _.omit(row, [...data.omit]);
}
return internalCertificate.cleanExpansions(row);
},
cleanExpansions: (row) => {
if (typeof row.proxy_hosts !== "undefined") {
row.proxy_hosts = utils.omitRows(["is_deleted"])(row.proxy_hosts);
}
if (typeof row.redirection_hosts !== "undefined") {
row.redirection_hosts = utils.omitRows(["is_deleted"])(row.redirection_hosts);
}
if (typeof row.dead_hosts !== "undefined") {
row.dead_hosts = utils.omitRows(["is_deleted"])(row.dead_hosts);
}
if (typeof row.streams !== "undefined") {
row.streams = utils.omitRows(["is_deleted"])(row.streams);
} }
return row; return row;
}, },
@@ -344,9 +343,7 @@ const internalCertificate = {
if (certificate.provider === "letsencrypt") { if (certificate.provider === "letsencrypt") {
const zipDirectory = internalCertificate.getLiveCertPath(data.id); const zipDirectory = internalCertificate.getLiveCertPath(data.id);
if (!fs.existsSync(zipDirectory)) { if (!fs.existsSync(zipDirectory)) {
throw new error.ItemNotFoundError( throw new error.ItemNotFoundError(`Certificate ${certificate.nice_name} does not exists`);
`Certificate ${certificate.nice_name} does not exists`,
);
} }
const certFiles = fs const certFiles = fs
@@ -363,9 +360,7 @@ const internalCertificate = {
fileName: opName, fileName: opName,
}; };
} }
throw new error.ValidationError( throw new error.ValidationError("Only Let'sEncrypt certificates can be downloaded");
"Only Let'sEncrypt certificates can be downloaded",
);
}, },
/** /**
@@ -441,7 +436,7 @@ const internalCertificate = {
.query() .query()
.where("is_deleted", 0) .where("is_deleted", 0)
.groupBy("id") .groupBy("id")
.allowGraph("[owner,proxy_hosts,redirection_hosts,dead_hosts]") .allowGraph("[owner,proxy_hosts,redirection_hosts,dead_hosts,streams]")
.orderBy("nice_name", "ASC"); .orderBy("nice_name", "ASC");
if (accessData.permission_visibility !== "all") { if (accessData.permission_visibility !== "all") {
@@ -459,7 +454,11 @@ const internalCertificate = {
query.withGraphFetched(`[${expand.join(", ")}]`); query.withGraphFetched(`[${expand.join(", ")}]`);
} }
return await query.then(utils.omitRows(omissions())); const r = await query.then(utils.omitRows(omissions()));
for (let i = 0; i < r.length; i++) {
r[i] = internalCertificate.cleanExpansions(r[i]);
}
return r;
}, },
/** /**
@@ -470,10 +469,7 @@ const internalCertificate = {
* @returns {Promise} * @returns {Promise}
*/ */
getCount: async (userId, visibility) => { getCount: async (userId, visibility) => {
const query = certificateModel const query = certificateModel.query().count("id as count").where("is_deleted", 0);
.query()
.count("id as count")
.where("is_deleted", 0);
if (visibility !== "all") { if (visibility !== "all") {
query.andWhere("owner_user_id", userId); query.andWhere("owner_user_id", userId);
@@ -521,17 +517,13 @@ const internalCertificate = {
}); });
}).then(() => { }).then(() => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
fs.writeFile( fs.writeFile(`${dir}/privkey.pem`, certificate.meta.certificate_key, (err) => {
`${dir}/privkey.pem`,
certificate.meta.certificate_key,
(err) => {
if (err) { if (err) {
reject(err); reject(err);
} else { } else {
resolve(); resolve();
} }
}, });
);
}); });
}); });
}, },
@@ -604,9 +596,7 @@ const internalCertificate = {
upload: async (access, data) => { upload: async (access, data) => {
const row = await internalCertificate.get(access, { id: data.id }); const row = await internalCertificate.get(access, { id: data.id });
if (row.provider !== "other") { if (row.provider !== "other") {
throw new error.ValidationError( throw new error.ValidationError("Cannot upload certificates for this type of provider");
"Cannot upload certificates for this type of provider",
);
} }
const validations = await internalCertificate.validate(data); const validations = await internalCertificate.validate(data);
@@ -622,9 +612,7 @@ const internalCertificate = {
const certificate = await internalCertificate.update(access, { const certificate = await internalCertificate.update(access, {
id: data.id, id: data.id,
expires_on: moment(validations.certificate.dates.to, "X").format( expires_on: moment(validations.certificate.dates.to, "X").format("YYYY-MM-DD HH:mm:ss"),
"YYYY-MM-DD HH:mm:ss",
),
domain_names: [validations.certificate.cn], domain_names: [validations.certificate.cn],
meta: _.clone(row.meta), // Prevent the update method from changing this value that we'll use later meta: _.clone(row.meta), // Prevent the update method from changing this value that we'll use later
}); });
@@ -649,9 +637,7 @@ const internalCertificate = {
}, 10000); }, 10000);
try { try {
const result = await utils.exec( const result = await utils.exec(`openssl pkey -in ${filepath} -check -noout 2>&1 `);
`openssl pkey -in ${filepath} -check -noout 2>&1 `,
);
clearTimeout(failTimeout); clearTimeout(failTimeout);
if (!result.toLowerCase().includes("key is valid")) { if (!result.toLowerCase().includes("key is valid")) {
throw new error.ValidationError(`Result Validation Error: ${result}`); throw new error.ValidationError(`Result Validation Error: ${result}`);
@@ -661,10 +647,7 @@ const internalCertificate = {
} catch (err) { } catch (err) {
clearTimeout(failTimeout); clearTimeout(failTimeout);
fs.unlinkSync(filepath); fs.unlinkSync(filepath);
throw new error.ValidationError( throw new error.ValidationError(`Certificate Key is not valid (${err.message})`, err);
`Certificate Key is not valid (${err.message})`,
err,
);
} }
}, },
@@ -678,10 +661,7 @@ const internalCertificate = {
getCertificateInfo: async (certificate, throwExpired) => { getCertificateInfo: async (certificate, throwExpired) => {
try { try {
const filepath = await tempWrite(certificate, "/tmp"); const filepath = await tempWrite(certificate, "/tmp");
const certData = await internalCertificate.getCertificateInfoFromFile( const certData = await internalCertificate.getCertificateInfoFromFile(filepath, throwExpired);
filepath,
throwExpired,
);
fs.unlinkSync(filepath); fs.unlinkSync(filepath);
return certData; return certData;
} catch (err) { } catch (err) {
@@ -701,13 +681,7 @@ const internalCertificate = {
const certData = {}; const certData = {};
try { try {
const result = await utils.execFile("openssl", [ const result = await utils.execFile("openssl", ["x509", "-in", certificateFile, "-subject", "-noout"]);
"x509",
"-in",
certificateFile,
"-subject",
"-noout",
]);
// Examples: // Examples:
// subject=CN = *.jc21.com // subject=CN = *.jc21.com
// subject=CN = something.example.com // subject=CN = something.example.com
@@ -717,13 +691,7 @@ const internalCertificate = {
certData.cn = match[1]; certData.cn = match[1];
} }
const result2 = await utils.execFile("openssl", [ const result2 = await utils.execFile("openssl", ["x509", "-in", certificateFile, "-issuer", "-noout"]);
"x509",
"-in",
certificateFile,
"-issuer",
"-noout",
]);
// Examples: // Examples:
// issuer=C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3 // issuer=C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
// issuer=C = US, O = Let's Encrypt, CN = E5 // issuer=C = US, O = Let's Encrypt, CN = E5
@@ -734,13 +702,7 @@ const internalCertificate = {
certData.issuer = match2[1]; certData.issuer = match2[1];
} }
const result3 = await utils.execFile("openssl", [ const result3 = await utils.execFile("openssl", ["x509", "-in", certificateFile, "-dates", "-noout"]);
"x509",
"-in",
certificateFile,
"-dates",
"-noout",
]);
// notBefore=Jul 14 04:04:29 2018 GMT // notBefore=Jul 14 04:04:29 2018 GMT
// notAfter=Oct 12 04:04:29 2018 GMT // notAfter=Oct 12 04:04:29 2018 GMT
let validFrom = null; let validFrom = null;
@@ -752,10 +714,7 @@ const internalCertificate = {
const match = regex.exec(str.trim()); const match = regex.exec(str.trim());
if (match && typeof match[2] !== "undefined") { if (match && typeof match[2] !== "undefined") {
const date = Number.parseInt( const date = Number.parseInt(moment(match[2], "MMM DD HH:mm:ss YYYY z").format("X"), 10);
moment(match[2], "MMM DD HH:mm:ss YYYY z").format("X"),
10,
);
if (match[1].toLowerCase() === "notbefore") { if (match[1].toLowerCase() === "notbefore") {
validFrom = date; validFrom = date;
@@ -767,15 +726,10 @@ const internalCertificate = {
}); });
if (!validFrom || !validTo) { if (!validFrom || !validTo) {
throw new error.ValidationError( throw new error.ValidationError(`Could not determine dates from certificate: ${result}`);
`Could not determine dates from certificate: ${result}`,
);
} }
if ( if (throw_expired && validTo < Number.parseInt(moment().format("X"), 10)) {
throw_expired &&
validTo < Number.parseInt(moment().format("X"), 10)
) {
throw new error.ValidationError("Certificate has expired"); throw new error.ValidationError("Certificate has expired");
} }
@@ -786,10 +740,7 @@ const internalCertificate = {
return certData; return certData;
} catch (err) { } catch (err) {
throw new error.ValidationError( throw new error.ValidationError(`Certificate is not valid (${err.message})`, err);
`Certificate is not valid (${err.message})`,
err,
);
} }
}, },
@@ -830,18 +781,18 @@ const internalCertificate = {
"--config", "--config",
letsencryptConfig, letsencryptConfig,
"--work-dir", "--work-dir",
"/tmp/letsencrypt-lib", certbotWorkDir,
"--logs-dir", "--logs-dir",
"/tmp/letsencrypt-log", certbotLogsDir,
"--cert-name", "--cert-name",
`npm-${certificate.id}`, `npm-${certificate.id}`,
"--agree-tos", "--agree-tos",
"--authenticator", "--authenticator",
"webroot", "webroot",
"--email", "-m",
email, email,
"--preferred-challenges", "--preferred-challenges",
"dns,http", "http",
"--domains", "--domains",
certificate.domain_names.join(","), certificate.domain_names.join(","),
]; ];
@@ -870,11 +821,7 @@ const internalCertificate = {
const credentialsLocation = `/etc/letsencrypt/credentials/credentials-${certificate.id}`; const credentialsLocation = `/etc/letsencrypt/credentials/credentials-${certificate.id}`;
fs.mkdirSync("/etc/letsencrypt/credentials", { recursive: true }); fs.mkdirSync("/etc/letsencrypt/credentials", { recursive: true });
fs.writeFileSync( fs.writeFileSync(credentialsLocation, certificate.meta.dns_provider_credentials, { mode: 0o600 });
credentialsLocation,
certificate.meta.dns_provider_credentials,
{ mode: 0o600 },
);
// Whether the plugin has a --<name>-credentials argument // Whether the plugin has a --<name>-credentials argument
const hasConfigArg = certificate.meta.dns_provider !== "route53"; const hasConfigArg = certificate.meta.dns_provider !== "route53";
@@ -884,14 +831,16 @@ const internalCertificate = {
"--config", "--config",
letsencryptConfig, letsencryptConfig,
"--work-dir", "--work-dir",
"/tmp/letsencrypt-lib", certbotWorkDir,
"--logs-dir", "--logs-dir",
"/tmp/letsencrypt-log", certbotLogsDir,
"--cert-name", "--cert-name",
`npm-${certificate.id}`, `npm-${certificate.id}`,
"--agree-tos", "--agree-tos",
"--email", "-m",
email, email,
"--preferred-challenges",
"dns",
"--domains", "--domains",
certificate.domain_names.join(","), certificate.domain_names.join(","),
"--authenticator", "--authenticator",
@@ -899,10 +848,7 @@ const internalCertificate = {
]; ];
if (hasConfigArg) { if (hasConfigArg) {
args.push( args.push(`--${dnsPlugin.full_plugin_name}-credentials`, credentialsLocation);
`--${dnsPlugin.full_plugin_name}-credentials`,
credentialsLocation,
);
} }
if (certificate.meta.propagation_seconds !== undefined) { if (certificate.meta.propagation_seconds !== undefined) {
args.push( args.push(
@@ -911,10 +857,7 @@ const internalCertificate = {
); );
} }
const adds = internalCertificate.getAdditionalCertbotArgs( const adds = internalCertificate.getAdditionalCertbotArgs(certificate.id, certificate.meta.dns_provider);
certificate.id,
certificate.meta.dns_provider,
);
args.push(...adds.args); args.push(...adds.args);
logger.info(`Command: ${certbotCommand} ${args ? args.join(" ") : ""}`); logger.info(`Command: ${certbotCommand} ${args ? args.join(" ") : ""}`);
@@ -950,12 +893,8 @@ const internalCertificate = {
`${internalCertificate.getLiveCertPath(certificate.id)}/fullchain.pem`, `${internalCertificate.getLiveCertPath(certificate.id)}/fullchain.pem`,
); );
const updatedCertificate = await certificateModel const updatedCertificate = await certificateModel.query().patchAndFetchById(certificate.id, {
.query() expires_on: moment(certInfo.dates.to, "X").format("YYYY-MM-DD HH:mm:ss"),
.patchAndFetchById(certificate.id, {
expires_on: moment(certInfo.dates.to, "X").format(
"YYYY-MM-DD HH:mm:ss",
),
}); });
// Add to audit log // Add to audit log
@@ -965,11 +904,11 @@ const internalCertificate = {
object_id: updatedCertificate.id, object_id: updatedCertificate.id,
meta: updatedCertificate, meta: updatedCertificate,
}); });
} else {
throw new error.ValidationError( return updatedCertificate;
"Only Let'sEncrypt certificates can be renewed",
);
} }
throw new error.ValidationError("Only Let'sEncrypt certificates can be renewed");
}, },
/** /**
@@ -987,21 +926,18 @@ const internalCertificate = {
"--config", "--config",
letsencryptConfig, letsencryptConfig,
"--work-dir", "--work-dir",
"/tmp/letsencrypt-lib", certbotWorkDir,
"--logs-dir", "--logs-dir",
"/tmp/letsencrypt-log", certbotLogsDir,
"--cert-name", "--cert-name",
`npm-${certificate.id}`, `npm-${certificate.id}`,
"--preferred-challenges", "--preferred-challenges",
"dns,http", "http",
"--no-random-sleep-on-renew", "--no-random-sleep-on-renew",
"--disable-hook-validation", "--disable-hook-validation",
]; ];
const adds = internalCertificate.getAdditionalCertbotArgs( const adds = internalCertificate.getAdditionalCertbotArgs(certificate.id, certificate.meta.dns_provider);
certificate.id,
certificate.meta.dns_provider,
);
args.push(...adds.args); args.push(...adds.args);
logger.info(`Command: ${certbotCommand} ${args ? args.join(" ") : ""}`); logger.info(`Command: ${certbotCommand} ${args ? args.join(" ") : ""}`);
@@ -1031,19 +967,18 @@ const internalCertificate = {
"--config", "--config",
letsencryptConfig, letsencryptConfig,
"--work-dir", "--work-dir",
"/tmp/letsencrypt-lib", certbotWorkDir,
"--logs-dir", "--logs-dir",
"/tmp/letsencrypt-log", certbotLogsDir,
"--cert-name", "--cert-name",
`npm-${certificate.id}`, `npm-${certificate.id}`,
"--preferred-challenges",
"dns",
"--disable-hook-validation", "--disable-hook-validation",
"--no-random-sleep-on-renew", "--no-random-sleep-on-renew",
]; ];
const adds = internalCertificate.getAdditionalCertbotArgs( const adds = internalCertificate.getAdditionalCertbotArgs(certificate.id, certificate.meta.dns_provider);
certificate.id,
certificate.meta.dns_provider,
);
args.push(...adds.args); args.push(...adds.args);
logger.info(`Command: ${certbotCommand} ${args ? args.join(" ") : ""}`); logger.info(`Command: ${certbotCommand} ${args ? args.join(" ") : ""}`);
@@ -1068,9 +1003,9 @@ const internalCertificate = {
"--config", "--config",
letsencryptConfig, letsencryptConfig,
"--work-dir", "--work-dir",
"/tmp/letsencrypt-lib", certbotWorkDir,
"--logs-dir", "--logs-dir",
"/tmp/letsencrypt-log", certbotLogsDir,
"--cert-path", "--cert-path",
`${internalCertificate.getLiveCertPath(certificate.id)}/fullchain.pem`, `${internalCertificate.getLiveCertPath(certificate.id)}/fullchain.pem`,
"--delete-after-revoke", "--delete-after-revoke",
@@ -1083,9 +1018,7 @@ const internalCertificate = {
try { try {
const result = await utils.execFile(certbotCommand, args, adds.opts); const result = await utils.execFile(certbotCommand, args, adds.opts);
await utils.exec( await utils.exec(`rm -f '/etc/letsencrypt/credentials/credentials-${certificate.id}' || true`);
`rm -f '/etc/letsencrypt/credentials/credentials-${certificate.id}' || true`,
);
logger.info(result); logger.info(result);
return result; return result;
} catch (err) { } catch (err) {
@@ -1102,10 +1035,7 @@ const internalCertificate = {
*/ */
hasLetsEncryptSslCerts: (certificate) => { hasLetsEncryptSslCerts: (certificate) => {
const letsencryptPath = internalCertificate.getLiveCertPath(certificate.id); const letsencryptPath = internalCertificate.getLiveCertPath(certificate.id);
return ( return fs.existsSync(`${letsencryptPath}/fullchain.pem`) && fs.existsSync(`${letsencryptPath}/privkey.pem`);
fs.existsSync(`${letsencryptPath}/fullchain.pem`) &&
fs.existsSync(`${letsencryptPath}/privkey.pem`)
);
}, },
/** /**
@@ -1119,24 +1049,15 @@ const internalCertificate = {
disableInUseHosts: async (inUseResult) => { disableInUseHosts: async (inUseResult) => {
if (inUseResult?.total_count) { if (inUseResult?.total_count) {
if (inUseResult?.proxy_hosts.length) { if (inUseResult?.proxy_hosts.length) {
await internalNginx.bulkDeleteConfigs( await internalNginx.bulkDeleteConfigs("proxy_host", inUseResult.proxy_hosts);
"proxy_host",
inUseResult.proxy_hosts,
);
} }
if (inUseResult?.redirection_hosts.length) { if (inUseResult?.redirection_hosts.length) {
await internalNginx.bulkDeleteConfigs( await internalNginx.bulkDeleteConfigs("redirection_host", inUseResult.redirection_hosts);
"redirection_host",
inUseResult.redirection_hosts,
);
} }
if (inUseResult?.dead_hosts.length) { if (inUseResult?.dead_hosts.length) {
await internalNginx.bulkDeleteConfigs( await internalNginx.bulkDeleteConfigs("dead_host", inUseResult.dead_hosts);
"dead_host",
inUseResult.dead_hosts,
);
} }
} }
}, },
@@ -1152,24 +1073,15 @@ const internalCertificate = {
enableInUseHosts: async (inUseResult) => { enableInUseHosts: async (inUseResult) => {
if (inUseResult.total_count) { if (inUseResult.total_count) {
if (inUseResult.proxy_hosts.length) { if (inUseResult.proxy_hosts.length) {
await internalNginx.bulkGenerateConfigs( await internalNginx.bulkGenerateConfigs("proxy_host", inUseResult.proxy_hosts);
"proxy_host",
inUseResult.proxy_hosts,
);
} }
if (inUseResult.redirection_hosts.length) { if (inUseResult.redirection_hosts.length) {
await internalNginx.bulkGenerateConfigs( await internalNginx.bulkGenerateConfigs("redirection_host", inUseResult.redirection_hosts);
"redirection_host",
inUseResult.redirection_hosts,
);
} }
if (inUseResult.dead_hosts.length) { if (inUseResult.dead_hosts.length) {
await internalNginx.bulkGenerateConfigs( await internalNginx.bulkGenerateConfigs("dead_host", inUseResult.dead_hosts);
"dead_host",
inUseResult.dead_hosts,
);
} }
} }
}, },
@@ -1184,8 +1096,7 @@ const internalCertificate = {
await access.can("certificates:list"); await access.can("certificates:list");
// Create a test challenge file // Create a test challenge file
const testChallengeDir = const testChallengeDir = "/data/letsencrypt-acme-challenge/.well-known/acme-challenge";
"/data/letsencrypt-acme-challenge/.well-known/acme-challenge";
const testChallengeFile = `${testChallengeDir}/test-challenge`; const testChallengeFile = `${testChallengeDir}/test-challenge`;
fs.mkdirSync(testChallengeDir, { recursive: true }); fs.mkdirSync(testChallengeDir, { recursive: true });
fs.writeFileSync(testChallengeFile, "Success", { encoding: "utf8" }); fs.writeFileSync(testChallengeFile, "Success", { encoding: "utf8" });
@@ -1215,10 +1126,7 @@ const internalCertificate = {
}; };
const result = await new Promise((resolve) => { const result = await new Promise((resolve) => {
const req = https.request( const req = https.request("https://www.site24x7.com/tools/restapi-tester", options, (res) => {
"https://www.site24x7.com/tools/restapi-tester",
options,
(res) => {
let responseBody = ""; let responseBody = "";
res.on("data", (chunk) => { res.on("data", (chunk) => {
@@ -1249,8 +1157,7 @@ const internalCertificate = {
resolve(undefined); resolve(undefined);
} }
}); });
}, });
);
// Make sure to write the request body. // Make sure to write the request body.
req.write(formBody); req.write(formBody);
@@ -1271,10 +1178,7 @@ const internalCertificate = {
); );
return `other:${result.error.msg}`; return `other:${result.error.msg}`;
} }
if ( if (`${result.responsecode}` === "200" && result.htmlresponse === "Success") {
`${result.responsecode}` === "200" &&
result.htmlresponse === "Success"
) {
// Server exists and has responded with the correct data // Server exists and has responded with the correct data
return "ok"; return "ok";
} }
@@ -1288,26 +1192,19 @@ const internalCertificate = {
} }
if (`${result.responsecode}` === "404") { if (`${result.responsecode}` === "404") {
// Server exists but responded with a 404 // Server exists but responded with a 404
logger.info( logger.info(`HTTP challenge test failed for domain ${domain} because code 404 was returned`);
`HTTP challenge test failed for domain ${domain} because code 404 was returned`,
);
return "404"; return "404";
} }
if ( if (
`${result.responsecode}` === "0" || `${result.responsecode}` === "0" ||
(typeof result.reason === "string" && (typeof result.reason === "string" && result.reason.toLowerCase() === "host unavailable")
result.reason.toLowerCase() === "host unavailable")
) { ) {
// Server does not exist at domain // Server does not exist at domain
logger.info( logger.info(`HTTP challenge test failed for domain ${domain} the host was not found`);
`HTTP challenge test failed for domain ${domain} the host was not found`,
);
return "no-host"; return "no-host";
} }
// Other errors // Other errors
logger.info( logger.info(`HTTP challenge test failed for domain ${domain} because code ${result.responsecode} was returned`);
`HTTP challenge test failed for domain ${domain} because code ${result.responsecode} was returned`,
);
return `other:${result.responsecode}`; return `other:${result.responsecode}`;
}, },

View File

@@ -381,14 +381,14 @@ const internalNginx = {
}, },
/** /**
* @param {String} host_type * @param {String} hostType
* @param {Array} hosts * @param {Array} hosts
* @returns {Promise} * @returns {Promise}
*/ */
bulkGenerateConfigs: (host_type, hosts) => { bulkGenerateConfigs: (hostType, hosts) => {
const promises = []; const promises = [];
hosts.map((host) => { hosts.map((host) => {
promises.push(internalNginx.generateConfig(host_type, host)); promises.push(internalNginx.generateConfig(hostType, host));
return true; return true;
}); });

View File

@@ -265,7 +265,7 @@ export default function (tokenString) {
schemas: [roleSchema, permsSchema, objectSchema, permissionSchema], schemas: [roleSchema, permsSchema, objectSchema, permissionSchema],
}); });
const valid = ajv.validate("permissions", dataSchema); const valid = await ajv.validate("permissions", dataSchema);
return valid && dataSchema[permission]; return valid && dataSchema[permission];
} catch (err) { } catch (err) {
err.permission = permission; err.permission = permission;

View File

@@ -8,6 +8,7 @@ import deadHostModel from "./dead_host.js";
import now from "./now_helper.js"; import now from "./now_helper.js";
import proxyHostModel from "./proxy_host.js"; import proxyHostModel from "./proxy_host.js";
import redirectionHostModel from "./redirection_host.js"; import redirectionHostModel from "./redirection_host.js";
import streamModel from "./stream.js";
import userModel from "./user.js"; import userModel from "./user.js";
Model.knex(db); Model.knex(db);
@@ -114,6 +115,17 @@ class Certificate extends Model {
qb.where("redirection_host.is_deleted", 0); qb.where("redirection_host.is_deleted", 0);
}, },
}, },
streams: {
relation: Model.HasManyRelation,
modelClass: streamModel,
join: {
from: "certificate.id",
to: "stream.certificate_id",
},
modify: (qb) => {
qb.where("stream.is_deleted", 0);
},
},
}; };
} }
} }

View File

@@ -20,9 +20,9 @@
"body-parser": "^1.20.3", "body-parser": "^1.20.3",
"compression": "^1.7.4", "compression": "^1.7.4",
"express": "^4.20.0", "express": "^4.20.0",
"express-fileupload": "^1.1.9", "express-fileupload": "^1.5.2",
"gravatar": "^1.8.0", "gravatar": "^1.8.2",
"jsonwebtoken": "^9.0.0", "jsonwebtoken": "^9.0.2",
"knex": "2.4.2", "knex": "2.4.2",
"liquidjs": "10.6.1", "liquidjs": "10.6.1",
"lodash": "^4.17.21", "lodash": "^4.17.21",
@@ -38,7 +38,7 @@
}, },
"devDependencies": { "devDependencies": {
"@apidevtools/swagger-parser": "^10.1.0", "@apidevtools/swagger-parser": "^10.1.0",
"@biomejs/biome": "^2.3.0", "@biomejs/biome": "^2.3.2",
"chalk": "4.1.2", "chalk": "4.1.2",
"nodemon": "^2.0.2" "nodemon": "^2.0.2"
}, },

View File

@@ -7,7 +7,8 @@
"description": "Unique identifier", "description": "Unique identifier",
"readOnly": true, "readOnly": true,
"type": "integer", "type": "integer",
"minimum": 1 "minimum": 1,
"example": 11
}, },
"expand": { "expand": {
"anyOf": [ "anyOf": [
@@ -38,35 +39,42 @@
"created_on": { "created_on": {
"description": "Date and time of creation", "description": "Date and time of creation",
"readOnly": true, "readOnly": true,
"type": "string" "type": "string",
"example": "2025-10-28T04:17:54.000Z"
}, },
"modified_on": { "modified_on": {
"description": "Date and time of last update", "description": "Date and time of last update",
"readOnly": true, "readOnly": true,
"type": "string" "type": "string",
"example": "2025-10-28T04:17:54.000Z"
}, },
"user_id": { "user_id": {
"description": "User ID", "description": "User ID",
"type": "integer", "type": "integer",
"minimum": 1 "minimum": 1,
"example": 2
}, },
"certificate_id": { "certificate_id": {
"description": "Certificate ID", "description": "Certificate ID",
"anyOf": [ "anyOf": [
{ {
"type": "integer", "type": "integer",
"minimum": 0 "minimum": 0,
"example": 5
}, },
{ {
"type": "string", "type": "string",
"pattern": "^new$" "pattern": "^new$",
"example": "new"
} }
] ],
"example": 5
}, },
"access_list_id": { "access_list_id": {
"description": "Access List ID", "description": "Access List ID",
"type": "integer", "type": "integer",
"minimum": 0 "minimum": 0,
"example": 3
}, },
"domain_names": { "domain_names": {
"description": "Domain Names separated by a comma", "description": "Domain Names separated by a comma",
@@ -77,44 +85,157 @@
"items": { "items": {
"type": "string", "type": "string",
"pattern": "^[^&| @!#%^();:/\\\\}{=+?<>,~`'\"]+$" "pattern": "^[^&| @!#%^();:/\\\\}{=+?<>,~`'\"]+$"
} },
"example": ["example.com", "www.example.com"]
}, },
"enabled": { "enabled": {
"description": "Is Enabled", "description": "Is Enabled",
"type": "boolean" "type": "boolean",
"example": false
}, },
"ssl_forced": { "ssl_forced": {
"description": "Is SSL Forced", "description": "Is SSL Forced",
"type": "boolean" "type": "boolean",
"example": true
}, },
"hsts_enabled": { "hsts_enabled": {
"description": "Is HSTS Enabled", "description": "Is HSTS Enabled",
"type": "boolean" "type": "boolean",
"example": true
}, },
"hsts_subdomains": { "hsts_subdomains": {
"description": "Is HSTS applicable to all subdomains", "description": "Is HSTS applicable to all subdomains",
"type": "boolean" "type": "boolean",
"example": true
}, },
"ssl_provider": { "ssl_provider": {
"type": "string", "type": "string",
"pattern": "^(letsencrypt|other)$" "pattern": "^(letsencrypt|other)$",
"example": "letsencrypt"
}, },
"http2_support": { "http2_support": {
"description": "HTTP2 Protocol Support", "description": "HTTP2 Protocol Support",
"type": "boolean" "type": "boolean",
"example": true
}, },
"block_exploits": { "block_exploits": {
"description": "Should we block common exploits", "description": "Should we block common exploits",
"type": "boolean" "type": "boolean",
"example": false
}, },
"caching_enabled": { "caching_enabled": {
"description": "Should we cache assets", "description": "Should we cache assets",
"type": "boolean" "type": "boolean",
"example": true
}, },
"email": { "email": {
"description": "Email address", "description": "Email address",
"type": "string", "type": "string",
"pattern": "^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$" "pattern": "^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$",
"example": "me@example.com"
},
"directive": {
"type": "string",
"enum": ["allow", "deny"],
"example": "allow"
},
"address": {
"oneOf": [
{
"type": "string",
"pattern": "^([0-9]{1,3}\\.){3}[0-9]{1,3}(/([0-9]|[1-2][0-9]|3[0-2]))?$"
},
{
"type": "string",
"pattern": "^s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*(/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8]))?$"
},
{
"type": "string",
"pattern": "^all$"
}
],
"example": "192.168.0.11"
},
"access_items": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"username": {
"type": "string",
"minLength": 1
},
"password": {
"type": "string"
}
},
"example": {
"username": "admin",
"password": "pass"
}
},
"example": [
{
"username": "admin",
"password": "pass"
}
]
},
"access_clients": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"address": {
"$ref": "#/properties/address"
},
"directive": {
"$ref": "#/properties/directive"
}
},
"example": {
"directive": "allow",
"address": "192.168.0.0/24"
}
},
"example": [
{
"directive": "allow",
"address": "192.168.0.0/24"
}
]
},
"certificate_files": {
"description": "Certificate Files",
"content": {
"multipart/form-data": {
"schema": {
"type": "object",
"additionalProperties": false,
"required": ["certificate", "certificate_key"],
"properties": {
"certificate": {
"type": "string",
"example": "-----BEGIN CERTIFICATE-----\nMIID...-----END CERTIFICATE-----"
},
"certificate_key": {
"type": "string",
"example": "-----BEGIN CERTIFICATE-----\nMIID...-----END CERTIFICATE-----"
},
"intermediate_certificate": {
"type": "string",
"example": "-----BEGIN CERTIFICATE-----\nMIID...-----END CERTIFICATE-----"
}
}
},
"example": {
"certificate": "-----BEGIN CERTIFICATE-----\nMIID...-----END CERTIFICATE-----",
"certificate_key": "-----BEGIN PRIVATE\nMIID...-----END CERTIFICATE-----"
}
}
}
} }
} }
} }

View File

@@ -1,8 +1,7 @@
{ {
"type": "object", "type": "object",
"description": "Access List object", "description": "Access List object",
"required": ["id", "created_on", "modified_on", "owner_user_id", "name", "directive", "address", "satisfy_any", "pass_auth", "meta"], "required": ["id", "created_on", "modified_on", "owner_user_id", "name", "meta", "satisfy_any", "pass_auth", "proxy_host_count"],
"additionalProperties": false,
"properties": { "properties": {
"id": { "id": {
"$ref": "../common.json#/properties/id" "$ref": "../common.json#/properties/id"
@@ -18,36 +17,25 @@
}, },
"name": { "name": {
"type": "string", "type": "string",
"minLength": 1 "minLength": 1,
}, "example": "My Access List"
"directive": {
"type": "string",
"enum": ["allow", "deny"]
},
"address": {
"oneOf": [
{
"type": "string",
"pattern": "^([0-9]{1,3}\\.){3}[0-9]{1,3}(/([0-9]|[1-2][0-9]|3[0-2]))?$"
},
{
"type": "string",
"pattern": "^s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*(/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8]))?$"
},
{
"type": "string",
"pattern": "^all$"
}
]
},
"satisfy_any": {
"type": "boolean"
},
"pass_auth": {
"type": "boolean"
}, },
"meta": { "meta": {
"type": "object" "type": "object",
"example": {}
},
"satisfy_any": {
"type": "boolean",
"example": true
},
"pass_auth": {
"type": "boolean",
"example": false
},
"proxy_host_count": {
"type": "integer",
"minimum": 0,
"example": 3
} }
} }
} }

View File

@@ -26,16 +26,19 @@
"$ref": "../common.json#/properties/user_id" "$ref": "../common.json#/properties/user_id"
}, },
"object_type": { "object_type": {
"type": "string" "type": "string",
"example": "certificate"
}, },
"object_id": { "object_id": {
"$ref": "../common.json#/properties/id" "$ref": "../common.json#/properties/id"
}, },
"action": { "action": {
"type": "string" "type": "string",
"example": "created"
}, },
"meta": { "meta": {
"type": "object" "type": "object",
"example": {}
}, },
"user": { "user": {
"$ref": "./user-object.json" "$ref": "./user-object.json"

View File

@@ -21,7 +21,8 @@
}, },
"nice_name": { "nice_name": {
"type": "string", "type": "string",
"description": "Nice Name for the custom certificate" "description": "Nice Name for the custom certificate",
"example": "My Custom Cert"
}, },
"domain_names": { "domain_names": {
"description": "Domain Names separated by a comma", "description": "Domain Names separated by a comma",
@@ -31,12 +32,14 @@
"items": { "items": {
"type": "string", "type": "string",
"pattern": "^[^&| @!#%^();:/\\\\}{=+?<>,~`'\"]+$" "pattern": "^[^&| @!#%^();:/\\\\}{=+?<>,~`'\"]+$"
} },
"example": ["example.com", "www.example.com"]
}, },
"expires_on": { "expires_on": {
"description": "Date and time of expiration", "description": "Date and time of expiration",
"readOnly": true, "readOnly": true,
"type": "string" "type": "string",
"example": "2025-10-28T04:17:54.000Z"
}, },
"owner": { "owner": {
"$ref": "./user-object.json" "$ref": "./user-object.json"
@@ -56,10 +59,10 @@
"dns_challenge": { "dns_challenge": {
"type": "boolean" "type": "boolean"
}, },
"dns_provider": { "dns_provider_credentials": {
"type": "string" "type": "string"
}, },
"dns_provider_credentials": { "dns_provider": {
"type": "string" "type": "string"
}, },
"letsencrypt_certificate": { "letsencrypt_certificate": {
@@ -69,6 +72,9 @@
"type": "integer", "type": "integer",
"minimum": 0 "minimum": 0
} }
},
"example": {
"dns_challenge": false
} }
} }
} }

View File

@@ -35,13 +35,30 @@
"$ref": "../common.json#/properties/http2_support" "$ref": "../common.json#/properties/http2_support"
}, },
"advanced_config": { "advanced_config": {
"type": "string" "type": "string",
"example": ""
}, },
"enabled": { "enabled": {
"$ref": "../common.json#/properties/enabled" "$ref": "../common.json#/properties/enabled"
}, },
"meta": { "meta": {
"type": "object" "type": "object",
"example": {}
},
"certificate": {
"oneOf": [
{
"type": "null",
"example": null
},
{
"$ref": "./certificate-object.json"
}
],
"example": null
},
"owner": {
"$ref": "./user-object.json"
} }
} }
} }

View File

@@ -5,10 +5,12 @@
"required": ["code", "message"], "required": ["code", "message"],
"properties": { "properties": {
"code": { "code": {
"type": "integer" "type": "integer",
"example": 400
}, },
"message": { "message": {
"type": "string" "type": "string",
"example": "Bad Request"
} }
} }
} }

View File

@@ -27,15 +27,18 @@
"properties": { "properties": {
"major": { "major": {
"type": "integer", "type": "integer",
"minimum": 0 "minimum": 0,
"example": 2
}, },
"minor": { "minor": {
"type": "integer", "type": "integer",
"minimum": 0 "minimum": 0,
"example": 10
}, },
"revision": { "revision": {
"type": "integer", "type": "integer",
"minimum": 0 "minimum": 0,
"example": 1
} }
} }
} }

View File

@@ -5,37 +5,44 @@
"visibility": { "visibility": {
"type": "string", "type": "string",
"description": "Visibility Type", "description": "Visibility Type",
"enum": ["all", "user"] "enum": ["all", "user"],
"example": "all"
}, },
"access_lists": { "access_lists": {
"type": "string", "type": "string",
"description": "Access Lists Permissions", "description": "Access Lists Permissions",
"enum": ["hidden", "view", "manage"] "enum": ["hidden", "view", "manage"],
"example": "view"
}, },
"dead_hosts": { "dead_hosts": {
"type": "string", "type": "string",
"description": "404 Hosts Permissions", "description": "404 Hosts Permissions",
"enum": ["hidden", "view", "manage"] "enum": ["hidden", "view", "manage"],
"example": "manage"
}, },
"proxy_hosts": { "proxy_hosts": {
"type": "string", "type": "string",
"description": "Proxy Hosts Permissions", "description": "Proxy Hosts Permissions",
"enum": ["hidden", "view", "manage"] "enum": ["hidden", "view", "manage"],
"example": "hidden"
}, },
"redirection_hosts": { "redirection_hosts": {
"type": "string", "type": "string",
"description": "Redirection Permissions", "description": "Redirection Permissions",
"enum": ["hidden", "view", "manage"] "enum": ["hidden", "view", "manage"],
"example": "view"
}, },
"streams": { "streams": {
"type": "string", "type": "string",
"description": "Streams Permissions", "description": "Streams Permissions",
"enum": ["hidden", "view", "manage"] "enum": ["hidden", "view", "manage"],
"example": "manage"
}, },
"certificates": { "certificates": {
"type": "string", "type": "string",
"description": "Certificates Permissions", "description": "Certificates Permissions",
"enum": ["hidden", "view", "manage"] "enum": ["hidden", "view", "manage"],
"example": "hidden"
} }
} }
} }

View File

@@ -24,7 +24,6 @@
"hsts_enabled", "hsts_enabled",
"hsts_subdomains" "hsts_subdomains"
], ],
"additionalProperties": false,
"properties": { "properties": {
"id": { "id": {
"$ref": "../common.json#/properties/id" "$ref": "../common.json#/properties/id"
@@ -44,12 +43,14 @@
"forward_host": { "forward_host": {
"type": "string", "type": "string",
"minLength": 1, "minLength": 1,
"maxLength": 255 "maxLength": 255,
"example": "127.0.0.1"
}, },
"forward_port": { "forward_port": {
"type": "integer", "type": "integer",
"minimum": 1, "minimum": 1,
"maximum": 65535 "maximum": 65535,
"example": 8080
}, },
"access_list_id": { "access_list_id": {
"$ref": "../common.json#/properties/access_list_id" "$ref": "../common.json#/properties/access_list_id"
@@ -67,22 +68,28 @@
"$ref": "../common.json#/properties/block_exploits" "$ref": "../common.json#/properties/block_exploits"
}, },
"advanced_config": { "advanced_config": {
"type": "string" "type": "string",
"example": ""
}, },
"meta": { "meta": {
"type": "object" "type": "object",
"example": {
"nginx_online": true,
"nginx_err": null
}
}, },
"allow_websocket_upgrade": { "allow_websocket_upgrade": {
"description": "Allow Websocket Upgrade for all paths", "description": "Allow Websocket Upgrade for all paths",
"example": true, "type": "boolean",
"type": "boolean" "example": true
}, },
"http2_support": { "http2_support": {
"$ref": "../common.json#/properties/http2_support" "$ref": "../common.json#/properties/http2_support"
}, },
"forward_scheme": { "forward_scheme": {
"type": "string", "type": "string",
"enum": ["http", "https"] "enum": ["http", "https"],
"example": "http"
}, },
"enabled": { "enabled": {
"$ref": "../common.json#/properties/enabled" "$ref": "../common.json#/properties/enabled"
@@ -118,7 +125,15 @@
"type": "string" "type": "string"
} }
} }
},
"example": [
{
"path": "/app",
"forward_scheme": "http",
"forward_host": "example.com",
"forward_port": 80
} }
]
}, },
"hsts_enabled": { "hsts_enabled": {
"$ref": "../common.json#/properties/hsts_enabled" "$ref": "../common.json#/properties/hsts_enabled"
@@ -129,12 +144,14 @@
"certificate": { "certificate": {
"oneOf": [ "oneOf": [
{ {
"type": "null" "type": "null",
"example": null
}, },
{ {
"$ref": "./certificate-object.json" "$ref": "./certificate-object.json"
} }
] ],
"example": null
}, },
"owner": { "owner": {
"$ref": "./user-object.json" "$ref": "./user-object.json"
@@ -142,12 +159,14 @@
"access_list": { "access_list": {
"oneOf": [ "oneOf": [
{ {
"type": "null" "type": "null",
"example": null
}, },
{ {
"$ref": "./access-list-object.json" "$ref": "./access-list-object.json"
} }
] ],
"example": null
} }
} }
} }

View File

@@ -1,7 +1,26 @@
{ {
"type": "object", "type": "object",
"description": "Redirection Host object", "description": "Redirection Host object",
"required": ["id", "created_on", "modified_on", "owner_user_id", "domain_names", "forward_http_code", "forward_scheme", "forward_domain_name", "preserve_path", "certificate_id", "ssl_forced", "hsts_enabled", "hsts_subdomains", "http2_support", "block_exploits", "advanced_config", "enabled", "meta"], "required": [
"id",
"created_on",
"modified_on",
"owner_user_id",
"domain_names",
"forward_http_code",
"forward_scheme",
"forward_domain_name",
"preserve_path",
"certificate_id",
"ssl_forced",
"hsts_enabled",
"hsts_subdomains",
"http2_support",
"block_exploits",
"advanced_config",
"enabled",
"meta"
],
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"id": { "id": {
@@ -21,25 +40,30 @@
}, },
"forward_http_code": { "forward_http_code": {
"description": "Redirect HTTP Status Code", "description": "Redirect HTTP Status Code",
"example": 302,
"type": "integer", "type": "integer",
"minimum": 300, "minimum": 300,
"maximum": 308 "maximum": 308,
"example": 302
}, },
"forward_scheme": { "forward_scheme": {
"type": "string", "type": "string",
"enum": ["auto", "http", "https"] "enum": [
"auto",
"http",
"https"
],
"example": "http"
}, },
"forward_domain_name": { "forward_domain_name": {
"description": "Domain Name", "description": "Domain Name",
"example": "jc21.com",
"type": "string", "type": "string",
"pattern": "^(?:[^.*]+\\.?)+[^.]$" "pattern": "^(?:[^.*]+\\.?)+[^.]$",
"example": "jc21.com"
}, },
"preserve_path": { "preserve_path": {
"description": "Should the path be preserved", "description": "Should the path be preserved",
"example": true, "type": "boolean",
"type": "boolean" "example": true
}, },
"certificate_id": { "certificate_id": {
"$ref": "../common.json#/properties/certificate_id" "$ref": "../common.json#/properties/certificate_id"
@@ -60,13 +84,33 @@
"$ref": "../common.json#/properties/block_exploits" "$ref": "../common.json#/properties/block_exploits"
}, },
"advanced_config": { "advanced_config": {
"type": "string" "type": "string",
"example": ""
}, },
"enabled": { "enabled": {
"$ref": "../common.json#/properties/enabled" "$ref": "../common.json#/properties/enabled"
}, },
"meta": { "meta": {
"type": "object" "type": "object",
"example": {
"nginx_online": true,
"nginx_err": null
}
},
"certificate": {
"oneOf": [
{
"type": "null",
"example": null
},
{
"$ref": "./certificate-object.json"
}
],
"example": null
},
"owner": {
"$ref": "./user-object.json"
} }
} }
} }

View File

@@ -1,6 +1,8 @@
{ {
"BearerAuth": { "bearerAuth": {
"type": "http", "type": "http",
"scheme": "bearer" "scheme": "bearer",
"bearerFormat": "JWT",
"description": "JWT Bearer Token authentication"
} }
} }

View File

@@ -1,7 +1,19 @@
{ {
"type": "object", "type": "object",
"description": "Stream object", "description": "Stream object",
"required": ["id", "created_on", "modified_on", "owner_user_id", "incoming_port", "forwarding_host", "forwarding_port", "tcp_forwarding", "udp_forwarding", "enabled", "meta"], "required": [
"id",
"created_on",
"modified_on",
"owner_user_id",
"incoming_port",
"forwarding_host",
"forwarding_port",
"tcp_forwarding",
"udp_forwarding",
"enabled",
"meta"
],
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"id": { "id": {
@@ -19,15 +31,16 @@
"incoming_port": { "incoming_port": {
"type": "integer", "type": "integer",
"minimum": 1, "minimum": 1,
"maximum": 65535 "maximum": 65535,
"example": 9090
}, },
"forwarding_host": { "forwarding_host": {
"anyOf": [ "anyOf": [
{ {
"description": "Domain Name", "description": "Domain Name",
"example": "jc21.com",
"type": "string", "type": "string",
"pattern": "^(?:[^.*]+\\.?)+[^.]$" "pattern": "^(?:[^.*]+\\.?)+[^.]$",
"example": "example.com"
}, },
{ {
"type": "string", "type": "string",
@@ -37,18 +50,22 @@
"type": "string", "type": "string",
"format": "ipv6" "format": "ipv6"
} }
] ],
"example": "example.com"
}, },
"forwarding_port": { "forwarding_port": {
"type": "integer", "type": "integer",
"minimum": 1, "minimum": 1,
"maximum": 65535 "maximum": 65535,
"example": 80
}, },
"tcp_forwarding": { "tcp_forwarding": {
"type": "boolean" "type": "boolean",
"example": true
}, },
"udp_forwarding": { "udp_forwarding": {
"type": "boolean" "type": "boolean",
"example": false
}, },
"enabled": { "enabled": {
"$ref": "../common.json#/properties/enabled" "$ref": "../common.json#/properties/enabled"
@@ -57,10 +74,8 @@
"$ref": "../common.json#/properties/certificate_id" "$ref": "../common.json#/properties/certificate_id"
}, },
"meta": { "meta": {
"type": "object" "type": "object",
}, "example": {}
"owner": {
"$ref": "./user-object.json"
}, },
"certificate": { "certificate": {
"oneOf": [ "oneOf": [
@@ -70,7 +85,11 @@
{ {
"$ref": "./certificate-object.json" "$ref": "./certificate-object.json"
} }
] ],
"example": null
},
"owner": {
"$ref": "./user-object.json"
} }
} }
} }

View File

@@ -77,37 +77,37 @@
"proxy_hosts": { "proxy_hosts": {
"type": "string", "type": "string",
"description": "Proxy Hosts access level", "description": "Proxy Hosts access level",
"example": "all", "example": "manage",
"pattern": "^(manage|view|hidden)$" "pattern": "^(manage|view|hidden)$"
}, },
"redirection_hosts": { "redirection_hosts": {
"type": "string", "type": "string",
"description": "Redirection Hosts access level", "description": "Redirection Hosts access level",
"example": "all", "example": "manage",
"pattern": "^(manage|view|hidden)$" "pattern": "^(manage|view|hidden)$"
}, },
"dead_hosts": { "dead_hosts": {
"type": "string", "type": "string",
"description": "Dead Hosts access level", "description": "Dead Hosts access level",
"example": "all", "example": "manage",
"pattern": "^(manage|view|hidden)$" "pattern": "^(manage|view|hidden)$"
}, },
"streams": { "streams": {
"type": "string", "type": "string",
"description": "Streams access level", "description": "Streams access level",
"example": "all", "example": "manage",
"pattern": "^(manage|view|hidden)$" "pattern": "^(manage|view|hidden)$"
}, },
"access_lists": { "access_lists": {
"type": "string", "type": "string",
"description": "Access Lists access level", "description": "Access Lists access level",
"example": "all", "example": "hidden",
"pattern": "^(manage|view|hidden)$" "pattern": "^(manage|view|hidden)$"
}, },
"certificates": { "certificates": {
"type": "string", "type": "string",
"description": "Certificates access level", "description": "Certificates access level",
"example": "all", "example": "view",
"pattern": "^(manage|view|hidden)$" "pattern": "^(manage|view|hidden)$"
} }
} }

View File

@@ -1,10 +1,10 @@
{ {
"operationId": "getAuditLogs", "operationId": "getAuditLogs",
"summary": "Get Audit Logs", "summary": "Get Audit Logs",
"tags": ["Audit Log"], "tags": ["audit-log"],
"security": [ "security": [
{ {
"BearerAuth": ["audit-log"] "bearerAuth": ["admin"]
} }
], ],
"responses": { "responses": {

View File

@@ -1,13 +1,11 @@
{ {
"operationId": "getAuditLog", "operationId": "getAuditLog",
"summary": "Get Audit Log Event", "summary": "Get Audit Log Event",
"tags": [ "tags": ["audit-log"],
"Audit Log"
],
"security": [ "security": [
{ {
"BearerAuth": [ "bearerAuth": [
"audit-log" "admin"
] ]
} }
], ],
@@ -15,6 +13,7 @@
{ {
"in": "path", "in": "path",
"name": "id", "name": "id",
"description": "Audit Log Event ID",
"schema": { "schema": {
"type": "integer", "type": "integer",
"minimum": 1 "minimum": 1

View File

@@ -1,7 +1,7 @@
{ {
"operationId": "health", "operationId": "health",
"summary": "Returns the API health status", "summary": "Returns the API health status",
"tags": ["Public"], "tags": ["public"],
"responses": { "responses": {
"200": { "200": {
"description": "200 response", "description": "200 response",

View File

@@ -1,10 +1,12 @@
{ {
"operationId": "getAccessLists", "operationId": "getAccessLists",
"summary": "Get all access lists", "summary": "Get all access lists",
"tags": ["Access Lists"], "tags": ["access-lists"],
"security": [ "security": [
{ {
"BearerAuth": ["access_lists"] "bearerAuth": [
"access_lists.view"
]
} }
], ],
"parameters": [ "parameters": [
@@ -14,7 +16,12 @@
"description": "Expansions", "description": "Expansions",
"schema": { "schema": {
"type": "string", "type": "string",
"enum": ["owner", "items", "clients", "proxy_hosts"] "enum": [
"owner",
"items",
"clients",
"proxy_hosts"
]
} }
} }
], ],
@@ -23,10 +30,7 @@
"description": "200 response", "description": "200 response",
"content": { "content": {
"application/json": { "application/json": {
"examples": { "example": {
"default": {
"value": [
{
"id": 1, "id": 1,
"created_on": "2024-10-08T22:15:40.000Z", "created_on": "2024-10-08T22:15:40.000Z",
"modified_on": "2024-10-08T22:15:40.000Z", "modified_on": "2024-10-08T22:15:40.000Z",
@@ -36,9 +40,6 @@
"satisfy_any": true, "satisfy_any": true,
"pass_auth": false, "pass_auth": false,
"proxy_host_count": 0 "proxy_host_count": 0
}
]
}
}, },
"schema": { "schema": {
"$ref": "../../../components/access-list-object.json" "$ref": "../../../components/access-list-object.json"

View File

@@ -1,16 +1,17 @@
{ {
"operationId": "deleteAccessList", "operationId": "deleteAccessList",
"summary": "Delete a Access List", "summary": "Delete a Access List",
"tags": ["Access Lists"], "tags": ["access-lists"],
"security": [ "security": [
{ {
"BearerAuth": ["access_lists"] "bearerAuth": ["access_lists.manage"]
} }
], ],
"parameters": [ "parameters": [
{ {
"in": "path", "in": "path",
"name": "listID", "name": "listID",
"description": "Access List ID",
"schema": { "schema": {
"type": "integer", "type": "integer",
"minimum": 1 "minimum": 1

View File

@@ -1,16 +1,21 @@
{ {
"operationId": "getAccessList", "operationId": "getAccessList",
"summary": "Get a access List", "summary": "Get a access List",
"tags": ["Access Lists"], "tags": [
"access-lists"
],
"security": [ "security": [
{ {
"BearerAuth": ["access_lists"] "bearerAuth": [
"access_lists.view"
]
} }
], ],
"parameters": [ "parameters": [
{ {
"in": "path", "in": "path",
"name": "listID", "name": "listID",
"description": "Access List ID",
"schema": { "schema": {
"type": "integer", "type": "integer",
"minimum": 1 "minimum": 1
@@ -28,14 +33,14 @@
"default": { "default": {
"value": { "value": {
"id": 1, "id": 1,
"created_on": "2020-01-30T09:36:08.000Z", "created_on": "2025-10-28T04:06:55.000Z",
"modified_on": "2020-01-30T09:41:04.000Z", "modified_on": "2025-10-29T22:48:20.000Z",
"is_disabled": false, "owner_user_id": 1,
"email": "jc@jc21.com", "name": "My Access List",
"name": "Jamie Curnow", "meta": {},
"nickname": "James", "satisfy_any": false,
"avatar": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm", "pass_auth": false,
"roles": ["admin"] "proxy_host_count": 1
} }
} }
}, },

View File

@@ -1,16 +1,17 @@
{ {
"operationId": "updateAccessList", "operationId": "updateAccessList",
"summary": "Update a Access List", "summary": "Update a Access List",
"tags": ["Access Lists"], "tags": ["access-lists"],
"security": [ "security": [
{ {
"BearerAuth": ["access_lists"] "bearerAuth": ["access_lists.manage"]
} }
], ],
"parameters": [ "parameters": [
{ {
"in": "path", "in": "path",
"name": "listID", "name": "listID",
"description": "Access List ID",
"schema": { "schema": {
"type": "integer", "type": "integer",
"minimum": 1 "minimum": 1
@@ -39,50 +40,29 @@
"$ref": "../../../../components/access-list-object.json#/properties/pass_auth" "$ref": "../../../../components/access-list-object.json#/properties/pass_auth"
}, },
"items": { "items": {
"type": "array", "$ref": "../../../../common.json#/properties/access_items"
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"username": {
"type": "string",
"minLength": 1
},
"password": {
"type": "string"
}
}
}
}, },
"clients": { "clients": {
"type": "array", "$ref": "../../../../common.json#/properties/access_clients"
"items": { }
"type": "object", }
"additionalProperties": false,
"properties": {
"address": {
"oneOf": [
{
"type": "string",
"pattern": "^([0-9]{1,3}\\.){3}[0-9]{1,3}(/([0-9]|[1-2][0-9]|3[0-2]))?$"
}, },
"example": {
"name": "My Access List",
"satisfy_any": true,
"pass_auth": false,
"items": [
{ {
"type": "string", "username": "admin2",
"pattern": "^s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*(/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8]))?$" "password": "pass2"
}, }
],
"clients": [
{ {
"type": "string", "directive": "allow",
"pattern": "^all$" "address": "192.168.0.0/24"
} }
] ]
},
"directive": {
"$ref": "../../../../components/access-list-object.json#/properties/directive"
}
}
}
}
}
} }
} }
} }
@@ -108,7 +88,6 @@
"id": 1, "id": 1,
"created_on": "2024-10-07T22:43:55.000Z", "created_on": "2024-10-07T22:43:55.000Z",
"modified_on": "2024-10-08T12:52:54.000Z", "modified_on": "2024-10-08T12:52:54.000Z",
"is_deleted": false,
"is_disabled": false, "is_disabled": false,
"email": "admin@example.com", "email": "admin@example.com",
"name": "Administrator", "name": "Administrator",

View File

@@ -1,10 +1,12 @@
{ {
"operationId": "createAccessList", "operationId": "createAccessList",
"summary": "Create a Access List", "summary": "Create a Access List",
"tags": ["Access Lists"], "tags": ["access-lists"],
"security": [ "security": [
{ {
"BearerAuth": ["access_lists"] "bearerAuth": [
"access_lists.manage"
]
} }
], ],
"requestBody": { "requestBody": {
@@ -15,7 +17,9 @@
"schema": { "schema": {
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"required": ["name"], "required": [
"name"
],
"properties": { "properties": {
"name": { "name": {
"$ref": "../../../components/access-list-object.json#/properties/name" "$ref": "../../../components/access-list-object.json#/properties/name"
@@ -27,54 +31,29 @@
"$ref": "../../../components/access-list-object.json#/properties/pass_auth" "$ref": "../../../components/access-list-object.json#/properties/pass_auth"
}, },
"items": { "items": {
"type": "array", "$ref": "../../../common.json#/properties/access_items"
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"username": {
"type": "string",
"minLength": 1
},
"password": {
"type": "string",
"minLength": 1
}
}
}
}, },
"clients": { "clients": {
"type": "array", "$ref": "../../../common.json#/properties/access_clients"
"items": { }
"type": "object", }
"additionalProperties": false,
"properties": {
"address": {
"oneOf": [
{
"type": "string",
"pattern": "^([0-9]{1,3}\\.){3}[0-9]{1,3}(/([0-9]|[1-2][0-9]|3[0-2]))?$"
}, },
"example": {
"name": "My Access List",
"satisfy_any": true,
"pass_auth": false,
"items": [
{ {
"type": "string", "username": "admin",
"pattern": "^s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*(/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8]))?$" "password": "pass"
}, }
],
"clients": [
{ {
"type": "string", "directive": "allow",
"pattern": "^all$" "address": "192.168.0.0/24"
} }
] ]
},
"directive": {
"$ref": "../../../components/access-list-object.json#/properties/directive"
}
}
}
},
"meta": {
"$ref": "../../../components/access-list-object.json#/properties/meta"
}
}
} }
} }
} }
@@ -100,13 +79,14 @@
"id": 1, "id": 1,
"created_on": "2024-10-07T22:43:55.000Z", "created_on": "2024-10-07T22:43:55.000Z",
"modified_on": "2024-10-08T12:52:54.000Z", "modified_on": "2024-10-08T12:52:54.000Z",
"is_deleted": false,
"is_disabled": false, "is_disabled": false,
"email": "admin@example.com", "email": "admin@example.com",
"name": "Administrator", "name": "Administrator",
"nickname": "some guy", "nickname": "some guy",
"avatar": "//www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?default=mm", "avatar": "//www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?default=mm",
"roles": ["admin"] "roles": [
"admin"
]
}, },
"items": [ "items": [
{ {

View File

@@ -1,16 +1,17 @@
{ {
"operationId": "deleteCertificate", "operationId": "deleteCertificate",
"summary": "Delete a Certificate", "summary": "Delete a Certificate",
"tags": ["Certificates"], "tags": ["certificates"],
"security": [ "security": [
{ {
"BearerAuth": ["certificates"] "bearerAuth": ["certificates.manage"]
} }
], ],
"parameters": [ "parameters": [
{ {
"in": "path", "in": "path",
"name": "certID", "name": "certID",
"description": "Certificate ID",
"schema": { "schema": {
"type": "integer", "type": "integer",
"minimum": 1 "minimum": 1

View File

@@ -1,16 +1,17 @@
{ {
"operationId": "downloadCertificate", "operationId": "downloadCertificate",
"summary": "Downloads a Certificate", "summary": "Downloads a Certificate",
"tags": ["Certificates"], "tags": ["certificates"],
"security": [ "security": [
{ {
"BearerAuth": ["certificates"] "bearerAuth": ["certificates.manage"]
} }
], ],
"parameters": [ "parameters": [
{ {
"in": "path", "in": "path",
"name": "certID", "name": "certID",
"description": "Certificate ID",
"schema": { "schema": {
"type": "integer", "type": "integer",
"minimum": 1 "minimum": 1

View File

@@ -1,16 +1,17 @@
{ {
"operationId": "getCertificate", "operationId": "getCertificate",
"summary": "Get a Certificate", "summary": "Get a Certificate",
"tags": ["Certificates"], "tags": ["certificates"],
"security": [ "security": [
{ {
"BearerAuth": ["certificates"] "bearerAuth": ["certificates.view"]
} }
], ],
"parameters": [ "parameters": [
{ {
"in": "path", "in": "path",
"name": "certID", "name": "certID",
"description": "Certificate ID",
"schema": { "schema": {
"type": "integer", "type": "integer",
"minimum": 1 "minimum": 1

View File

@@ -1,16 +1,17 @@
{ {
"operationId": "renewCertificate", "operationId": "renewCertificate",
"summary": "Renews a Certificate", "summary": "Renews a Certificate",
"tags": ["Certificates"], "tags": ["certificates"],
"security": [ "security": [
{ {
"BearerAuth": ["certificates"] "bearerAuth": ["certificates.manage"]
} }
], ],
"parameters": [ "parameters": [
{ {
"in": "path", "in": "path",
"name": "certID", "name": "certID",
"description": "Certificate ID",
"schema": { "schema": {
"type": "integer", "type": "integer",
"minimum": 1 "minimum": 1
@@ -32,7 +33,6 @@
"id": 4, "id": 4,
"created_on": "2024-10-09T05:31:58.000Z", "created_on": "2024-10-09T05:31:58.000Z",
"owner_user_id": 1, "owner_user_id": 1,
"is_deleted": false,
"provider": "letsencrypt", "provider": "letsencrypt",
"nice_name": "My Test Cert", "nice_name": "My Test Cert",
"domain_names": ["test.jc21.supernerd.pro"], "domain_names": ["test.jc21.supernerd.pro"],

View File

@@ -1,16 +1,17 @@
{ {
"operationId": "uploadCertificate", "operationId": "uploadCertificate",
"summary": "Uploads a custom Certificate", "summary": "Uploads a custom Certificate",
"tags": ["Certificates"], "tags": ["certificates"],
"security": [ "security": [
{ {
"BearerAuth": ["certificates"] "bearerAuth": ["certificates.manage"]
} }
], ],
"parameters": [ "parameters": [
{ {
"in": "path", "in": "path",
"name": "certID", "name": "certID",
"description": "Certificate ID",
"schema": { "schema": {
"type": "integer", "type": "integer",
"minimum": 1 "minimum": 1
@@ -20,28 +21,7 @@
} }
], ],
"requestBody": { "requestBody": {
"description": "Certificate Files", "$ref": "../../../../../common.json#/properties/certificate_files"
"required": true,
"content": {
"multipart/form-data": {
"schema": {
"type": "object",
"additionalProperties": false,
"required": ["certificate", "certificate_key"],
"properties": {
"certificate": {
"type": "string"
},
"certificate_key": {
"type": "string"
},
"intermediate_certificate": {
"type": "string"
}
}
}
}
}
}, },
"responses": { "responses": {
"200": { "200": {
@@ -63,15 +43,18 @@
"properties": { "properties": {
"certificate": { "certificate": {
"type": "string", "type": "string",
"minLength": 1 "minLength": 1,
"example": "-----BEGIN CERTIFICATE-----\nMIID...-----END CERTIFICATE-----"
}, },
"certificate_key": { "certificate_key": {
"type": "string", "type": "string",
"minLength": 1 "minLength": 1,
"example": "-----BEGIN CERTIFICATE-----\nMIID...-----END CERTIFICATE-----"
}, },
"intermediate_certificate": { "intermediate_certificate": {
"type": "string", "type": "string",
"minLength": 1 "minLength": 1,
"example": "-----BEGIN CERTIFICATE-----\nMIID...-----END CERTIFICATE-----"
} }
} }
} }

View File

@@ -1,14 +1,10 @@
{ {
"operationId": "getDNSProviders", "operationId": "getDNSProviders",
"summary": "Get DNS Providers for Certificates", "summary": "Get DNS Providers for Certificates",
"tags": [ "tags": ["certificates"],
"Certificates"
],
"security": [ "security": [
{ {
"BearerAuth": [ "bearerAuth": ["certificates.view"]
"certificates"
]
} }
], ],
"responses": { "responses": {

View File

@@ -1,10 +1,10 @@
{ {
"operationId": "getCertificates", "operationId": "getCertificates",
"summary": "Get all certificates", "summary": "Get all certificates",
"tags": ["Certificates"], "tags": ["certificates"],
"security": [ "security": [
{ {
"BearerAuth": ["certificates"] "bearerAuth": ["certificates.view"]
} }
], ],
"parameters": [ "parameters": [

View File

@@ -1,10 +1,10 @@
{ {
"operationId": "createCertificate", "operationId": "createCertificate",
"summary": "Create a Certificate", "summary": "Create a Certificate",
"tags": ["Certificates"], "tags": ["certificates"],
"security": [ "security": [
{ {
"BearerAuth": ["certificates"] "bearerAuth": ["certificates.manage"]
} }
], ],
"requestBody": { "requestBody": {
@@ -30,6 +30,13 @@
"$ref": "../../../components/certificate-object.json#/properties/meta" "$ref": "../../../components/certificate-object.json#/properties/meta"
} }
} }
},
"example": {
"provider": "letsencrypt",
"domain_names": ["test.example.com"],
"meta": {
"dns_challenge": false
}
} }
} }
} }
@@ -47,7 +54,6 @@
"id": 5, "id": 5,
"created_on": "2024-10-09 05:28:35", "created_on": "2024-10-09 05:28:35",
"owner_user_id": 1, "owner_user_id": 1,
"is_deleted": false,
"provider": "letsencrypt", "provider": "letsencrypt",
"nice_name": "test.example.com", "nice_name": "test.example.com",
"domain_names": ["test.example.com"], "domain_names": ["test.example.com"],

View File

@@ -1,10 +1,10 @@
{ {
"operationId": "testHttpReach", "operationId": "testHttpReach",
"summary": "Test HTTP Reachability", "summary": "Test HTTP Reachability",
"tags": ["Certificates"], "tags": ["certificates"],
"security": [ "security": [
{ {
"BearerAuth": ["certificates"] "bearerAuth": ["certificates.view"]
} }
], ],
"requestBody": { "requestBody": {

View File

@@ -1,35 +1,14 @@
{ {
"operationId": "validateCertificates", "operationId": "validateCertificates",
"summary": "Validates given Custom Certificates", "summary": "Validates given Custom Certificates",
"tags": ["Certificates"], "tags": ["certificates"],
"security": [ "security": [
{ {
"BearerAuth": ["certificates"] "bearerAuth": ["certificates.manage"]
} }
], ],
"requestBody": { "requestBody": {
"description": "Certificate Files", "$ref": "../../../../common.json#/properties/certificate_files"
"required": true,
"content": {
"multipart/form-data": {
"schema": {
"type": "object",
"additionalProperties": false,
"required": ["certificate", "certificate_key"],
"properties": {
"certificate": {
"type": "string"
},
"certificate_key": {
"type": "string"
},
"intermediate_certificate": {
"type": "string"
}
}
}
}
}
}, },
"responses": { "responses": {
"200": { "200": {
@@ -62,10 +41,12 @@
"required": ["cn", "issuer", "dates"], "required": ["cn", "issuer", "dates"],
"properties": { "properties": {
"cn": { "cn": {
"type": "string" "type": "string",
"example": "example.com"
}, },
"issuer": { "issuer": {
"type": "string" "type": "string",
"example": "C = US, O = Let's Encrypt, CN = E5"
}, },
"dates": { "dates": {
"type": "object", "type": "object",
@@ -78,12 +59,17 @@
"to": { "to": {
"type": "integer" "type": "integer"
} }
},
"example": {
"from": 1728448218,
"to": 1736224217
} }
} }
} }
}, },
"certificate_key": { "certificate_key": {
"type": "boolean" "type": "boolean",
"example": true
} }
} }
} }

View File

@@ -1,10 +1,10 @@
{ {
"operationId": "getDeadHosts", "operationId": "getDeadHosts",
"summary": "Get all 404 hosts", "summary": "Get all 404 hosts",
"tags": ["404 Hosts"], "tags": ["404-hosts"],
"security": [ "security": [
{ {
"BearerAuth": ["dead_hosts"] "bearerAuth": ["dead_hosts.view"]
} }
], ],
"parameters": [ "parameters": [

View File

@@ -1,16 +1,17 @@
{ {
"operationId": "deleteDeadHost", "operationId": "deleteDeadHost",
"summary": "Delete a 404 Host", "summary": "Delete a 404 Host",
"tags": ["404 Hosts"], "tags": ["404-hosts"],
"security": [ "security": [
{ {
"BearerAuth": ["dead_hosts"] "bearerAuth": ["dead_hosts.manage"]
} }
], ],
"parameters": [ "parameters": [
{ {
"in": "path", "in": "path",
"name": "hostID", "name": "hostID",
"description": "The ID of the 404 Host",
"schema": { "schema": {
"type": "integer", "type": "integer",
"minimum": 1 "minimum": 1

View File

@@ -1,16 +1,17 @@
{ {
"operationId": "disableDeadHost", "operationId": "disableDeadHost",
"summary": "Disable a 404 Host", "summary": "Disable a 404 Host",
"tags": ["404 Hosts"], "tags": ["404-hosts"],
"security": [ "security": [
{ {
"BearerAuth": ["dead_hosts"] "bearerAuth": ["dead_hosts.manage"]
} }
], ],
"parameters": [ "parameters": [
{ {
"in": "path", "in": "path",
"name": "hostID", "name": "hostID",
"description": "The ID of the 404 Host",
"schema": { "schema": {
"type": "integer", "type": "integer",
"minimum": 1 "minimum": 1

View File

@@ -1,16 +1,17 @@
{ {
"operationId": "enableDeadHost", "operationId": "enableDeadHost",
"summary": "Enable a 404 Host", "summary": "Enable a 404 Host",
"tags": ["404 Hosts"], "tags": ["404-hosts"],
"security": [ "security": [
{ {
"BearerAuth": ["dead_hosts"] "bearerAuth": ["dead_hosts.manage"]
} }
], ],
"parameters": [ "parameters": [
{ {
"in": "path", "in": "path",
"name": "hostID", "name": "hostID",
"description": "The ID of the 404 Host",
"schema": { "schema": {
"type": "integer", "type": "integer",
"minimum": 1 "minimum": 1

View File

@@ -1,16 +1,17 @@
{ {
"operationId": "getDeadHost", "operationId": "getDeadHost",
"summary": "Get a 404 Host", "summary": "Get a 404 Host",
"tags": ["404 Hosts"], "tags": ["404-hosts"],
"security": [ "security": [
{ {
"BearerAuth": ["dead_hosts"] "bearerAuth": ["dead_hosts.view"]
} }
], ],
"parameters": [ "parameters": [
{ {
"in": "path", "in": "path",
"name": "hostID", "name": "hostID",
"description": "The ID of the 404 Host",
"schema": { "schema": {
"type": "integer", "type": "integer",
"minimum": 1 "minimum": 1

View File

@@ -1,16 +1,17 @@
{ {
"operationId": "updateDeadHost", "operationId": "updateDeadHost",
"summary": "Update a 404 Host", "summary": "Update a 404 Host",
"tags": ["404 Hosts"], "tags": ["404-hosts"],
"security": [ "security": [
{ {
"BearerAuth": ["dead_hosts"] "bearerAuth": ["dead_hosts.manage"]
} }
], ],
"parameters": [ "parameters": [
{ {
"in": "path", "in": "path",
"name": "hostID", "name": "hostID",
"description": "The ID of the 404 Host",
"schema": { "schema": {
"type": "integer", "type": "integer",
"minimum": 1 "minimum": 1
@@ -86,7 +87,6 @@
"id": 1, "id": 1,
"created_on": "2024-10-09T00:59:56.000Z", "created_on": "2024-10-09T00:59:56.000Z",
"modified_on": "2024-10-09T00:59:56.000Z", "modified_on": "2024-10-09T00:59:56.000Z",
"is_deleted": false,
"is_disabled": false, "is_disabled": false,
"email": "admin@example.com", "email": "admin@example.com",
"name": "Administrator", "name": "Administrator",

View File

@@ -1,10 +1,12 @@
{ {
"operationId": "create404Host", "operationId": "create404Host",
"summary": "Create a 404 Host", "summary": "Create a 404 Host",
"tags": ["404 Hosts"], "tags": ["404-hosts"],
"security": [ "security": [
{ {
"BearerAuth": ["dead_hosts"] "bearerAuth": [
"dead_hosts.manage"
]
} }
], ],
"requestBody": { "requestBody": {
@@ -15,7 +17,9 @@
"schema": { "schema": {
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"required": ["domain_names"], "required": [
"domain_names"
],
"properties": { "properties": {
"domain_names": { "domain_names": {
"$ref": "../../../components/dead-host-object.json#/properties/domain_names" "$ref": "../../../components/dead-host-object.json#/properties/domain_names"
@@ -42,6 +46,18 @@
"$ref": "../../../components/dead-host-object.json#/properties/meta" "$ref": "../../../components/dead-host-object.json#/properties/meta"
} }
} }
},
"example": {
"domain_names": [
"test.example.com"
],
"certificate_id": 0,
"ssl_forced": false,
"advanced_config": "",
"http2_support": false,
"hsts_enabled": false,
"hsts_subdomains": false,
"meta": {}
} }
} }
} }
@@ -58,7 +74,9 @@
"created_on": "2024-10-09T01:38:52.000Z", "created_on": "2024-10-09T01:38:52.000Z",
"modified_on": "2024-10-09T01:38:52.000Z", "modified_on": "2024-10-09T01:38:52.000Z",
"owner_user_id": 1, "owner_user_id": 1,
"domain_names": ["test.example.com"], "domain_names": [
"test.example.com"
],
"certificate_id": 0, "certificate_id": 0,
"ssl_forced": false, "ssl_forced": false,
"advanced_config": "", "advanced_config": "",
@@ -72,13 +90,14 @@
"id": 1, "id": 1,
"created_on": "2024-10-09T00:59:56.000Z", "created_on": "2024-10-09T00:59:56.000Z",
"modified_on": "2024-10-09T00:59:56.000Z", "modified_on": "2024-10-09T00:59:56.000Z",
"is_deleted": false,
"is_disabled": false, "is_disabled": false,
"email": "admin@example.com", "email": "admin@example.com",
"name": "Administrator", "name": "Administrator",
"nickname": "Admin", "nickname": "Admin",
"avatar": "", "avatar": "",
"roles": ["admin"] "roles": [
"admin"
]
} }
} }
} }

View File

@@ -1,10 +1,12 @@
{ {
"operationId": "getProxyHosts", "operationId": "getProxyHosts",
"summary": "Get all proxy hosts", "summary": "Get all proxy hosts",
"tags": ["Proxy Hosts"], "tags": ["proxy-hosts"],
"security": [ "security": [
{ {
"BearerAuth": ["proxy_hosts"] "bearerAuth": [
"proxy_hosts.view"
]
} }
], ],
"parameters": [ "parameters": [
@@ -14,7 +16,11 @@
"description": "Expansions", "description": "Expansions",
"schema": { "schema": {
"type": "string", "type": "string",
"enum": ["access_list", "owner", "certificate"] "enum": [
"access_list",
"owner",
"certificate"
]
} }
} }
], ],
@@ -28,14 +34,16 @@
"value": [ "value": [
{ {
"id": 1, "id": 1,
"created_on": "2024-10-08T23:23:03.000Z", "created_on": "2025-10-28T01:10:26.000Z",
"modified_on": "2024-10-08T23:23:04.000Z", "modified_on": "2025-10-28T04:07:16.000Z",
"owner_user_id": 1, "owner_user_id": 1,
"domain_names": ["test.example.com"], "domain_names": [
"test.jc21com"
],
"forward_host": "127.0.0.1", "forward_host": "127.0.0.1",
"forward_port": 8989, "forward_port": 8081,
"access_list_id": 0, "access_list_id": 1,
"certificate_id": 0, "certificate_id": 1,
"ssl_forced": false, "ssl_forced": false,
"caching_enabled": false, "caching_enabled": false,
"block_exploits": false, "block_exploits": false,
@@ -48,7 +56,7 @@
"http2_support": false, "http2_support": false,
"forward_scheme": "http", "forward_scheme": "http",
"enabled": true, "enabled": true,
"locations": null, "locations": [],
"hsts_enabled": false, "hsts_enabled": false,
"hsts_subdomains": false "hsts_subdomains": false
} }

View File

@@ -1,16 +1,17 @@
{ {
"operationId": "deleteProxyHost", "operationId": "deleteProxyHost",
"summary": "Delete a Proxy Host", "summary": "Delete a Proxy Host",
"tags": ["Proxy Hosts"], "tags": ["proxy-hosts"],
"security": [ "security": [
{ {
"BearerAuth": ["proxy_hosts"] "bearerAuth": ["proxy_hosts.manage"]
} }
], ],
"parameters": [ "parameters": [
{ {
"in": "path", "in": "path",
"name": "hostID", "name": "hostID",
"description": "The ID of the Proxy Host",
"schema": { "schema": {
"type": "integer", "type": "integer",
"minimum": 1 "minimum": 1

View File

@@ -1,16 +1,17 @@
{ {
"operationId": "disableProxyHost", "operationId": "disableProxyHost",
"summary": "Disable a Proxy Host", "summary": "Disable a Proxy Host",
"tags": ["Proxy Hosts"], "tags": ["proxy-hosts"],
"security": [ "security": [
{ {
"BearerAuth": ["proxy_hosts"] "bearerAuth": ["proxy_hosts.manage"]
} }
], ],
"parameters": [ "parameters": [
{ {
"in": "path", "in": "path",
"name": "hostID", "name": "hostID",
"description": "The ID of the Proxy Host",
"schema": { "schema": {
"type": "integer", "type": "integer",
"minimum": 1 "minimum": 1

View File

@@ -1,16 +1,17 @@
{ {
"operationId": "enableProxyHost", "operationId": "enableProxyHost",
"summary": "Enable a Proxy Host", "summary": "Enable a Proxy Host",
"tags": ["Proxy Hosts"], "tags": ["proxy-hosts"],
"security": [ "security": [
{ {
"BearerAuth": ["proxy_hosts"] "bearerAuth": ["proxy_hosts.manage"]
} }
], ],
"parameters": [ "parameters": [
{ {
"in": "path", "in": "path",
"name": "hostID", "name": "hostID",
"description": "The ID of the Proxy Host",
"schema": { "schema": {
"type": "integer", "type": "integer",
"minimum": 1 "minimum": 1

View File

@@ -1,16 +1,19 @@
{ {
"operationId": "getProxyHost", "operationId": "getProxyHost",
"summary": "Get a Proxy Host", "summary": "Get a Proxy Host",
"tags": ["Proxy Hosts"], "tags": ["proxy-hosts"],
"security": [ "security": [
{ {
"BearerAuth": ["proxy_hosts"] "bearerAuth": [
"proxy_hosts.view"
]
} }
], ],
"parameters": [ "parameters": [
{ {
"in": "path", "in": "path",
"name": "hostID", "name": "hostID",
"description": "The ID of the Proxy Host",
"schema": { "schema": {
"type": "integer", "type": "integer",
"minimum": 1 "minimum": 1
@@ -27,13 +30,15 @@
"examples": { "examples": {
"default": { "default": {
"value": { "value": {
"id": 1, "id": 3,
"created_on": "2024-10-08T23:23:03.000Z", "created_on": "2025-10-30T01:12:05.000Z",
"modified_on": "2024-10-08T23:26:38.000Z", "modified_on": "2025-10-30T01:12:05.000Z",
"owner_user_id": 1, "owner_user_id": 1,
"domain_names": ["test.example.com"], "domain_names": [
"forward_host": "192.168.0.10", "test.example.com"
"forward_port": 8989, ],
"forward_host": "127.0.0.1",
"forward_port": 8080,
"access_list_id": 0, "access_list_id": 0,
"certificate_id": 0, "certificate_id": 0,
"ssl_forced": false, "ssl_forced": false,
@@ -48,9 +53,22 @@
"http2_support": false, "http2_support": false,
"forward_scheme": "http", "forward_scheme": "http",
"enabled": true, "enabled": true,
"locations": null, "locations": [],
"hsts_enabled": false, "hsts_enabled": false,
"hsts_subdomains": false "hsts_subdomains": false,
"owner": {
"id": 1,
"created_on": "2025-10-28T00:50:24.000Z",
"modified_on": "2025-10-28T00:50:24.000Z",
"is_disabled": false,
"email": "jc@jc21.com",
"name": "jamiec",
"nickname": "jamiec",
"avatar": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm",
"roles": [
"admin"
]
}
} }
} }
}, },

View File

@@ -1,16 +1,19 @@
{ {
"operationId": "updateProxyHost", "operationId": "updateProxyHost",
"summary": "Update a Proxy Host", "summary": "Update a Proxy Host",
"tags": ["Proxy Hosts"], "tags": ["proxy-hosts"],
"security": [ "security": [
{ {
"BearerAuth": ["proxy_hosts"] "bearerAuth": [
"proxy_hosts.manage"
]
} }
], ],
"parameters": [ "parameters": [
{ {
"in": "path", "in": "path",
"name": "hostID", "name": "hostID",
"description": "The ID of the Proxy Host",
"schema": { "schema": {
"type": "integer", "type": "integer",
"minimum": 1 "minimum": 1
@@ -93,13 +96,15 @@
"examples": { "examples": {
"default": { "default": {
"value": { "value": {
"id": 1, "id": 3,
"created_on": "2024-10-08T23:23:03.000Z", "created_on": "2025-10-30T01:12:05.000Z",
"modified_on": "2024-10-08T23:26:37.000Z", "modified_on": "2025-10-30T01:17:06.000Z",
"owner_user_id": 1, "owner_user_id": 1,
"domain_names": ["test.example.com"], "domain_names": [
"forward_host": "192.168.0.10", "test.example.com"
"forward_port": 8989, ],
"forward_host": "127.0.0.1",
"forward_port": 8080,
"access_list_id": 0, "access_list_id": 0,
"certificate_id": 0, "certificate_id": 0,
"ssl_forced": false, "ssl_forced": false,
@@ -114,19 +119,21 @@
"http2_support": false, "http2_support": false,
"forward_scheme": "http", "forward_scheme": "http",
"enabled": true, "enabled": true,
"locations": [],
"hsts_enabled": false, "hsts_enabled": false,
"hsts_subdomains": false, "hsts_subdomains": false,
"owner": { "owner": {
"id": 1, "id": 1,
"created_on": "2024-10-07T22:43:55.000Z", "created_on": "2025-10-28T00:50:24.000Z",
"modified_on": "2024-10-08T12:52:54.000Z", "modified_on": "2025-10-28T00:50:24.000Z",
"is_deleted": false,
"is_disabled": false, "is_disabled": false,
"email": "admin@example.com", "email": "jc@jc21.com",
"name": "Administrator", "name": "jamiec",
"nickname": "some guy", "nickname": "jamiec",
"avatar": "//www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?default=mm", "avatar": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm",
"roles": ["admin"] "roles": [
"admin"
]
}, },
"certificate": null, "certificate": null,
"access_list": null "access_list": null

View File

@@ -1,10 +1,12 @@
{ {
"operationId": "createProxyHost", "operationId": "createProxyHost",
"summary": "Create a Proxy Host", "summary": "Create a Proxy Host",
"tags": ["Proxy Hosts"], "tags": ["proxy-hosts"],
"security": [ "security": [
{ {
"BearerAuth": ["proxy_hosts"] "bearerAuth": [
"proxy_hosts.manage"
]
} }
], ],
"requestBody": { "requestBody": {
@@ -15,7 +17,12 @@
"schema": { "schema": {
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"required": ["domain_names", "forward_scheme", "forward_host", "forward_port"], "required": [
"domain_names",
"forward_scheme",
"forward_host",
"forward_port"
],
"properties": { "properties": {
"domain_names": { "domain_names": {
"$ref": "../../../components/proxy-host-object.json#/properties/domain_names" "$ref": "../../../components/proxy-host-object.json#/properties/domain_names"
@@ -69,6 +76,14 @@
"$ref": "../../../components/proxy-host-object.json#/properties/locations" "$ref": "../../../components/proxy-host-object.json#/properties/locations"
} }
} }
},
"example": {
"domain_names": [
"test.example.com"
],
"forward_scheme": "http",
"forward_host": "127.0.0.1",
"forward_port": 8080
} }
} }
} }
@@ -81,13 +96,15 @@
"examples": { "examples": {
"default": { "default": {
"value": { "value": {
"id": 1, "id": 3,
"created_on": "2024-10-08T23:23:03.000Z", "created_on": "2025-10-30T01:12:05.000Z",
"modified_on": "2024-10-08T23:23:03.000Z", "modified_on": "2025-10-30T01:12:05.000Z",
"owner_user_id": 1, "owner_user_id": 1,
"domain_names": ["test.example.com"], "domain_names": [
"test.example.com"
],
"forward_host": "127.0.0.1", "forward_host": "127.0.0.1",
"forward_port": 8989, "forward_port": 8080,
"access_list_id": 0, "access_list_id": 0,
"certificate_id": 0, "certificate_id": 0,
"ssl_forced": false, "ssl_forced": false,
@@ -99,20 +116,22 @@
"http2_support": false, "http2_support": false,
"forward_scheme": "http", "forward_scheme": "http",
"enabled": true, "enabled": true,
"locations": [],
"hsts_enabled": false, "hsts_enabled": false,
"hsts_subdomains": false, "hsts_subdomains": false,
"certificate": null, "certificate": null,
"owner": { "owner": {
"id": 1, "id": 1,
"created_on": "2024-10-07T22:43:55.000Z", "created_on": "2025-10-28T00:50:24.000Z",
"modified_on": "2024-10-08T12:52:54.000Z", "modified_on": "2025-10-28T00:50:24.000Z",
"is_deleted": false,
"is_disabled": false, "is_disabled": false,
"email": "admin@example.com", "email": "jc@jc21.com",
"name": "Administrator", "name": "jamiec",
"nickname": "some guy", "nickname": "jamiec",
"avatar": "//www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?default=mm", "avatar": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm",
"roles": ["admin"] "roles": [
"admin"
]
}, },
"access_list": null "access_list": null
} }

View File

@@ -1,10 +1,10 @@
{ {
"operationId": "getRedirectionHosts", "operationId": "getRedirectionHosts",
"summary": "Get all Redirection hosts", "summary": "Get all Redirection hosts",
"tags": ["Redirection Hosts"], "tags": ["redirection-hosts"],
"security": [ "security": [
{ {
"BearerAuth": ["redirection_hosts"] "bearerAuth": ["redirection_hosts.view"]
} }
], ],
"parameters": [ "parameters": [

View File

@@ -1,16 +1,17 @@
{ {
"operationId": "deleteRedirectionHost", "operationId": "deleteRedirectionHost",
"summary": "Delete a Redirection Host", "summary": "Delete a Redirection Host",
"tags": ["Redirection Hosts"], "tags": ["redirection-hosts"],
"security": [ "security": [
{ {
"BearerAuth": ["redirection_hosts"] "bearerAuth": ["redirection_hosts.manage"]
} }
], ],
"parameters": [ "parameters": [
{ {
"in": "path", "in": "path",
"name": "hostID", "name": "hostID",
"description": "The ID of the Redirection Host",
"schema": { "schema": {
"type": "integer", "type": "integer",
"minimum": 1 "minimum": 1

View File

@@ -1,16 +1,17 @@
{ {
"operationId": "disableRedirectionHost", "operationId": "disableRedirectionHost",
"summary": "Disable a Redirection Host", "summary": "Disable a Redirection Host",
"tags": ["Redirection Hosts"], "tags": ["redirection-hosts"],
"security": [ "security": [
{ {
"BearerAuth": ["redirection_hosts"] "bearerAuth": ["redirection_hosts.manage"]
} }
], ],
"parameters": [ "parameters": [
{ {
"in": "path", "in": "path",
"name": "hostID", "name": "hostID",
"description": "The ID of the Redirection Host",
"schema": { "schema": {
"type": "integer", "type": "integer",
"minimum": 1 "minimum": 1

View File

@@ -1,16 +1,17 @@
{ {
"operationId": "enableRedirectionHost", "operationId": "enableRedirectionHost",
"summary": "Enable a Redirection Host", "summary": "Enable a Redirection Host",
"tags": ["Redirection Hosts"], "tags": ["redirection-hosts"],
"security": [ "security": [
{ {
"BearerAuth": ["redirection_hosts"] "bearerAuth": ["redirection_hosts.manage"]
} }
], ],
"parameters": [ "parameters": [
{ {
"in": "path", "in": "path",
"name": "hostID", "name": "hostID",
"description": "The ID of the Redirection Host",
"schema": { "schema": {
"type": "integer", "type": "integer",
"minimum": 1 "minimum": 1

View File

@@ -1,16 +1,17 @@
{ {
"operationId": "getRedirectionHost", "operationId": "getRedirectionHost",
"summary": "Get a Redirection Host", "summary": "Get a Redirection Host",
"tags": ["Redirection Hosts"], "tags": ["redirection-hosts"],
"security": [ "security": [
{ {
"BearerAuth": ["redirection_hosts"] "bearerAuth": ["redirection_hosts.view"]
} }
], ],
"parameters": [ "parameters": [
{ {
"in": "path", "in": "path",
"name": "hostID", "name": "hostID",
"description": "The ID of the Redirection Host",
"schema": { "schema": {
"type": "integer", "type": "integer",
"minimum": 1 "minimum": 1

View File

@@ -1,16 +1,17 @@
{ {
"operationId": "updateRedirectionHost", "operationId": "updateRedirectionHost",
"summary": "Update a Redirection Host", "summary": "Update a Redirection Host",
"tags": ["Redirection Hosts"], "tags": ["redirection-hosts"],
"security": [ "security": [
{ {
"BearerAuth": ["redirection_hosts"] "bearerAuth": ["redirection_hosts.manage"]
} }
], ],
"parameters": [ "parameters": [
{ {
"in": "path", "in": "path",
"name": "hostID", "name": "hostID",
"description": "The ID of the Redirection Host",
"schema": { "schema": {
"type": "integer", "type": "integer",
"minimum": 1 "minimum": 1
@@ -106,7 +107,6 @@
"id": 1, "id": 1,
"created_on": "2024-10-09T00:59:56.000Z", "created_on": "2024-10-09T00:59:56.000Z",
"modified_on": "2024-10-09T00:59:56.000Z", "modified_on": "2024-10-09T00:59:56.000Z",
"is_deleted": false,
"is_disabled": false, "is_disabled": false,
"email": "admin@example.com", "email": "admin@example.com",
"name": "Administrator", "name": "Administrator",

View File

@@ -1,10 +1,12 @@
{ {
"operationId": "createRedirectionHost", "operationId": "createRedirectionHost",
"summary": "Create a Redirection Host", "summary": "Create a Redirection Host",
"tags": ["Redirection Hosts"], "tags": ["redirection-hosts"],
"security": [ "security": [
{ {
"BearerAuth": ["redirection_hosts"] "bearerAuth": [
"redirection_hosts.manage"
]
} }
], ],
"requestBody": { "requestBody": {
@@ -15,7 +17,12 @@
"schema": { "schema": {
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"required": ["domain_names", "forward_scheme", "forward_http_code", "forward_domain_name"], "required": [
"domain_names",
"forward_scheme",
"forward_http_code",
"forward_domain_name"
],
"properties": { "properties": {
"domain_names": { "domain_names": {
"$ref": "../../../components/redirection-host-object.json#/properties/domain_names" "$ref": "../../../components/redirection-host-object.json#/properties/domain_names"
@@ -57,6 +64,23 @@
"$ref": "../../../components/redirection-host-object.json#/properties/meta" "$ref": "../../../components/redirection-host-object.json#/properties/meta"
} }
} }
},
"example": {
"domain_names": [
"test.example.com"
],
"forward_domain_name": "example.com",
"forward_scheme": "auto",
"forward_http_code": 301,
"preserve_path": false,
"block_exploits": false,
"certificate_id": 0,
"ssl_forced": false,
"http2_support": false,
"hsts_enabled": false,
"hsts_subdomains": false,
"advanced_config": "",
"meta": {}
} }
} }
} }
@@ -69,12 +93,14 @@
"examples": { "examples": {
"default": { "default": {
"value": { "value": {
"id": 1, "id": 2,
"created_on": "2024-10-09T01:13:12.000Z", "created_on": "2025-10-30T01:27:04.000Z",
"modified_on": "2024-10-09T01:13:12.000Z", "modified_on": "2025-10-30T01:27:04.000Z",
"owner_user_id": 1, "owner_user_id": 1,
"domain_names": ["test.example.com"], "domain_names": [
"forward_domain_name": "something-else.com", "test.example.com"
],
"forward_domain_name": "example.com",
"preserve_path": false, "preserve_path": false,
"certificate_id": 0, "certificate_id": 0,
"ssl_forced": false, "ssl_forced": false,
@@ -85,20 +111,21 @@
"enabled": true, "enabled": true,
"hsts_enabled": false, "hsts_enabled": false,
"hsts_subdomains": false, "hsts_subdomains": false,
"forward_scheme": "http", "forward_scheme": "auto",
"forward_http_code": 301, "forward_http_code": 301,
"certificate": null, "certificate": null,
"owner": { "owner": {
"id": 1, "id": 1,
"created_on": "2024-10-09T00:59:56.000Z", "created_on": "2025-10-28T00:50:24.000Z",
"modified_on": "2024-10-09T00:59:56.000Z", "modified_on": "2025-10-28T00:50:24.000Z",
"is_deleted": false,
"is_disabled": false, "is_disabled": false,
"email": "admin@example.com", "email": "jc@jc21.com",
"name": "Administrator", "name": "jamiec",
"nickname": "Admin", "nickname": "jamiec",
"avatar": "", "avatar": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm",
"roles": ["admin"] "roles": [
"admin"
]
} }
} }
} }

View File

@@ -1,10 +1,10 @@
{ {
"operationId": "getStreams", "operationId": "getStreams",
"summary": "Get all streams", "summary": "Get all streams",
"tags": ["Streams"], "tags": ["streams"],
"security": [ "security": [
{ {
"BearerAuth": ["streams"] "bearerAuth": ["streams.view"]
} }
], ],
"parameters": [ "parameters": [

View File

@@ -1,10 +1,12 @@
{ {
"operationId": "createStream", "operationId": "createStream",
"summary": "Create a Stream", "summary": "Create a Stream",
"tags": ["Streams"], "tags": ["streams"],
"security": [ "security": [
{ {
"BearerAuth": ["streams"] "bearerAuth": [
"streams.manage"
]
} }
], ],
"requestBody": { "requestBody": {
@@ -15,7 +17,11 @@
"schema": { "schema": {
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"required": ["incoming_port", "forwarding_host", "forwarding_port"], "required": [
"incoming_port",
"forwarding_host",
"forwarding_port"
],
"properties": { "properties": {
"incoming_port": { "incoming_port": {
"$ref": "../../../components/stream-object.json#/properties/incoming_port" "$ref": "../../../components/stream-object.json#/properties/incoming_port"
@@ -42,6 +48,15 @@
"$ref": "../../../components/dead-host-object.json#/properties/domain_names" "$ref": "../../../components/dead-host-object.json#/properties/domain_names"
} }
} }
},
"example": {
"incoming_port": 8888,
"forwarding_host": "127.0.0.1",
"forwarding_port": 8080,
"tcp_forwarding": true,
"udp_forwarding": false,
"certificate_id": 0,
"meta": {}
} }
} }
} }
@@ -72,13 +87,14 @@
"id": 1, "id": 1,
"created_on": "2024-10-09T02:33:16.000Z", "created_on": "2024-10-09T02:33:16.000Z",
"modified_on": "2024-10-09T02:33:16.000Z", "modified_on": "2024-10-09T02:33:16.000Z",
"is_deleted": false,
"is_disabled": false, "is_disabled": false,
"email": "admin@example.com", "email": "admin@example.com",
"name": "Administrator", "name": "Administrator",
"nickname": "Admin", "nickname": "Admin",
"avatar": "", "avatar": "",
"roles": ["admin"] "roles": [
"admin"
]
}, },
"certificate_id": 0 "certificate_id": 0
} }

View File

@@ -1,16 +1,17 @@
{ {
"operationId": "deleteStream", "operationId": "deleteStream",
"summary": "Delete a Stream", "summary": "Delete a Stream",
"tags": ["Streams"], "tags": ["streams"],
"security": [ "security": [
{ {
"BearerAuth": ["streams"] "bearerAuth": ["streams.manage"]
} }
], ],
"parameters": [ "parameters": [
{ {
"in": "path", "in": "path",
"name": "streamID", "name": "streamID",
"description": "The ID of the Stream",
"schema": { "schema": {
"type": "integer", "type": "integer",
"minimum": 1 "minimum": 1

View File

@@ -1,16 +1,17 @@
{ {
"operationId": "disableStream", "operationId": "disableStream",
"summary": "Disable a Stream", "summary": "Disable a Stream",
"tags": ["Streams"], "tags": ["streams"],
"security": [ "security": [
{ {
"BearerAuth": ["streams"] "bearerAuth": ["streams.manage"]
} }
], ],
"parameters": [ "parameters": [
{ {
"in": "path", "in": "path",
"name": "streamID", "name": "streamID",
"description": "The ID of the Stream",
"schema": { "schema": {
"type": "integer", "type": "integer",
"minimum": 1 "minimum": 1

View File

@@ -1,16 +1,17 @@
{ {
"operationId": "enableStream", "operationId": "enableStream",
"summary": "Enable a Stream", "summary": "Enable a Stream",
"tags": ["Streams"], "tags": ["streams"],
"security": [ "security": [
{ {
"BearerAuth": ["streams"] "bearerAuth": ["streams.manage"]
} }
], ],
"parameters": [ "parameters": [
{ {
"in": "path", "in": "path",
"name": "streamID", "name": "streamID",
"description": "The ID of the Stream",
"schema": { "schema": {
"type": "integer", "type": "integer",
"minimum": 1 "minimum": 1

View File

@@ -1,16 +1,17 @@
{ {
"operationId": "getStream", "operationId": "getStream",
"summary": "Get a Stream", "summary": "Get a Stream",
"tags": ["Streams"], "tags": ["streams"],
"security": [ "security": [
{ {
"BearerAuth": ["streams"] "bearerAuth": ["streams.view"]
} }
], ],
"parameters": [ "parameters": [
{ {
"in": "path", "in": "path",
"name": "streamID", "name": "streamID",
"description": "The ID of the Stream",
"schema": { "schema": {
"type": "integer", "type": "integer",
"minimum": 1 "minimum": 1

View File

@@ -1,16 +1,17 @@
{ {
"operationId": "updateStream", "operationId": "updateStream",
"summary": "Update a Stream", "summary": "Update a Stream",
"tags": ["Streams"], "tags": ["streams"],
"security": [ "security": [
{ {
"BearerAuth": ["streams"] "bearerAuth": ["streams.manage"]
} }
], ],
"parameters": [ "parameters": [
{ {
"in": "path", "in": "path",
"name": "streamID", "name": "streamID",
"description": "The ID of the Stream",
"schema": { "schema": {
"type": "integer", "type": "integer",
"minimum": 1 "minimum": 1
@@ -81,7 +82,6 @@
"id": 1, "id": 1,
"created_on": "2024-10-09T02:33:16.000Z", "created_on": "2024-10-09T02:33:16.000Z",
"modified_on": "2024-10-09T02:33:16.000Z", "modified_on": "2024-10-09T02:33:16.000Z",
"is_deleted": false,
"is_disabled": false, "is_disabled": false,
"email": "admin@example.com", "email": "admin@example.com",
"name": "Administrator", "name": "Administrator",

View File

@@ -1,10 +1,10 @@
{ {
"operationId": "reportsHosts", "operationId": "reportsHosts",
"summary": "Report on Host Statistics", "summary": "Report on Host Statistics",
"tags": ["Reports"], "tags": ["reports"],
"security": [ "security": [
{ {
"BearerAuth": ["reports"] "bearerAuth": []
} }
], ],
"responses": { "responses": {
@@ -27,19 +27,23 @@
"properties": { "properties": {
"proxy": { "proxy": {
"type": "integer", "type": "integer",
"description": "Proxy Hosts Count" "description": "Proxy Hosts Count",
"example": 20
}, },
"redirection": { "redirection": {
"type": "integer", "type": "integer",
"description": "Redirection Hosts Count" "description": "Redirection Hosts Count",
"example": 2
}, },
"stream": { "stream": {
"type": "integer", "type": "integer",
"description": "Streams Count" "description": "Streams Count",
"example": 0
}, },
"dead": { "dead": {
"type": "integer", "type": "integer",
"description": "404 Hosts Count" "description": "404 Hosts Count",
"example": 3
} }
} }
} }

View File

@@ -1,7 +1,7 @@
{ {
"operationId": "schema", "operationId": "schema",
"summary": "Returns this swagger API schema", "summary": "Returns this swagger API schema",
"tags": ["Public"], "tags": ["public"],
"responses": { "responses": {
"200": { "200": {
"description": "200 response" "description": "200 response"

View File

@@ -1,10 +1,10 @@
{ {
"operationId": "getSettings", "operationId": "getSettings",
"summary": "Get all settings", "summary": "Get all settings",
"tags": ["Settings"], "tags": ["settings"],
"security": [ "security": [
{ {
"BearerAuth": ["settings"] "bearerAuth": ["admin"]
} }
], ],
"responses": { "responses": {

View File

@@ -1,10 +1,10 @@
{ {
"operationId": "getSetting", "operationId": "getSetting",
"summary": "Get a setting", "summary": "Get a setting",
"tags": ["Settings"], "tags": ["settings"],
"security": [ "security": [
{ {
"BearerAuth": ["settings"] "bearerAuth": ["admin"]
} }
], ],
"parameters": [ "parameters": [

View File

@@ -1,10 +1,10 @@
{ {
"operationId": "updateSetting", "operationId": "updateSetting",
"summary": "Update a setting", "summary": "Update a setting",
"tags": ["Settings"], "tags": ["settings"],
"security": [ "security": [
{ {
"BearerAuth": ["settings"] "bearerAuth": ["admin"]
} }
], ],
"parameters": [ "parameters": [
@@ -34,7 +34,8 @@
"value": { "value": {
"type": "string", "type": "string",
"minLength": 1, "minLength": 1,
"enum": ["congratulations", "404", "444", "redirect", "html"] "enum": ["congratulations", "404", "444", "redirect", "html"],
"example": "html"
}, },
"meta": { "meta": {
"type": "object", "type": "object",
@@ -46,9 +47,16 @@
"html": { "html": {
"type": "string" "type": "string"
} }
},
"example": {
"html": "<p>hello world</p>"
} }
} }
} }
},
"example": {
"value": "congratulations",
"meta": {}
} }
} }
} }

View File

@@ -1,10 +1,10 @@
{ {
"operationId": "refreshToken", "operationId": "refreshToken",
"summary": "Refresh your access token", "summary": "Refresh your access token",
"tags": ["Tokens"], "tags": ["tokens"],
"security": [ "security": [
{ {
"BearerAuth": ["tokens"] "bearerAuth": []
} }
], ],
"responses": { "responses": {

View File

@@ -1,7 +1,7 @@
{ {
"operationId": "requestToken", "operationId": "requestToken",
"summary": "Request a new access token from credentials", "summary": "Request a new access token from credentials",
"tags": ["Tokens"], "tags": ["tokens"],
"requestBody": { "requestBody": {
"description": "Credentials Payload", "description": "Credentials Payload",
"required": true, "required": true,
@@ -12,20 +12,27 @@
"properties": { "properties": {
"identity": { "identity": {
"minLength": 1, "minLength": 1,
"type": "string" "type": "string",
"example": "me@example.com"
}, },
"scope": { "scope": {
"minLength": 1, "minLength": 1,
"type": "string", "type": "string",
"enum": ["user"] "enum": ["user"],
"example": "user"
}, },
"secret": { "secret": {
"minLength": 1, "minLength": 1,
"type": "string" "type": "string",
"example": "bigredhorsebanana"
} }
}, },
"required": ["identity", "secret"], "required": ["identity", "secret"],
"type": "object" "type": "object"
},
"example": {
"identity": "me@example.com",
"secret": "bigredhorsebanana"
} }
} }
} }
@@ -37,12 +44,10 @@
"examples": { "examples": {
"default": { "default": {
"value": { "value": {
"result": {
"expires": "2025-02-04T20:40:46.340Z", "expires": "2025-02-04T20:40:46.340Z",
"token": "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.ey...xaHKYr3Kk6MvkUjcC4" "token": "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.ey...xaHKYr3Kk6MvkUjcC4"
} }
} }
}
}, },
"schema": { "schema": {
"$ref": "../../components/token-object.json" "$ref": "../../components/token-object.json"

View File

@@ -1,10 +1,10 @@
{ {
"operationId": "getUsers", "operationId": "getUsers",
"summary": "Get all users", "summary": "Get all users",
"tags": ["Users"], "tags": ["users"],
"security": [ "security": [
{ {
"BearerAuth": ["users"] "bearerAuth": ["admin"]
} }
], ],
"parameters": [ "parameters": [

View File

@@ -1,10 +1,10 @@
{ {
"operationId": "createUser", "operationId": "createUser",
"summary": "Create a User", "summary": "Create a User",
"tags": ["Users"], "tags": ["users"],
"security": [ "security": [
{ {
"BearerAuth": ["users"] "bearerAuth": ["admin"]
} }
], ],
"requestBody": { "requestBody": {

View File

@@ -1,10 +1,10 @@
{ {
"operationId": "updateUserAuth", "operationId": "updateUserAuth",
"summary": "Update a User's Authentication", "summary": "Update a User's Authentication",
"tags": ["Users"], "tags": ["users"],
"security": [ "security": [
{ {
"BearerAuth": ["users"] "bearerAuth": ["admin"]
} }
], ],
"parameters": [ "parameters": [

View File

@@ -1,10 +1,10 @@
{ {
"operationId": "deleteUser", "operationId": "deleteUser",
"summary": "Delete a User", "summary": "Delete a User",
"tags": ["Users"], "tags": ["users"],
"security": [ "security": [
{ {
"BearerAuth": ["users"] "bearerAuth": ["admin"]
} }
], ],
"parameters": [ "parameters": [

View File

@@ -1,10 +1,10 @@
{ {
"operationId": "getUser", "operationId": "getUser",
"summary": "Get a user", "summary": "Get a user",
"tags": ["Users"], "tags": ["users"],
"security": [ "security": [
{ {
"BearerAuth": ["users"] "bearerAuth": ["admin"]
} }
], ],
"parameters": [ "parameters": [

View File

@@ -1,10 +1,10 @@
{ {
"operationId": "loginAsUser", "operationId": "loginAsUser",
"summary": "Login as this user", "summary": "Login as this user",
"tags": ["Users"], "tags": ["users"],
"security": [ "security": [
{ {
"BearerAuth": ["users"] "bearerAuth": ["admin"]
} }
], ],
"parameters": [ "parameters": [
@@ -35,11 +35,11 @@
"created_on": "2020-01-30T10:43:44.000Z", "created_on": "2020-01-30T10:43:44.000Z",
"modified_on": "2020-01-30T10:43:44.000Z", "modified_on": "2020-01-30T10:43:44.000Z",
"is_disabled": false, "is_disabled": false,
"email": "jc@jc21.com", "email": "user2@example.com",
"name": "Jamie Curnow", "name": "John Doe",
"nickname": "James", "nickname": "Jonny",
"avatar": "//www.gravatar.com/avatar/3c8d73f45fd8763f827b964c76e6032a?default=mm", "avatar": "//www.gravatar.com/avatar/3c8d73f45fd8763f827b964c76e6032a?default=mm",
"roles": ["admin"] "roles": []
} }
} }
} }
@@ -50,16 +50,15 @@
"required": ["expires", "token", "user"], "required": ["expires", "token", "user"],
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"expires": {
"description": "Token Expiry Unix Time",
"example": 1566540249,
"minimum": 1,
"type": "number"
},
"token": { "token": {
"description": "JWT Token", "description": "JWT Token",
"example": "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.ey...xaHKYr3Kk6MvkUjcC4", "type": "string",
"type": "string" "example": "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.ey...xaHKYr3Kk6MvkUjcC4"
},
"expires": {
"description": "Token Expiry Timestamp",
"type": "string",
"example": "2020-01-30T10:43:44.000Z"
}, },
"user": { "user": {
"$ref": "../../../../components/user-object.json" "$ref": "../../../../components/user-object.json"

View File

@@ -1,10 +1,10 @@
{ {
"operationId": "updateUserPermissions", "operationId": "updateUserPermissions",
"summary": "Update a User's Permissions", "summary": "Update a User's Permissions",
"tags": ["Users"], "tags": ["users"],
"security": [ "security": [
{ {
"BearerAuth": ["users"] "bearerAuth": ["admin"]
} }
], ],
"parameters": [ "parameters": [
@@ -27,6 +27,15 @@
"application/json": { "application/json": {
"schema": { "schema": {
"$ref": "../../../../components/permission-object.json" "$ref": "../../../../components/permission-object.json"
},
"example": {
"visibility": "all",
"access_lists": "view",
"certificates": "hidden",
"dead_hosts": "hidden",
"proxy_hosts": "manage",
"redirection_hosts": "hidden",
"streams": "hidden"
} }
} }
} }

View File

@@ -1,10 +1,10 @@
{ {
"operationId": "updateUser", "operationId": "updateUser",
"summary": "Update a User", "summary": "Update a User",
"tags": ["Users"], "tags": ["users"],
"security": [ "security": [
{ {
"BearerAuth": ["users"] "bearerAuth": ["admin"]
} }
], ],
"parameters": [ "parameters": [

View File

@@ -2,7 +2,8 @@
"openapi": "3.1.0", "openapi": "3.1.0",
"info": { "info": {
"title": "Nginx Proxy Manager API", "title": "Nginx Proxy Manager API",
"version": "2.x.x" "version": "2.x.x",
"description": "This is the official API documentation for Nginx Proxy Manager.\n\nMost endpoints require authentication via Bearer Token (JWT). You can generate a token by logging in via the `POST /tokens` endpoint.\n\nFor more information, visit the [Nginx Proxy Manager Documentation](https://nginxproxymanager.com)."
}, },
"servers": [ "servers": [
{ {
@@ -11,13 +12,59 @@
], ],
"components": { "components": {
"securitySchemes": { "securitySchemes": {
"bearerAuth": { "$ref": "./components/security-schemes.json"
"type": "http",
"scheme": "bearer",
"bearerFormat": "JWT"
}
} }
}, },
"tags": [
{
"name": "public",
"description": "Endpoints that do not require authentication"
},
{
"name": "audit-log",
"description": "Endpoints related to Audit Logs"
},
{
"name": "access-lists",
"description": "Endpoints related to Access Lists"
},
{
"name": "certificates",
"description": "Endpoints related to Certificates"
},
{
"name": "404-hosts",
"description": "Endpoints related to 404 Hosts"
},
{
"name": "proxy-hosts",
"description": "Endpoints related to Proxy Hosts"
},
{
"name": "redirection-hosts",
"description": "Endpoints related to Redirection Hosts"
},
{
"name": "streams",
"description": "Endpoints related to Streams"
},
{
"name": "reports",
"description": "Endpoints for viewing reports"
},
{
"name": "settings",
"description": "Endpoints for managing application settings"
},
{
"name": "tokens",
"description": "Endpoints for managing authentication tokens"
},
{
"name": "users",
"description": "Endpoints for managing users"
}
],
"paths": { "paths": {
"/": { "/": {
"get": { "get": {

View File

@@ -43,59 +43,59 @@
ajv-draft-04 "^1.0.0" ajv-draft-04 "^1.0.0"
call-me-maybe "^1.0.2" call-me-maybe "^1.0.2"
"@biomejs/biome@^2.3.0": "@biomejs/biome@^2.3.2":
version "2.3.0" version "2.3.2"
resolved "https://registry.yarnpkg.com/@biomejs/biome/-/biome-2.3.0.tgz#80030b68d94bd0a0761ac2cd22cc4f2c0f23f4f9" resolved "https://registry.yarnpkg.com/@biomejs/biome/-/biome-2.3.2.tgz#aeeb5f12c39571a18f36a919be63ba7dbc7b290a"
integrity sha512-shdUY5H3S3tJVUWoVWo5ua+GdPW5lRHf+b0IwZ4OC1o2zOKQECZ6l2KbU6t89FNhtd3Qx5eg5N7/UsQWGQbAFw== integrity sha512-8e9tzamuDycx7fdrcJ/F/GDZ8SYukc5ud6tDicjjFqURKYFSWMl0H0iXNXZEGmcmNUmABgGuHThPykcM41INgg==
optionalDependencies: optionalDependencies:
"@biomejs/cli-darwin-arm64" "2.3.0" "@biomejs/cli-darwin-arm64" "2.3.2"
"@biomejs/cli-darwin-x64" "2.3.0" "@biomejs/cli-darwin-x64" "2.3.2"
"@biomejs/cli-linux-arm64" "2.3.0" "@biomejs/cli-linux-arm64" "2.3.2"
"@biomejs/cli-linux-arm64-musl" "2.3.0" "@biomejs/cli-linux-arm64-musl" "2.3.2"
"@biomejs/cli-linux-x64" "2.3.0" "@biomejs/cli-linux-x64" "2.3.2"
"@biomejs/cli-linux-x64-musl" "2.3.0" "@biomejs/cli-linux-x64-musl" "2.3.2"
"@biomejs/cli-win32-arm64" "2.3.0" "@biomejs/cli-win32-arm64" "2.3.2"
"@biomejs/cli-win32-x64" "2.3.0" "@biomejs/cli-win32-x64" "2.3.2"
"@biomejs/cli-darwin-arm64@2.3.0": "@biomejs/cli-darwin-arm64@2.3.2":
version "2.3.0" version "2.3.2"
resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.3.0.tgz#78cef4d7415adbf0718c7854e7160e181d916652" resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.3.2.tgz#93f866161abe32e702987ccbddf492c1aabe016f"
integrity sha512-3cJVT0Z5pbTkoBmbjmDZTDFYxIkRcrs9sYVJbIBHU8E6qQxgXAaBfSVjjCreG56rfDuQBr43GzwzmaHPcu4vlw== integrity sha512-4LECm4kc3If0JISai4c3KWQzukoUdpxy4fRzlrPcrdMSRFksR9ZoXK7JBcPuLBmd2SoT4/d7CQS33VnZpgBjew==
"@biomejs/cli-darwin-x64@2.3.0": "@biomejs/cli-darwin-x64@2.3.2":
version "2.3.0" version "2.3.2"
resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.3.0.tgz#068baf1f0f748c01658ba9bb511d8d18461d922b" resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.3.2.tgz#9c3dffdac12e4f4d8db7680ca20f58ace1f38c23"
integrity sha512-6LIkhglh3UGjuDqJXsK42qCA0XkD1Ke4K/raFOii7QQPbM8Pia7Qj2Hji4XuF2/R78hRmEx7uKJH3t/Y9UahtQ== integrity sha512-jNMnfwHT4N3wi+ypRfMTjLGnDmKYGzxVr1EYAPBcauRcDnICFXN81wD6wxJcSUrLynoyyYCdfW6vJHS/IAoTDA==
"@biomejs/cli-linux-arm64-musl@2.3.0": "@biomejs/cli-linux-arm64-musl@2.3.2":
version "2.3.0" version "2.3.2"
resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.3.0.tgz#9a1350184abcea8092957a9519098cac7629705a" resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.3.2.tgz#a0424d2fe355cc43c375b3fbf3e42d39b7221d0e"
integrity sha512-nDksoFdwZ2YrE7NiYDhtMhL2UgFn8Kb7Y0bYvnTAakHnqEdb4lKindtBc1f+xg2Snz0JQhJUYO7r9CDBosRU5w== integrity sha512-2Zz4usDG1GTTPQnliIeNx6eVGGP2ry5vE/v39nT73a3cKN6t5H5XxjcEoZZh62uVZvED7hXXikclvI64vZkYqw==
"@biomejs/cli-linux-arm64@2.3.0": "@biomejs/cli-linux-arm64@2.3.2":
version "2.3.0" version "2.3.2"
resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.3.0.tgz#f322daebb32fe0b18f7981c8cdbe84a06852bfee" resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.3.2.tgz#f85717c04d420ede20523d173a1fc10df60d4d37"
integrity sha512-uhAsbXySX7xsXahegDg5h3CDgfMcRsJvWLFPG0pjkylgBb9lErbK2C0UINW52zhwg0cPISB09lxHPxCau4e2xA== integrity sha512-amnqvk+gWybbQleRRq8TMe0rIv7GHss8mFJEaGuEZYWg1Tw14YKOkeo8h6pf1c+d3qR+JU4iT9KXnBKGON4klw==
"@biomejs/cli-linux-x64-musl@2.3.0": "@biomejs/cli-linux-x64-musl@2.3.2":
version "2.3.0" version "2.3.2"
resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.3.0.tgz#ce441d5c00eda977b74e4116f9723f2edc579485" resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.3.2.tgz#d3e114c744c32d2c50a77c13476bd941819c92d8"
integrity sha512-+i9UcJwl99uAhtRQDz9jUAh+Xkb097eekxs/D9j4deWDg5/yB/jPWzISe1nBHvlzTXsdUSj0VvB4Go2DSpKIMw== integrity sha512-gzB19MpRdTuOuLtPpFBGrV3Lq424gHyq2lFj8wfX9tvLMLdmA/R9C7k/mqBp/spcbWuHeIEKgEs3RviOPcWGBA==
"@biomejs/cli-linux-x64@2.3.0": "@biomejs/cli-linux-x64@2.3.2":
version "2.3.0" version "2.3.2"
resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-x64/-/cli-linux-x64-2.3.0.tgz#da7ea904307b3211df62a4b42e5a022f8f583009" resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-x64/-/cli-linux-x64-2.3.2.tgz#f66ce85d2d757d45e6edecce04753a805bd816f0"
integrity sha512-uxa8reA2s1VgoH8MhbGlCmMOt3JuSE1vJBifkh1ulaPiuk0SPx8cCdpnm9NWnTe2x/LfWInWx4sZ7muaXTPGGw== integrity sha512-8BG/vRAhFz1pmuyd24FQPhNeueLqPtwvZk6yblABY2gzL2H8fLQAF/Z2OPIc+BPIVPld+8cSiKY/KFh6k81xfA==
"@biomejs/cli-win32-arm64@2.3.0": "@biomejs/cli-win32-arm64@2.3.2":
version "2.3.0" version "2.3.2"
resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.3.0.tgz#cdc0f8bbf025fb28c5b03b326128cce363ecffa5" resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.3.2.tgz#b46f8b47a3d97e766cc5ad5eb67d90eeb230b2cb"
integrity sha512-ynjmsJLIKrAjC3CCnKMMhzcnNy8dbQWjKfSU5YA0mIruTxBNMbkAJp+Pr2iV7/hFou+66ZSD/WV8hmLEmhUaXA== integrity sha512-lCruqQlfWjhMlOdyf5pDHOxoNm4WoyY2vZ4YN33/nuZBRstVDuqPPjS0yBkbUlLEte11FbpW+wWSlfnZfSIZvg==
"@biomejs/cli-win32-x64@2.3.0": "@biomejs/cli-win32-x64@2.3.2":
version "2.3.0" version "2.3.2"
resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-x64/-/cli-win32-x64-2.3.0.tgz#10e1de6222e272a1e3e395b3d845ee66cb6febd8" resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-x64/-/cli-win32-x64-2.3.2.tgz#a14f5e220dd496705278315ee3e5e028dd657344"
integrity sha512-zOCYmCRVkWXc9v8P7OLbLlGGMxQTKMvi+5IC4v7O8DkjLCOHRzRVK/Lno2pGZNo0lzKM60pcQOhH8HVkXMQdFg== integrity sha512-6Ee9P26DTb4D8sN9nXxgbi9Dw5vSOfH98M7UlmkjKB2vtUbrRqCbZiNfryGiwnPIpd6YUoTl7rLVD2/x1CyEHQ==
"@gar/promisify@^1.0.1": "@gar/promisify@^1.0.1":
version "1.1.3" version "1.1.3"
@@ -861,7 +861,7 @@ expand-template@^2.0.3:
resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c"
integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==
express-fileupload@^1.1.9: express-fileupload@^1.5.2:
version "1.5.2" version "1.5.2"
resolved "https://registry.yarnpkg.com/express-fileupload/-/express-fileupload-1.5.2.tgz#4da70ba6f2ffd4c736eab0776445865a9dbd9bfa" resolved "https://registry.yarnpkg.com/express-fileupload/-/express-fileupload-1.5.2.tgz#4da70ba6f2ffd4c736eab0776445865a9dbd9bfa"
integrity sha512-wxUJn2vTHvj/kZCVmc5/bJO15C7aSMyHeuXYY3geKpeKibaAoQGcEv5+sM6nHS2T7VF+QHS4hTWPiY2mKofEdg== integrity sha512-wxUJn2vTHvj/kZCVmc5/bJO15C7aSMyHeuXYY3geKpeKibaAoQGcEv5+sM6nHS2T7VF+QHS4hTWPiY2mKofEdg==
@@ -1108,7 +1108,7 @@ graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.2.0, graceful-fs@^4.2.6:
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
gravatar@^1.8.0: gravatar@^1.8.2:
version "1.8.2" version "1.8.2"
resolved "https://registry.yarnpkg.com/gravatar/-/gravatar-1.8.2.tgz#f298642b1562ed685af2ae938dbe31ec0c542cc1" resolved "https://registry.yarnpkg.com/gravatar/-/gravatar-1.8.2.tgz#f298642b1562ed685af2ae938dbe31ec0c542cc1"
integrity sha512-GdRwLM3oYpFQKy47MKuluw9hZ2gaCtiKPbDGdcDEuYDKlc8eNnW27KYL9LVbIDzEsx88WtDWQm2ClBcsgBnj6w== integrity sha512-GdRwLM3oYpFQKy47MKuluw9hZ2gaCtiKPbDGdcDEuYDKlc8eNnW27KYL9LVbIDzEsx88WtDWQm2ClBcsgBnj6w==
@@ -1352,7 +1352,7 @@ json-schema-traverse@^1.0.0:
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2"
integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==
jsonwebtoken@^9.0.0: jsonwebtoken@^9.0.2:
version "9.0.2" version "9.0.2"
resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz#65ff91f4abef1784697d40952bb1998c504caaf3" resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz#65ff91f4abef1784697d40952bb1998c504caaf3"
integrity sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ== integrity sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==

View File

@@ -1,5 +1,5 @@
{ {
"$schema": "https://biomejs.dev/schemas/2.2.4/schema.json", "$schema": "https://biomejs.dev/schemas/2.3.1/schema.json",
"vcs": { "vcs": {
"enabled": true, "enabled": true,
"clientKind": "git", "clientKind": "git",

View File

@@ -8,14 +8,11 @@
const allLocales = [ const allLocales = [
["en", "en-US"], ["en", "en-US"],
["de", "de-DE"],
["fa", "fa-IR"], ["fa", "fa-IR"],
]; ];
const ignoreUnused = [ const ignoreUnused = [
/^capability\..*$/, /^.*$/,
/^status\..*$/,
/^type\..*$/,
]; ];
const { spawnSync } = require("child_process"); const { spawnSync } = require("child_process");
@@ -119,20 +116,10 @@ const compareLocale = (locale) => {
const checkForMissing = (locale) => { const checkForMissing = (locale) => {
allKeys.forEach((key) => { allKeys.forEach((key) => {
if (typeof locale.data[key] === "undefined") { if (typeof locale.data[key] === "undefined") {
let ignored = false;
ignoreMissing.map((regex) => {
if (key.match(regex)) {
ignored = true;
}
return null;
});
if (!ignored) {
allWarnings.push( allWarnings.push(
"WARN: `" + locale[0] + "` does not contain item: `" + key + "`", "WARN: `" + locale[0] + "` does not contain item: `" + key + "`",
); );
} }
}
return null; return null;
}); });
}; };

View File

@@ -33,13 +33,14 @@
"react-bootstrap": "^2.10.10", "react-bootstrap": "^2.10.10",
"react-dom": "^19.2.0", "react-dom": "^19.2.0",
"react-intl": "^7.1.14", "react-intl": "^7.1.14",
"react-markdown": "^10.1.0",
"react-router-dom": "^7.9.4", "react-router-dom": "^7.9.4",
"react-select": "^5.10.2", "react-select": "^5.10.2",
"react-toastify": "^11.0.5", "react-toastify": "^11.0.5",
"rooks": "^9.3.0" "rooks": "^9.3.0"
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "^2.3.0", "@biomejs/biome": "^2.3.1",
"@formatjs/cli": "^6.7.4", "@formatjs/cli": "^6.7.4",
"@tanstack/react-query-devtools": "^5.90.2", "@tanstack/react-query-devtools": "^5.90.2",
"@testing-library/dom": "^10.4.1", "@testing-library/dom": "^10.4.1",

View File

@@ -80,8 +80,16 @@ export async function get(args: GetArgs, abortController?: AbortController) {
return processResponse(await baseGet(args, abortController)); return processResponse(await baseGet(args, abortController));
} }
export async function download(args: GetArgs, abortController?: AbortController) { export async function download({ url, params }: GetArgs, filename = "download.file") {
return (await baseGet(args, abortController)).text(); const headers = buildAuthHeader();
const res = await fetch(buildUrl({ url, params }), { headers });
const bl = await res.blob();
const u = window.URL.createObjectURL(bl);
const a = document.createElement("a");
a.href = u;
a.download = filename;
a.click();
window.URL.revokeObjectURL(url);
} }
interface PostArgs { interface PostArgs {

View File

@@ -4,7 +4,6 @@ import type { AccessList } from "./models";
export async function createAccessList(item: AccessList): Promise<AccessList> { export async function createAccessList(item: AccessList): Promise<AccessList> {
return await api.post({ return await api.post({
url: "/nginx/access-lists", url: "/nginx/access-lists",
// todo: only use whitelist of fields for this data
data: item, data: item,
}); });
} }

View File

@@ -4,7 +4,6 @@ import type { DeadHost } from "./models";
export async function createDeadHost(item: DeadHost): Promise<DeadHost> { export async function createDeadHost(item: DeadHost): Promise<DeadHost> {
return await api.post({ return await api.post({
url: "/nginx/dead-hosts", url: "/nginx/dead-hosts",
// todo: only use whitelist of fields for this data
data: item, data: item,
}); });
} }

View File

@@ -4,7 +4,6 @@ import type { ProxyHost } from "./models";
export async function createProxyHost(item: ProxyHost): Promise<ProxyHost> { export async function createProxyHost(item: ProxyHost): Promise<ProxyHost> {
return await api.post({ return await api.post({
url: "/nginx/proxy-hosts", url: "/nginx/proxy-hosts",
// todo: only use whitelist of fields for this data
data: item, data: item,
}); });
} }

View File

@@ -4,7 +4,6 @@ import type { RedirectionHost } from "./models";
export async function createRedirectionHost(item: RedirectionHost): Promise<RedirectionHost> { export async function createRedirectionHost(item: RedirectionHost): Promise<RedirectionHost> {
return await api.post({ return await api.post({
url: "/nginx/redirection-hosts", url: "/nginx/redirection-hosts",
// todo: only use whitelist of fields for this data
data: item, data: item,
}); });
} }

View File

@@ -4,7 +4,6 @@ import type { Stream } from "./models";
export async function createStream(item: Stream): Promise<Stream> { export async function createStream(item: Stream): Promise<Stream> {
return await api.post({ return await api.post({
url: "/nginx/streams", url: "/nginx/streams",
// todo: only use whitelist of fields for this data
data: item, data: item,
}); });
} }

View File

@@ -18,7 +18,6 @@ export interface NewUser {
export async function createUser(item: NewUser, noAuth?: boolean): Promise<User> { export async function createUser(item: NewUser, noAuth?: boolean): Promise<User> {
return await api.post({ return await api.post({
url: "/users", url: "/users",
// todo: only use whitelist of fields for this data
data: item, data: item,
noAuth, noAuth,
}); });

View File

@@ -1,8 +1,10 @@
import * as api from "./base"; import * as api from "./base";
import type { Binary } from "./responseTypes";
export async function downloadCertificate(id: number): Promise<Binary> { export async function downloadCertificate(id: number): Promise<void> {
return await api.get({ await api.download(
{
url: `/nginx/certificates/${id}/download`, url: `/nginx/certificates/${id}/download`,
}); },
`certificate-${id}.zip`,
);
} }

View File

@@ -1,6 +1,6 @@
export type AccessListExpansion = "owner" | "items" | "clients"; export type AccessListExpansion = "owner" | "items" | "clients";
export type AuditLogExpansion = "user"; export type AuditLogExpansion = "user";
export type CertificateExpansion = "owner" | "proxy_hosts" | "redirection_hosts" | "dead_hosts"; export type CertificateExpansion = "owner" | "proxy_hosts" | "redirection_hosts" | "dead_hosts" | "streams";
export type HostExpansion = "owner" | "certificate"; export type HostExpansion = "owner" | "certificate";
export type ProxyHostExpansion = "owner" | "access_list" | "certificate"; export type ProxyHostExpansion = "owner" | "access_list" | "certificate";
export type UserExpansion = "permissions"; export type UserExpansion = "permissions";

View File

@@ -37,6 +37,7 @@ export * from "./getToken";
export * from "./getUser"; export * from "./getUser";
export * from "./getUsers"; export * from "./getUsers";
export * from "./helpers"; export * from "./helpers";
export * from "./loginAsUser";
export * from "./models"; export * from "./models";
export * from "./refreshToken"; export * from "./refreshToken";
export * from "./renewCertificate"; export * from "./renewCertificate";

View File

@@ -0,0 +1,8 @@
import * as api from "./base";
import type { LoginAsTokenResponse } from "./responseTypes";
export async function loginAsUser(id: number): Promise<LoginAsTokenResponse> {
return await api.post({
url: `/users/${id}/login`,
});
}

View File

@@ -196,10 +196,10 @@ export interface Stream {
export interface Setting { export interface Setting {
id: string; id: string;
name: string; name?: string;
description: string; description?: string;
value: string; value: string;
meta: Record<string, any>; meta?: Record<string, any>;
} }
export interface DNSProvider { export interface DNSProvider {

View File

@@ -1,4 +1,4 @@
import type { AppVersion } from "./models"; import type { AppVersion, User } from "./models";
export interface HealthResponse { export interface HealthResponse {
status: string; status: string;
@@ -16,4 +16,6 @@ export interface ValidatedCertificateResponse {
certificateKey: boolean; certificateKey: boolean;
} }
export type Binary = number & { readonly __brand: unique symbol }; export interface LoginAsTokenResponse extends TokenResponse {
user: User;
}

View File

@@ -1,14 +1,9 @@
import * as api from "./base"; import * as api from "./base";
import type { Certificate } from "./models"; import type { Certificate } from "./models";
export async function uploadCertificate( export async function uploadCertificate(id: number, data: FormData): Promise<Certificate> {
id: number,
certificate: string,
certificateKey: string,
intermediateCertificate?: string,
): Promise<Certificate> {
return await api.post({ return await api.post({
url: `/nginx/certificates/${id}/upload`, url: `/nginx/certificates/${id}/upload`,
data: { certificate, certificateKey, intermediateCertificate }, data,
}); });
} }

Some files were not shown because too many files have changed in this diff Show More