Compare commits

..

63 Commits

Author SHA1 Message Date
jc21
e88d55f1d2 Merge pull request #4839 from NginxProxyManager/develop
v2.13.1
2025-11-05 15:40:32 +10:00
Jamie Curnow
4cb85f6480 Fix #4833 supports the usual proxy env vars for outgoing admin related requests
All checks were successful
Close stale issues and PRs / stale (push) Successful in 20s
2025-11-05 15:16:42 +10:00
jc21
df7dea2d16 Merge branch 'master' into develop 2025-11-05 12:35:06 +10:00
Jamie Curnow
23f4948bde Bump version 2025-11-05 12:33:59 +10:00
Jamie Curnow
0ceb7d0892 Fix #4838 when showing avatars of deleted users 2025-11-05 12:33:13 +10:00
Jamie Curnow
f35671db21 Fix #4837 for those with older config 2025-11-05 10:56:23 +10:00
Jamie Curnow
a3a0614948 Fix #4828 showing incorrect certicificate value 2025-11-05 10:21:55 +10:00
Jamie Curnow
06b67ed4bc Remove user name column from audit log
All checks were successful
Close stale issues and PRs / stale (push) Successful in 20s
2025-11-04 14:57:10 +10:00
Jamie Curnow
4a0e27572e Fix missing translation for renew cert dialog 2025-11-04 14:54:02 +10:00
jc21
fbea8dfa9e Merge pull request #4825 from NginxProxyManager/develop
v2.13.0
2025-11-04 14:23:00 +10:00
Jamie Curnow
8c37348b65 Properly wrap debug calls 2025-11-04 13:43:52 +10:00
Jamie Curnow
2b3e9d72f4 Updated docs screenshots 2025-11-04 13:05:21 +10:00
jc21
a3e5235d81 Merge branch 'master' into develop
All checks were successful
Close stale issues and PRs / stale (push) Successful in 26s
2025-11-04 07:47:04 +10:00
jc21
9875fa92f1 Merge pull request #4794 from Johno-ACSLive/develop
Add basic MySQL TLS support
2025-11-04 07:13:15 +10:00
jc21
ef5156b613 Merge pull request #4813 from potatojuicemachine/develop
Adds Hetzner Cloud to available plugins
2025-11-03 13:38:11 +10:00
Jamie Curnow
b9a34ebb7e Revert to cypress 14, 15 was causing problems with executing external commands 2025-11-03 12:53:23 +10:00
Jamie Curnow
7642d0a000 Cleanup cypress tests 2025-11-03 12:35:58 +10:00
Jamie Curnow
7a6a9de0ea Update frontend deps
All checks were successful
Close stale issues and PRs / stale (push) Successful in 19s
2025-11-03 10:53:46 +10:00
Jamie Curnow
a5d50f9588 Update test deps 2025-11-03 10:52:53 +10:00
Jamie Curnow
612695c2e8 Upgrade biomejs 2025-11-03 10:51:16 +10:00
Jonathon Aroutsidis
71a2277b9b Replace spaces with tabs 2025-11-03 10:48:14 +11:00
Jonathon Aroutsidis
5acf287ea7 Aligned Assignments and arrow-parens 2025-11-03 10:48:14 +11:00
Jonathon Aroutsidis
e34206b526 Include SSL Options for MySQL 2025-11-03 10:46:20 +11:00
jc21
6b00adf8b9 Merge pull request #4725 from NginxProxyManager/dependabot/npm_and_yarn/test/eslint/plugin-kit-0.3.5
Bump @eslint/plugin-kit from 0.3.2 to 0.3.5 in /test
2025-11-03 08:49:30 +10:00
jc21
a93558278e Merge pull request #4763 from NginxProxyManager/dependabot/npm_and_yarn/test/axios-1.12.0
Bump axios from 1.10.0 to 1.12.0 in /test
2025-11-03 08:37:03 +10:00
jc21
bc2867b357 Merge pull request #4803 from NginxProxyManager/dependabot/npm_and_yarn/docs/vite-5.4.21
Bump vite from 5.4.19 to 5.4.21 in /docs
2025-11-03 08:18:00 +10:00
jc21
52093ba258 Merge pull request #4805 from vlauciani/patch-1
Update PostgreSQL volume path in setup documentation for 18+
2025-11-03 08:15:23 +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
Tim Burr
e0985bee43 Merge remote-tracking branch 'base/react' into develop 2025-10-29 13:15:58 +01:00
Tim Burr
51dd6e6a1b Sets postgres version to 17 2025-10-29 10:59:01 +01: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
Tim Burr
a2ea63a539 Adds Hetzner Cloud 2025-10-27 13:48:41 +01: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
jc21
2893ffb1e4 Merge pull request #4801 from sopex/react
QoL: Link to dashboard 2.13
2025-10-27 09:52:50 +10:00
Jamie Curnow
1a117a267c Fix to postgres 17 2025-10-27 08:13:03 +10:00
Jamie Curnow
c303b69649 Update deps, the safe ones 2025-10-26 00:39:06 +10:00
Jamie Curnow
bb6c9c8daf Certificates section react work 2025-10-26 00:28:39 +10:00
Jamie Curnow
5b7013b8d5 Moved certrbot plugin list to backend
frontend doesn't include when building in react version
adds swagger for existing dns-providers endpoint
2025-10-26 00:28:03 +10:00
Valentino Lauciani
bfcd057755 Update PostgreSQL volume path in setup documentation for 18+ 2025-10-24 09:30:19 +02:00
dependabot[bot]
08bdc23131 Bump vite from 5.4.19 to 5.4.21 in /docs
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.4.19 to 5.4.21.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v5.4.21/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v5.4.21/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 5.4.21
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-21 07:13:05 +00:00
Konstantinos Spartalis
b8e3e594fb ;) 2025-10-17 16:00:59 +03:00
Konstantinos Spartalis
71251d2a0d :) 2025-10-17 13:51:06 +03:00
dependabot[bot]
c9aba0c928 Bump axios from 1.10.0 to 1.12.0 in /test
Bumps [axios](https://github.com/axios/axios) from 1.10.0 to 1.12.0.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.10.0...v1.12.0)

---
updated-dependencies:
- dependency-name: axios
  dependency-version: 1.12.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-13 15:18:55 +00:00
dependabot[bot]
4397f57a51 Bump @eslint/plugin-kit from 0.3.2 to 0.3.5 in /test
Bumps [@eslint/plugin-kit](https://github.com/eslint/rewrite/tree/HEAD/packages/plugin-kit) from 0.3.2 to 0.3.5.
- [Release notes](https://github.com/eslint/rewrite/releases)
- [Changelog](https://github.com/eslint/rewrite/blob/main/packages/plugin-kit/CHANGELOG.md)
- [Commits](https://github.com/eslint/rewrite/commits/plugin-kit-v0.3.5/packages/plugin-kit)

---
updated-dependencies:
- dependency-name: "@eslint/plugin-kit"
  dependency-version: 0.3.5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-22 02:38:28 +00:00
jc21
356eaa0691 Merge pull request #4653 from NginxProxyManager/develop
v2.12.6
2025-07-10 07:18:53 +10:00
279 changed files with 6391 additions and 2355 deletions

1
.gitignore vendored
View File

@@ -1,5 +1,6 @@
.DS_Store .DS_Store
.idea .idea
.qodo
._* ._*
.vscode .vscode
certbot-help.txt certbot-help.txt

View File

@@ -1 +1 @@
2.13.0 2.13.1

View File

@@ -1,7 +1,7 @@
<p align="center"> <p align="center">
<img src="https://nginxproxymanager.com/github.png"> <img src="https://nginxproxymanager.com/github.png">
<br><br> <br><br>
<img src="https://img.shields.io/badge/version-2.13.0-green.svg?style=for-the-badge"> <img src="https://img.shields.io/badge/version-2.13.1-green.svg?style=for-the-badge">
<a href="https://hub.docker.com/repository/docker/jc21/nginx-proxy-manager"> <a href="https://hub.docker.com/repository/docker/jc21/nginx-proxy-manager">
<img src="https://img.shields.io/docker/stars/jc21/nginx-proxy-manager.svg?style=for-the-badge"> <img src="https://img.shields.io/docker/stars/jc21/nginx-proxy-manager.svg?style=for-the-badge">
</a> </a>

View File

@@ -5,7 +5,7 @@ import fileUpload from "express-fileupload";
import { isDebugMode } from "./lib/config.js"; import { isDebugMode } from "./lib/config.js";
import cors from "./lib/express/cors.js"; import cors from "./lib/express/cors.js";
import jwt from "./lib/express/jwt.js"; import jwt from "./lib/express/jwt.js";
import { express as logger } from "./logger.js"; import { debug, express as logger } from "./logger.js";
import mainRoutes from "./routes/main.js"; import mainRoutes from "./routes/main.js";
/** /**
@@ -80,7 +80,7 @@ app.use((err, req, res, _) => {
// Not every error is worth logging - but this is good for now until it gets annoying. // Not every error is worth logging - but this is good for now until it gets annoying.
if (typeof err.stack !== "undefined" && err.stack) { if (typeof err.stack !== "undefined" && err.stack) {
logger.debug(err.stack); debug(logger, err.stack);
if (typeof err.public === "undefined" || !err.public) { if (typeof err.public === "undefined" || !err.public) {
logger.warn(err.message); logger.warn(err.message);
} }

View File

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

View File

@@ -1,4 +1,4 @@
# certbot-dns-plugins # Certbot dns-plugins
This file contains info about available Certbot DNS plugins. This file contains info about available Certbot DNS plugins.
This only works for plugins which use the standard argument structure, so: This only works for plugins which use the standard argument structure, so:

View File

@@ -295,6 +295,14 @@
"credentials": "dns_hetzner_api_token = 0123456789abcdef0123456789abcdef", "credentials": "dns_hetzner_api_token = 0123456789abcdef0123456789abcdef",
"full_plugin_name": "dns-hetzner" "full_plugin_name": "dns-hetzner"
}, },
"hetzner-cloud": {
"name": "Hetzner Cloud",
"package_name": "certbot-dns-hetzner-cloud",
"version": "~=1.0.4",
"dependencies": "",
"credentials": "dns_hetzner_cloud_api_token = your_api_token_here",
"full_plugin_name": "dns-hetzner-cloud"
},
"hostingnl": { "hostingnl": {
"name": "Hosting.nl", "name": "Hosting.nl",
"package_name": "certbot-dns-hostingnl", "package_name": "certbot-dns-hostingnl",

View File

@@ -21,7 +21,8 @@ const generateDbConfig = () => {
user: cfg.user, user: cfg.user,
password: cfg.password, password: cfg.password,
database: cfg.name, database: cfg.name,
port: cfg.port, port: cfg.port,
...(cfg.ssl ? { ssl: cfg.ssl } : {})
}, },
migrations: { migrations: {
tableName: "migrations", tableName: "migrations",

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

@@ -4,13 +4,14 @@ 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 { ProxyAgent } from "proxy-agent";
import tempWrite from "temp-write"; import tempWrite from "temp-write";
import dnsPlugins from "../global/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";
import { useLetsencryptServer, useLetsencryptStaging } from "../lib/config.js"; import { useLetsencryptServer, useLetsencryptStaging } from "../lib/config.js";
import error from "../lib/error.js"; import error from "../lib/error.js";
import utils from "../lib/utils.js"; import utils from "../lib/utils.js";
import { ssl as logger } from "../logger.js"; import { debug, ssl as logger } from "../logger.js";
import certificateModel from "../models/certificate.js"; import certificateModel from "../models/certificate.js";
import tokenModel from "../models/token.js"; import tokenModel from "../models/token.js";
import userModel from "../models/user.js"; import userModel from "../models/user.js";
@@ -20,9 +21,11 @@ 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 = {
@@ -119,104 +122,118 @@ const internalCertificate = {
data.nice_name = data.domain_names.join(", "); data.nice_name = data.domain_names.join(", ");
} }
const certificate = await certificateModel.query().insertAndFetch(data).then(utils.omitRow(omissions())); // this command really should clean up and delete the cert if it can't fully succeed
const certificate = await certificateModel.query().insertAndFetch(data);
if (certificate.provider === "letsencrypt") { try {
// Request a new Cert from LE. Let the fun begin. if (certificate.provider === "letsencrypt") {
// Request a new Cert from LE. Let the fun begin.
// 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
// 2. Disable them in nginx temporarily // 2. Disable them in nginx temporarily
// 3. Generate the LE config
// 4. Request cert
// 5. Remove LE config
// 6. Re-instate previously disabled hosts
// 1. Find out any hosts that are using any of the hostnames in this cert
const inUseResult = await internalHost.getHostsWithDomains(certificate.domain_names);
// 2. Disable them in nginx temporarily
await internalCertificate.disableInUseHosts(inUseResult);
const user = await userModel.query().where("is_deleted", 0).andWhere("id", data.owner_user_id).first();
if (!user || !user.email) {
throw new error.ValidationError(
"A valid email address must be set on your user account to use Let's Encrypt",
);
}
// With DNS challenge no config is needed, so skip 3 and 5.
if (certificate.meta?.dns_challenge) {
try {
await internalNginx.reload();
// 4. Request cert
await internalCertificate.requestLetsEncryptSslWithDnsChallenge(certificate, user.email);
await internalNginx.reload();
// 6. Re-instate previously disabled hosts
await internalCertificate.enableInUseHosts(inUseResult);
} catch (err) {
// In the event of failure, revert things and throw err back
await internalCertificate.enableInUseHosts(inUseResult);
await internalNginx.reload();
throw err;
}
} else {
// 3. Generate the LE config // 3. Generate the LE config
// 4. Request cert
// 5. Remove LE config
// 6. Re-instate previously disabled hosts
// 1. Find out any hosts that are using any of the hostnames in this cert
const inUseResult = await internalHost.getHostsWithDomains(certificate.domain_names);
// 2. Disable them in nginx temporarily
await internalCertificate.disableInUseHosts(inUseResult);
const user = await userModel.query().where("is_deleted", 0).andWhere("id", data.owner_user_id).first();
if (!user || !user.email) {
throw new error.ValidationError(
"A valid email address must be set on your user account to use Let's Encrypt",
);
}
// With DNS challenge no config is needed, so skip 3 and 5.
if (certificate.meta?.dns_challenge) {
try {
await internalNginx.reload();
// 4. Request cert
await internalCertificate.requestLetsEncryptSslWithDnsChallenge(certificate, user.email);
await internalNginx.reload();
// 6. Re-instate previously disabled hosts
await internalCertificate.enableInUseHosts(inUseResult);
} catch (err) {
// In the event of failure, revert things and throw err back
await internalCertificate.enableInUseHosts(inUseResult);
await internalNginx.reload();
throw err;
}
} else {
// 3. Generate the LE config
try {
await internalNginx.generateLetsEncryptRequestConfig(certificate);
await internalNginx.reload();
setTimeout(() => {}, 5000);
// 4. Request cert
await internalCertificate.requestLetsEncryptSsl(certificate, user.email);
// 5. Remove LE config
await internalNginx.deleteLetsEncryptRequestConfig(certificate);
await internalNginx.reload();
// 6. Re-instate previously disabled hosts
await internalCertificate.enableInUseHosts(inUseResult);
} catch (err) {
// In the event of failure, revert things and throw err back
await internalNginx.deleteLetsEncryptRequestConfig(certificate);
await internalCertificate.enableInUseHosts(inUseResult);
await internalNginx.reload();
throw err;
}
}
// At this point, the letsencrypt cert should exist on disk.
// Lets get the expiry date from the file and update the row silently
try { try {
await internalNginx.generateLetsEncryptRequestConfig(certificate); const certInfo = await internalCertificate.getCertificateInfoFromFile(
await internalNginx.reload(); `${internalCertificate.getLiveCertPath(certificate.id)}/fullchain.pem`,
setTimeout(() => {}, 5000); );
// 4. Request cert const savedRow = await certificateModel
await internalCertificate.requestLetsEncryptSsl(certificate, user.email); .query()
// 5. Remove LE config .patchAndFetchById(certificate.id, {
await internalNginx.deleteLetsEncryptRequestConfig(certificate); expires_on: moment(certInfo.dates.to, "X").format("YYYY-MM-DD HH:mm:ss"),
await internalNginx.reload(); })
// 6. Re-instate previously disabled hosts .then(utils.omitRow(omissions()));
await internalCertificate.enableInUseHosts(inUseResult);
// Add cert data for audit log
savedRow.meta = _.assign({}, savedRow.meta, {
letsencrypt_certificate: certInfo,
});
await internalCertificate.addCreatedAuditLog(access, certificate.id, savedRow);
return savedRow;
} catch (err) { } catch (err) {
// In the event of failure, revert things and throw err back // Delete the certificate from the database if it was not created successfully
await internalNginx.deleteLetsEncryptRequestConfig(certificate); await certificateModel.query().deleteById(certificate.id);
await internalCertificate.enableInUseHosts(inUseResult);
await internalNginx.reload();
throw err; throw err;
} }
} }
} catch (err) {
// At this point, the letsencrypt cert should exist on disk. // Delete the certificate here. This is a hard delete, since it never existed properly
// Lets get the expiry date from the file and update the row silently await certificateModel.query().deleteById(certificate.id);
try { throw err;
const certInfo = await internalCertificate.getCertificateInfoFromFile(
`${internalCertificate.getLiveCertPath(certificate.id)}/fullchain.pem`,
);
const savedRow = await certificateModel
.query()
.patchAndFetchById(certificate.id, {
expires_on: moment(certInfo.dates.to, "X").format("YYYY-MM-DD HH:mm:ss"),
})
.then(utils.omitRow(omissions()));
// Add cert data for audit log
savedRow.meta = _.assign({}, savedRow.meta, {
letsencrypt_certificate: certInfo,
});
return savedRow;
} catch (err) {
// Delete the certificate from the database if it was not created successfully
await certificateModel.query().deleteById(certificate.id);
throw err;
}
} }
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;
}, },
/** /**
@@ -276,10 +293,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") {
@@ -296,7 +310,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;
}, },
@@ -325,7 +356,7 @@ const internalCertificate = {
const opName = `/tmp/${downloadName}`; const opName = `/tmp/${downloadName}`;
await internalCertificate.zipFiles(certFiles, opName); await internalCertificate.zipFiles(certFiles, opName);
logger.debug("zip completed : ", opName); debug(logger, "zip completed : ", opName);
return { return {
fileName: opName, fileName: opName,
}; };
@@ -345,7 +376,7 @@ const internalCertificate = {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
source.map((fl) => { source.map((fl) => {
const fileName = path.basename(fl); const fileName = path.basename(fl);
logger.debug(fl, "added to certificate zip"); debug(logger, fl, "added to certificate zip");
archive.file(fl, { name: fileName }); archive.file(fl, { name: fileName });
return true; return true;
}); });
@@ -406,7 +437,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") {
@@ -424,7 +455,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;
}, },
/** /**
@@ -747,18 +782,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(","),
]; ];
@@ -797,14 +832,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",
@@ -868,9 +905,11 @@ const internalCertificate = {
object_id: updatedCertificate.id, object_id: updatedCertificate.id,
meta: updatedCertificate, meta: updatedCertificate,
}); });
} else {
throw new error.ValidationError("Only Let'sEncrypt certificates can be renewed"); return updatedCertificate;
} }
throw new error.ValidationError("Only Let'sEncrypt certificates can be renewed");
}, },
/** /**
@@ -888,13 +927,13 @@ 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",
]; ];
@@ -929,11 +968,13 @@ 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",
]; ];
@@ -963,9 +1004,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",
@@ -1046,124 +1087,24 @@ const internalCertificate = {
} }
}, },
testHttpsChallenge: async (access, domains) => { /**
*
* @param {Object} payload
* @param {string[]} payload.domains
* @returns
*/
testHttpsChallenge: async (access, payload) => {
await access.can("certificates:list"); await access.can("certificates:list");
if (!isArray(domains)) {
throw new error.InternalValidationError("Domains must be an array of strings");
}
if (domains.length === 0) {
throw new error.InternalValidationError("No domains provided");
}
// Create a test challenge file // Create a test challenge file
const testChallengeDir = "/data/letsencrypt-acme-challenge/.well-known/acme-challenge"; const testChallengeDir = "/data/letsencrypt-acme-challenge/.well-known/acme-challenge";
const testChallengeFile = `${testChallengeDir}/test-challenge`; const testChallengeFile = `${testChallengeDir}/test-challenge`;
fs.mkdirSync(testChallengeDir, { recursive: true }); fs.mkdirSync(testChallengeDir, { recursive: true });
fs.writeFileSync(testChallengeFile, "Success", { encoding: "utf8" }); fs.writeFileSync(testChallengeFile, "Success", { encoding: "utf8" });
async function performTestForDomain(domain) {
logger.info(`Testing http challenge for ${domain}`);
const url = `http://${domain}/.well-known/acme-challenge/test-challenge`;
const formBody = `method=G&url=${encodeURI(url)}&bodytype=T&requestbody=&headername=User-Agent&headervalue=None&locationid=1&ch=false&cc=false`;
const options = {
method: "POST",
headers: {
"User-Agent": "Mozilla/5.0",
"Content-Type": "application/x-www-form-urlencoded",
"Content-Length": Buffer.byteLength(formBody),
},
};
const result = await new Promise((resolve) => {
const req = https.request("https://www.site24x7.com/tools/restapi-tester", options, (res) => {
let responseBody = "";
res.on("data", (chunk) => {
responseBody = responseBody + chunk;
});
res.on("end", () => {
try {
const parsedBody = JSON.parse(`${responseBody}`);
if (res.statusCode !== 200) {
logger.warn(
`Failed to test HTTP challenge for domain ${domain} because HTTP status code ${res.statusCode} was returned: ${parsedBody.message}`,
);
resolve(undefined);
} else {
resolve(parsedBody);
}
} catch (err) {
if (res.statusCode !== 200) {
logger.warn(
`Failed to test HTTP challenge for domain ${domain} because HTTP status code ${res.statusCode} was returned`,
);
} else {
logger.warn(
`Failed to test HTTP challenge for domain ${domain} because response failed to be parsed: ${err.message}`,
);
}
resolve(undefined);
}
});
});
// Make sure to write the request body.
req.write(formBody);
req.end();
req.on("error", (e) => {
logger.warn(`Failed to test HTTP challenge for domain ${domain}`, e);
resolve(undefined);
});
});
if (!result) {
// Some error occurred while trying to get the data
return "failed";
}
if (result.error) {
logger.info(
`HTTP challenge test failed for domain ${domain} because error was returned: ${result.error.msg}`,
);
return `other:${result.error.msg}`;
}
if (`${result.responsecode}` === "200" && result.htmlresponse === "Success") {
// Server exists and has responded with the correct data
return "ok";
}
if (`${result.responsecode}` === "200") {
// Server exists but has responded with wrong data
logger.info(
`HTTP challenge test failed for domain ${domain} because of invalid returned data:`,
result.htmlresponse,
);
return "wrong-data";
}
if (`${result.responsecode}` === "404") {
// Server exists but responded with a 404
logger.info(`HTTP challenge test failed for domain ${domain} because code 404 was returned`);
return "404";
}
if (
`${result.responsecode}` === "0" ||
(typeof result.reason === "string" && result.reason.toLowerCase() === "host unavailable")
) {
// Server does not exist at domain
logger.info(`HTTP challenge test failed for domain ${domain} the host was not found`);
return "no-host";
}
// Other errors
logger.info(
`HTTP challenge test failed for domain ${domain} because code ${result.responsecode} was returned`,
);
return `other:${result.responsecode}`;
}
const results = {}; const results = {};
for (const domain of payload.domains) {
for (const domain of domains) { results[domain] = await internalCertificate.performTestForDomain(domain);
results[domain] = await performTestForDomain(domain);
} }
// Remove the test challenge file // Remove the test challenge file
@@ -1172,6 +1113,104 @@ const internalCertificate = {
return results; return results;
}, },
performTestForDomain: async (domain) => {
logger.info(`Testing http challenge for ${domain}`);
const agent = new ProxyAgent();
const url = `http://${domain}/.well-known/acme-challenge/test-challenge`;
const formBody = `method=G&url=${encodeURI(url)}&bodytype=T&requestbody=&headername=User-Agent&headervalue=None&locationid=1&ch=false&cc=false`;
const options = {
method: "POST",
headers: {
"User-Agent": "Mozilla/5.0",
"Content-Type": "application/x-www-form-urlencoded",
"Content-Length": Buffer.byteLength(formBody),
},
agent,
};
const result = await new Promise((resolve) => {
const req = https.request("https://www.site24x7.com/tools/restapi-tester", options, (res) => {
let responseBody = "";
res.on("data", (chunk) => {
responseBody = responseBody + chunk;
});
res.on("end", () => {
try {
const parsedBody = JSON.parse(`${responseBody}`);
if (res.statusCode !== 200) {
logger.warn(
`Failed to test HTTP challenge for domain ${domain} because HTTP status code ${res.statusCode} was returned: ${parsedBody.message}`,
);
resolve(undefined);
} else {
resolve(parsedBody);
}
} catch (err) {
if (res.statusCode !== 200) {
logger.warn(
`Failed to test HTTP challenge for domain ${domain} because HTTP status code ${res.statusCode} was returned`,
);
} else {
logger.warn(
`Failed to test HTTP challenge for domain ${domain} because response failed to be parsed: ${err.message}`,
);
}
resolve(undefined);
}
});
});
// Make sure to write the request body.
req.write(formBody);
req.end();
req.on("error", (e) => {
logger.warn(`Failed to test HTTP challenge for domain ${domain}`, e);
resolve(undefined);
});
});
if (!result) {
// Some error occurred while trying to get the data
return "failed";
}
if (result.error) {
logger.info(
`HTTP challenge test failed for domain ${domain} because error was returned: ${result.error.msg}`,
);
return `other:${result.error.msg}`;
}
if (`${result.responsecode}` === "200" && result.htmlresponse === "Success") {
// Server exists and has responded with the correct data
return "ok";
}
if (`${result.responsecode}` === "200") {
// Server exists but has responded with wrong data
logger.info(
`HTTP challenge test failed for domain ${domain} because of invalid returned data:`,
result.htmlresponse,
);
return "wrong-data";
}
if (`${result.responsecode}` === "404") {
// Server exists but responded with a 404
logger.info(`HTTP challenge test failed for domain ${domain} because code 404 was returned`);
return "404";
}
if (
`${result.responsecode}` === "0" ||
(typeof result.reason === "string" && result.reason.toLowerCase() === "host unavailable")
) {
// Server does not exist at domain
logger.info(`HTTP challenge test failed for domain ${domain} the host was not found`);
return "no-host";
}
// Other errors
logger.info(`HTTP challenge test failed for domain ${domain} because code ${result.responsecode} was returned`);
return `other:${result.responsecode}`;
},
getAdditionalCertbotArgs: (certificate_id, dns_provider) => { getAdditionalCertbotArgs: (certificate_id, dns_provider) => {
const args = []; const args = [];
if (useLetsencryptServer() !== null) { if (useLetsencryptServer() !== null) {

View File

@@ -2,6 +2,7 @@ import fs from "node:fs";
import https from "node:https"; import https from "node:https";
import { dirname } from "node:path"; import { dirname } from "node:path";
import { fileURLToPath } from "node:url"; import { fileURLToPath } from "node:url";
import { ProxyAgent } from "proxy-agent";
import errs from "../lib/error.js"; import errs from "../lib/error.js";
import utils from "../lib/utils.js"; import utils from "../lib/utils.js";
import { ipRanges as logger } from "../logger.js"; import { ipRanges as logger } from "../logger.js";
@@ -29,10 +30,11 @@ const internalIpRanges = {
}, },
fetchUrl: (url) => { fetchUrl: (url) => {
const agent = new ProxyAgent();
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
logger.info(`Fetching ${url}`); logger.info(`Fetching ${url}`);
return https return https
.get(url, (res) => { .get(url, { agent }, (res) => {
res.setEncoding("utf8"); res.setEncoding("utf8");
let raw_data = ""; let raw_data = "";
res.on("data", (chunk) => { res.on("data", (chunk) => {

View File

@@ -4,7 +4,7 @@ import { fileURLToPath } from "node:url";
import _ from "lodash"; import _ from "lodash";
import errs from "../lib/error.js"; import errs from "../lib/error.js";
import utils from "../lib/utils.js"; import utils from "../lib/utils.js";
import { nginx as logger } from "../logger.js"; import { debug, nginx as logger } from "../logger.js";
const __filename = fileURLToPath(import.meta.url); const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename); const __dirname = dirname(__filename);
@@ -68,7 +68,7 @@ const internalNginx = {
return true; return true;
}); });
logger.debug("Nginx test failed:", valid_lines.join("\n")); debug(logger, "Nginx test failed:", valid_lines.join("\n"));
// config is bad, update meta and delete config // config is bad, update meta and delete config
combined_meta = _.assign({}, host.meta, { combined_meta = _.assign({}, host.meta, {
@@ -102,7 +102,7 @@ const internalNginx = {
* @returns {Promise} * @returns {Promise}
*/ */
test: () => { test: () => {
logger.debug("Testing Nginx configuration"); debug(logger, "Testing Nginx configuration");
return utils.execFile("/usr/sbin/nginx", ["-t", "-g", "error_log off;"]); return utils.execFile("/usr/sbin/nginx", ["-t", "-g", "error_log off;"]);
}, },
@@ -190,7 +190,7 @@ const internalNginx = {
const host = JSON.parse(JSON.stringify(host_row)); const host = JSON.parse(JSON.stringify(host_row));
const nice_host_type = internalNginx.getFileFriendlyHostType(host_type); const nice_host_type = internalNginx.getFileFriendlyHostType(host_type);
logger.debug(`Generating ${nice_host_type} Config:`, JSON.stringify(host, null, 2)); debug(logger, `Generating ${nice_host_type} Config:`, JSON.stringify(host, null, 2));
const renderEngine = utils.getRenderEngine(); const renderEngine = utils.getRenderEngine();
@@ -241,7 +241,7 @@ const internalNginx = {
.parseAndRender(template, host) .parseAndRender(template, host)
.then((config_text) => { .then((config_text) => {
fs.writeFileSync(filename, config_text, { encoding: "utf8" }); fs.writeFileSync(filename, config_text, { encoding: "utf8" });
logger.debug("Wrote config:", filename, config_text); debug(logger, "Wrote config:", filename, config_text);
// Restore locations array // Restore locations array
host.locations = origLocations; host.locations = origLocations;
@@ -249,7 +249,7 @@ const internalNginx = {
resolve(true); resolve(true);
}) })
.catch((err) => { .catch((err) => {
logger.debug(`Could not write ${filename}:`, err.message); debug(logger, `Could not write ${filename}:`, err.message);
reject(new errs.ConfigurationError(err.message)); reject(new errs.ConfigurationError(err.message));
}); });
}); });
@@ -265,7 +265,7 @@ const internalNginx = {
* @returns {Promise} * @returns {Promise}
*/ */
generateLetsEncryptRequestConfig: (certificate) => { generateLetsEncryptRequestConfig: (certificate) => {
logger.debug("Generating LetsEncrypt Request Config:", certificate); debug(logger, "Generating LetsEncrypt Request Config:", certificate);
const renderEngine = utils.getRenderEngine(); const renderEngine = utils.getRenderEngine();
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@@ -285,11 +285,11 @@ const internalNginx = {
.parseAndRender(template, certificate) .parseAndRender(template, certificate)
.then((config_text) => { .then((config_text) => {
fs.writeFileSync(filename, config_text, { encoding: "utf8" }); fs.writeFileSync(filename, config_text, { encoding: "utf8" });
logger.debug("Wrote config:", filename, config_text); debug(logger, "Wrote config:", filename, config_text);
resolve(true); resolve(true);
}) })
.catch((err) => { .catch((err) => {
logger.debug(`Could not write ${filename}:`, err.message); debug(logger, `Could not write ${filename}:`, err.message);
reject(new errs.ConfigurationError(err.message)); reject(new errs.ConfigurationError(err.message));
}); });
}); });
@@ -305,10 +305,10 @@ const internalNginx = {
return; return;
} }
try { try {
logger.debug(`Deleting file: ${filename}`); debug(logger, `Deleting file: ${filename}`);
fs.unlinkSync(filename); fs.unlinkSync(filename);
} catch (err) { } catch (err) {
logger.debug("Could not delete file:", JSON.stringify(err, null, 2)); debug(logger, "Could not delete file:", JSON.stringify(err, null, 2));
} }
}, },
@@ -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

@@ -1,5 +1,5 @@
import batchflow from "batchflow"; import batchflow from "batchflow";
import dnsPlugins from "../global/certbot-dns-plugins.json" with { type: "json" }; import dnsPlugins from "../certbot/dns-plugins.json" with { type: "json" };
import { certbot as logger } from "../logger.js"; import { certbot as logger } from "../logger.js";
import errs from "./error.js"; import errs from "./error.js";
import utils from "./utils.js"; import utils from "./utils.js";
@@ -8,7 +8,7 @@ const CERTBOT_VERSION_REPLACEMENT = "$(certbot --version | grep -Eo '[0-9](\\.[0
/** /**
* Installs a cerbot plugin given the key for the object from * Installs a cerbot plugin given the key for the object from
* ../global/certbot-dns-plugins.json * ../certbot/dns-plugins.json
* *
* @param {string} pluginKey * @param {string} pluginKey
* @returns {Object} * @returns {Object}

View File

@@ -25,15 +25,26 @@ const configure = () => {
if (configData?.database) { if (configData?.database) {
logger.info(`Using configuration from file: ${filename}`); logger.info(`Using configuration from file: ${filename}`);
// Migrate those who have "mysql" engine to "mysql2"
if (configData.database.engine === "mysql") {
configData.database.engine = mysqlEngine;
}
instance = configData; instance = configData;
instance.keys = getKeys(); instance.keys = getKeys();
return; return;
} }
} }
const envMysqlHost = process.env.DB_MYSQL_HOST || null; const toBool = (v) => /^(1|true|yes|on)$/i.test((v || '').trim());
const envMysqlUser = process.env.DB_MYSQL_USER || null;
const envMysqlName = process.env.DB_MYSQL_NAME || null; const envMysqlHost = process.env.DB_MYSQL_HOST || null;
const envMysqlUser = process.env.DB_MYSQL_USER || null;
const envMysqlName = process.env.DB_MYSQL_NAME || null;
const envMysqlSSL = toBool(process.env.DB_MYSQL_SSL);
const envMysqlSSLRejectUnauthorized = process.env.DB_MYSQL_SSL_REJECT_UNAUTHORIZED === undefined ? true : toBool(process.env.DB_MYSQL_SSL_REJECT_UNAUTHORIZED);
const envMysqlSSLVerifyIdentity = process.env.DB_MYSQL_SSL_VERIFY_IDENTITY === undefined ? true : toBool(process.env.DB_MYSQL_SSL_VERIFY_IDENTITY);
if (envMysqlHost && envMysqlUser && envMysqlName) { if (envMysqlHost && envMysqlUser && envMysqlName) {
// we have enough mysql creds to go with mysql // we have enough mysql creds to go with mysql
logger.info("Using MySQL configuration"); logger.info("Using MySQL configuration");
@@ -44,7 +55,8 @@ const configure = () => {
port: process.env.DB_MYSQL_PORT || 3306, port: process.env.DB_MYSQL_PORT || 3306,
user: envMysqlUser, user: envMysqlUser,
password: process.env.DB_MYSQL_PASSWORD, password: process.env.DB_MYSQL_PASSWORD,
name: envMysqlName, name: envMysqlName,
ssl: envMysqlSSL ? { rejectUnauthorized: envMysqlSSLRejectUnauthorized, verifyIdentity: envMysqlSSLVerifyIdentity } : false,
}, },
keys: getKeys(), keys: getKeys(),
}; };
@@ -90,7 +102,9 @@ const configure = () => {
const getKeys = () => { const getKeys = () => {
// Get keys from file // Get keys from file
logger.debug("Cheecking for keys file:", keysFile); if (isDebugMode()) {
logger.debug("Checking for keys file:", keysFile);
}
if (!fs.existsSync(keysFile)) { if (!fs.existsSync(keysFile)) {
generateKeys(); generateKeys();
} else if (process.env.DEBUG) { } else if (process.env.DEBUG) {

View File

@@ -3,14 +3,14 @@ import { dirname } from "node:path";
import { fileURLToPath } from "node:url"; import { fileURLToPath } from "node:url";
import { Liquid } from "liquidjs"; import { Liquid } from "liquidjs";
import _ from "lodash"; import _ from "lodash";
import { global as logger } from "../logger.js"; import { debug, global as logger } from "../logger.js";
import errs from "./error.js"; import errs from "./error.js";
const __filename = fileURLToPath(import.meta.url); const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename); const __dirname = dirname(__filename);
const exec = async (cmd, options = {}) => { const exec = async (cmd, options = {}) => {
logger.debug("CMD:", cmd); debug(logger, "CMD:", cmd);
const { stdout, stderr } = await new Promise((resolve, reject) => { const { stdout, stderr } = await new Promise((resolve, reject) => {
const child = nodeExec(cmd, options, (isError, stdout, stderr) => { const child = nodeExec(cmd, options, (isError, stdout, stderr) => {
if (isError) { if (isError) {
@@ -34,7 +34,7 @@ const exec = async (cmd, options = {}) => {
* @returns {Promise} * @returns {Promise}
*/ */
const execFile = (cmd, args, options) => { const execFile = (cmd, args, options) => {
logger.debug(`CMD: ${cmd} ${args ? args.join(" ") : ""}`); debug(logger, `CMD: ${cmd} ${args ? args.join(" ") : ""}`);
const opts = options || {}; const opts = options || {};
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {

View File

@@ -24,16 +24,21 @@ const apiValidator = async (schema, payload /*, description*/) => {
throw new errs.ValidationError("Payload is undefined"); throw new errs.ValidationError("Payload is undefined");
} }
const validate = ajv.compile(schema); const validate = ajv.compile(schema);
const valid = validate(payload); const valid = validate(payload);
if (valid && !validate.errors) { if (valid && !validate.errors) {
return payload; return payload;
} }
const message = ajv.errorsText(validate.errors); const message = ajv.errorsText(validate.errors);
const err = new errs.ValidationError(message); const err = new errs.ValidationError(message);
err.debug = [validate.errors, payload]; err.debug = {validationErrors: validate.errors, payload};
throw err; throw err;
}; };

View File

@@ -1,4 +1,5 @@
import signale from "signale"; import signale from "signale";
import { isDebugMode } from "./lib/config.js";
const opts = { const opts = {
logLevel: "info", logLevel: "info",
@@ -15,4 +16,10 @@ const importer = new signale.Signale({ scope: "Importer ", ...opts });
const setup = new signale.Signale({ scope: "Setup ", ...opts }); const setup = new signale.Signale({ scope: "Setup ", ...opts });
const ipRanges = new signale.Signale({ scope: "IP Ranges", ...opts }); const ipRanges = new signale.Signale({ scope: "IP Ranges", ...opts });
export { global, migrate, express, access, nginx, ssl, certbot, importer, setup, ipRanges }; const debug = (logger, ...args) => {
if (isDebugMode()) {
logger.debug(...args);
}
};
export { debug, global, migrate, express, access, nginx, ssl, certbot, importer, setup, ipRanges };

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,25 +20,26 @@
"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",
"moment": "^2.29.4", "moment": "^2.30.1",
"mysql2": "^3.11.1", "mysql2": "^3.15.3",
"node-rsa": "^1.0.8", "node-rsa": "^1.1.1",
"objection": "3.0.1", "objection": "3.0.1",
"path": "^0.12.7", "path": "^0.12.7",
"pg": "^8.13.1", "pg": "^8.16.3",
"proxy-agent": "^6.5.0",
"signale": "1.4.0", "signale": "1.4.0",
"sqlite3": "5.1.6", "sqlite3": "^5.1.7",
"temp-write": "^4.0.0" "temp-write": "^4.0.0"
}, },
"devDependencies": { "devDependencies": {
"@apidevtools/swagger-parser": "^10.1.0", "@apidevtools/swagger-parser": "^10.1.0",
"@biomejs/biome": "^2.2.4", "@biomejs/biome": "^2.3.2",
"chalk": "4.1.2", "chalk": "4.1.2",
"nodemon": "^2.0.2" "nodemon": "^2.0.2"
}, },

View File

@@ -2,7 +2,7 @@ import express from "express";
import internalAuditLog from "../internal/audit-log.js"; import internalAuditLog from "../internal/audit-log.js";
import jwtdecode from "../lib/express/jwt-decode.js"; import jwtdecode from "../lib/express/jwt-decode.js";
import validator from "../lib/validator/index.js"; import validator from "../lib/validator/index.js";
import { express as logger } from "../logger.js"; import { debug, express as logger } from "../logger.js";
const router = express.Router({ const router = express.Router({
caseSensitive: true, caseSensitive: true,
@@ -47,7 +47,7 @@ router
const rows = await internalAuditLog.getAll(res.locals.access, data.expand, data.query); const rows = await internalAuditLog.getAll(res.locals.access, data.expand, data.query);
res.status(200).send(rows); res.status(200).send(rows);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}); });
@@ -99,7 +99,7 @@ router
}); });
res.status(200).send(item); res.status(200).send(item);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}); });

View File

@@ -3,7 +3,7 @@ import internalAccessList from "../../internal/access-list.js";
import jwtdecode from "../../lib/express/jwt-decode.js"; import jwtdecode from "../../lib/express/jwt-decode.js";
import apiValidator from "../../lib/validator/api.js"; import apiValidator from "../../lib/validator/api.js";
import validator from "../../lib/validator/index.js"; import validator from "../../lib/validator/index.js";
import { express as logger } from "../../logger.js"; import { debug, express as logger } from "../../logger.js";
import { getValidationSchema } from "../../schema/index.js"; import { getValidationSchema } from "../../schema/index.js";
const router = express.Router({ const router = express.Router({
@@ -49,7 +49,7 @@ router
const rows = await internalAccessList.getAll(res.locals.access, data.expand, data.query); const rows = await internalAccessList.getAll(res.locals.access, data.expand, data.query);
res.status(200).send(rows); res.status(200).send(rows);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}) })
@@ -65,7 +65,7 @@ router
const result = await internalAccessList.create(res.locals.access, payload); const result = await internalAccessList.create(res.locals.access, payload);
res.status(201).send(result); res.status(201).send(result);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}); });
@@ -113,7 +113,7 @@ router
}); });
res.status(200).send(row); res.status(200).send(row);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}) })
@@ -130,7 +130,7 @@ router
const result = await internalAccessList.update(res.locals.access, payload); const result = await internalAccessList.update(res.locals.access, payload);
res.status(200).send(result); res.status(200).send(result);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}) })
@@ -147,7 +147,7 @@ router
}); });
res.status(200).send(result); res.status(200).send(result);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}); });

View File

@@ -1,11 +1,11 @@
import express from "express"; import express from "express";
import dnsPlugins from "../../global/certbot-dns-plugins.json" with { type: "json" }; import dnsPlugins from "../../certbot/dns-plugins.json" with { type: "json" };
import internalCertificate from "../../internal/certificate.js"; import internalCertificate from "../../internal/certificate.js";
import errs from "../../lib/error.js"; import errs from "../../lib/error.js";
import jwtdecode from "../../lib/express/jwt-decode.js"; import jwtdecode from "../../lib/express/jwt-decode.js";
import apiValidator from "../../lib/validator/api.js"; import apiValidator from "../../lib/validator/api.js";
import validator from "../../lib/validator/index.js"; import validator from "../../lib/validator/index.js";
import { express as logger } from "../../logger.js"; import { debug, express as logger } from "../../logger.js";
import { getValidationSchema } from "../../schema/index.js"; import { getValidationSchema } from "../../schema/index.js";
const router = express.Router({ const router = express.Router({
@@ -44,14 +44,21 @@ router
}, },
}, },
{ {
expand: typeof req.query.expand === "string" ? req.query.expand.split(",") : null, expand:
typeof req.query.expand === "string"
? req.query.expand.split(",")
: null,
query: typeof req.query.query === "string" ? req.query.query : null, query: typeof req.query.query === "string" ? req.query.query : null,
}, },
); );
const rows = await internalCertificate.getAll(res.locals.access, data.expand, data.query); const rows = await internalCertificate.getAll(
res.locals.access,
data.expand,
data.query,
);
res.status(200).send(rows); res.status(200).send(rows);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}) })
@@ -63,12 +70,18 @@ router
*/ */
.post(async (req, res, next) => { .post(async (req, res, next) => {
try { try {
const payload = await apiValidator(getValidationSchema("/nginx/certificates", "post"), req.body); const payload = await apiValidator(
getValidationSchema("/nginx/certificates", "post"),
req.body,
);
req.setTimeout(900000); // 15 minutes timeout req.setTimeout(900000); // 15 minutes timeout
const result = await internalCertificate.create(res.locals.access, payload); const result = await internalCertificate.create(
res.locals.access,
payload,
);
res.status(201).send(result); res.status(201).send(result);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}); });
@@ -102,7 +115,7 @@ router
clean.sort((a, b) => a.name.localeCompare(b.name)); clean.sort((a, b) => a.name.localeCompare(b.name));
res.status(200).send(clean); res.status(200).send(clean);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}); });
@@ -120,29 +133,29 @@ router
.all(jwtdecode()) .all(jwtdecode())
/** /**
* GET /api/nginx/certificates/test-http * POST /api/nginx/certificates/test-http
* *
* Test HTTP challenge for domains * Test HTTP challenge for domains
*/ */
.get(async (req, res, next) => { .post(async (req, res, next) => {
if (req.query.domains === undefined) {
next(new errs.ValidationError("Domains are required as query parameters"));
return;
}
try { try {
const payload = await apiValidator(
getValidationSchema("/nginx/certificates/test-http", "post"),
req.body,
);
req.setTimeout(60000); // 1 minute timeout
const result = await internalCertificate.testHttpsChallenge( const result = await internalCertificate.testHttpsChallenge(
res.locals.access, res.locals.access,
JSON.parse(req.query.domains), payload,
); );
res.status(200).send(result); res.status(200).send(result);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}); });
/** /**
* Validate Certs before saving * Validate Certs before saving
* *
@@ -172,7 +185,7 @@ router
}); });
res.status(200).send(result); res.status(200).send(result);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}); });
@@ -211,7 +224,10 @@ router
}, },
{ {
certificate_id: req.params.certificate_id, certificate_id: req.params.certificate_id,
expand: typeof req.query.expand === "string" ? req.query.expand.split(",") : null, expand:
typeof req.query.expand === "string"
? req.query.expand.split(",")
: null,
}, },
); );
const row = await internalCertificate.get(res.locals.access, { const row = await internalCertificate.get(res.locals.access, {
@@ -220,7 +236,7 @@ router
}); });
res.status(200).send(row); res.status(200).send(row);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}) })
@@ -237,7 +253,7 @@ router
}); });
res.status(200).send(result); res.status(200).send(result);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}); });
@@ -272,7 +288,7 @@ router
}); });
res.status(200).send(result); res.status(200).send(result);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}); });
@@ -302,7 +318,7 @@ router
}); });
res.status(200).send(result); res.status(200).send(result);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}); });
@@ -331,7 +347,7 @@ router
}); });
res.status(200).download(result.fileName); res.status(200).download(result.fileName);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}); });

View File

@@ -3,7 +3,7 @@ import internalDeadHost from "../../internal/dead-host.js";
import jwtdecode from "../../lib/express/jwt-decode.js"; import jwtdecode from "../../lib/express/jwt-decode.js";
import apiValidator from "../../lib/validator/api.js"; import apiValidator from "../../lib/validator/api.js";
import validator from "../../lib/validator/index.js"; import validator from "../../lib/validator/index.js";
import { express as logger } from "../../logger.js"; import { debug, express as logger } from "../../logger.js";
import { getValidationSchema } from "../../schema/index.js"; import { getValidationSchema } from "../../schema/index.js";
const router = express.Router({ const router = express.Router({
@@ -49,7 +49,7 @@ router
const rows = await internalDeadHost.getAll(res.locals.access, data.expand, data.query); const rows = await internalDeadHost.getAll(res.locals.access, data.expand, data.query);
res.status(200).send(rows); res.status(200).send(rows);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}) })
@@ -65,7 +65,7 @@ router
const result = await internalDeadHost.create(res.locals.access, payload); const result = await internalDeadHost.create(res.locals.access, payload);
res.status(201).send(result); res.status(201).send(result);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}); });
@@ -113,7 +113,7 @@ router
}); });
res.status(200).send(row); res.status(200).send(row);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}) })
@@ -130,7 +130,7 @@ router
const result = await internalDeadHost.update(res.locals.access, payload); const result = await internalDeadHost.update(res.locals.access, payload);
res.status(200).send(result); res.status(200).send(result);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}) })
@@ -147,7 +147,7 @@ router
}); });
res.status(200).send(result); res.status(200).send(result);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}); });
@@ -174,7 +174,7 @@ router
}); });
res.status(200).send(result); res.status(200).send(result);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}); });
@@ -199,7 +199,7 @@ router
const result = internalDeadHost.disable(res.locals.access, { id: Number.parseInt(req.params.host_id, 10) }); const result = internalDeadHost.disable(res.locals.access, { id: Number.parseInt(req.params.host_id, 10) });
res.status(200).send(result); res.status(200).send(result);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}); });

View File

@@ -3,7 +3,7 @@ import internalProxyHost from "../../internal/proxy-host.js";
import jwtdecode from "../../lib/express/jwt-decode.js"; import jwtdecode from "../../lib/express/jwt-decode.js";
import apiValidator from "../../lib/validator/api.js"; import apiValidator from "../../lib/validator/api.js";
import validator from "../../lib/validator/index.js"; import validator from "../../lib/validator/index.js";
import { express as logger } from "../../logger.js"; import { debug, express as logger } from "../../logger.js";
import { getValidationSchema } from "../../schema/index.js"; import { getValidationSchema } from "../../schema/index.js";
const router = express.Router({ const router = express.Router({
@@ -49,7 +49,7 @@ router
const rows = await internalProxyHost.getAll(res.locals.access, data.expand, data.query); const rows = await internalProxyHost.getAll(res.locals.access, data.expand, data.query);
res.status(200).send(rows); res.status(200).send(rows);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}) })
@@ -65,7 +65,7 @@ router
const result = await internalProxyHost.create(res.locals.access, payload); const result = await internalProxyHost.create(res.locals.access, payload);
res.status(201).send(result); res.status(201).send(result);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err} ${JSON.stringify(err.debug, null, 2)}`);
next(err); next(err);
} }
}); });
@@ -113,7 +113,7 @@ router
}); });
res.status(200).send(row); res.status(200).send(row);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}) })
@@ -130,7 +130,7 @@ router
const result = await internalProxyHost.update(res.locals.access, payload); const result = await internalProxyHost.update(res.locals.access, payload);
res.status(200).send(result); res.status(200).send(result);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}) })
@@ -147,7 +147,7 @@ router
}); });
res.status(200).send(result); res.status(200).send(result);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}); });
@@ -174,7 +174,7 @@ router
}); });
res.status(200).send(result); res.status(200).send(result);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}); });
@@ -201,7 +201,7 @@ router
}); });
res.status(200).send(result); res.status(200).send(result);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}); });

View File

@@ -3,7 +3,7 @@ import internalRedirectionHost from "../../internal/redirection-host.js";
import jwtdecode from "../../lib/express/jwt-decode.js"; import jwtdecode from "../../lib/express/jwt-decode.js";
import apiValidator from "../../lib/validator/api.js"; import apiValidator from "../../lib/validator/api.js";
import validator from "../../lib/validator/index.js"; import validator from "../../lib/validator/index.js";
import { express as logger } from "../../logger.js"; import { debug, express as logger } from "../../logger.js";
import { getValidationSchema } from "../../schema/index.js"; import { getValidationSchema } from "../../schema/index.js";
const router = express.Router({ const router = express.Router({
@@ -49,7 +49,7 @@ router
const rows = await internalRedirectionHost.getAll(res.locals.access, data.expand, data.query); const rows = await internalRedirectionHost.getAll(res.locals.access, data.expand, data.query);
res.status(200).send(rows); res.status(200).send(rows);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}) })
@@ -65,7 +65,7 @@ router
const result = await internalRedirectionHost.create(res.locals.access, payload); const result = await internalRedirectionHost.create(res.locals.access, payload);
res.status(201).send(result); res.status(201).send(result);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}); });
@@ -113,7 +113,7 @@ router
}); });
res.status(200).send(row); res.status(200).send(row);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}) })
@@ -133,7 +133,7 @@ router
const result = await internalRedirectionHost.update(res.locals.access, payload); const result = await internalRedirectionHost.update(res.locals.access, payload);
res.status(200).send(result); res.status(200).send(result);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}) })
@@ -150,7 +150,7 @@ router
}); });
res.status(200).send(result); res.status(200).send(result);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}); });
@@ -177,7 +177,7 @@ router
}); });
res.status(200).send(result); res.status(200).send(result);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}); });
@@ -204,7 +204,7 @@ router
}); });
res.status(200).send(result); res.status(200).send(result);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}); });

View File

@@ -3,7 +3,7 @@ import internalStream from "../../internal/stream.js";
import jwtdecode from "../../lib/express/jwt-decode.js"; import jwtdecode from "../../lib/express/jwt-decode.js";
import apiValidator from "../../lib/validator/api.js"; import apiValidator from "../../lib/validator/api.js";
import validator from "../../lib/validator/index.js"; import validator from "../../lib/validator/index.js";
import { express as logger } from "../../logger.js"; import { debug, express as logger } from "../../logger.js";
import { getValidationSchema } from "../../schema/index.js"; import { getValidationSchema } from "../../schema/index.js";
const router = express.Router({ const router = express.Router({
@@ -49,7 +49,7 @@ router
const rows = await internalStream.getAll(res.locals.access, data.expand, data.query); const rows = await internalStream.getAll(res.locals.access, data.expand, data.query);
res.status(200).send(rows); res.status(200).send(rows);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}) })
@@ -65,7 +65,7 @@ router
const result = await internalStream.create(res.locals.access, payload); const result = await internalStream.create(res.locals.access, payload);
res.status(201).send(result); res.status(201).send(result);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}); });
@@ -113,7 +113,7 @@ router
}); });
res.status(200).send(row); res.status(200).send(row);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}) })
@@ -130,7 +130,7 @@ router
const result = await internalStream.update(res.locals.access, payload); const result = await internalStream.update(res.locals.access, payload);
res.status(200).send(result); res.status(200).send(result);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}) })
@@ -147,7 +147,7 @@ router
}); });
res.status(200).send(result); res.status(200).send(result);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}); });
@@ -174,7 +174,7 @@ router
}); });
res.status(200).send(result); res.status(200).send(result);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}); });
@@ -201,7 +201,7 @@ router
}); });
res.status(200).send(result); res.status(200).send(result);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}); });

View File

@@ -1,7 +1,7 @@
import express from "express"; import express from "express";
import internalReport from "../internal/report.js"; import internalReport from "../internal/report.js";
import jwtdecode from "../lib/express/jwt-decode.js"; import jwtdecode from "../lib/express/jwt-decode.js";
import { express as logger } from "../logger.js"; import { debug, express as logger } from "../logger.js";
const router = express.Router({ const router = express.Router({
caseSensitive: true, caseSensitive: true,
@@ -24,7 +24,7 @@ router
const data = await internalReport.getHostsReport(res.locals.access); const data = await internalReport.getHostsReport(res.locals.access);
res.status(200).send(data); res.status(200).send(data);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}); });

View File

@@ -1,5 +1,5 @@
import express from "express"; import express from "express";
import { express as logger } from "../logger.js"; import { debug, express as logger } from "../logger.js";
import PACKAGE from "../package.json" with { type: "json" }; import PACKAGE from "../package.json" with { type: "json" };
import { getCompiledSchema } from "../schema/index.js"; import { getCompiledSchema } from "../schema/index.js";
@@ -36,7 +36,7 @@ router
swaggerJSON.servers[0].url = `${origin}/api`; swaggerJSON.servers[0].url = `${origin}/api`;
res.status(200).send(swaggerJSON); res.status(200).send(swaggerJSON);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}); });

View File

@@ -3,7 +3,7 @@ import internalSetting from "../internal/setting.js";
import jwtdecode from "../lib/express/jwt-decode.js"; import jwtdecode from "../lib/express/jwt-decode.js";
import apiValidator from "../lib/validator/api.js"; import apiValidator from "../lib/validator/api.js";
import validator from "../lib/validator/index.js"; import validator from "../lib/validator/index.js";
import { express as logger } from "../logger.js"; import { debug, express as logger } from "../logger.js";
import { getValidationSchema } from "../schema/index.js"; import { getValidationSchema } from "../schema/index.js";
const router = express.Router({ const router = express.Router({
@@ -32,7 +32,7 @@ router
const rows = await internalSetting.getAll(res.locals.access); const rows = await internalSetting.getAll(res.locals.access);
res.status(200).send(rows); res.status(200).send(rows);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}); });
@@ -76,7 +76,7 @@ router
}); });
res.status(200).send(row); res.status(200).send(row);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}) })
@@ -93,7 +93,7 @@ router
const result = await internalSetting.update(res.locals.access, payload); const result = await internalSetting.update(res.locals.access, payload);
res.status(200).send(result); res.status(200).send(result);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}); });

View File

@@ -2,7 +2,7 @@ import express from "express";
import internalToken from "../internal/token.js"; import internalToken from "../internal/token.js";
import jwtdecode from "../lib/express/jwt-decode.js"; import jwtdecode from "../lib/express/jwt-decode.js";
import apiValidator from "../lib/validator/api.js"; import apiValidator from "../lib/validator/api.js";
import { express as logger } from "../logger.js"; import { debug, express as logger } from "../logger.js";
import { getValidationSchema } from "../schema/index.js"; import { getValidationSchema } from "../schema/index.js";
const router = express.Router({ const router = express.Router({
@@ -32,7 +32,7 @@ router
}); });
res.status(200).send(data); res.status(200).send(data);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}) })
@@ -48,7 +48,7 @@ router
const result = await internalToken.getTokenFromEmail(data); const result = await internalToken.getTokenFromEmail(data);
res.status(200).send(result); res.status(200).send(result);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}); });

View File

@@ -7,7 +7,7 @@ import jwtdecode from "../lib/express/jwt-decode.js";
import userIdFromMe from "../lib/express/user-id-from-me.js"; import userIdFromMe from "../lib/express/user-id-from-me.js";
import apiValidator from "../lib/validator/api.js"; import apiValidator from "../lib/validator/api.js";
import validator from "../lib/validator/index.js"; import validator from "../lib/validator/index.js";
import { express as logger } from "../logger.js"; import { debug, express as logger } from "../logger.js";
import { getValidationSchema } from "../schema/index.js"; import { getValidationSchema } from "../schema/index.js";
import { isSetup } from "../setup.js"; import { isSetup } from "../setup.js";
@@ -61,7 +61,7 @@ router
); );
res.status(200).send(users); res.status(200).send(users);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}) })
@@ -101,7 +101,7 @@ router
const user = await internalUser.create(res.locals.access, payload); const user = await internalUser.create(res.locals.access, payload);
res.status(201).send(user); res.status(201).send(user);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}) })
@@ -124,7 +124,7 @@ router
await internalUser.deleteAll(); await internalUser.deleteAll();
res.status(200).send(true); res.status(200).send(true);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
return; return;
@@ -185,7 +185,7 @@ router
}); });
res.status(200).send(user); res.status(200).send(user);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}) })
@@ -205,7 +205,7 @@ router
const result = await internalUser.update(res.locals.access, payload); const result = await internalUser.update(res.locals.access, payload);
res.status(200).send(result); res.status(200).send(result);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}) })
@@ -222,7 +222,7 @@ router
}); });
res.status(200).send(result); res.status(200).send(result);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}); });
@@ -255,7 +255,7 @@ router
const result = await internalUser.setPassword(res.locals.access, payload); const result = await internalUser.setPassword(res.locals.access, payload);
res.status(200).send(result); res.status(200).send(result);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}); });
@@ -291,7 +291,7 @@ router
); );
res.status(200).send(result); res.status(200).send(result);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}); });
@@ -320,7 +320,7 @@ router
}); });
res.status(200).send(result); res.status(200).send(result);
} catch (err) { } catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err); next(err);
} }
}); });

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

@@ -0,0 +1,23 @@
{
"type": "array",
"description": "DNS Providers list",
"items": {
"type": "object",
"required": ["id", "name", "credentials"],
"additionalProperties": false,
"properties": {
"id": {
"type": "string",
"description": "Unique identifier for the DNS provider, matching the python package"
},
"name": {
"type": "string",
"description": "Human-readable name of the DNS provider"
},
"credentials": {
"type": "string",
"description": "Instructions on how to format the credentials for this DNS provider"
}
}
}
}

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,22 +30,16 @@
"description": "200 response", "description": "200 response",
"content": { "content": {
"application/json": { "application/json": {
"examples": { "example": {
"default": { "id": 1,
"value": [ "created_on": "2024-10-08T22:15:40.000Z",
{ "modified_on": "2024-10-08T22:15:40.000Z",
"id": 1, "owner_user_id": 1,
"created_on": "2024-10-08T22:15:40.000Z", "name": "test1234",
"modified_on": "2024-10-08T22:15:40.000Z", "meta": {},
"owner_user_id": 1, "satisfy_any": true,
"name": "test1234", "pass_auth": false,
"meta": {}, "proxy_host_count": 0
"satisfy_any": true,
"pass_auth": false,
"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,49 +1,54 @@
{ {
"operationId": "getAccessList", "operationId": "getAccessList",
"summary": "Get a access List", "summary": "Get a access List",
"tags": ["Access Lists"], "tags": [
"security": [ "access-lists"
{ ],
"BearerAuth": ["access_lists"] "security": [
} {
], "bearerAuth": [
"parameters": [ "access_lists.view"
{ ]
"in": "path", }
"name": "listID", ],
"schema": { "parameters": [
"type": "integer", {
"minimum": 1 "in": "path",
}, "name": "listID",
"required": true, "description": "Access List ID",
"example": 1 "schema": {
} "type": "integer",
], "minimum": 1
"responses": { },
"200": { "required": true,
"description": "200 response", "example": 1
"content": { }
"application/json": { ],
"examples": { "responses": {
"default": { "200": {
"value": { "description": "200 response",
"id": 1, "content": {
"created_on": "2020-01-30T09:36:08.000Z", "application/json": {
"modified_on": "2020-01-30T09:41:04.000Z", "examples": {
"is_disabled": false, "default": {
"email": "jc@jc21.com", "value": {
"name": "Jamie Curnow", "id": 1,
"nickname": "James", "created_on": "2025-10-28T04:06:55.000Z",
"avatar": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm", "modified_on": "2025-10-29T22:48:20.000Z",
"roles": ["admin"] "owner_user_id": 1,
} "name": "My Access List",
} "meta": {},
}, "satisfy_any": false,
"schema": { "pass_auth": false,
"$ref": "../../../../components/access-list-object.json" "proxy_host_count": 1
} }
} }
} },
} "schema": {
} "$ref": "../../../../components/access-list-object.json"
}
}
}
}
}
} }

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]))?$"
},
{
"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$"
}
]
},
"directive": {
"$ref": "../../../../components/access-list-object.json#/properties/directive"
}
}
}
} }
} }
},
"example": {
"name": "My Access List",
"satisfy_any": true,
"pass_auth": false,
"items": [
{
"username": "admin2",
"password": "pass2"
}
],
"clients": [
{
"directive": "allow",
"address": "192.168.0.0/24"
}
]
} }
} }
} }
@@ -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]))?$"
},
{
"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$"
}
]
},
"directive": {
"$ref": "../../../components/access-list-object.json#/properties/directive"
}
}
}
},
"meta": {
"$ref": "../../../components/access-list-object.json#/properties/meta"
} }
} }
},
"example": {
"name": "My Access List",
"satisfy_any": true,
"pass_auth": false,
"items": [
{
"username": "admin",
"password": "pass"
}
],
"clients": [
{
"directive": "allow",
"address": "192.168.0.0/24"
}
]
} }
} }
} }
@@ -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

@@ -0,0 +1,48 @@
{
"operationId": "getDNSProviders",
"summary": "Get DNS Providers for Certificates",
"tags": ["certificates"],
"security": [
{
"bearerAuth": ["certificates.view"]
}
],
"responses": {
"200": {
"description": "200 response",
"content": {
"application/json": {
"examples": {
"default": {
"value": [
{
"id": "vultr",
"name": "Vultr",
"credentials": "dns_vultr_key = YOUR_VULTR_API_KEY"
},
{
"id": "websupport",
"name": "Websupport.sk",
"credentials": "dns_websupport_identifier = <api_key>\ndns_websupport_secret_key = <secret>"
},
{
"id": "wedos",
"name": "Wedos",
"credentials": "dns_wedos_user = <wedos_registration>\ndns_wedos_auth = <wapi_password>"
},
{
"id": "zoneedit",
"name": "ZoneEdit",
"credentials": "dns_zoneedit_user = <login-user-id>\ndns_zoneedit_token = <dyn-authentication-token>"
}
]
}
},
"schema": {
"$ref": "../../../../components/dns-providers-list.json"
}
}
}
}
}
}

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,24 +1,30 @@
{ {
"operationId": "testHttpReach", "operationId": "testHttpReach",
"summary": "Test HTTP Reachability", "summary": "Test HTTP Reachability",
"tags": ["Certificates"], "tags": ["certificates"],
"security": [ "security": [
{ {
"BearerAuth": ["certificates"] "bearerAuth": ["certificates.view"]
} }
], ],
"parameters": [ "requestBody": {
{ "description": "Test Payload",
"in": "query", "required": true,
"name": "domains", "content": {
"description": "Expansions", "application/json": {
"required": true, "schema": {
"schema": { "type": "object",
"type": "string", "additionalProperties": false,
"example": "[\"test.example.ord\",\"test.example.com\",\"nonexistent.example.com\"]" "required": ["domains"],
"properties": {
"domains": {
"$ref": "../../../../common.json#/properties/domain_names"
}
}
}
} }
} }
], },
"responses": { "responses": {
"200": { "200": {
"description": "200 response", "description": "200 response",

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,10 +44,8 @@
"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"
}
} }
} }
}, },

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": [

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