diff --git a/.version b/.version index 22e3b6b01b..3cf561c0b6 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -2.11.3 +2.12.1 diff --git a/Jenkinsfile b/Jenkinsfile index 1d8680a6b1..9b29ee9708 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -56,6 +56,13 @@ pipeline { sh 'sed -i -E "s/(version-)[0-9]+\\.[0-9]+\\.[0-9]+(-green)/\\1${BUILD_VERSION}\\2/" README.md' } } + stage('Docker Login') { + steps { + withCredentials([usernamePassword(credentialsId: 'jc21-dockerhub', passwordVariable: 'dpass', usernameVariable: 'duser')]) { + sh 'docker login -u "${duser}" -p "${dpass}"' + } + } + } } } stage('Builds') { @@ -120,6 +127,11 @@ pipeline { junit 'test/results/junit/*' sh 'docker-compose down --remove-orphans --volumes -t 30 || true' } + unstable { + dir(path: 'testing/results') { + archiveArtifacts(allowEmptyArchive: true, artifacts: '**/*', excludes: '**/*.xml') + } + } } } stage('Test Mysql') { @@ -148,6 +160,11 @@ pipeline { junit 'test/results/junit/*' sh 'docker-compose down --remove-orphans --volumes -t 30 || true' } + unstable { + dir(path: 'testing/results') { + archiveArtifacts(allowEmptyArchive: true, artifacts: '**/*', excludes: '**/*.xml') + } + } } } stage('MultiArch Build') { @@ -157,10 +174,7 @@ pipeline { } } steps { - withCredentials([usernamePassword(credentialsId: 'jc21-dockerhub', passwordVariable: 'dpass', usernameVariable: 'duser')]) { - sh 'docker login -u "${duser}" -p "${dpass}"' - sh "./scripts/buildx --push ${buildxPushTags}" - } + sh "./scripts/buildx --push ${buildxPushTags}" } } stage('Docs / Comment') { @@ -200,20 +214,13 @@ pipeline { always { sh 'echo Reverting ownership' sh 'docker run --rm -v "$(pwd):/data" jc21/ci-tools chown -R "$(id -u):$(id -g)" /data' - } - success { - juxtapose event: 'success' - sh 'figlet "SUCCESS"' + printResult(true) } failure { archiveArtifacts(artifacts: 'debug/**/*.*', allowEmptyArchive: true) - juxtapose event: 'failure' - sh 'figlet "FAILURE"' } unstable { archiveArtifacts(artifacts: 'debug/**/*.*', allowEmptyArchive: true) - juxtapose event: 'unstable' - sh 'figlet "UNSTABLE"' } } } diff --git a/README.md b/README.md index 55a986d185..9ac6a6c857 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@
hello world hello world
-
+
diff --git a/backend/.vscode/settings.json b/backend/.vscode/settings.json
deleted file mode 100644
index 4e540ab309..0000000000
--- a/backend/.vscode/settings.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "editor.insertSpaces": false,
- "editor.formatOnSave": true,
- "files.trimTrailingWhitespace": true,
- "editor.codeActionsOnSave": {
- "source.fixAll.eslint": true
- }
-}
\ No newline at end of file
diff --git a/backend/app.js b/backend/app.js
index e528a0bbe1..59f7def205 100644
--- a/backend/app.js
+++ b/backend/app.js
@@ -52,7 +52,7 @@ app.use(function (req, res, next) {
});
app.use(require('./lib/express/jwt')());
-app.use('/', require('./routes/api/main'));
+app.use('/', require('./routes/main'));
// production error handler
// no stacktraces leaked to user
diff --git a/backend/config/default.json b/backend/config/default.json
index 64ab577c8c..154e66e489 100644
--- a/backend/config/default.json
+++ b/backend/config/default.json
@@ -1,6 +1,6 @@
{
"database": {
- "engine": "mysql",
+ "engine": "mysql2",
"host": "db",
"name": "npm",
"user": "npm",
diff --git a/backend/doc/api.swagger.json b/backend/doc/api.swagger.json
deleted file mode 100644
index 3fa19fc4b3..0000000000
--- a/backend/doc/api.swagger.json
+++ /dev/null
@@ -1,1456 +0,0 @@
-{
- "openapi": "3.0.0",
- "info": {
- "title": "Nginx Proxy Manager API",
- "version": "2.x.x"
- },
- "servers": [
- {
- "url": "http://127.0.0.1:81/api"
- }
- ],
- "paths": {
- "/": {
- "get": {
- "operationId": "health",
- "summary": "Returns the API health status",
- "responses": {
- "200": {
- "description": "200 response",
- "content": {
- "application/json": {
- "examples": {
- "default": {
- "value": {
- "status": "OK",
- "version": {
- "major": 2,
- "minor": 1,
- "revision": 0
- }
- }
- }
- },
- "schema": {
- "$ref": "#/components/schemas/HealthObject"
- }
- }
- }
- }
- }
- }
- },
- "/nginx/proxy-hosts": {
- "get": {
- "operationId": "getProxyHosts",
- "summary": "Get all proxy hosts",
- "tags": ["Proxy Hosts"],
- "security": [
- {
- "BearerAuth": ["users"]
- }
- ],
- "parameters": [
- {
- "in": "query",
- "name": "expand",
- "description": "Expansions",
- "schema": {
- "type": "string",
- "enum": ["access_list", "owner", "certificate"]
- }
- }
- ],
- "responses": {
- "200": {
- "description": "200 response",
- "content": {
- "application/json": {
- "examples": {
- "default": {
- "value": [
- {
- "id": 1,
- "created_on": "2023-03-30T01:12:23.000Z",
- "modified_on": "2023-03-30T02:15:40.000Z",
- "owner_user_id": 1,
- "domain_names": ["aasdasdad"],
- "forward_host": "asdasd",
- "forward_port": 80,
- "access_list_id": 0,
- "certificate_id": 0,
- "ssl_forced": 0,
- "caching_enabled": 0,
- "block_exploits": 0,
- "advanced_config": "sdfsdfsdf",
- "meta": {
- "letsencrypt_agree": false,
- "dns_challenge": false,
- "nginx_online": false,
- "nginx_err": "Command failed: /usr/sbin/nginx -t -g \"error_log off;\"\nnginx: [emerg] unknown directive \"sdfsdfsdf\" in /data/nginx/proxy_host/1.conf:37\nnginx: configuration file /etc/nginx/nginx.conf test failed\n"
- },
- "allow_websocket_upgrade": 0,
- "http2_support": 0,
- "forward_scheme": "http",
- "enabled": 1,
- "locations": [],
- "hsts_enabled": 0,
- "hsts_subdomains": 0,
- "owner": {
- "id": 1,
- "created_on": "2023-03-30T01:11:50.000Z",
- "modified_on": "2023-03-30T01:11:50.000Z",
- "is_deleted": 0,
- "is_disabled": 0,
- "email": "admin@example.com",
- "name": "Administrator",
- "nickname": "Admin",
- "avatar": "",
- "roles": ["admin"]
- },
- "access_list": null,
- "certificate": null
- },
- {
- "id": 2,
- "created_on": "2023-03-30T02:11:49.000Z",
- "modified_on": "2023-03-30T02:11:49.000Z",
- "owner_user_id": 1,
- "domain_names": ["test.example.com"],
- "forward_host": "1.1.1.1",
- "forward_port": 80,
- "access_list_id": 0,
- "certificate_id": 0,
- "ssl_forced": 0,
- "caching_enabled": 0,
- "block_exploits": 0,
- "advanced_config": "",
- "meta": {
- "letsencrypt_agree": false,
- "dns_challenge": false,
- "nginx_online": true,
- "nginx_err": null
- },
- "allow_websocket_upgrade": 0,
- "http2_support": 0,
- "forward_scheme": "http",
- "enabled": 1,
- "locations": [],
- "hsts_enabled": 0,
- "hsts_subdomains": 0,
- "owner": {
- "id": 1,
- "created_on": "2023-03-30T01:11:50.000Z",
- "modified_on": "2023-03-30T01:11:50.000Z",
- "is_deleted": 0,
- "is_disabled": 0,
- "email": "admin@example.com",
- "name": "Administrator",
- "nickname": "Admin",
- "avatar": "",
- "roles": ["admin"]
- },
- "access_list": null,
- "certificate": null
- }
- ]
- }
- },
- "schema": {
- "$ref": "#/components/schemas/ProxyHostsList"
- }
- }
- }
- }
- }
- },
- "post": {
- "operationId": "createProxyHost",
- "summary": "Create a Proxy Host",
- "tags": ["Proxy Hosts"],
- "security": [
- {
- "BearerAuth": ["users"]
- }
- ],
- "parameters": [
- {
- "in": "body",
- "name": "proxyhost",
- "description": "Proxy Host Payload",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/ProxyHostObject"
- }
- }
- ],
- "responses": {
- "201": {
- "description": "201 response",
- "content": {
- "application/json": {
- "examples": {
- "default": {
- "value": {
- "id": 3,
- "created_on": "2023-03-30T02:31:27.000Z",
- "modified_on": "2023-03-30T02:31:27.000Z",
- "owner_user_id": 1,
- "domain_names": ["test2.example.com"],
- "forward_host": "1.1.1.1",
- "forward_port": 80,
- "access_list_id": 0,
- "certificate_id": 0,
- "ssl_forced": 0,
- "caching_enabled": 0,
- "block_exploits": 0,
- "advanced_config": "",
- "meta": {
- "letsencrypt_agree": false,
- "dns_challenge": false
- },
- "allow_websocket_upgrade": 0,
- "http2_support": 0,
- "forward_scheme": "http",
- "enabled": 1,
- "locations": [],
- "hsts_enabled": 0,
- "hsts_subdomains": 0,
- "certificate": null,
- "owner": {
- "id": 1,
- "created_on": "2023-03-30T01:11:50.000Z",
- "modified_on": "2023-03-30T01:11:50.000Z",
- "is_deleted": 0,
- "is_disabled": 0,
- "email": "admin@example.com",
- "name": "Administrator",
- "nickname": "Admin",
- "avatar": "",
- "roles": ["admin"]
- },
- "access_list": null,
- "use_default_location": true,
- "ipv6": true
- }
- }
- },
- "schema": {
- "$ref": "#/components/schemas/ProxyHostObject"
- }
- }
- }
- }
- }
- }
- },
- "/schema": {
- "get": {
- "operationId": "schema",
- "responses": {
- "200": {
- "description": "200 response"
- }
- },
- "summary": "Returns this swagger API schema"
- }
- },
- "/tokens": {
- "get": {
- "operationId": "refreshToken",
- "summary": "Refresh your access token",
- "tags": ["Tokens"],
- "security": [
- {
- "BearerAuth": ["tokens"]
- }
- ],
- "responses": {
- "200": {
- "description": "200 response",
- "content": {
- "application/json": {
- "examples": {
- "default": {
- "value": {
- "expires": 1566540510,
- "token": "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.ey...xaHKYr3Kk6MvkUjcC4"
- }
- }
- },
- "schema": {
- "$ref": "#/components/schemas/TokenObject"
- }
- }
- }
- }
- }
- },
- "post": {
- "operationId": "requestToken",
- "parameters": [
- {
- "description": "Credentials Payload",
- "in": "body",
- "name": "credentials",
- "required": true,
- "schema": {
- "additionalProperties": false,
- "properties": {
- "identity": {
- "minLength": 1,
- "type": "string"
- },
- "scope": {
- "minLength": 1,
- "type": "string",
- "enum": ["user"]
- },
- "secret": {
- "minLength": 1,
- "type": "string"
- }
- },
- "required": ["identity", "secret"],
- "type": "object"
- }
- }
- ],
- "responses": {
- "200": {
- "content": {
- "application/json": {
- "examples": {
- "default": {
- "value": {
- "result": {
- "expires": 1566540510,
- "token": "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.ey...xaHKYr3Kk6MvkUjcC4"
- }
- }
- }
- },
- "schema": {
- "$ref": "#/components/schemas/TokenObject"
- }
- }
- },
- "description": "200 response"
- }
- },
- "summary": "Request a new access token from credentials",
- "tags": ["Tokens"]
- }
- },
- "/settings": {
- "get": {
- "operationId": "getSettings",
- "summary": "Get all settings",
- "tags": ["Settings"],
- "security": [
- {
- "BearerAuth": ["settings"]
- }
- ],
- "responses": {
- "200": {
- "description": "200 response",
- "content": {
- "application/json": {
- "examples": {
- "default": {
- "value": [
- {
- "id": "default-site",
- "name": "Default Site",
- "description": "What to show when Nginx is hit with an unknown Host",
- "value": "congratulations",
- "meta": {}
- }
- ]
- }
- },
- "schema": {
- "$ref": "#/components/schemas/SettingsList"
- }
- }
- }
- }
- }
- }
- },
- "/settings/{settingID}": {
- "get": {
- "operationId": "getSetting",
- "summary": "Get a setting",
- "tags": ["Settings"],
- "security": [
- {
- "BearerAuth": ["settings"]
- }
- ],
- "parameters": [
- {
- "in": "path",
- "name": "settingID",
- "schema": {
- "type": "string",
- "minLength": 1
- },
- "required": true,
- "description": "Setting ID",
- "example": "default-site"
- }
- ],
- "responses": {
- "200": {
- "description": "200 response",
- "content": {
- "application/json": {
- "examples": {
- "default": {
- "value": {
- "id": "default-site",
- "name": "Default Site",
- "description": "What to show when Nginx is hit with an unknown Host",
- "value": "congratulations",
- "meta": {}
- }
- }
- },
- "schema": {
- "$ref": "#/components/schemas/SettingObject"
- }
- }
- }
- }
- }
- },
- "put": {
- "operationId": "updateSetting",
- "summary": "Update a setting",
- "tags": ["Settings"],
- "security": [
- {
- "BearerAuth": ["settings"]
- }
- ],
- "parameters": [
- {
- "in": "path",
- "name": "settingID",
- "schema": {
- "type": "string",
- "minLength": 1
- },
- "required": true,
- "description": "Setting ID",
- "example": "default-site"
- },
- {
- "in": "body",
- "name": "setting",
- "description": "Setting Payload",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/SettingObject"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "200 response",
- "content": {
- "application/json": {
- "examples": {
- "default": {
- "value": {
- "id": "default-site",
- "name": "Default Site",
- "description": "What to show when Nginx is hit with an unknown Host",
- "value": "congratulations",
- "meta": {}
- }
- }
- },
- "schema": {
- "$ref": "#/components/schemas/SettingObject"
- }
- }
- }
- }
- }
- }
- },
- "/users": {
- "get": {
- "operationId": "getUsers",
- "summary": "Get all users",
- "tags": ["Users"],
- "security": [
- {
- "BearerAuth": ["users"]
- }
- ],
- "parameters": [
- {
- "in": "query",
- "name": "expand",
- "description": "Expansions",
- "schema": {
- "type": "string",
- "enum": ["permissions"]
- }
- }
- ],
- "responses": {
- "200": {
- "description": "200 response",
- "content": {
- "application/json": {
- "examples": {
- "default": {
- "value": [
- {
- "id": 1,
- "created_on": "2020-01-30T09:36:08.000Z",
- "modified_on": "2020-01-30T09:41:04.000Z",
- "is_disabled": 0,
- "email": "jc@jc21.com",
- "name": "Jamie Curnow",
- "nickname": "James",
- "avatar": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm",
- "roles": ["admin"]
- }
- ]
- },
- "withPermissions": {
- "value": [
- {
- "id": 1,
- "created_on": "2020-01-30T09:36:08.000Z",
- "modified_on": "2020-01-30T09:41:04.000Z",
- "is_disabled": 0,
- "email": "jc@jc21.com",
- "name": "Jamie Curnow",
- "nickname": "James",
- "avatar": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm",
- "roles": ["admin"],
- "permissions": {
- "visibility": "all",
- "proxy_hosts": "manage",
- "redirection_hosts": "manage",
- "dead_hosts": "manage",
- "streams": "manage",
- "access_lists": "manage",
- "certificates": "manage"
- }
- }
- ]
- }
- },
- "schema": {
- "$ref": "#/components/schemas/UsersList"
- }
- }
- }
- }
- }
- },
- "post": {
- "operationId": "createUser",
- "summary": "Create a User",
- "tags": ["Users"],
- "security": [
- {
- "BearerAuth": ["users"]
- }
- ],
- "parameters": [
- {
- "in": "body",
- "name": "user",
- "description": "User Payload",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/UserObject"
- }
- }
- ],
- "responses": {
- "201": {
- "description": "201 response",
- "content": {
- "application/json": {
- "examples": {
- "default": {
- "value": {
- "id": 2,
- "created_on": "2020-01-30T09:36:08.000Z",
- "modified_on": "2020-01-30T09:41:04.000Z",
- "is_disabled": 0,
- "email": "jc@jc21.com",
- "name": "Jamie Curnow",
- "nickname": "James",
- "avatar": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm",
- "roles": ["admin"],
- "permissions": {
- "visibility": "all",
- "proxy_hosts": "manage",
- "redirection_hosts": "manage",
- "dead_hosts": "manage",
- "streams": "manage",
- "access_lists": "manage",
- "certificates": "manage"
- }
- }
- }
- },
- "schema": {
- "$ref": "#/components/schemas/UserObject"
- }
- }
- }
- }
- }
- }
- },
- "/users/{userID}": {
- "get": {
- "operationId": "getUser",
- "summary": "Get a user",
- "tags": ["Users"],
- "security": [
- {
- "BearerAuth": ["users"]
- }
- ],
- "parameters": [
- {
- "in": "path",
- "name": "userID",
- "schema": {
- "oneOf": [
- {
- "type": "string",
- "pattern": "^me$"
- },
- {
- "type": "integer",
- "minimum": 1
- }
- ]
- },
- "required": true,
- "description": "User ID or 'me' for yourself",
- "example": 1
- }
- ],
- "responses": {
- "200": {
- "description": "200 response",
- "content": {
- "application/json": {
- "examples": {
- "default": {
- "value": {
- "id": 1,
- "created_on": "2020-01-30T09:36:08.000Z",
- "modified_on": "2020-01-30T09:41:04.000Z",
- "is_disabled": 0,
- "email": "jc@jc21.com",
- "name": "Jamie Curnow",
- "nickname": "James",
- "avatar": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm",
- "roles": ["admin"]
- }
- }
- },
- "schema": {
- "$ref": "#/components/schemas/UserObject"
- }
- }
- }
- }
- }
- },
- "put": {
- "operationId": "updateUser",
- "summary": "Update a User",
- "tags": ["Users"],
- "security": [
- {
- "BearerAuth": ["users"]
- }
- ],
- "parameters": [
- {
- "in": "path",
- "name": "userID",
- "schema": {
- "oneOf": [
- {
- "type": "string",
- "pattern": "^me$"
- },
- {
- "type": "integer",
- "minimum": 1
- }
- ]
- },
- "required": true,
- "description": "User ID or 'me' for yourself",
- "example": 2
- },
- {
- "in": "body",
- "name": "user",
- "description": "User Payload",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/UserObject"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "200 response",
- "content": {
- "application/json": {
- "examples": {
- "default": {
- "value": {
- "id": 2,
- "created_on": "2020-01-30T09:36:08.000Z",
- "modified_on": "2020-01-30T09:41:04.000Z",
- "is_disabled": 0,
- "email": "jc@jc21.com",
- "name": "Jamie Curnow",
- "nickname": "James",
- "avatar": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm",
- "roles": ["admin"]
- }
- }
- },
- "schema": {
- "$ref": "#/components/schemas/UserObject"
- }
- }
- }
- }
- }
- },
- "delete": {
- "operationId": "deleteUser",
- "summary": "Delete a User",
- "tags": ["Users"],
- "security": [
- {
- "BearerAuth": ["users"]
- }
- ],
- "parameters": [
- {
- "in": "path",
- "name": "userID",
- "schema": {
- "type": "integer",
- "minimum": 1
- },
- "required": true,
- "description": "User ID",
- "example": 2
- }
- ],
- "responses": {
- "200": {
- "description": "200 response",
- "content": {
- "application/json": {
- "examples": {
- "default": {
- "value": true
- }
- },
- "schema": {
- "type": "boolean"
- }
- }
- }
- }
- }
- }
- },
- "/users/{userID}/auth": {
- "put": {
- "operationId": "updateUserAuth",
- "summary": "Update a User's Authentication",
- "tags": ["Users"],
- "security": [
- {
- "BearerAuth": ["users"]
- }
- ],
- "parameters": [
- {
- "in": "path",
- "name": "userID",
- "schema": {
- "oneOf": [
- {
- "type": "string",
- "pattern": "^me$"
- },
- {
- "type": "integer",
- "minimum": 1
- }
- ]
- },
- "required": true,
- "description": "User ID or 'me' for yourself",
- "example": 2
- },
- {
- "in": "body",
- "name": "user",
- "description": "User Payload",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/AuthObject"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "200 response",
- "content": {
- "application/json": {
- "examples": {
- "default": {
- "value": true
- }
- },
- "schema": {
- "type": "boolean"
- }
- }
- }
- }
- }
- }
- },
- "/users/{userID}/permissions": {
- "put": {
- "operationId": "updateUserPermissions",
- "summary": "Update a User's Permissions",
- "tags": ["Users"],
- "security": [
- {
- "BearerAuth": ["users"]
- }
- ],
- "parameters": [
- {
- "in": "path",
- "name": "userID",
- "schema": {
- "type": "integer",
- "minimum": 1
- },
- "required": true,
- "description": "User ID",
- "example": 2
- },
- {
- "in": "body",
- "name": "user",
- "description": "Permissions Payload",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/PermissionsObject"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "200 response",
- "content": {
- "application/json": {
- "examples": {
- "default": {
- "value": true
- }
- },
- "schema": {
- "type": "boolean"
- }
- }
- }
- }
- }
- }
- },
- "/users/{userID}/login": {
- "put": {
- "operationId": "loginAsUser",
- "summary": "Login as this user",
- "tags": ["Users"],
- "security": [
- {
- "BearerAuth": ["users"]
- }
- ],
- "parameters": [
- {
- "in": "path",
- "name": "userID",
- "schema": {
- "type": "integer",
- "minimum": 1
- },
- "required": true,
- "description": "User ID",
- "example": 2
- }
- ],
- "responses": {
- "200": {
- "description": "200 response",
- "content": {
- "application/json": {
- "examples": {
- "default": {
- "value": {
- "token": "eyJhbGciOiJSUzI1NiIsInR...16OjT8B3NLyXg",
- "expires": "2020-01-31T10:56:23.239Z",
- "user": {
- "id": 1,
- "created_on": "2020-01-30T10:43:44.000Z",
- "modified_on": "2020-01-30T10:43:44.000Z",
- "is_disabled": 0,
- "email": "jc@jc21.com",
- "name": "Jamie Curnow",
- "nickname": "James",
- "avatar": "//www.gravatar.com/avatar/3c8d73f45fd8763f827b964c76e6032a?default=mm",
- "roles": ["admin"]
- }
- }
- }
- },
- "schema": {
- "type": "object",
- "description": "Login object",
- "required": ["expires", "token", "user"],
- "additionalProperties": false,
- "properties": {
- "expires": {
- "description": "Token Expiry Unix Time",
- "example": 1566540249,
- "minimum": 1,
- "type": "number"
- },
- "token": {
- "description": "JWT Token",
- "example": "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.ey...xaHKYr3Kk6MvkUjcC4",
- "type": "string"
- },
- "user": {
- "$ref": "#/components/schemas/UserObject"
- }
- }
- }
- }
- }
- }
- }
- }
- },
- "/reports/hosts": {
- "get": {
- "operationId": "reportsHosts",
- "summary": "Report on Host Statistics",
- "tags": ["Reports"],
- "security": [
- {
- "BearerAuth": ["reports"]
- }
- ],
- "responses": {
- "200": {
- "description": "200 response",
- "content": {
- "application/json": {
- "examples": {
- "default": {
- "value": {
- "proxy": 20,
- "redirection": 1,
- "stream": 0,
- "dead": 1
- }
- }
- },
- "schema": {
- "$ref": "#/components/schemas/HostReportObject"
- }
- }
- }
- }
- }
- }
- },
- "/audit-log": {
- "get": {
- "operationId": "getAuditLog",
- "summary": "Get Audit Log",
- "tags": ["Audit Log"],
- "security": [
- {
- "BearerAuth": ["audit-log"]
- }
- ],
- "responses": {
- "200": {
- "description": "200 response",
- "content": {
- "application/json": {
- "examples": {
- "default": {
- "value": {
- "proxy": 20,
- "redirection": 1,
- "stream": 0,
- "dead": 1
- }
- }
- },
- "schema": {
- "$ref": "#/components/schemas/HostReportObject"
- }
- }
- }
- }
- }
- }
- }
- },
- "components": {
- "securitySchemes": {
- "BearerAuth": {
- "type": "http",
- "scheme": "bearer"
- }
- },
- "schemas": {
- "HealthObject": {
- "type": "object",
- "description": "Health object",
- "additionalProperties": false,
- "required": ["status", "version"],
- "properties": {
- "status": {
- "type": "string",
- "description": "Healthy",
- "example": "OK"
- },
- "version": {
- "type": "object",
- "description": "The version object",
- "example": {
- "major": 2,
- "minor": 0,
- "revision": 0
- },
- "additionalProperties": false,
- "required": ["major", "minor", "revision"],
- "properties": {
- "major": {
- "type": "integer",
- "minimum": 0
- },
- "minor": {
- "type": "integer",
- "minimum": 0
- },
- "revision": {
- "type": "integer",
- "minimum": 0
- }
- }
- }
- }
- },
- "TokenObject": {
- "type": "object",
- "description": "Token object",
- "required": ["expires", "token"],
- "additionalProperties": false,
- "properties": {
- "expires": {
- "description": "Token Expiry Unix Time",
- "example": 1566540249,
- "minimum": 1,
- "type": "number"
- },
- "token": {
- "description": "JWT Token",
- "example": "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.ey...xaHKYr3Kk6MvkUjcC4",
- "type": "string"
- }
- }
- },
- "ProxyHostObject": {
- "type": "object",
- "description": "Proxy Host object",
- "required": [
- "id",
- "created_on",
- "modified_on",
- "owner_user_id",
- "domain_names",
- "forward_host",
- "forward_port",
- "access_list_id",
- "certificate_id",
- "ssl_forced",
- "caching_enabled",
- "block_exploits",
- "advanced_config",
- "meta",
- "allow_websocket_upgrade",
- "http2_support",
- "forward_scheme",
- "enabled",
- "locations",
- "hsts_enabled",
- "hsts_subdomains",
- "certificate",
- "use_default_location",
- "ipv6"
- ],
- "additionalProperties": false,
- "properties": {
- "id": {
- "type": "integer",
- "description": "Proxy Host ID",
- "minimum": 1,
- "example": 1
- },
- "created_on": {
- "type": "string",
- "description": "Created Date",
- "example": "2020-01-30T09:36:08.000Z"
- },
- "modified_on": {
- "type": "string",
- "description": "Modified Date",
- "example": "2020-01-30T09:41:04.000Z"
- },
- "owner_user_id": {
- "type": "integer",
- "minimum": 1,
- "example": 1
- },
- "domain_names": {
- "type": "array",
- "minItems": 1,
- "items": {
- "type": "string",
- "minLength": 1
- }
- },
- "forward_host": {
- "type": "string",
- "minLength": 1
- },
- "forward_port": {
- "type": "integer",
- "minimum": 1
- },
- "access_list_id": {
- "type": "integer"
- },
- "certificate_id": {
- "type": "integer"
- },
- "ssl_forced": {
- "type": "integer"
- },
- "caching_enabled": {
- "type": "integer"
- },
- "block_exploits": {
- "type": "integer"
- },
- "advanced_config": {
- "type": "string"
- },
- "meta": {
- "type": "object"
- },
- "allow_websocket_upgrade": {
- "type": "integer"
- },
- "http2_support": {
- "type": "integer"
- },
- "forward_scheme": {
- "type": "string"
- },
- "enabled": {
- "type": "integer"
- },
- "locations": {
- "type": "array"
- },
- "hsts_enabled": {
- "type": "integer"
- },
- "hsts_subdomains": {
- "type": "integer"
- },
- "certificate": {
- "type": "object",
- "nullable": true
- },
- "owner": {
- "type": "object",
- "nullable": true
- },
- "access_list": {
- "type": "object",
- "nullable": true
- },
- "use_default_location": {
- "type": "boolean"
- },
- "ipv6": {
- "type": "boolean"
- }
- }
- },
- "ProxyHostsList": {
- "type": "array",
- "description": "Proxyn Hosts list",
- "items": {
- "$ref": "#/components/schemas/ProxyHostObject"
- }
- },
- "SettingObject": {
- "type": "object",
- "description": "Setting object",
- "required": ["id", "name", "description", "value", "meta"],
- "additionalProperties": false,
- "properties": {
- "id": {
- "type": "string",
- "description": "Setting ID",
- "minLength": 1,
- "example": "default-site"
- },
- "name": {
- "type": "string",
- "description": "Setting Display Name",
- "minLength": 1,
- "example": "Default Site"
- },
- "description": {
- "type": "string",
- "description": "Meaningful description",
- "minLength": 1,
- "example": "What to show when Nginx is hit with an unknown Host"
- },
- "value": {
- "description": "Value in almost any form",
- "example": "congratulations",
- "oneOf": [
- {
- "type": "string",
- "minLength": 1
- },
- {
- "type": "integer"
- },
- {
- "type": "object"
- },
- {
- "type": "number"
- },
- {
- "type": "array"
- }
- ]
- },
- "meta": {
- "description": "Extra metadata",
- "example": {},
- "type": "object"
- }
- }
- },
- "SettingsList": {
- "type": "array",
- "description": "Setting list",
- "items": {
- "$ref": "#/components/schemas/SettingObject"
- }
- },
- "UserObject": {
- "type": "object",
- "description": "User object",
- "required": ["id", "created_on", "modified_on", "is_disabled", "email", "name", "nickname", "avatar", "roles"],
- "additionalProperties": false,
- "properties": {
- "id": {
- "type": "integer",
- "description": "User ID",
- "minimum": 1,
- "example": 1
- },
- "created_on": {
- "type": "string",
- "description": "Created Date",
- "example": "2020-01-30T09:36:08.000Z"
- },
- "modified_on": {
- "type": "string",
- "description": "Modified Date",
- "example": "2020-01-30T09:41:04.000Z"
- },
- "is_disabled": {
- "type": "integer",
- "minimum": 0,
- "maximum": 1,
- "description": "Is user Disabled (0 = false, 1 = true)",
- "example": 0
- },
- "email": {
- "type": "string",
- "description": "Email",
- "minLength": 3,
- "example": "jc@jc21.com"
- },
- "name": {
- "type": "string",
- "description": "Name",
- "minLength": 1,
- "example": "Jamie Curnow"
- },
- "nickname": {
- "type": "string",
- "description": "Nickname",
- "example": "James"
- },
- "avatar": {
- "type": "string",
- "description": "Gravatar URL based on email, without scheme",
- "example": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm"
- },
- "roles": {
- "description": "Roles applied",
- "example": ["admin"],
- "type": "array",
- "items": {
- "type": "string"
- }
- }
- }
- },
- "UsersList": {
- "type": "array",
- "description": "User list",
- "items": {
- "$ref": "#/components/schemas/UserObject"
- }
- },
- "AuthObject": {
- "type": "object",
- "description": "Authentication Object",
- "required": ["type", "secret"],
- "properties": {
- "type": {
- "type": "string",
- "pattern": "^password$",
- "example": "password"
- },
- "current": {
- "type": "string",
- "minLength": 1,
- "maxLength": 64,
- "example": "changeme"
- },
- "secret": {
- "type": "string",
- "minLength": 8,
- "maxLength": 64,
- "example": "mySuperN3wP@ssword!"
- }
- }
- },
- "PermissionsObject": {
- "type": "object",
- "properties": {
- "visibility": {
- "type": "string",
- "description": "Visibility Type",
- "enum": ["all", "user"]
- },
- "access_lists": {
- "type": "string",
- "description": "Access Lists Permissions",
- "enum": ["hidden", "view", "manage"]
- },
- "dead_hosts": {
- "type": "string",
- "description": "404 Hosts Permissions",
- "enum": ["hidden", "view", "manage"]
- },
- "proxy_hosts": {
- "type": "string",
- "description": "Proxy Hosts Permissions",
- "enum": ["hidden", "view", "manage"]
- },
- "redirection_hosts": {
- "type": "string",
- "description": "Redirection Permissions",
- "enum": ["hidden", "view", "manage"]
- },
- "streams": {
- "type": "string",
- "description": "Streams Permissions",
- "enum": ["hidden", "view", "manage"]
- },
- "certificates": {
- "type": "string",
- "description": "Certificates Permissions",
- "enum": ["hidden", "view", "manage"]
- }
- }
- },
- "HostReportObject": {
- "type": "object",
- "properties": {
- "proxy": {
- "type": "integer",
- "description": "Proxy Hosts Count"
- },
- "redirection": {
- "type": "integer",
- "description": "Redirection Hosts Count"
- },
- "stream": {
- "type": "integer",
- "description": "Streams Count"
- },
- "dead": {
- "type": "integer",
- "description": "404 Hosts Count"
- }
- }
- }
- }
- }
-}
diff --git a/backend/index.js b/backend/index.js
index 3d6d60071b..5513782519 100644
--- a/backend/index.js
+++ b/backend/index.js
@@ -1,23 +1,20 @@
#!/usr/bin/env node
+const schema = require('./schema');
const logger = require('./logger').global;
async function appStart () {
const migrate = require('./migrate');
const setup = require('./setup');
const app = require('./app');
- const apiValidator = require('./lib/validator/api');
const internalCertificate = require('./internal/certificate');
const internalIpRanges = require('./internal/ip_ranges');
return migrate.latest()
.then(setup)
- .then(() => {
- return apiValidator.loadSchemas;
- })
+ .then(schema.getCompiledSchema)
.then(internalIpRanges.fetch)
.then(() => {
-
internalCertificate.initTimer();
internalIpRanges.initTimer();
@@ -34,7 +31,7 @@ async function appStart () {
});
})
.catch((err) => {
- logger.error(err.message);
+ logger.error(err.message, err);
setTimeout(appStart, 1000);
});
}
diff --git a/backend/internal/access-list.js b/backend/internal/access-list.js
index 017fc738c2..72326be68d 100644
--- a/backend/internal/access-list.js
+++ b/backend/internal/access-list.js
@@ -269,7 +269,7 @@ const internalAccessList = {
return query.then(utils.omitRow(omissions()));
})
.then((row) => {
- if (!row) {
+ if (!row || !row.id) {
throw new error.ItemNotFoundError(data.id);
}
if (!skip_masking && typeof row.items !== 'undefined' && row.items) {
@@ -296,7 +296,7 @@ const internalAccessList = {
return internalAccessList.get(access, {id: data.id, expand: ['proxy_hosts', 'items', 'clients']});
})
.then((row) => {
- if (!row) {
+ if (!row || !row.id) {
throw new error.ItemNotFoundError(data.id);
}
diff --git a/backend/internal/certificate.js b/backend/internal/certificate.js
index 291056caa6..34b8fdf5a7 100644
--- a/backend/internal/certificate.js
+++ b/backend/internal/certificate.js
@@ -3,27 +3,29 @@ const fs = require('fs');
const https = require('https');
const tempWrite = require('temp-write');
const moment = require('moment');
+const archiver = require('archiver');
+const path = require('path');
+const { isArray } = require('lodash');
const logger = require('../logger').ssl;
const config = require('../lib/config');
const error = require('../lib/error');
const utils = require('../lib/utils');
+const certbot = require('../lib/certbot');
const certificateModel = require('../models/certificate');
const tokenModel = require('../models/token');
const dnsPlugins = require('../global/certbot-dns-plugins.json');
const internalAuditLog = require('./audit-log');
const internalNginx = require('./nginx');
const internalHost = require('./host');
-const certbot = require('../lib/certbot');
-const archiver = require('archiver');
-const path = require('path');
-const { isArray } = require('lodash');
+
const letsencryptStaging = config.useLetsencryptStaging();
+const letsencryptServer = config.useLetsencryptServer();
const letsencryptConfig = '/etc/letsencrypt.ini';
const certbotCommand = 'certbot';
function omissions() {
- return ['is_deleted'];
+ return ['is_deleted', 'owner.is_deleted'];
}
const internalCertificate = {
@@ -207,6 +209,7 @@ const internalCertificate = {
.patchAndFetchById(certificate.id, {
expires_on: moment(cert_info.dates.to, 'X').format('YYYY-MM-DD HH:mm:ss')
})
+ .then(utils.omitRow(omissions()))
.then((saved_row) => {
// Add cert data for audit log
saved_row.meta = _.assign({}, saved_row.meta, {
@@ -323,7 +326,7 @@ const internalCertificate = {
return query.then(utils.omitRow(omissions()));
})
.then((row) => {
- if (!row) {
+ if (!row || !row.id) {
throw new error.ItemNotFoundError(data.id);
}
// Custom omissions
@@ -412,7 +415,7 @@ const internalCertificate = {
return internalCertificate.get(access, {id: data.id});
})
.then((row) => {
- if (!row) {
+ if (!row || !row.id) {
throw new error.ItemNotFoundError(data.id);
}
@@ -730,29 +733,29 @@ const internalCertificate = {
return utils.exec('openssl x509 -in ' + certificate_file + ' -subject -noout')
.then((result) => {
+ // Examples:
+ // subject=CN = *.jc21.com
// subject=CN = something.example.com
const regex = /(?:subject=)?[^=]+=\s+(\S+)/gim;
const match = regex.exec(result);
-
- if (typeof match[1] === 'undefined') {
- throw new error.ValidationError('Could not determine subject from certificate: ' + result);
+ if (match && typeof match[1] !== 'undefined') {
+ certData['cn'] = match[1];
}
-
- certData['cn'] = match[1];
})
.then(() => {
return utils.exec('openssl x509 -in ' + certificate_file + ' -issuer -noout');
})
+
.then((result) => {
+ // Examples:
// issuer=C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
+ // issuer=C = US, O = Let's Encrypt, CN = E5
+ // issuer=O = NginxProxyManager, CN = NginxProxyManager Intermediate CA","O = NginxProxyManager, CN = NginxProxyManager Intermediate CA
const regex = /^(?:issuer=)?(.*)$/gim;
const match = regex.exec(result);
-
- if (typeof match[1] === 'undefined') {
- throw new error.ValidationError('Could not determine issuer from certificate: ' + result);
+ if (match && typeof match[1] !== 'undefined') {
+ certData['issuer'] = match[1];
}
-
- certData['issuer'] = match[1];
})
.then(() => {
return utils.exec('openssl x509 -in ' + certificate_file + ' -dates -noout');
@@ -827,17 +830,18 @@ const internalCertificate = {
requestLetsEncryptSsl: (certificate) => {
logger.info('Requesting Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
- const cmd = certbotCommand + ' certonly ' +
- '--config "' + letsencryptConfig + '" ' +
+ const cmd = `${certbotCommand} certonly ` +
+ `--config '${letsencryptConfig}' ` +
'--work-dir "/tmp/letsencrypt-lib" ' +
'--logs-dir "/tmp/letsencrypt-log" ' +
- '--cert-name "npm-' + certificate.id + '" ' +
+ `--cert-name "npm-${certificate.id}" ` +
'--agree-tos ' +
'--authenticator webroot ' +
- '--email "' + certificate.meta.letsencrypt_email + '" ' +
+ `--email '${certificate.meta.letsencrypt_email}' ` +
'--preferred-challenges "dns,http" ' +
- '--domains "' + certificate.domain_names.join(',') + '" ' +
- (letsencryptStaging ? '--staging' : '');
+ `--domains "${certificate.domain_names.join(',')}" ` +
+ (letsencryptServer !== null ? `--server '${letsencryptServer}' ` : '') +
+ (letsencryptStaging && letsencryptServer === null ? '--staging ' : '');
logger.info('Command:', cmd);
@@ -868,25 +872,26 @@ const internalCertificate = {
const hasConfigArg = certificate.meta.dns_provider !== 'route53';
let mainCmd = certbotCommand + ' certonly ' +
- '--config "' + letsencryptConfig + '" ' +
+ `--config '${letsencryptConfig}' ` +
'--work-dir "/tmp/letsencrypt-lib" ' +
'--logs-dir "/tmp/letsencrypt-log" ' +
- '--cert-name "npm-' + certificate.id + '" ' +
+ `--cert-name 'npm-${certificate.id}' ` +
'--agree-tos ' +
- '--email "' + certificate.meta.letsencrypt_email + '" ' +
- '--domains "' + certificate.domain_names.join(',') + '" ' +
- '--authenticator ' + dnsPlugin.full_plugin_name + ' ' +
+ `--email '${certificate.meta.letsencrypt_email}' ` +
+ `--domains '${certificate.domain_names.join(',')}' ` +
+ `--authenticator '${dnsPlugin.full_plugin_name}' ` +
(
hasConfigArg
- ? '--' + dnsPlugin.full_plugin_name + '-credentials "' + credentialsLocation + '"'
+ ? `--${dnsPlugin.full_plugin_name}-credentials '${credentialsLocation}' `
: ''
) +
(
certificate.meta.propagation_seconds !== undefined
- ? ' --' + dnsPlugin.full_plugin_name + '-propagation-seconds ' + certificate.meta.propagation_seconds
+ ? `--${dnsPlugin.full_plugin_name}-propagation-seconds '${certificate.meta.propagation_seconds}' `
: ''
) +
- (letsencryptStaging ? ' --staging' : '');
+ (letsencryptServer !== null ? `--server '${letsencryptServer}' ` : '') +
+ (letsencryptStaging && letsencryptServer === null ? '--staging ' : '');
// Prepend the path to the credentials file as an environment variable
if (certificate.meta.dns_provider === 'route53') {
@@ -963,14 +968,15 @@ const internalCertificate = {
logger.info('Renewing Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
const cmd = certbotCommand + ' renew --force-renewal ' +
- '--config "' + letsencryptConfig + '" ' +
+ `--config '${letsencryptConfig}' ` +
'--work-dir "/tmp/letsencrypt-lib" ' +
'--logs-dir "/tmp/letsencrypt-log" ' +
- '--cert-name "npm-' + certificate.id + '" ' +
+ `--cert-name 'npm-${certificate.id}' ` +
'--preferred-challenges "dns,http" ' +
'--no-random-sleep-on-renew ' +
'--disable-hook-validation ' +
- (letsencryptStaging ? '--staging' : '');
+ (letsencryptServer !== null ? `--server '${letsencryptServer}' ` : '') +
+ (letsencryptStaging && letsencryptServer === null ? '--staging ' : '');
logger.info('Command:', cmd);
@@ -995,13 +1001,14 @@ const internalCertificate = {
logger.info(`Renewing Let'sEncrypt certificates via ${dnsPlugin.name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);
let mainCmd = certbotCommand + ' renew --force-renewal ' +
- '--config "' + letsencryptConfig + '" ' +
+ `--config "${letsencryptConfig}" ` +
'--work-dir "/tmp/letsencrypt-lib" ' +
'--logs-dir "/tmp/letsencrypt-log" ' +
- '--cert-name "npm-' + certificate.id + '" ' +
+ `--cert-name 'npm-${certificate.id}' ` +
'--disable-hook-validation ' +
'--no-random-sleep-on-renew ' +
- (letsencryptStaging ? ' --staging' : '');
+ (letsencryptServer !== null ? `--server '${letsencryptServer}' ` : '') +
+ (letsencryptStaging && letsencryptServer === null ? '--staging ' : '');
// Prepend the path to the credentials file as an environment variable
if (certificate.meta.dns_provider === 'route53') {
@@ -1027,12 +1034,13 @@ const internalCertificate = {
logger.info('Revoking Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
const mainCmd = certbotCommand + ' revoke ' +
- '--config "' + letsencryptConfig + '" ' +
+ `--config '${letsencryptConfig}' ` +
'--work-dir "/tmp/letsencrypt-lib" ' +
'--logs-dir "/tmp/letsencrypt-log" ' +
- '--cert-path "/etc/letsencrypt/live/npm-' + certificate.id + '/fullchain.pem" ' +
+ `--cert-path '/etc/letsencrypt/live/npm-${certificate.id}/fullchain.pem' ` +
'--delete-after-revoke ' +
- (letsencryptStaging ? '--staging' : '');
+ (letsencryptServer !== null ? `--server '${letsencryptServer}' ` : '') +
+ (letsencryptStaging && letsencryptServer === null ? '--staging ' : '');
// Don't fail command if file does not exist
const delete_credentialsCmd = `rm -f '/etc/letsencrypt/credentials/credentials-${certificate.id}' || true`;
diff --git a/backend/internal/dead-host.js b/backend/internal/dead-host.js
index 2a6258e96f..e672775eb0 100644
--- a/backend/internal/dead-host.js
+++ b/backend/internal/dead-host.js
@@ -48,6 +48,12 @@ const internalDeadHost = {
data.owner_user_id = access.token.getUserId(1);
data = internalHost.cleanSslHstsData(data);
+ // Fix for db field not having a default value
+ // for this optional field.
+ if (typeof data.advanced_config === 'undefined') {
+ data.advanced_config = '';
+ }
+
return deadHostModel
.query()
.insertAndFetch(data)
@@ -233,7 +239,7 @@ const internalDeadHost = {
return query.then(utils.omitRow(omissions()));
})
.then((row) => {
- if (!row) {
+ if (!row || !row.id) {
throw new error.ItemNotFoundError(data.id);
}
// Custom omissions
@@ -257,7 +263,7 @@ const internalDeadHost = {
return internalDeadHost.get(access, {id: data.id});
})
.then((row) => {
- if (!row) {
+ if (!row || !row.id) {
throw new error.ItemNotFoundError(data.id);
}
@@ -305,7 +311,7 @@ const internalDeadHost = {
});
})
.then((row) => {
- if (!row) {
+ if (!row || !row.id) {
throw new error.ItemNotFoundError(data.id);
} else if (row.enabled) {
throw new error.ValidationError('Host is already enabled');
@@ -351,7 +357,7 @@ const internalDeadHost = {
return internalDeadHost.get(access, {id: data.id});
})
.then((row) => {
- if (!row) {
+ if (!row || !row.id) {
throw new error.ItemNotFoundError(data.id);
} else if (!row.enabled) {
throw new error.ValidationError('Host is already disabled');
diff --git a/backend/internal/nginx.js b/backend/internal/nginx.js
index 77933e733b..5f802c0048 100644
--- a/backend/internal/nginx.js
+++ b/backend/internal/nginx.js
@@ -181,7 +181,9 @@ const internalNginx = {
* @param {Object} host
* @returns {Promise}
*/
- generateConfig: (host_type, host) => {
+ generateConfig: (host_type, host_row) => {
+ // Prevent modifying the original object:
+ let host = JSON.parse(JSON.stringify(host_row));
const nice_host_type = internalNginx.getFileFriendlyHostType(host_type);
if (config.debug()) {
diff --git a/backend/internal/proxy-host.js b/backend/internal/proxy-host.js
index dbff1147d0..61ac8b8c7a 100644
--- a/backend/internal/proxy-host.js
+++ b/backend/internal/proxy-host.js
@@ -8,7 +8,7 @@ const internalAuditLog = require('./audit-log');
const internalCertificate = require('./certificate');
function omissions () {
- return ['is_deleted'];
+ return ['is_deleted', 'owner.is_deleted'];
}
const internalProxyHost = {
@@ -48,6 +48,12 @@ const internalProxyHost = {
data.owner_user_id = access.token.getUserId(1);
data = internalHost.cleanSslHstsData(data);
+ // Fix for db field not having a default value
+ // for this optional field.
+ if (typeof data.advanced_config === 'undefined') {
+ data.advanced_config = '';
+ }
+
return proxyHostModel
.query()
.insertAndFetch(data)
@@ -239,7 +245,7 @@ const internalProxyHost = {
return query.then(utils.omitRow(omissions()));
})
.then((row) => {
- if (!row) {
+ if (!row || !row.id) {
throw new error.ItemNotFoundError(data.id);
}
row = internalHost.cleanRowCertificateMeta(row);
@@ -264,7 +270,7 @@ const internalProxyHost = {
return internalProxyHost.get(access, {id: data.id});
})
.then((row) => {
- if (!row) {
+ if (!row || !row.id) {
throw new error.ItemNotFoundError(data.id);
}
@@ -312,7 +318,7 @@ const internalProxyHost = {
});
})
.then((row) => {
- if (!row) {
+ if (!row || !row.id) {
throw new error.ItemNotFoundError(data.id);
} else if (row.enabled) {
throw new error.ValidationError('Host is already enabled');
@@ -358,7 +364,7 @@ const internalProxyHost = {
return internalProxyHost.get(access, {id: data.id});
})
.then((row) => {
- if (!row) {
+ if (!row || !row.id) {
throw new error.ItemNotFoundError(data.id);
} else if (!row.enabled) {
throw new error.ValidationError('Host is already disabled');
diff --git a/backend/internal/redirection-host.js b/backend/internal/redirection-host.js
index 775d94f3a4..41ff5b0937 100644
--- a/backend/internal/redirection-host.js
+++ b/backend/internal/redirection-host.js
@@ -48,6 +48,12 @@ const internalRedirectionHost = {
data.owner_user_id = access.token.getUserId(1);
data = internalHost.cleanSslHstsData(data);
+ // Fix for db field not having a default value
+ // for this optional field.
+ if (typeof data.advanced_config === 'undefined') {
+ data.advanced_config = '';
+ }
+
return redirectionHostModel
.query()
.insertAndFetch(data)
@@ -232,7 +238,7 @@ const internalRedirectionHost = {
return query.then(utils.omitRow(omissions()));
})
.then((row) => {
- if (!row) {
+ if (!row || !row.id) {
throw new error.ItemNotFoundError(data.id);
}
row = internalHost.cleanRowCertificateMeta(row);
@@ -257,7 +263,7 @@ const internalRedirectionHost = {
return internalRedirectionHost.get(access, {id: data.id});
})
.then((row) => {
- if (!row) {
+ if (!row || !row.id) {
throw new error.ItemNotFoundError(data.id);
}
@@ -305,7 +311,7 @@ const internalRedirectionHost = {
});
})
.then((row) => {
- if (!row) {
+ if (!row || !row.id) {
throw new error.ItemNotFoundError(data.id);
} else if (row.enabled) {
throw new error.ValidationError('Host is already enabled');
@@ -351,7 +357,7 @@ const internalRedirectionHost = {
return internalRedirectionHost.get(access, {id: data.id});
})
.then((row) => {
- if (!row) {
+ if (!row || !row.id) {
throw new error.ItemNotFoundError(data.id);
} else if (!row.enabled) {
throw new error.ValidationError('Host is already disabled');
diff --git a/backend/internal/stream.js b/backend/internal/stream.js
index 6c9ab24df7..8dcb0507eb 100644
--- a/backend/internal/stream.js
+++ b/backend/internal/stream.js
@@ -197,7 +197,7 @@ const internalStream = {
return query.then(utils.omitRow(omissions()));
})
.then((row) => {
- if (!row) {
+ if (!row || !row.id) {
throw new error.ItemNotFoundError(data.id);
}
row = internalHost.cleanRowCertificateMeta(row);
@@ -222,7 +222,7 @@ const internalStream = {
return internalStream.get(access, {id: data.id});
})
.then((row) => {
- if (!row) {
+ if (!row || !row.id) {
throw new error.ItemNotFoundError(data.id);
}
@@ -270,7 +270,7 @@ const internalStream = {
});
})
.then((row) => {
- if (!row) {
+ if (!row || !row.id) {
throw new error.ItemNotFoundError(data.id);
} else if (row.enabled) {
throw new error.ValidationError('Stream is already enabled');
@@ -316,7 +316,7 @@ const internalStream = {
return internalStream.get(access, {id: data.id});
})
.then((row) => {
- if (!row) {
+ if (!row || !row.id) {
throw new error.ItemNotFoundError(data.id);
} else if (!row.enabled) {
throw new error.ValidationError('Stream is already disabled');
diff --git a/backend/internal/user.js b/backend/internal/user.js
index a1d90447fe..742ab65d33 100644
--- a/backend/internal/user.js
+++ b/backend/internal/user.js
@@ -194,7 +194,7 @@ const internalUser = {
return query.then(utils.omitRow(omissions()));
})
.then((row) => {
- if (!row) {
+ if (!row || !row.id) {
throw new error.ItemNotFoundError(data.id);
}
// Custom omissions
diff --git a/backend/knexfile.js b/backend/knexfile.js
index 391ca00508..607552f6fc 100644
--- a/backend/knexfile.js
+++ b/backend/knexfile.js
@@ -1,6 +1,6 @@
module.exports = {
development: {
- client: 'mysql',
+ client: 'mysql2',
migrations: {
tableName: 'migrations',
stub: 'lib/migrate_template.js',
@@ -9,7 +9,7 @@ module.exports = {
},
production: {
- client: 'mysql',
+ client: 'mysql2',
migrations: {
tableName: 'migrations',
stub: 'lib/migrate_template.js',
diff --git a/backend/lib/access.js b/backend/lib/access.js
index 5b9ebc93c4..0e658a6561 100644
--- a/backend/lib/access.js
+++ b/backend/lib/access.js
@@ -10,7 +10,7 @@
const _ = require('lodash');
const logger = require('../logger').access;
-const validator = require('ajv');
+const Ajv = require('ajv/dist/2020');
const error = require('./error');
const userModel = require('../models/user');
const proxyHostModel = require('../models/proxy_host');
@@ -174,7 +174,6 @@ module.exports = function (token_string) {
let schema = {
$id: 'objects',
- $schema: 'http://json-schema.org/draft-07/schema#',
description: 'Actor Properties',
type: 'object',
additionalProperties: false,
@@ -251,7 +250,7 @@ module.exports = function (token_string) {
// Initialised, token decoded ok
return this.getObjectSchema(permission)
.then((objectSchema) => {
- let data_schema = {
+ const data_schema = {
[permission]: {
data: data,
scope: Token.get('scope'),
@@ -267,24 +266,18 @@ module.exports = function (token_string) {
};
let permissionSchema = {
- $schema: 'http://json-schema.org/draft-07/schema#',
$async: true,
$id: 'permissions',
+ type: 'object',
additionalProperties: false,
properties: {}
};
permissionSchema.properties[permission] = require('./access/' + permission.replace(/:/gim, '-') + '.json');
- // logger.info('objectSchema', JSON.stringify(objectSchema, null, 2));
- // logger.info('permissionSchema', JSON.stringify(permissionSchema, null, 2));
- // logger.info('data_schema', JSON.stringify(data_schema, null, 2));
-
- let ajv = validator({
+ const ajv = new Ajv({
verbose: true,
allErrors: true,
- format: 'full',
- missingRefs: 'fail',
breakOnError: true,
coerceTypes: true,
schemas: [
diff --git a/backend/lib/access/permissions.json b/backend/lib/access/permissions.json
index 8480f9a1c8..e7a82ece3b 100644
--- a/backend/lib/access/permissions.json
+++ b/backend/lib/access/permissions.json
@@ -1,5 +1,4 @@
{
- "$schema": "http://json-schema.org/draft-07/schema#",
"$id": "perms",
"definitions": {
"view": {
diff --git a/backend/lib/access/roles.json b/backend/lib/access/roles.json
index 16b33b55bb..c97313da81 100644
--- a/backend/lib/access/roles.json
+++ b/backend/lib/access/roles.json
@@ -1,5 +1,4 @@
{
- "$schema": "http://json-schema.org/draft-07/schema#",
"$id": "roles",
"definitions": {
"admin": {
diff --git a/backend/lib/config.js b/backend/lib/config.js
index a484fc5b2a..f7fbdca6fd 100644
--- a/backend/lib/config.js
+++ b/backend/lib/config.js
@@ -34,7 +34,7 @@ const configure = () => {
logger.info('Using MySQL configuration');
instance = {
database: {
- engine: 'mysql',
+ engine: 'mysql2',
host: envMysqlHost,
port: process.env.DB_MYSQL_PORT || 3306,
user: envMysqlUser,
@@ -180,5 +180,15 @@ module.exports = {
*/
useLetsencryptStaging: function () {
return !!process.env.LE_STAGING;
+ },
+
+ /**
+ * @returns {string|null}
+ */
+ useLetsencryptServer: function () {
+ if (process.env.LE_SERVER) {
+ return process.env.LE_SERVER;
+ }
+ return null;
}
};
diff --git a/backend/lib/express/cors.js b/backend/lib/express/cors.js
index c9befeec86..6d5b8b5fba 100644
--- a/backend/lib/express/cors.js
+++ b/backend/lib/express/cors.js
@@ -1,40 +1,16 @@
-const validator = require('../validator');
-
module.exports = function (req, res, next) {
-
if (req.headers.origin) {
-
- const originSchema = {
- oneOf: [
- {
- type: 'string',
- pattern: '^[a-z\\-]+:\\/\\/(?:[\\w\\-\\.]+(:[0-9]+)?/?)?$'
- },
- {
- type: 'string',
- pattern: '^[a-z\\-]+:\\/\\/(?:\\[([a-z0-9]{0,4}\\:?)+\\])?/?(:[0-9]+)?$'
- }
- ]
- };
-
- // very relaxed validation....
- validator(originSchema, req.headers.origin)
- .then(function () {
- res.set({
- 'Access-Control-Allow-Origin': req.headers.origin,
- 'Access-Control-Allow-Credentials': true,
- 'Access-Control-Allow-Methods': 'OPTIONS, GET, POST',
- 'Access-Control-Allow-Headers': 'Content-Type, Cache-Control, Pragma, Expires, Authorization, X-Dataset-Total, X-Dataset-Offset, X-Dataset-Limit',
- 'Access-Control-Max-Age': 5 * 60,
- 'Access-Control-Expose-Headers': 'X-Dataset-Total, X-Dataset-Offset, X-Dataset-Limit'
- });
- next();
- })
- .catch(next);
-
+ res.set({
+ 'Access-Control-Allow-Origin': req.headers.origin,
+ 'Access-Control-Allow-Credentials': true,
+ 'Access-Control-Allow-Methods': 'OPTIONS, GET, POST',
+ 'Access-Control-Allow-Headers': 'Content-Type, Cache-Control, Pragma, Expires, Authorization, X-Dataset-Total, X-Dataset-Offset, X-Dataset-Limit',
+ 'Access-Control-Max-Age': 5 * 60,
+ 'Access-Control-Expose-Headers': 'X-Dataset-Total, X-Dataset-Offset, X-Dataset-Limit'
+ });
+ next();
} else {
// No origin
next();
}
-
};
diff --git a/backend/lib/helpers.js b/backend/lib/helpers.js
index e38be991e4..f7e98bebcc 100644
--- a/backend/lib/helpers.js
+++ b/backend/lib/helpers.js
@@ -27,6 +27,24 @@ module.exports = {
}
return null;
+ },
+
+ convertIntFieldsToBool: function (obj, fields) {
+ fields.forEach(function (field) {
+ if (typeof obj[field] !== 'undefined') {
+ obj[field] = obj[field] === 1;
+ }
+ });
+ return obj;
+ },
+
+ convertBoolFieldsToInt: function (obj, fields) {
+ fields.forEach(function (field) {
+ if (typeof obj[field] !== 'undefined') {
+ obj[field] = obj[field] ? 1 : 0;
+ }
+ });
+ return obj;
}
};
diff --git a/backend/lib/validator/api.js b/backend/lib/validator/api.js
index 3f51b59699..fb31e64c6f 100644
--- a/backend/lib/validator/api.js
+++ b/backend/lib/validator/api.js
@@ -1,13 +1,12 @@
-const error = require('../error');
-const path = require('path');
-const parser = require('json-schema-ref-parser');
+const Ajv = require('ajv/dist/2020');
+const error = require('../error');
-const ajv = require('ajv')({
- verbose: true,
- validateSchema: true,
- allErrors: false,
- format: 'full',
- coerceTypes: true
+const ajv = new Ajv({
+ verbose: true,
+ allErrors: true,
+ allowUnionTypes: true,
+ strict: false,
+ coerceTypes: true,
});
/**
@@ -17,12 +16,18 @@ const ajv = require('ajv')({
*/
function apiValidator (schema, payload/*, description*/) {
return new Promise(function Promise_apiValidator (resolve, reject) {
+ if (schema === null) {
+ reject(new error.ValidationError('Schema is undefined'));
+ return;
+ }
+
if (typeof payload === 'undefined') {
reject(new error.ValidationError('Payload is undefined'));
+ return;
}
- let validate = ajv.compile(schema);
- let valid = validate(payload);
+ const validate = ajv.compile(schema);
+ const valid = validate(payload);
if (valid && !validate.errors) {
resolve(payload);
@@ -35,11 +40,4 @@ function apiValidator (schema, payload/*, description*/) {
});
}
-apiValidator.loadSchemas = parser
- .dereference(path.resolve('schema/index.json'))
- .then((schema) => {
- ajv.addSchema(schema);
- return schema;
- });
-
module.exports = apiValidator;
diff --git a/backend/lib/validator/index.js b/backend/lib/validator/index.js
index d09c9be5f1..c6d240967e 100644
--- a/backend/lib/validator/index.js
+++ b/backend/lib/validator/index.js
@@ -1,17 +1,17 @@
-const _ = require('lodash');
-const error = require('../error');
-const definitions = require('../../schema/definitions.json');
+const _ = require('lodash');
+const Ajv = require('ajv/dist/2020');
+const error = require('../error');
+const commonDefinitions = require('../../schema/common.json');
RegExp.prototype.toJSON = RegExp.prototype.toString;
-const ajv = require('ajv')({
- verbose: true,
- allErrors: true,
- format: 'full', // strict regexes for format checks
- coerceTypes: true,
- schemas: [
- definitions
- ]
+const ajv = new Ajv({
+ verbose: true,
+ allErrors: true,
+ allowUnionTypes: true,
+ coerceTypes: true,
+ strict: false,
+ schemas: [commonDefinitions]
});
/**
@@ -27,23 +27,19 @@ function validator (schema, payload) {
} else {
try {
let validate = ajv.compile(schema);
+ let valid = validate(payload);
- let valid = validate(payload);
if (valid && !validate.errors) {
resolve(_.cloneDeep(payload));
} else {
let message = ajv.errorsText(validate.errors);
reject(new error.InternalValidationError(message));
}
-
} catch (err) {
reject(err);
}
-
}
-
});
-
}
module.exports = validator;
diff --git a/backend/models/access_list.js b/backend/models/access_list.js
index fbf9bda770..959df05f3d 100644
--- a/backend/models/access_list.js
+++ b/backend/models/access_list.js
@@ -2,6 +2,7 @@
// http://vincit.github.io/objection.js/
const db = require('../db');
+const helpers = require('../lib/helpers');
const Model = require('objection').Model;
const User = require('./user');
const AccessListAuth = require('./access_list_auth');
@@ -10,6 +11,12 @@ const now = require('./now_helper');
Model.knex(db);
+const boolFields = [
+ 'is_deleted',
+ 'satisfy_any',
+ 'pass_auth',
+];
+
class AccessList extends Model {
$beforeInsert () {
this.created_on = now();
@@ -25,6 +32,16 @@ class AccessList extends Model {
this.modified_on = now();
}
+ $parseDatabaseJson(json) {
+ json = super.$parseDatabaseJson(json);
+ return helpers.convertIntFieldsToBool(json, boolFields);
+ }
+
+ $formatDatabaseJson(json) {
+ json = helpers.convertBoolFieldsToInt(json, boolFields);
+ return super.$formatDatabaseJson(json);
+ }
+
static get name () {
return 'AccessList';
}
diff --git a/backend/models/auth.js b/backend/models/auth.js
index 2ee4319755..469e96bf4f 100644
--- a/backend/models/auth.js
+++ b/backend/models/auth.js
@@ -1,14 +1,19 @@
// Objection Docs:
// http://vincit.github.io/objection.js/
-const bcrypt = require('bcrypt');
-const db = require('../db');
-const Model = require('objection').Model;
-const User = require('./user');
-const now = require('./now_helper');
+const bcrypt = require('bcrypt');
+const db = require('../db');
+const helpers = require('../lib/helpers');
+const Model = require('objection').Model;
+const User = require('./user');
+const now = require('./now_helper');
Model.knex(db);
+const boolFields = [
+ 'is_deleted',
+];
+
function encryptPassword () {
/* jshint -W040 */
let _this = this;
@@ -41,6 +46,16 @@ class Auth extends Model {
return encryptPassword.apply(this, queryContext);
}
+ $parseDatabaseJson(json) {
+ json = super.$parseDatabaseJson(json);
+ return helpers.convertIntFieldsToBool(json, boolFields);
+ }
+
+ $formatDatabaseJson(json) {
+ json = helpers.convertBoolFieldsToInt(json, boolFields);
+ return super.$formatDatabaseJson(json);
+ }
+
/**
* Verify a plain password against the encrypted password
*
diff --git a/backend/models/certificate.js b/backend/models/certificate.js
index 4f0f2ef648..534d927cbb 100644
--- a/backend/models/certificate.js
+++ b/backend/models/certificate.js
@@ -1,13 +1,18 @@
// Objection Docs:
// http://vincit.github.io/objection.js/
-const db = require('../db');
-const Model = require('objection').Model;
-const User = require('./user');
-const now = require('./now_helper');
+const db = require('../db');
+const helpers = require('../lib/helpers');
+const Model = require('objection').Model;
+const User = require('./user');
+const now = require('./now_helper');
Model.knex(db);
+const boolFields = [
+ 'is_deleted',
+];
+
class Certificate extends Model {
$beforeInsert () {
this.created_on = now();
@@ -40,6 +45,16 @@ class Certificate extends Model {
}
}
+ $parseDatabaseJson(json) {
+ json = super.$parseDatabaseJson(json);
+ return helpers.convertIntFieldsToBool(json, boolFields);
+ }
+
+ $formatDatabaseJson(json) {
+ json = helpers.convertBoolFieldsToInt(json, boolFields);
+ return super.$formatDatabaseJson(json);
+ }
+
static get name () {
return 'Certificate';
}
diff --git a/backend/models/dead_host.js b/backend/models/dead_host.js
index 2e31043ae4..483da3b6be 100644
--- a/backend/models/dead_host.js
+++ b/backend/models/dead_host.js
@@ -2,6 +2,7 @@
// http://vincit.github.io/objection.js/
const db = require('../db');
+const helpers = require('../lib/helpers');
const Model = require('objection').Model;
const User = require('./user');
const Certificate = require('./certificate');
@@ -9,6 +10,11 @@ const now = require('./now_helper');
Model.knex(db);
+const boolFields = [
+ 'is_deleted',
+ 'enabled',
+];
+
class DeadHost extends Model {
$beforeInsert () {
this.created_on = now();
@@ -36,6 +42,16 @@ class DeadHost extends Model {
}
}
+ $parseDatabaseJson(json) {
+ json = super.$parseDatabaseJson(json);
+ return helpers.convertIntFieldsToBool(json, boolFields);
+ }
+
+ $formatDatabaseJson(json) {
+ json = helpers.convertBoolFieldsToInt(json, boolFields);
+ return super.$formatDatabaseJson(json);
+ }
+
static get name () {
return 'DeadHost';
}
diff --git a/backend/models/proxy_host.js b/backend/models/proxy_host.js
index d84181cf18..07aa5dd3c8 100644
--- a/backend/models/proxy_host.js
+++ b/backend/models/proxy_host.js
@@ -2,6 +2,7 @@
// http://vincit.github.io/objection.js/
const db = require('../db');
+const helpers = require('../lib/helpers');
const Model = require('objection').Model;
const User = require('./user');
const AccessList = require('./access_list');
@@ -10,6 +11,18 @@ const now = require('./now_helper');
Model.knex(db);
+const boolFields = [
+ 'is_deleted',
+ 'ssl_forced',
+ 'caching_enabled',
+ 'block_exploits',
+ 'allow_websocket_upgrade',
+ 'http2_support',
+ 'enabled',
+ 'hsts_enabled',
+ 'hsts_subdomains',
+];
+
class ProxyHost extends Model {
$beforeInsert () {
this.created_on = now();
@@ -37,6 +50,16 @@ class ProxyHost extends Model {
}
}
+ $parseDatabaseJson(json) {
+ json = super.$parseDatabaseJson(json);
+ return helpers.convertIntFieldsToBool(json, boolFields);
+ }
+
+ $formatDatabaseJson(json) {
+ json = helpers.convertBoolFieldsToInt(json, boolFields);
+ return super.$formatDatabaseJson(json);
+ }
+
static get name () {
return 'ProxyHost';
}
diff --git a/backend/models/redirection_host.js b/backend/models/redirection_host.js
index c90a6de6c1..556742f0c6 100644
--- a/backend/models/redirection_host.js
+++ b/backend/models/redirection_host.js
@@ -3,6 +3,7 @@
// http://vincit.github.io/objection.js/
const db = require('../db');
+const helpers = require('../lib/helpers');
const Model = require('objection').Model;
const User = require('./user');
const Certificate = require('./certificate');
@@ -10,6 +11,14 @@ const now = require('./now_helper');
Model.knex(db);
+const boolFields = [
+ 'is_deleted',
+ 'enabled',
+ 'preserve_path',
+ 'ssl_forced',
+ 'block_exploits',
+];
+
class RedirectionHost extends Model {
$beforeInsert () {
this.created_on = now();
@@ -37,6 +46,16 @@ class RedirectionHost extends Model {
}
}
+ $parseDatabaseJson(json) {
+ json = super.$parseDatabaseJson(json);
+ return helpers.convertIntFieldsToBool(json, boolFields);
+ }
+
+ $formatDatabaseJson(json) {
+ json = helpers.convertBoolFieldsToInt(json, boolFields);
+ return super.$formatDatabaseJson(json);
+ }
+
static get name () {
return 'RedirectionHost';
}
diff --git a/backend/models/stream.js b/backend/models/stream.js
index 23dd7ac0d8..c28b86cde0 100644
--- a/backend/models/stream.js
+++ b/backend/models/stream.js
@@ -2,6 +2,7 @@
// http://vincit.github.io/objection.js/
const db = require('../db');
+const helpers = require('../lib/helpers');
const Model = require('objection').Model;
const User = require('./user');
const now = require('./now_helper');
@@ -9,6 +10,12 @@ const Certificate = require('./certificate');
Model.knex(db);
+const boolFields = [
+ 'is_deleted',
+ 'tcp_forwarding',
+ 'udp_forwarding',
+];
+
class Stream extends Model {
$beforeInsert () {
this.created_on = now();
@@ -24,6 +31,16 @@ class Stream extends Model {
this.modified_on = now();
}
+ $parseDatabaseJson(json) {
+ json = super.$parseDatabaseJson(json);
+ return helpers.convertIntFieldsToBool(json, boolFields);
+ }
+
+ $formatDatabaseJson(json) {
+ json = helpers.convertBoolFieldsToInt(json, boolFields);
+ return super.$formatDatabaseJson(json);
+ }
+
static get name () {
return 'Stream';
}
diff --git a/backend/models/user.js b/backend/models/user.js
index 93489fefef..78fd3dd67d 100644
--- a/backend/models/user.js
+++ b/backend/models/user.js
@@ -2,12 +2,18 @@
// http://vincit.github.io/objection.js/
const db = require('../db');
+const helpers = require('../lib/helpers');
const Model = require('objection').Model;
const UserPermission = require('./user_permission');
const now = require('./now_helper');
Model.knex(db);
+const boolFields = [
+ 'is_deleted',
+ 'is_disabled',
+];
+
class User extends Model {
$beforeInsert () {
this.created_on = now();
@@ -23,6 +29,16 @@ class User extends Model {
this.modified_on = now();
}
+ $parseDatabaseJson(json) {
+ json = super.$parseDatabaseJson(json);
+ return helpers.convertIntFieldsToBool(json, boolFields);
+ }
+
+ $formatDatabaseJson(json) {
+ json = helpers.convertBoolFieldsToInt(json, boolFields);
+ return super.$formatDatabaseJson(json);
+ }
+
static get name () {
return 'User';
}
diff --git a/backend/package.json b/backend/package.json
index b938c9a9ca..1bc3ef1653 100644
--- a/backend/package.json
+++ b/backend/package.json
@@ -2,24 +2,24 @@
"name": "nginx-proxy-manager",
"version": "0.0.0",
"description": "A beautiful interface for creating Nginx endpoints",
- "main": "js/index.js",
+ "main": "index.js",
"dependencies": {
- "ajv": "^6.12.0",
+ "@apidevtools/json-schema-ref-parser": "^11.7.0",
+ "ajv": "^8.17.1",
"archiver": "^5.3.0",
"batchflow": "^0.4.0",
"bcrypt": "^5.0.0",
- "body-parser": "^1.19.0",
+ "body-parser": "^1.20.3",
"compression": "^1.7.4",
- "express": "^4.19.2",
+ "express": "^4.20.0",
"express-fileupload": "^1.1.9",
"gravatar": "^1.8.0",
- "json-schema-ref-parser": "^8.0.0",
"jsonwebtoken": "^9.0.0",
"knex": "2.4.2",
"liquidjs": "10.6.1",
"lodash": "^4.17.21",
"moment": "^2.29.4",
- "mysql": "^2.18.1",
+ "mysql2": "^3.11.1",
"node-rsa": "^1.0.8",
"objection": "3.0.1",
"path": "^0.12.7",
@@ -34,9 +34,14 @@
"author": "Jamie Curnow 404
"
+ },
+ "type": "object"
+ }
+ }
+}
diff --git a/backend/schema/components/stream-list.json b/backend/schema/components/stream-list.json
new file mode 100644
index 0000000000..39789b4a73
--- /dev/null
+++ b/backend/schema/components/stream-list.json
@@ -0,0 +1,7 @@
+{
+ "type": "array",
+ "description": "Proxy Hosts list",
+ "items": {
+ "$ref": "./proxy-host-object.json"
+ }
+}
diff --git a/backend/schema/components/stream-object.json b/backend/schema/components/stream-object.json
new file mode 100644
index 0000000000..cd8e1374f8
--- /dev/null
+++ b/backend/schema/components/stream-object.json
@@ -0,0 +1,76 @@
+{
+ "type": "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"],
+ "additionalProperties": false,
+ "properties": {
+ "id": {
+ "$ref": "../common.json#/properties/id"
+ },
+ "created_on": {
+ "$ref": "../common.json#/properties/created_on"
+ },
+ "modified_on": {
+ "$ref": "../common.json#/properties/modified_on"
+ },
+ "owner_user_id": {
+ "$ref": "../common.json#/properties/user_id"
+ },
+ "incoming_port": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 65535
+ },
+ "forwarding_host": {
+ "anyOf": [
+ {
+ "description": "Domain Name",
+ "example": "jc21.com",
+ "type": "string",
+ "pattern": "^(?:[^.*]+\\.?)+[^.]$"
+ },
+ {
+ "type": "string",
+ "format": "ipv4"
+ },
+ {
+ "type": "string",
+ "format": "ipv6"
+ }
+ ]
+ },
+ "forwarding_port": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 65535
+ },
+ "tcp_forwarding": {
+ "type": "boolean"
+ },
+ "udp_forwarding": {
+ "type": "boolean"
+ },
+ "domain_names": {
+ "$ref": "../common.json#/properties/domain_names"
+ },
+ "certificate_id": {
+ "$ref": "../common.json#/properties/certificate_id"
+ },
+ "certificate": {
+ "oneOf": [
+ {
+ "type": "null"
+ },
+ {
+ "$ref": "./certificate-object.json"
+ }
+ ]
+ },
+ "enabled": {
+ "$ref": "../common.json#/properties/enabled"
+ },
+ "meta": {
+ "type": "object"
+ }
+ }
+}
diff --git a/backend/schema/components/token-object.json b/backend/schema/components/token-object.json
new file mode 100644
index 0000000000..a7044bce9b
--- /dev/null
+++ b/backend/schema/components/token-object.json
@@ -0,0 +1,19 @@
+{
+ "type": "object",
+ "description": "Token object",
+ "required": ["expires", "token"],
+ "additionalProperties": false,
+ "properties": {
+ "expires": {
+ "description": "Token Expiry Unix Time",
+ "example": 1566540249,
+ "minimum": 1,
+ "type": "number"
+ },
+ "token": {
+ "description": "JWT Token",
+ "example": "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.ey...xaHKYr3Kk6MvkUjcC4",
+ "type": "string"
+ }
+ }
+}
diff --git a/backend/schema/components/user-list.json b/backend/schema/components/user-list.json
new file mode 100644
index 0000000000..c5c0f71169
--- /dev/null
+++ b/backend/schema/components/user-list.json
@@ -0,0 +1,7 @@
+{
+ "type": "array",
+ "description": "User list",
+ "items": {
+ "$ref": "./user-object.json"
+ }
+}
diff --git a/backend/schema/components/user-object.json b/backend/schema/components/user-object.json
new file mode 100644
index 0000000000..180e8f1976
--- /dev/null
+++ b/backend/schema/components/user-object.json
@@ -0,0 +1,59 @@
+{
+ "type": "object",
+ "description": "User object",
+ "required": ["id", "created_on", "modified_on", "is_disabled", "email", "name", "nickname", "avatar", "roles"],
+ "additionalProperties": false,
+ "properties": {
+ "id": {
+ "type": "integer",
+ "description": "User ID",
+ "minimum": 1,
+ "example": 1
+ },
+ "created_on": {
+ "type": "string",
+ "description": "Created Date",
+ "example": "2020-01-30T09:36:08.000Z"
+ },
+ "modified_on": {
+ "type": "string",
+ "description": "Modified Date",
+ "example": "2020-01-30T09:41:04.000Z"
+ },
+ "is_disabled": {
+ "type": "boolean",
+ "description": "Is user Disabled",
+ "example": true
+ },
+ "email": {
+ "type": "string",
+ "description": "Email",
+ "minLength": 3,
+ "example": "jc@jc21.com"
+ },
+ "name": {
+ "type": "string",
+ "description": "Name",
+ "minLength": 1,
+ "example": "Jamie Curnow"
+ },
+ "nickname": {
+ "type": "string",
+ "description": "Nickname",
+ "example": "James"
+ },
+ "avatar": {
+ "type": "string",
+ "description": "Gravatar URL based on email, without scheme",
+ "example": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm"
+ },
+ "roles": {
+ "description": "Roles applied",
+ "example": ["admin"],
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+}
diff --git a/backend/schema/definitions.json b/backend/schema/definitions.json
deleted file mode 100644
index 640093a088..0000000000
--- a/backend/schema/definitions.json
+++ /dev/null
@@ -1,240 +0,0 @@
-{
- "$schema": "http://json-schema.org/draft-07/schema#",
- "$id": "definitions",
- "definitions": {
- "id": {
- "description": "Unique identifier",
- "example": 123456,
- "readOnly": true,
- "type": "integer",
- "minimum": 1
- },
- "setting_id": {
- "description": "Unique identifier for a Setting",
- "example": "default-site",
- "readOnly": true,
- "type": "string",
- "minLength": 2
- },
- "token": {
- "type": "string",
- "minLength": 10
- },
- "expand": {
- "anyOf": [
- {
- "type": "null"
- },
- {
- "type": "array",
- "minItems": 1,
- "items": {
- "type": "string"
- }
- }
- ]
- },
- "sort": {
- "type": "array",
- "minItems": 1,
- "items": {
- "type": "object",
- "required": [
- "field",
- "dir"
- ],
- "additionalProperties": false,
- "properties": {
- "field": {
- "type": "string"
- },
- "dir": {
- "type": "string",
- "pattern": "^(asc|desc)$"
- }
- }
- }
- },
- "query": {
- "anyOf": [
- {
- "type": "null"
- },
- {
- "type": "string",
- "minLength": 1,
- "maxLength": 255
- }
- ]
- },
- "criteria": {
- "anyOf": [
- {
- "type": "null"
- },
- {
- "type": "object"
- }
- ]
- },
- "fields": {
- "anyOf": [
- {
- "type": "null"
- },
- {
- "type": "array",
- "minItems": 1,
- "items": {
- "type": "string"
- }
- }
- ]
- },
- "omit": {
- "anyOf": [
- {
- "type": "null"
- },
- {
- "type": "array",
- "minItems": 1,
- "items": {
- "type": "string"
- }
- }
- ]
- },
- "created_on": {
- "description": "Date and time of creation",
- "format": "date-time",
- "readOnly": true,
- "type": "string"
- },
- "modified_on": {
- "description": "Date and time of last update",
- "format": "date-time",
- "readOnly": true,
- "type": "string"
- },
- "user_id": {
- "description": "User ID",
- "example": 1234,
- "type": "integer",
- "minimum": 1
- },
- "certificate_id": {
- "description": "Certificate ID",
- "example": 1234,
- "anyOf": [
- {
- "type": "integer",
- "minimum": 0
- },
- {
- "type": "string",
- "pattern": "^new$"
- }
- ]
- },
- "access_list_id": {
- "description": "Access List ID",
- "example": 1234,
- "type": "integer",
- "minimum": 0
- },
- "name": {
- "type": "string",
- "minLength": 1,
- "maxLength": 255
- },
- "email": {
- "description": "Email Address",
- "example": "john@example.com",
- "format": "email",
- "type": "string",
- "minLength": 6,
- "maxLength": 100
- },
- "password": {
- "description": "Password",
- "type": "string",
- "minLength": 8,
- "maxLength": 255
- },
- "domain_name": {
- "description": "Domain Name",
- "example": "jc21.com",
- "type": "string",
- "pattern": "^(?:[^.*]+\\.?)+[^.]$"
- },
- "domain_names": {
- "description": "Domain Names separated by a comma",
- "example": "*.jc21.com,blog.jc21.com",
- "type": "array",
- "maxItems": 100,
- "uniqueItems": true,
- "items": {
- "type": "string",
- "pattern": "^(?:\\*\\.)?(?:[^.*]+\\.?)+[^.]$"
- }
- },
- "http_code": {
- "description": "Redirect HTTP Status Code",
- "example": 302,
- "type": "integer",
- "minimum": 300,
- "maximum": 308
- },
- "scheme": {
- "description": "RFC Protocol",
- "example": "HTTPS or $scheme",
- "type": "string",
- "minLength": 4
- },
- "enabled": {
- "description": "Is Enabled",
- "example": true,
- "type": "boolean"
- },
- "ssl_enabled": {
- "description": "Is SSL Enabled",
- "example": true,
- "type": "boolean"
- },
- "ssl_forced": {
- "description": "Is SSL Forced",
- "example": false,
- "type": "boolean"
- },
- "hsts_enabled": {
- "description": "Is HSTS Enabled",
- "example": false,
- "type": "boolean"
- },
- "hsts_subdomains": {
- "description": "Is HSTS applicable to all subdomains",
- "example": false,
- "type": "boolean"
- },
- "ssl_provider": {
- "type": "string",
- "pattern": "^(letsencrypt|other)$"
- },
- "http2_support": {
- "description": "HTTP2 Protocol Support",
- "example": false,
- "type": "boolean"
- },
- "block_exploits": {
- "description": "Should we block common exploits",
- "example": true,
- "type": "boolean"
- },
- "caching_enabled": {
- "description": "Should we cache assets",
- "example": true,
- "type": "boolean"
- }
- }
-}
diff --git a/backend/schema/endpoints/access-lists.json b/backend/schema/endpoints/access-lists.json
deleted file mode 100644
index 404e323765..0000000000
--- a/backend/schema/endpoints/access-lists.json
+++ /dev/null
@@ -1,236 +0,0 @@
-{
- "$schema": "http://json-schema.org/draft-07/schema#",
- "$id": "endpoints/access-lists",
- "title": "Access Lists",
- "description": "Endpoints relating to Access Lists",
- "stability": "stable",
- "type": "object",
- "definitions": {
- "id": {
- "$ref": "../definitions.json#/definitions/id"
- },
- "created_on": {
- "$ref": "../definitions.json#/definitions/created_on"
- },
- "modified_on": {
- "$ref": "../definitions.json#/definitions/modified_on"
- },
- "name": {
- "type": "string",
- "description": "Name of the 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": {
- "type": "object"
- }
- },
- "properties": {
- "id": {
- "$ref": "#/definitions/id"
- },
- "created_on": {
- "$ref": "#/definitions/created_on"
- },
- "modified_on": {
- "$ref": "#/definitions/modified_on"
- },
- "name": {
- "$ref": "#/definitions/name"
- },
- "meta": {
- "$ref": "#/definitions/meta"
- }
- },
- "links": [
- {
- "title": "List",
- "description": "Returns a list of Access Lists",
- "href": "/nginx/access-lists",
- "access": "private",
- "method": "GET",
- "rel": "self",
- "http_header": {
- "$ref": "../examples.json#/definitions/auth_header"
- },
- "targetSchema": {
- "type": "array",
- "items": {
- "$ref": "#/properties"
- }
- }
- },
- {
- "title": "Create",
- "description": "Creates a new Access List",
- "href": "/nginx/access-list",
- "access": "private",
- "method": "POST",
- "rel": "create",
- "http_header": {
- "$ref": "../examples.json#/definitions/auth_header"
- },
- "schema": {
- "type": "object",
- "additionalProperties": false,
- "required": ["name"],
- "properties": {
- "name": {
- "$ref": "#/definitions/name"
- },
- "satisfy_any": {
- "$ref": "#/definitions/satisfy_any"
- },
- "pass_auth": {
- "$ref": "#/definitions/pass_auth"
- },
- "items": {
- "type": "array",
- "minItems": 0,
- "items": {
- "type": "object",
- "additionalProperties": false,
- "properties": {
- "username": {
- "type": "string",
- "minLength": 1
- },
- "password": {
- "type": "string",
- "minLength": 1
- }
- }
- }
- },
- "clients": {
- "type": "array",
- "minItems": 0,
- "items": {
- "type": "object",
- "additionalProperties": false,
- "properties": {
- "address": {
- "$ref": "#/definitions/address"
- },
- "directive": {
- "$ref": "#/definitions/directive"
- }
- }
- }
- },
- "meta": {
- "$ref": "#/definitions/meta"
- }
- }
- },
- "targetSchema": {
- "properties": {
- "$ref": "#/properties"
- }
- }
- },
- {
- "title": "Update",
- "description": "Updates a existing Access List",
- "href": "/nginx/access-list/{definitions.identity.example}",
- "access": "private",
- "method": "PUT",
- "rel": "update",
- "http_header": {
- "$ref": "../examples.json#/definitions/auth_header"
- },
- "schema": {
- "type": "object",
- "additionalProperties": false,
- "properties": {
- "name": {
- "$ref": "#/definitions/name"
- },
- "satisfy_any": {
- "$ref": "#/definitions/satisfy_any"
- },
- "pass_auth": {
- "$ref": "#/definitions/pass_auth"
- },
- "items": {
- "type": "array",
- "minItems": 0,
- "items": {
- "type": "object",
- "additionalProperties": false,
- "properties": {
- "username": {
- "type": "string",
- "minLength": 1
- },
- "password": {
- "type": "string",
- "minLength": 0
- }
- }
- }
- },
- "clients": {
- "type": "array",
- "minItems": 0,
- "items": {
- "type": "object",
- "additionalProperties": false,
- "properties": {
- "address": {
- "$ref": "#/definitions/address"
- },
- "directive": {
- "$ref": "#/definitions/directive"
- }
- }
- }
- }
- }
- },
- "targetSchema": {
- "properties": {
- "$ref": "#/properties"
- }
- }
- },
- {
- "title": "Delete",
- "description": "Deletes a existing Access List",
- "href": "/nginx/access-list/{definitions.identity.example}",
- "access": "private",
- "method": "DELETE",
- "rel": "delete",
- "http_header": {
- "$ref": "../examples.json#/definitions/auth_header"
- },
- "targetSchema": {
- "type": "boolean"
- }
- }
- ]
-}
diff --git a/backend/schema/endpoints/certificates.json b/backend/schema/endpoints/certificates.json
deleted file mode 100644
index 955ca75c97..0000000000
--- a/backend/schema/endpoints/certificates.json
+++ /dev/null
@@ -1,173 +0,0 @@
-{
- "$schema": "http://json-schema.org/draft-07/schema#",
- "$id": "endpoints/certificates",
- "title": "Certificates",
- "description": "Endpoints relating to Certificates",
- "stability": "stable",
- "type": "object",
- "definitions": {
- "id": {
- "$ref": "../definitions.json#/definitions/id"
- },
- "created_on": {
- "$ref": "../definitions.json#/definitions/created_on"
- },
- "modified_on": {
- "$ref": "../definitions.json#/definitions/modified_on"
- },
- "provider": {
- "$ref": "../definitions.json#/definitions/ssl_provider"
- },
- "nice_name": {
- "type": "string",
- "description": "Nice Name for the custom certificate"
- },
- "domain_names": {
- "$ref": "../definitions.json#/definitions/domain_names"
- },
- "expires_on": {
- "description": "Date and time of expiration",
- "format": "date-time",
- "readOnly": true,
- "type": "string"
- },
- "meta": {
- "type": "object",
- "additionalProperties": false,
- "properties": {
- "letsencrypt_email": {
- "type": "string",
- "format": "email"
- },
- "letsencrypt_agree": {
- "type": "boolean"
- },
- "dns_challenge": {
- "type": "boolean"
- },
- "dns_provider": {
- "type": "string"
- },
- "dns_provider_credentials": {
- "type": "string"
- },
- "propagation_seconds": {
- "anyOf": [
- {
- "type": "integer",
- "minimum": 0
- }
- ]
-
- }
- }
- }
- },
- "properties": {
- "id": {
- "$ref": "#/definitions/id"
- },
- "created_on": {
- "$ref": "#/definitions/created_on"
- },
- "modified_on": {
- "$ref": "#/definitions/modified_on"
- },
- "provider": {
- "$ref": "#/definitions/provider"
- },
- "nice_name": {
- "$ref": "#/definitions/nice_name"
- },
- "domain_names": {
- "$ref": "#/definitions/domain_names"
- },
- "expires_on": {
- "$ref": "#/definitions/expires_on"
- },
- "meta": {
- "$ref": "#/definitions/meta"
- }
- },
- "links": [
- {
- "title": "List",
- "description": "Returns a list of Certificates",
- "href": "/nginx/certificates",
- "access": "private",
- "method": "GET",
- "rel": "self",
- "http_header": {
- "$ref": "../examples.json#/definitions/auth_header"
- },
- "targetSchema": {
- "type": "array",
- "items": {
- "$ref": "#/properties"
- }
- }
- },
- {
- "title": "Create",
- "description": "Creates a new Certificate",
- "href": "/nginx/certificates",
- "access": "private",
- "method": "POST",
- "rel": "create",
- "http_header": {
- "$ref": "../examples.json#/definitions/auth_header"
- },
- "schema": {
- "type": "object",
- "additionalProperties": false,
- "required": [
- "provider"
- ],
- "properties": {
- "provider": {
- "$ref": "#/definitions/provider"
- },
- "nice_name": {
- "$ref": "#/definitions/nice_name"
- },
- "domain_names": {
- "$ref": "#/definitions/domain_names"
- },
- "meta": {
- "$ref": "#/definitions/meta"
- }
- }
- },
- "targetSchema": {
- "properties": {
- "$ref": "#/properties"
- }
- }
- },
- {
- "title": "Delete",
- "description": "Deletes a existing Certificate",
- "href": "/nginx/certificates/{definitions.identity.example}",
- "access": "private",
- "method": "DELETE",
- "rel": "delete",
- "http_header": {
- "$ref": "../examples.json#/definitions/auth_header"
- },
- "targetSchema": {
- "type": "boolean"
- }
- },
- {
- "title": "Test HTTP Challenge",
- "description": "Tests whether the HTTP challenge should work",
- "href": "/nginx/certificates/{definitions.identity.example}/test-http",
- "access": "private",
- "method": "GET",
- "rel": "info",
- "http_header": {
- "$ref": "../examples.json#/definitions/auth_header"
- }
- }
- ]
-}
diff --git a/backend/schema/endpoints/dead-hosts.json b/backend/schema/endpoints/dead-hosts.json
deleted file mode 100644
index 0c73c3be16..0000000000
--- a/backend/schema/endpoints/dead-hosts.json
+++ /dev/null
@@ -1,240 +0,0 @@
-{
- "$schema": "http://json-schema.org/draft-07/schema#",
- "$id": "endpoints/dead-hosts",
- "title": "404 Hosts",
- "description": "Endpoints relating to 404 Hosts",
- "stability": "stable",
- "type": "object",
- "definitions": {
- "id": {
- "$ref": "../definitions.json#/definitions/id"
- },
- "created_on": {
- "$ref": "../definitions.json#/definitions/created_on"
- },
- "modified_on": {
- "$ref": "../definitions.json#/definitions/modified_on"
- },
- "domain_names": {
- "$ref": "../definitions.json#/definitions/domain_names"
- },
- "certificate_id": {
- "$ref": "../definitions.json#/definitions/certificate_id"
- },
- "ssl_forced": {
- "$ref": "../definitions.json#/definitions/ssl_forced"
- },
- "hsts_enabled": {
- "$ref": "../definitions.json#/definitions/hsts_enabled"
- },
- "hsts_subdomains": {
- "$ref": "../definitions.json#/definitions/hsts_subdomains"
- },
- "http2_support": {
- "$ref": "../definitions.json#/definitions/http2_support"
- },
- "advanced_config": {
- "type": "string"
- },
- "enabled": {
- "$ref": "../definitions.json#/definitions/enabled"
- },
- "meta": {
- "type": "object"
- }
- },
- "properties": {
- "id": {
- "$ref": "#/definitions/id"
- },
- "created_on": {
- "$ref": "#/definitions/created_on"
- },
- "modified_on": {
- "$ref": "#/definitions/modified_on"
- },
- "domain_names": {
- "$ref": "#/definitions/domain_names"
- },
- "certificate_id": {
- "$ref": "#/definitions/certificate_id"
- },
- "ssl_forced": {
- "$ref": "#/definitions/ssl_forced"
- },
- "hsts_enabled": {
- "$ref": "#/definitions/hsts_enabled"
- },
- "hsts_subdomains": {
- "$ref": "#/definitions/hsts_subdomains"
- },
- "http2_support": {
- "$ref": "#/definitions/http2_support"
- },
- "advanced_config": {
- "$ref": "#/definitions/advanced_config"
- },
- "enabled": {
- "$ref": "#/definitions/enabled"
- },
- "meta": {
- "$ref": "#/definitions/meta"
- }
- },
- "links": [
- {
- "title": "List",
- "description": "Returns a list of 404 Hosts",
- "href": "/nginx/dead-hosts",
- "access": "private",
- "method": "GET",
- "rel": "self",
- "http_header": {
- "$ref": "../examples.json#/definitions/auth_header"
- },
- "targetSchema": {
- "type": "array",
- "items": {
- "$ref": "#/properties"
- }
- }
- },
- {
- "title": "Create",
- "description": "Creates a new 404 Host",
- "href": "/nginx/dead-hosts",
- "access": "private",
- "method": "POST",
- "rel": "create",
- "http_header": {
- "$ref": "../examples.json#/definitions/auth_header"
- },
- "schema": {
- "type": "object",
- "additionalProperties": false,
- "required": [
- "domain_names"
- ],
- "properties": {
- "domain_names": {
- "$ref": "#/definitions/domain_names"
- },
- "certificate_id": {
- "$ref": "#/definitions/certificate_id"
- },
- "ssl_forced": {
- "$ref": "#/definitions/ssl_forced"
- },
- "hsts_enabled": {
- "$ref": "#/definitions/hsts_enabled"
- },
- "hsts_subdomains": {
- "$ref": "#/definitions/hsts_enabled"
- },
- "http2_support": {
- "$ref": "#/definitions/http2_support"
- },
- "advanced_config": {
- "$ref": "#/definitions/advanced_config"
- },
- "meta": {
- "$ref": "#/definitions/meta"
- }
- }
- },
- "targetSchema": {
- "properties": {
- "$ref": "#/properties"
- }
- }
- },
- {
- "title": "Update",
- "description": "Updates a existing 404 Host",
- "href": "/nginx/dead-hosts/{definitions.identity.example}",
- "access": "private",
- "method": "PUT",
- "rel": "update",
- "http_header": {
- "$ref": "../examples.json#/definitions/auth_header"
- },
- "schema": {
- "type": "object",
- "additionalProperties": false,
- "properties": {
- "domain_names": {
- "$ref": "#/definitions/domain_names"
- },
- "certificate_id": {
- "$ref": "#/definitions/certificate_id"
- },
- "ssl_forced": {
- "$ref": "#/definitions/ssl_forced"
- },
- "hsts_enabled": {
- "$ref": "#/definitions/hsts_enabled"
- },
- "hsts_subdomains": {
- "$ref": "#/definitions/hsts_enabled"
- },
- "http2_support": {
- "$ref": "#/definitions/http2_support"
- },
- "advanced_config": {
- "$ref": "#/definitions/advanced_config"
- },
- "meta": {
- "$ref": "#/definitions/meta"
- }
- }
- },
- "targetSchema": {
- "properties": {
- "$ref": "#/properties"
- }
- }
- },
- {
- "title": "Delete",
- "description": "Deletes a existing 404 Host",
- "href": "/nginx/dead-hosts/{definitions.identity.example}",
- "access": "private",
- "method": "DELETE",
- "rel": "delete",
- "http_header": {
- "$ref": "../examples.json#/definitions/auth_header"
- },
- "targetSchema": {
- "type": "boolean"
- }
- },
- {
- "title": "Enable",
- "description": "Enables a existing 404 Host",
- "href": "/nginx/dead-hosts/{definitions.identity.example}/enable",
- "access": "private",
- "method": "POST",
- "rel": "update",
- "http_header": {
- "$ref": "../examples.json#/definitions/auth_header"
- },
- "targetSchema": {
- "type": "boolean"
- }
- },
- {
- "title": "Disable",
- "description": "Disables a existing 404 Host",
- "href": "/nginx/dead-hosts/{definitions.identity.example}/disable",
- "access": "private",
- "method": "POST",
- "rel": "update",
- "http_header": {
- "$ref": "../examples.json#/definitions/auth_header"
- },
- "targetSchema": {
- "type": "boolean"
- }
- }
- ]
-}
diff --git a/backend/schema/endpoints/proxy-hosts.json b/backend/schema/endpoints/proxy-hosts.json
deleted file mode 100644
index 9a3fff2fc7..0000000000
--- a/backend/schema/endpoints/proxy-hosts.json
+++ /dev/null
@@ -1,387 +0,0 @@
-{
- "$schema": "http://json-schema.org/draft-07/schema#",
- "$id": "endpoints/proxy-hosts",
- "title": "Proxy Hosts",
- "description": "Endpoints relating to Proxy Hosts",
- "stability": "stable",
- "type": "object",
- "definitions": {
- "id": {
- "$ref": "../definitions.json#/definitions/id"
- },
- "created_on": {
- "$ref": "../definitions.json#/definitions/created_on"
- },
- "modified_on": {
- "$ref": "../definitions.json#/definitions/modified_on"
- },
- "domain_names": {
- "$ref": "../definitions.json#/definitions/domain_names"
- },
- "forward_scheme": {
- "type": "string",
- "enum": ["http", "https"]
- },
- "forward_host": {
- "type": "string",
- "minLength": 1,
- "maxLength": 255
- },
- "forward_port": {
- "type": "integer",
- "minimum": 1,
- "maximum": 65535
- },
- "certificate_id": {
- "$ref": "../definitions.json#/definitions/certificate_id"
- },
- "ssl_forced": {
- "$ref": "../definitions.json#/definitions/ssl_forced"
- },
- "hsts_enabled": {
- "$ref": "../definitions.json#/definitions/hsts_enabled"
- },
- "hsts_subdomains": {
- "$ref": "../definitions.json#/definitions/hsts_subdomains"
- },
- "http2_support": {
- "$ref": "../definitions.json#/definitions/http2_support"
- },
- "block_exploits": {
- "$ref": "../definitions.json#/definitions/block_exploits"
- },
- "caching_enabled": {
- "$ref": "../definitions.json#/definitions/caching_enabled"
- },
- "allow_websocket_upgrade": {
- "description": "Allow Websocket Upgrade for all paths",
- "example": true,
- "type": "boolean"
- },
- "access_list_id": {
- "$ref": "../definitions.json#/definitions/access_list_id"
- },
- "advanced_config": {
- "type": "string"
- },
- "enabled": {
- "$ref": "../definitions.json#/definitions/enabled"
- },
- "meta": {
- "type": "object"
- },
- "locations": {
- "type": "array",
- "minItems": 0,
- "items": {
- "type": "object",
- "required": [
- "forward_scheme",
- "forward_host",
- "forward_port",
- "path"
- ],
- "additionalProperties": false,
- "properties": {
- "id": {
- "type": ["integer", "null"]
- },
- "path": {
- "type": "string",
- "minLength": 1
- },
- "forward_scheme": {
- "$ref": "#/definitions/forward_scheme"
- },
- "forward_host": {
- "$ref": "#/definitions/forward_host"
- },
- "forward_port": {
- "$ref": "#/definitions/forward_port"
- },
- "forward_path": {
- "type": "string"
- },
- "advanced_config": {
- "type": "string"
- }
- }
- }
- }
- },
- "properties": {
- "id": {
- "$ref": "#/definitions/id"
- },
- "created_on": {
- "$ref": "#/definitions/created_on"
- },
- "modified_on": {
- "$ref": "#/definitions/modified_on"
- },
- "domain_names": {
- "$ref": "#/definitions/domain_names"
- },
- "forward_scheme": {
- "$ref": "#/definitions/forward_scheme"
- },
- "forward_host": {
- "$ref": "#/definitions/forward_host"
- },
- "forward_port": {
- "$ref": "#/definitions/forward_port"
- },
- "certificate_id": {
- "$ref": "#/definitions/certificate_id"
- },
- "ssl_forced": {
- "$ref": "#/definitions/ssl_forced"
- },
- "hsts_enabled": {
- "$ref": "#/definitions/hsts_enabled"
- },
- "hsts_subdomains": {
- "$ref": "#/definitions/hsts_subdomains"
- },
- "http2_support": {
- "$ref": "#/definitions/http2_support"
- },
- "block_exploits": {
- "$ref": "#/definitions/block_exploits"
- },
- "caching_enabled": {
- "$ref": "#/definitions/caching_enabled"
- },
- "allow_websocket_upgrade": {
- "$ref": "#/definitions/allow_websocket_upgrade"
- },
- "access_list_id": {
- "$ref": "#/definitions/access_list_id"
- },
- "advanced_config": {
- "$ref": "#/definitions/advanced_config"
- },
- "enabled": {
- "$ref": "#/definitions/enabled"
- },
- "meta": {
- "$ref": "#/definitions/meta"
- },
- "locations": {
- "$ref": "#/definitions/locations"
- }
- },
- "links": [
- {
- "title": "List",
- "description": "Returns a list of Proxy Hosts",
- "href": "/nginx/proxy-hosts",
- "access": "private",
- "method": "GET",
- "rel": "self",
- "http_header": {
- "$ref": "../examples.json#/definitions/auth_header"
- },
- "targetSchema": {
- "type": "array",
- "items": {
- "$ref": "#/properties"
- }
- }
- },
- {
- "title": "Create",
- "description": "Creates a new Proxy Host",
- "href": "/nginx/proxy-hosts",
- "access": "private",
- "method": "POST",
- "rel": "create",
- "http_header": {
- "$ref": "../examples.json#/definitions/auth_header"
- },
- "schema": {
- "type": "object",
- "additionalProperties": false,
- "required": [
- "domain_names",
- "forward_scheme",
- "forward_host",
- "forward_port"
- ],
- "properties": {
- "domain_names": {
- "$ref": "#/definitions/domain_names"
- },
- "forward_scheme": {
- "$ref": "#/definitions/forward_scheme"
- },
- "forward_host": {
- "$ref": "#/definitions/forward_host"
- },
- "forward_port": {
- "$ref": "#/definitions/forward_port"
- },
- "certificate_id": {
- "$ref": "#/definitions/certificate_id"
- },
- "ssl_forced": {
- "$ref": "#/definitions/ssl_forced"
- },
- "hsts_enabled": {
- "$ref": "#/definitions/hsts_enabled"
- },
- "hsts_subdomains": {
- "$ref": "#/definitions/hsts_enabled"
- },
- "http2_support": {
- "$ref": "#/definitions/http2_support"
- },
- "block_exploits": {
- "$ref": "#/definitions/block_exploits"
- },
- "caching_enabled": {
- "$ref": "#/definitions/caching_enabled"
- },
- "allow_websocket_upgrade": {
- "$ref": "#/definitions/allow_websocket_upgrade"
- },
- "access_list_id": {
- "$ref": "#/definitions/access_list_id"
- },
- "advanced_config": {
- "$ref": "#/definitions/advanced_config"
- },
- "enabled": {
- "$ref": "#/definitions/enabled"
- },
- "meta": {
- "$ref": "#/definitions/meta"
- },
- "locations": {
- "$ref": "#/definitions/locations"
- }
- }
- },
- "targetSchema": {
- "properties": {
- "$ref": "#/properties"
- }
- }
- },
- {
- "title": "Update",
- "description": "Updates a existing Proxy Host",
- "href": "/nginx/proxy-hosts/{definitions.identity.example}",
- "access": "private",
- "method": "PUT",
- "rel": "update",
- "http_header": {
- "$ref": "../examples.json#/definitions/auth_header"
- },
- "schema": {
- "type": "object",
- "additionalProperties": false,
- "properties": {
- "domain_names": {
- "$ref": "#/definitions/domain_names"
- },
- "forward_scheme": {
- "$ref": "#/definitions/forward_scheme"
- },
- "forward_host": {
- "$ref": "#/definitions/forward_host"
- },
- "forward_port": {
- "$ref": "#/definitions/forward_port"
- },
- "certificate_id": {
- "$ref": "#/definitions/certificate_id"
- },
- "ssl_forced": {
- "$ref": "#/definitions/ssl_forced"
- },
- "hsts_enabled": {
- "$ref": "#/definitions/hsts_enabled"
- },
- "hsts_subdomains": {
- "$ref": "#/definitions/hsts_enabled"
- },
- "http2_support": {
- "$ref": "#/definitions/http2_support"
- },
- "block_exploits": {
- "$ref": "#/definitions/block_exploits"
- },
- "caching_enabled": {
- "$ref": "#/definitions/caching_enabled"
- },
- "allow_websocket_upgrade": {
- "$ref": "#/definitions/allow_websocket_upgrade"
- },
- "access_list_id": {
- "$ref": "#/definitions/access_list_id"
- },
- "advanced_config": {
- "$ref": "#/definitions/advanced_config"
- },
- "enabled": {
- "$ref": "#/definitions/enabled"
- },
- "meta": {
- "$ref": "#/definitions/meta"
- },
- "locations": {
- "$ref": "#/definitions/locations"
- }
- }
- },
- "targetSchema": {
- "properties": {
- "$ref": "#/properties"
- }
- }
- },
- {
- "title": "Delete",
- "description": "Deletes a existing Proxy Host",
- "href": "/nginx/proxy-hosts/{definitions.identity.example}",
- "access": "private",
- "method": "DELETE",
- "rel": "delete",
- "http_header": {
- "$ref": "../examples.json#/definitions/auth_header"
- },
- "targetSchema": {
- "type": "boolean"
- }
- },
- {
- "title": "Enable",
- "description": "Enables a existing Proxy Host",
- "href": "/nginx/proxy-hosts/{definitions.identity.example}/enable",
- "access": "private",
- "method": "POST",
- "rel": "update",
- "http_header": {
- "$ref": "../examples.json#/definitions/auth_header"
- },
- "targetSchema": {
- "type": "boolean"
- }
- },
- {
- "title": "Disable",
- "description": "Disables a existing Proxy Host",
- "href": "/nginx/proxy-hosts/{definitions.identity.example}/disable",
- "access": "private",
- "method": "POST",
- "rel": "update",
- "http_header": {
- "$ref": "../examples.json#/definitions/auth_header"
- },
- "targetSchema": {
- "type": "boolean"
- }
- }
- ]
-}
diff --git a/backend/schema/endpoints/redirection-hosts.json b/backend/schema/endpoints/redirection-hosts.json
deleted file mode 100644
index 14a4699853..0000000000
--- a/backend/schema/endpoints/redirection-hosts.json
+++ /dev/null
@@ -1,305 +0,0 @@
-{
- "$schema": "http://json-schema.org/draft-07/schema#",
- "$id": "endpoints/redirection-hosts",
- "title": "Redirection Hosts",
- "description": "Endpoints relating to Redirection Hosts",
- "stability": "stable",
- "type": "object",
- "definitions": {
- "id": {
- "$ref": "../definitions.json#/definitions/id"
- },
- "created_on": {
- "$ref": "../definitions.json#/definitions/created_on"
- },
- "modified_on": {
- "$ref": "../definitions.json#/definitions/modified_on"
- },
- "domain_names": {
- "$ref": "../definitions.json#/definitions/domain_names"
- },
- "forward_http_code": {
- "$ref": "../definitions.json#/definitions/http_code"
- },
- "forward_scheme": {
- "$ref": "../definitions.json#/definitions/scheme"
- },
- "forward_domain_name": {
- "$ref": "../definitions.json#/definitions/domain_name"
- },
- "preserve_path": {
- "description": "Should the path be preserved",
- "example": true,
- "type": "boolean"
- },
- "certificate_id": {
- "$ref": "../definitions.json#/definitions/certificate_id"
- },
- "ssl_forced": {
- "$ref": "../definitions.json#/definitions/ssl_forced"
- },
- "hsts_enabled": {
- "$ref": "../definitions.json#/definitions/hsts_enabled"
- },
- "hsts_subdomains": {
- "$ref": "../definitions.json#/definitions/hsts_subdomains"
- },
- "http2_support": {
- "$ref": "../definitions.json#/definitions/http2_support"
- },
- "block_exploits": {
- "$ref": "../definitions.json#/definitions/block_exploits"
- },
- "advanced_config": {
- "type": "string"
- },
- "enabled": {
- "$ref": "../definitions.json#/definitions/enabled"
- },
- "meta": {
- "type": "object"
- }
- },
- "properties": {
- "id": {
- "$ref": "#/definitions/id"
- },
- "created_on": {
- "$ref": "#/definitions/created_on"
- },
- "modified_on": {
- "$ref": "#/definitions/modified_on"
- },
- "domain_names": {
- "$ref": "#/definitions/domain_names"
- },
- "forward_http_code": {
- "$ref": "#/definitions/forward_http_code"
- },
- "forward_scheme": {
- "$ref": "#/definitions/forward_scheme"
- },
- "forward_domain_name": {
- "$ref": "#/definitions/forward_domain_name"
- },
- "preserve_path": {
- "$ref": "#/definitions/preserve_path"
- },
- "certificate_id": {
- "$ref": "#/definitions/certificate_id"
- },
- "ssl_forced": {
- "$ref": "#/definitions/ssl_forced"
- },
- "hsts_enabled": {
- "$ref": "#/definitions/hsts_enabled"
- },
- "hsts_subdomains": {
- "$ref": "#/definitions/hsts_subdomains"
- },
- "http2_support": {
- "$ref": "#/definitions/http2_support"
- },
- "block_exploits": {
- "$ref": "#/definitions/block_exploits"
- },
- "advanced_config": {
- "$ref": "#/definitions/advanced_config"
- },
- "enabled": {
- "$ref": "#/definitions/enabled"
- },
- "meta": {
- "$ref": "#/definitions/meta"
- }
- },
- "links": [
- {
- "title": "List",
- "description": "Returns a list of Redirection Hosts",
- "href": "/nginx/redirection-hosts",
- "access": "private",
- "method": "GET",
- "rel": "self",
- "http_header": {
- "$ref": "../examples.json#/definitions/auth_header"
- },
- "targetSchema": {
- "type": "array",
- "items": {
- "$ref": "#/properties"
- }
- }
- },
- {
- "title": "Create",
- "description": "Creates a new Redirection Host",
- "href": "/nginx/redirection-hosts",
- "access": "private",
- "method": "POST",
- "rel": "create",
- "http_header": {
- "$ref": "../examples.json#/definitions/auth_header"
- },
- "schema": {
- "type": "object",
- "additionalProperties": false,
- "required": [
- "domain_names",
- "forward_scheme",
- "forward_http_code",
- "forward_domain_name"
- ],
- "properties": {
- "domain_names": {
- "$ref": "#/definitions/domain_names"
- },
- "forward_http_code": {
- "$ref": "#/definitions/forward_http_code"
- },
- "forward_scheme": {
- "$ref": "#/definitions/forward_scheme"
- },
- "forward_domain_name": {
- "$ref": "#/definitions/forward_domain_name"
- },
- "preserve_path": {
- "$ref": "#/definitions/preserve_path"
- },
- "certificate_id": {
- "$ref": "#/definitions/certificate_id"
- },
- "ssl_forced": {
- "$ref": "#/definitions/ssl_forced"
- },
- "hsts_enabled": {
- "$ref": "#/definitions/hsts_enabled"
- },
- "hsts_subdomains": {
- "$ref": "#/definitions/hsts_enabled"
- },
- "http2_support": {
- "$ref": "#/definitions/http2_support"
- },
- "block_exploits": {
- "$ref": "#/definitions/block_exploits"
- },
- "advanced_config": {
- "$ref": "#/definitions/advanced_config"
- },
- "meta": {
- "$ref": "#/definitions/meta"
- }
- }
- },
- "targetSchema": {
- "properties": {
- "$ref": "#/properties"
- }
- }
- },
- {
- "title": "Update",
- "description": "Updates a existing Redirection Host",
- "href": "/nginx/redirection-hosts/{definitions.identity.example}",
- "access": "private",
- "method": "PUT",
- "rel": "update",
- "http_header": {
- "$ref": "../examples.json#/definitions/auth_header"
- },
- "schema": {
- "type": "object",
- "additionalProperties": false,
- "properties": {
- "domain_names": {
- "$ref": "#/definitions/domain_names"
- },
- "forward_http_code": {
- "$ref": "#/definitions/forward_http_code"
- },
- "forward_scheme": {
- "$ref": "#/definitions/forward_scheme"
- },
- "forward_domain_name": {
- "$ref": "#/definitions/forward_domain_name"
- },
- "preserve_path": {
- "$ref": "#/definitions/preserve_path"
- },
- "certificate_id": {
- "$ref": "#/definitions/certificate_id"
- },
- "ssl_forced": {
- "$ref": "#/definitions/ssl_forced"
- },
- "hsts_enabled": {
- "$ref": "#/definitions/hsts_enabled"
- },
- "hsts_subdomains": {
- "$ref": "#/definitions/hsts_enabled"
- },
- "http2_support": {
- "$ref": "#/definitions/http2_support"
- },
- "block_exploits": {
- "$ref": "#/definitions/block_exploits"
- },
- "advanced_config": {
- "$ref": "#/definitions/advanced_config"
- },
- "meta": {
- "$ref": "#/definitions/meta"
- }
- }
- },
- "targetSchema": {
- "properties": {
- "$ref": "#/properties"
- }
- }
- },
- {
- "title": "Delete",
- "description": "Deletes a existing Redirection Host",
- "href": "/nginx/redirection-hosts/{definitions.identity.example}",
- "access": "private",
- "method": "DELETE",
- "rel": "delete",
- "http_header": {
- "$ref": "../examples.json#/definitions/auth_header"
- },
- "targetSchema": {
- "type": "boolean"
- }
- },
- {
- "title": "Enable",
- "description": "Enables a existing Redirection Host",
- "href": "/nginx/redirection-hosts/{definitions.identity.example}/enable",
- "access": "private",
- "method": "POST",
- "rel": "update",
- "http_header": {
- "$ref": "../examples.json#/definitions/auth_header"
- },
- "targetSchema": {
- "type": "boolean"
- }
- },
- {
- "title": "Disable",
- "description": "Disables a existing Redirection Host",
- "href": "/nginx/redirection-hosts/{definitions.identity.example}/disable",
- "access": "private",
- "method": "POST",
- "rel": "update",
- "http_header": {
- "$ref": "../examples.json#/definitions/auth_header"
- },
- "targetSchema": {
- "type": "boolean"
- }
- }
- ]
-}
diff --git a/backend/schema/endpoints/settings.json b/backend/schema/endpoints/settings.json
deleted file mode 100644
index 29e2865ae8..0000000000
--- a/backend/schema/endpoints/settings.json
+++ /dev/null
@@ -1,99 +0,0 @@
-{
- "$schema": "http://json-schema.org/draft-07/schema#",
- "$id": "endpoints/settings",
- "title": "Settings",
- "description": "Endpoints relating to Settings",
- "stability": "stable",
- "type": "object",
- "definitions": {
- "id": {
- "$ref": "../definitions.json#/definitions/setting_id"
- },
- "name": {
- "description": "Name",
- "example": "Default Site",
- "type": "string",
- "minLength": 2,
- "maxLength": 100
- },
- "description": {
- "description": "Description",
- "example": "Default Site",
- "type": "string",
- "minLength": 2,
- "maxLength": 255
- },
- "value": {
- "description": "Value",
- "example": "404",
- "type": "string",
- "maxLength": 255
- },
- "meta": {
- "type": "object"
- }
- },
- "links": [
- {
- "title": "List",
- "description": "Returns a list of Settings",
- "href": "/settings",
- "access": "private",
- "method": "GET",
- "rel": "self",
- "http_header": {
- "$ref": "../examples.json#/definitions/auth_header"
- },
- "targetSchema": {
- "type": "array",
- "items": {
- "$ref": "#/properties"
- }
- }
- },
- {
- "title": "Update",
- "description": "Updates a existing Setting",
- "href": "/settings/{definitions.identity.example}",
- "access": "private",
- "method": "PUT",
- "rel": "update",
- "http_header": {
- "$ref": "../examples.json#/definitions/auth_header"
- },
- "schema": {
- "type": "object",
- "properties": {
- "value": {
- "$ref": "#/definitions/value"
- },
- "meta": {
- "$ref": "#/definitions/meta"
- }
- }
- },
- "targetSchema": {
- "properties": {
- "$ref": "#/properties"
- }
- }
- }
- ],
- "properties": {
- "id": {
- "$ref": "#/definitions/id"
- },
- "name": {
- "$ref": "#/definitions/description"
- },
- "description": {
- "$ref": "#/definitions/description"
- },
- "value": {
- "$ref": "#/definitions/value"
- },
- "meta": {
- "$ref": "#/definitions/meta"
- }
- }
-}
diff --git a/backend/schema/endpoints/streams.json b/backend/schema/endpoints/streams.json
deleted file mode 100644
index 7d97d2ab58..0000000000
--- a/backend/schema/endpoints/streams.json
+++ /dev/null
@@ -1,258 +0,0 @@
-{
- "$schema": "http://json-schema.org/draft-07/schema#",
- "$id": "endpoints/streams",
- "title": "Streams",
- "description": "Endpoints relating to Streams",
- "stability": "stable",
- "type": "object",
- "definitions": {
- "id": {
- "$ref": "../definitions.json#/definitions/id"
- },
- "created_on": {
- "$ref": "../definitions.json#/definitions/created_on"
- },
- "modified_on": {
- "$ref": "../definitions.json#/definitions/modified_on"
- },
- "incoming_port": {
- "type": "integer",
- "minimum": 1,
- "maximum": 65535
- },
- "forwarding_host": {
- "anyOf": [
- {
- "$ref": "../definitions.json#/definitions/domain_name"
- },
- {
- "type": "string",
- "format": "ipv4"
- },
- {
- "type": "string",
- "format": "ipv6"
- }
- ]
- },
- "forwarding_port": {
- "type": "integer",
- "minimum": 1,
- "maximum": 65535
- },
- "tcp_forwarding": {
- "type": "boolean"
- },
- "udp_forwarding": {
- "type": "boolean"
- },
- "domain_names": {
- "$ref": "../definitions.json#/definitions/domain_names"
- },
- "certificate_id": {
- "$ref": "../definitions.json#/definitions/certificate_id"
- },
- "enabled": {
- "$ref": "../definitions.json#/definitions/enabled"
- },
- "meta": {
- "type": "object"
- }
- },
- "properties": {
- "id": {
- "$ref": "#/definitions/id"
- },
- "created_on": {
- "$ref": "#/definitions/created_on"
- },
- "modified_on": {
- "$ref": "#/definitions/modified_on"
- },
- "incoming_port": {
- "$ref": "#/definitions/incoming_port"
- },
- "forwarding_host": {
- "$ref": "#/definitions/forwarding_host"
- },
- "forwarding_port": {
- "$ref": "#/definitions/forwarding_port"
- },
- "tcp_forwarding": {
- "$ref": "#/definitions/tcp_forwarding"
- },
- "udp_forwarding": {
- "$ref": "#/definitions/udp_forwarding"
- },
- "domain_names": {
- "$ref": "../definitions.json#/definitions/domain_names"
- },
- "certificate_id": {
- "$ref": "#/definitions/certificate_id"
- },
- "enabled": {
- "$ref": "#/definitions/enabled"
- },
- "meta": {
- "$ref": "#/definitions/meta"
- }
- },
- "links": [
- {
- "title": "List",
- "description": "Returns a list of Steams",
- "href": "/nginx/streams",
- "access": "private",
- "method": "GET",
- "rel": "self",
- "http_header": {
- "$ref": "../examples.json#/definitions/auth_header"
- },
- "targetSchema": {
- "type": "array",
- "items": {
- "$ref": "#/properties"
- }
- }
- },
- {
- "title": "Create",
- "description": "Creates a new Stream",
- "href": "/nginx/streams",
- "access": "private",
- "method": "POST",
- "rel": "create",
- "http_header": {
- "$ref": "../examples.json#/definitions/auth_header"
- },
- "schema": {
- "type": "object",
- "additionalProperties": false,
- "required": [
- "incoming_port",
- "forwarding_host",
- "forwarding_port"
- ],
- "properties": {
- "incoming_port": {
- "$ref": "#/definitions/incoming_port"
- },
- "forwarding_host": {
- "$ref": "#/definitions/forwarding_host"
- },
- "forwarding_port": {
- "$ref": "#/definitions/forwarding_port"
- },
- "tcp_forwarding": {
- "$ref": "#/definitions/tcp_forwarding"
- },
- "udp_forwarding": {
- "$ref": "#/definitions/udp_forwarding"
- },
- "domain_names": {
- "$ref": "../definitions.json#/definitions/domain_names"
- },
- "certificate_id": {
- "$ref": "#/definitions/certificate_id"
- },
- "meta": {
- "$ref": "#/definitions/meta"
- }
- }
- },
- "targetSchema": {
- "properties": {
- "$ref": "#/properties"
- }
- }
- },
- {
- "title": "Update",
- "description": "Updates a existing Stream",
- "href": "/nginx/streams/{definitions.identity.example}",
- "access": "private",
- "method": "PUT",
- "rel": "update",
- "http_header": {
- "$ref": "../examples.json#/definitions/auth_header"
- },
- "schema": {
- "type": "object",
- "additionalProperties": false,
- "properties": {
- "incoming_port": {
- "$ref": "#/definitions/incoming_port"
- },
- "forwarding_host": {
- "$ref": "#/definitions/forwarding_host"
- },
- "forwarding_port": {
- "$ref": "#/definitions/forwarding_port"
- },
- "tcp_forwarding": {
- "$ref": "#/definitions/tcp_forwarding"
- },
- "udp_forwarding": {
- "$ref": "#/definitions/udp_forwarding"
- },
- "domain_names": {
- "$ref": "../definitions.json#/definitions/domain_names"
- },
- "certificate_id": {
- "$ref": "#/definitions/certificate_id"
- },
- "meta": {
- "$ref": "#/definitions/meta"
- }
- }
- },
- "targetSchema": {
- "properties": {
- "$ref": "#/properties"
- }
- }
- },
- {
- "title": "Delete",
- "description": "Deletes a existing Stream",
- "href": "/nginx/streams/{definitions.identity.example}",
- "access": "private",
- "method": "DELETE",
- "rel": "delete",
- "http_header": {
- "$ref": "../examples.json#/definitions/auth_header"
- },
- "targetSchema": {
- "type": "boolean"
- }
- },
- {
- "title": "Enable",
- "description": "Enables a existing Stream",
- "href": "/nginx/streams/{definitions.identity.example}/enable",
- "access": "private",
- "method": "POST",
- "rel": "update",
- "http_header": {
- "$ref": "../examples.json#/definitions/auth_header"
- },
- "targetSchema": {
- "type": "boolean"
- }
- },
- {
- "title": "Disable",
- "description": "Disables a existing Stream",
- "href": "/nginx/streams/{definitions.identity.example}/disable",
- "access": "private",
- "method": "POST",
- "rel": "update",
- "http_header": {
- "$ref": "../examples.json#/definitions/auth_header"
- },
- "targetSchema": {
- "type": "boolean"
- }
- }
- ]
-}
diff --git a/backend/schema/endpoints/tokens.json b/backend/schema/endpoints/tokens.json
deleted file mode 100644
index 920af63f4a..0000000000
--- a/backend/schema/endpoints/tokens.json
+++ /dev/null
@@ -1,100 +0,0 @@
-{
- "$schema": "http://json-schema.org/draft-07/schema#",
- "$id": "endpoints/tokens",
- "title": "Token",
- "description": "Tokens are required to authenticate against the API",
- "stability": "stable",
- "type": "object",
- "definitions": {
- "identity": {
- "description": "Email Address or other 3rd party providers identifier",
- "example": "john@example.com",
- "type": "string"
- },
- "secret": {
- "description": "A password or key",
- "example": "correct horse battery staple",
- "type": "string"
- },
- "token": {
- "description": "JWT",
- "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.O_frfYM8RzmRsUNigHtu0_jZ_utSejyr1axMGa8rlsk",
- "type": "string"
- },
- "expires": {
- "description": "Token expiry time",
- "format": "date-time",
- "type": "string"
- },
- "scope": {
- "description": "Scope of the Token, defaults to 'user'",
- "example": "user",
- "type": "string"
- }
- },
- "links": [
- {
- "title": "Create",
- "description": "Creates a new token.",
- "href": "/tokens",
- "access": "public",
- "method": "POST",
- "rel": "create",
- "schema": {
- "type": "object",
- "required": [
- "identity",
- "secret"
- ],
- "properties": {
- "identity": {
- "$ref": "#/definitions/identity"
- },
- "secret": {
- "$ref": "#/definitions/secret"
- },
- "scope": {
- "$ref": "#/definitions/scope"
- }
- }
- },
- "targetSchema": {
- "type": "object",
- "properties": {
- "token": {
- "$ref": "#/definitions/token"
- },
- "expires": {
- "$ref": "#/definitions/expires"
- }
- }
- }
- },
- {
- "title": "Refresh",
- "description": "Returns a new token.",
- "href": "/tokens",
- "access": "private",
- "method": "GET",
- "rel": "self",
- "http_header": {
- "$ref": "../examples.json#/definitions/auth_header"
- },
- "schema": {},
- "targetSchema": {
- "type": "object",
- "properties": {
- "token": {
- "$ref": "#/definitions/token"
- },
- "expires": {
- "$ref": "#/definitions/expires"
- },
- "scope": {
- "$ref": "#/definitions/scope"
- }
- }
- }
- }
- ]
-}
diff --git a/backend/schema/endpoints/users.json b/backend/schema/endpoints/users.json
deleted file mode 100644
index 42f44eac7c..0000000000
--- a/backend/schema/endpoints/users.json
+++ /dev/null
@@ -1,287 +0,0 @@
-{
- "$schema": "http://json-schema.org/draft-07/schema#",
- "$id": "endpoints/users",
- "title": "Users",
- "description": "Endpoints relating to Users",
- "stability": "stable",
- "type": "object",
- "definitions": {
- "id": {
- "$ref": "../definitions.json#/definitions/id"
- },
- "created_on": {
- "$ref": "../definitions.json#/definitions/created_on"
- },
- "modified_on": {
- "$ref": "../definitions.json#/definitions/modified_on"
- },
- "name": {
- "description": "Name",
- "example": "Jamie Curnow",
- "type": "string",
- "minLength": 2,
- "maxLength": 100
- },
- "nickname": {
- "description": "Nickname",
- "example": "Jamie",
- "type": "string",
- "minLength": 2,
- "maxLength": 50
- },
- "email": {
- "$ref": "../definitions.json#/definitions/email"
- },
- "avatar": {
- "description": "Avatar",
- "example": "http://somewhere.jpg",
- "type": "string",
- "minLength": 2,
- "maxLength": 150,
- "readOnly": true
- },
- "roles": {
- "description": "Roles",
- "example": [
- "admin"
- ],
- "type": "array"
- },
- "is_disabled": {
- "description": "Is Disabled",
- "example": false,
- "type": "boolean"
- }
- },
- "links": [
- {
- "title": "List",
- "description": "Returns a list of Users",
- "href": "/users",
- "access": "private",
- "method": "GET",
- "rel": "self",
- "http_header": {
- "$ref": "../examples.json#/definitions/auth_header"
- },
- "targetSchema": {
- "type": "array",
- "items": {
- "$ref": "#/properties"
- }
- }
- },
- {
- "title": "Create",
- "description": "Creates a new User",
- "href": "/users",
- "access": "private",
- "method": "POST",
- "rel": "create",
- "http_header": {
- "$ref": "../examples.json#/definitions/auth_header"
- },
- "schema": {
- "type": "object",
- "required": [
- "name",
- "nickname",
- "email"
- ],
- "properties": {
- "name": {
- "$ref": "#/definitions/name"
- },
- "nickname": {
- "$ref": "#/definitions/nickname"
- },
- "email": {
- "$ref": "#/definitions/email"
- },
- "roles": {
- "$ref": "#/definitions/roles"
- },
- "is_disabled": {
- "$ref": "#/definitions/is_disabled"
- },
- "auth": {
- "type": "object",
- "description": "Auth Credentials",
- "example": {
- "type": "password",
- "secret": "bigredhorsebanana"
- }
- }
- }
- },
- "targetSchema": {
- "properties": {
- "$ref": "#/properties"
- }
- }
- },
- {
- "title": "Update",
- "description": "Updates a existing User",
- "href": "/users/{definitions.identity.example}",
- "access": "private",
- "method": "PUT",
- "rel": "update",
- "http_header": {
- "$ref": "../examples.json#/definitions/auth_header"
- },
- "schema": {
- "type": "object",
- "properties": {
- "name": {
- "$ref": "#/definitions/name"
- },
- "nickname": {
- "$ref": "#/definitions/nickname"
- },
- "email": {
- "$ref": "#/definitions/email"
- },
- "roles": {
- "$ref": "#/definitions/roles"
- },
- "is_disabled": {
- "$ref": "#/definitions/is_disabled"
- }
- }
- },
- "targetSchema": {
- "properties": {
- "$ref": "#/properties"
- }
- }
- },
- {
- "title": "Delete",
- "description": "Deletes a existing User",
- "href": "/users/{definitions.identity.example}",
- "access": "private",
- "method": "DELETE",
- "rel": "delete",
- "http_header": {
- "$ref": "../examples.json#/definitions/auth_header"
- },
- "targetSchema": {
- "type": "boolean"
- }
- },
- {
- "title": "Set Password",
- "description": "Sets a password for an existing User",
- "href": "/users/{definitions.identity.example}/auth",
- "access": "private",
- "method": "PUT",
- "rel": "update",
- "http_header": {
- "$ref": "../examples.json#/definitions/auth_header"
- },
- "schema": {
- "type": "object",
- "required": [
- "type",
- "secret"
- ],
- "properties": {
- "type": {
- "type": "string",
- "pattern": "^password$"
- },
- "current": {
- "type": "string",
- "minLength": 1,
- "maxLength": 64
- },
- "secret": {
- "type": "string",
- "minLength": 8,
- "maxLength": 64
- }
- }
- },
- "targetSchema": {
- "type": "boolean"
- }
- },
- {
- "title": "Set Permissions",
- "description": "Sets Permissions for a User",
- "href": "/users/{definitions.identity.example}/permissions",
- "access": "private",
- "method": "PUT",
- "rel": "update",
- "http_header": {
- "$ref": "../examples.json#/definitions/auth_header"
- },
- "schema": {
- "type": "object",
- "properties": {
- "visibility": {
- "type": "string",
- "pattern": "^(all|user)$"
- },
- "access_lists": {
- "type": "string",
- "pattern": "^(hidden|view|manage)$"
- },
- "dead_hosts": {
- "type": "string",
- "pattern": "^(hidden|view|manage)$"
- },
- "proxy_hosts": {
- "type": "string",
- "pattern": "^(hidden|view|manage)$"
- },
- "redirection_hosts": {
- "type": "string",
- "pattern": "^(hidden|view|manage)$"
- },
- "streams": {
- "type": "string",
- "pattern": "^(hidden|view|manage)$"
- },
- "certificates": {
- "type": "string",
- "pattern": "^(hidden|view|manage)$"
- }
- }
- },
- "targetSchema": {
- "type": "boolean"
- }
- }
- ],
- "properties": {
- "id": {
- "$ref": "#/definitions/id"
- },
- "created_on": {
- "$ref": "#/definitions/created_on"
- },
- "modified_on": {
- "$ref": "#/definitions/modified_on"
- },
- "name": {
- "$ref": "#/definitions/name"
- },
- "nickname": {
- "$ref": "#/definitions/nickname"
- },
- "email": {
- "$ref": "#/definitions/email"
- },
- "avatar": {
- "$ref": "#/definitions/avatar"
- },
- "roles": {
- "$ref": "#/definitions/roles"
- },
- "is_disabled": {
- "$ref": "#/definitions/is_disabled"
- }
- }
-}
diff --git a/backend/schema/examples.json b/backend/schema/examples.json
deleted file mode 100644
index 37bc6c4d3b..0000000000
--- a/backend/schema/examples.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
- "$schema": "http://json-schema.org/draft-07/schema#",
- "$id": "examples",
- "type": "object",
- "definitions": {
- "name": {
- "description": "Name",
- "example": "John Smith",
- "type": "string",
- "minLength": 1,
- "maxLength": 255
- },
- "auth_header": {
- "Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.O_frfYM8RzmRsUNigHtu0_jZ_utSejyr1axMGa8rlsk",
- "X-API-Version": "next"
- },
- "token": {
- "type": "string",
- "description": "JWT",
- "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.O_frfYM8RzmRsUNigHtu0_jZ_utSejyr1axMGa8rlsk"
- }
- }
-}
diff --git a/backend/schema/index.js b/backend/schema/index.js
new file mode 100644
index 0000000000..87b75f2570
--- /dev/null
+++ b/backend/schema/index.js
@@ -0,0 +1,41 @@
+const refParser = require('@apidevtools/json-schema-ref-parser');
+
+let compiledSchema = null;
+
+module.exports = {
+
+ /**
+ * Compiles the schema, by dereferencing it, only once
+ * and returns the memory cached value
+ */
+ getCompiledSchema: async () => {
+ if (compiledSchema === null) {
+ compiledSchema = await refParser.dereference(__dirname + '/swagger.json', {
+ mutateInputSchema: false,
+ });
+ }
+ return compiledSchema;
+ },
+
+ /**
+ * Scans the schema for the validation schema for the given path and method
+ * and returns it.
+ *
+ * @param {string} path
+ * @param {string} method
+ * @returns string|null
+ */
+ getValidationSchema: (path, method) => {
+ if (compiledSchema !== null &&
+ typeof compiledSchema.paths[path] !== 'undefined' &&
+ typeof compiledSchema.paths[path][method] !== 'undefined' &&
+ typeof compiledSchema.paths[path][method].requestBody !== 'undefined' &&
+ typeof compiledSchema.paths[path][method].requestBody.content !== 'undefined' &&
+ typeof compiledSchema.paths[path][method].requestBody.content['application/json'] !== 'undefined' &&
+ typeof compiledSchema.paths[path][method].requestBody.content['application/json'].schema !== 'undefined'
+ ) {
+ return compiledSchema.paths[path][method].requestBody.content['application/json'].schema;
+ }
+ return null;
+ }
+};
diff --git a/backend/schema/index.json b/backend/schema/index.json
deleted file mode 100644
index 6e7d1c8af9..0000000000
--- a/backend/schema/index.json
+++ /dev/null
@@ -1,42 +0,0 @@
-{
- "$schema": "http://json-schema.org/draft-07/schema#",
- "$id": "root",
- "title": "Nginx Proxy Manager REST API",
- "description": "This is the Nginx Proxy Manager REST API",
- "version": "2.0.0",
- "links": [
- {
- "href": "http://npm.example.com/api",
- "rel": "self"
- }
- ],
- "properties": {
- "tokens": {
- "$ref": "endpoints/tokens.json"
- },
- "users": {
- "$ref": "endpoints/users.json"
- },
- "proxy-hosts": {
- "$ref": "endpoints/proxy-hosts.json"
- },
- "redirection-hosts": {
- "$ref": "endpoints/redirection-hosts.json"
- },
- "dead-hosts": {
- "$ref": "endpoints/dead-hosts.json"
- },
- "streams": {
- "$ref": "endpoints/streams.json"
- },
- "certificates": {
- "$ref": "endpoints/certificates.json"
- },
- "access-lists": {
- "$ref": "endpoints/access-lists.json"
- },
- "settings": {
- "$ref": "endpoints/settings.json"
- }
- }
-}
diff --git a/backend/schema/paths/audit-log/get.json b/backend/schema/paths/audit-log/get.json
new file mode 100644
index 0000000000..bc43e29ddc
--- /dev/null
+++ b/backend/schema/paths/audit-log/get.json
@@ -0,0 +1,53 @@
+{
+ "operationId": "getAuditLog",
+ "summary": "Get Audit Log",
+ "tags": ["Audit Log"],
+ "security": [
+ {
+ "BearerAuth": ["audit-log"]
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": [
+ {
+ "id": 7,
+ "created_on": "2024-10-08T13:09:54.000Z",
+ "modified_on": "2024-10-08T13:09:54.000Z",
+ "user_id": 1,
+ "object_type": "user",
+ "object_id": 3,
+ "action": "updated",
+ "meta": {
+ "name": "John Doe",
+ "permissions": {
+ "user_id": 3,
+ "visibility": "all",
+ "access_lists": "manage",
+ "dead_hosts": "hidden",
+ "proxy_hosts": "manage",
+ "redirection_hosts": "view",
+ "streams": "hidden",
+ "certificates": "manage",
+ "id": 3,
+ "modified_on": "2024-10-08T13:09:54.000Z",
+ "created_on": "2024-10-08T13:09:51.000Z"
+ }
+ }
+ }
+ ]
+ }
+ },
+ "schema": {
+ "$ref": "../../components/audit-log-object.json"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/get.json b/backend/schema/paths/get.json
new file mode 100644
index 0000000000..8c3a4e025f
--- /dev/null
+++ b/backend/schema/paths/get.json
@@ -0,0 +1,29 @@
+{
+ "operationId": "health",
+ "summary": "Returns the API health status",
+ "tags": ["Public"],
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": {
+ "status": "OK",
+ "version": {
+ "major": 2,
+ "minor": 1,
+ "revision": 0
+ }
+ }
+ }
+ },
+ "schema": {
+ "$ref": "../components/health-object.json"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/nginx/access-lists/get.json b/backend/schema/paths/nginx/access-lists/get.json
new file mode 100644
index 0000000000..a8b9adc699
--- /dev/null
+++ b/backend/schema/paths/nginx/access-lists/get.json
@@ -0,0 +1,50 @@
+{
+ "operationId": "getAccessLists",
+ "summary": "Get all access lists",
+ "tags": ["Access Lists"],
+ "security": [
+ {
+ "BearerAuth": ["access_lists"]
+ }
+ ],
+ "parameters": [
+ {
+ "in": "query",
+ "name": "expand",
+ "description": "Expansions",
+ "schema": {
+ "type": "string",
+ "enum": ["owner", "items", "clients", "proxy_hosts"]
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": [
+ {
+ "id": 1,
+ "created_on": "2024-10-08T22:15:40.000Z",
+ "modified_on": "2024-10-08T22:15:40.000Z",
+ "owner_user_id": 1,
+ "name": "test1234",
+ "meta": {},
+ "satisfy_any": true,
+ "pass_auth": false,
+ "proxy_host_count": 0
+ }
+ ]
+ }
+ },
+ "schema": {
+ "$ref": "../../../components/access-list-object.json"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/nginx/access-lists/listID/delete.json b/backend/schema/paths/nginx/access-lists/listID/delete.json
new file mode 100644
index 0000000000..073585c8bd
--- /dev/null
+++ b/backend/schema/paths/nginx/access-lists/listID/delete.json
@@ -0,0 +1,39 @@
+{
+ "operationId": "deleteAccessList",
+ "summary": "Delete a Access List",
+ "tags": ["Access Lists"],
+ "security": [
+ {
+ "BearerAuth": ["access_lists"]
+ }
+ ],
+ "parameters": [
+ {
+ "in": "path",
+ "name": "listID",
+ "schema": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "required": true,
+ "example": 2
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": true
+ }
+ },
+ "schema": {
+ "type": "boolean"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/nginx/access-lists/listID/get.json b/backend/schema/paths/nginx/access-lists/listID/get.json
new file mode 100644
index 0000000000..e67023f89d
--- /dev/null
+++ b/backend/schema/paths/nginx/access-lists/listID/get.json
@@ -0,0 +1,49 @@
+{
+ "operationId": "getAccessList",
+ "summary": "Get a access List",
+ "tags": ["Access Lists"],
+ "security": [
+ {
+ "BearerAuth": ["access_lists"]
+ }
+ ],
+ "parameters": [
+ {
+ "in": "path",
+ "name": "listID",
+ "schema": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "required": true,
+ "example": 1
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": {
+ "id": 1,
+ "created_on": "2020-01-30T09:36:08.000Z",
+ "modified_on": "2020-01-30T09:41:04.000Z",
+ "is_disabled": false,
+ "email": "jc@jc21.com",
+ "name": "Jamie Curnow",
+ "nickname": "James",
+ "avatar": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm",
+ "roles": ["admin"]
+ }
+ }
+ },
+ "schema": {
+ "$ref": "../../../../components/access-list-object.json"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/nginx/access-lists/listID/put.json b/backend/schema/paths/nginx/access-lists/listID/put.json
new file mode 100644
index 0000000000..3a69f85674
--- /dev/null
+++ b/backend/schema/paths/nginx/access-lists/listID/put.json
@@ -0,0 +1,164 @@
+{
+ "operationId": "updateAccessList",
+ "summary": "Update a Access List",
+ "tags": ["Access Lists"],
+ "security": [
+ {
+ "BearerAuth": ["access_lists"]
+ }
+ ],
+ "parameters": [
+ {
+ "in": "path",
+ "name": "listID",
+ "schema": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "required": true,
+ "example": 2
+ }
+ ],
+ "requestBody": {
+ "description": "Access List Payload",
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "additionalProperties": false,
+ "minProperties": 1,
+ "properties": {
+ "name": {
+ "$ref": "../../../../components/access-list-object.json#/properties/name"
+ },
+ "satisfy_any": {
+ "$ref": "../../../../components/access-list-object.json#/properties/satisfy_any"
+ },
+ "pass_auth": {
+ "$ref": "../../../../components/access-list-object.json#/properties/pass_auth"
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "username": {
+ "type": "string",
+ "minLength": 1
+ },
+ "password": {
+ "type": "string",
+ "minLength": 1
+ }
+ }
+ }
+ },
+ "clients": {
+ "type": "array",
+ "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"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": {
+ "id": 1,
+ "created_on": "2024-10-08T22:15:40.000Z",
+ "modified_on": "2024-10-08T22:34:34.000Z",
+ "owner_user_id": 1,
+ "name": "test123!!",
+ "meta": {},
+ "satisfy_any": true,
+ "pass_auth": false,
+ "proxy_host_count": 0,
+ "owner": {
+ "id": 1,
+ "created_on": "2024-10-07T22:43:55.000Z",
+ "modified_on": "2024-10-08T12:52:54.000Z",
+ "is_deleted": false,
+ "is_disabled": false,
+ "email": "admin@example.com",
+ "name": "Administrator",
+ "nickname": "some guy",
+ "avatar": "//www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?default=mm",
+ "roles": ["admin"]
+ },
+ "items": [
+ {
+ "id": 1,
+ "created_on": "2024-10-08T22:15:40.000Z",
+ "modified_on": "2024-10-08T22:15:40.000Z",
+ "access_list_id": 1,
+ "username": "admin",
+ "password": "",
+ "meta": {},
+ "hint": "a****"
+ },
+ {
+ "id": 2,
+ "created_on": "2024-10-08T22:15:40.000Z",
+ "modified_on": "2024-10-08T22:15:40.000Z",
+ "access_list_id": 1,
+ "username": "asdad",
+ "password": "",
+ "meta": {},
+ "hint": "a*****"
+ }
+ ],
+ "clients": [
+ {
+ "id": 1,
+ "created_on": "2024-10-08T22:15:40.000Z",
+ "modified_on": "2024-10-08T22:15:40.000Z",
+ "access_list_id": 1,
+ "address": "127.0.0.1",
+ "directive": "allow",
+ "meta": {}
+ }
+ ],
+ "proxy_hosts": []
+ }
+ }
+ },
+ "schema": {
+ "$ref": "../../../../components/access-list-object.json"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/nginx/access-lists/post.json b/backend/schema/paths/nginx/access-lists/post.json
new file mode 100644
index 0000000000..4c5a4edd29
--- /dev/null
+++ b/backend/schema/paths/nginx/access-lists/post.json
@@ -0,0 +1,155 @@
+{
+ "operationId": "createAccessList",
+ "summary": "Create a Access List",
+ "tags": ["Access Lists"],
+ "security": [
+ {
+ "BearerAuth": ["access_lists"]
+ }
+ ],
+ "requestBody": {
+ "description": "Access List Payload",
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": ["name"],
+ "properties": {
+ "name": {
+ "$ref": "../../../components/access-list-object.json#/properties/name"
+ },
+ "satisfy_any": {
+ "$ref": "../../../components/access-list-object.json#/properties/satisfy_any"
+ },
+ "pass_auth": {
+ "$ref": "../../../components/access-list-object.json#/properties/pass_auth"
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "username": {
+ "type": "string",
+ "minLength": 1
+ },
+ "password": {
+ "type": "string",
+ "minLength": 1
+ }
+ }
+ }
+ },
+ "clients": {
+ "type": "array",
+ "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"
+ }
+ }
+ }
+ }
+ }
+ },
+ "responses": {
+ "201": {
+ "description": "201 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": {
+ "id": 1,
+ "created_on": "2024-10-08T22:15:40.000Z",
+ "modified_on": "2024-10-08T22:15:40.000Z",
+ "owner_user_id": 1,
+ "name": "test1234",
+ "meta": {},
+ "satisfy_any": true,
+ "pass_auth": false,
+ "proxy_host_count": 0,
+ "owner": {
+ "id": 1,
+ "created_on": "2024-10-07T22:43:55.000Z",
+ "modified_on": "2024-10-08T12:52:54.000Z",
+ "is_deleted": false,
+ "is_disabled": false,
+ "email": "admin@example.com",
+ "name": "Administrator",
+ "nickname": "some guy",
+ "avatar": "//www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?default=mm",
+ "roles": ["admin"]
+ },
+ "items": [
+ {
+ "id": 1,
+ "created_on": "2024-10-08T22:15:40.000Z",
+ "modified_on": "2024-10-08T22:15:40.000Z",
+ "access_list_id": 1,
+ "username": "admin",
+ "password": "",
+ "meta": {},
+ "hint": "a****"
+ },
+ {
+ "id": 2,
+ "created_on": "2024-10-08T22:15:40.000Z",
+ "modified_on": "2024-10-08T22:15:40.000Z",
+ "access_list_id": 1,
+ "username": "asdad",
+ "password": "",
+ "meta": {},
+ "hint": "a*****"
+ }
+ ],
+ "proxy_hosts": [],
+ "clients": [
+ {
+ "id": 1,
+ "created_on": "2024-10-08T22:15:40.000Z",
+ "modified_on": "2024-10-08T22:15:40.000Z",
+ "access_list_id": 1,
+ "address": "127.0.0.1",
+ "directive": "allow",
+ "meta": {}
+ }
+ ]
+ }
+ }
+ },
+ "schema": {
+ "$ref": "../../../components/access-list-object.json"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/nginx/certificates/certID/delete.json b/backend/schema/paths/nginx/certificates/certID/delete.json
new file mode 100644
index 0000000000..0d40bcb8c4
--- /dev/null
+++ b/backend/schema/paths/nginx/certificates/certID/delete.json
@@ -0,0 +1,39 @@
+{
+ "operationId": "deleteCertificate",
+ "summary": "Delete a Certificate",
+ "tags": ["Certificates"],
+ "security": [
+ {
+ "BearerAuth": ["certificates"]
+ }
+ ],
+ "parameters": [
+ {
+ "in": "path",
+ "name": "certID",
+ "schema": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "required": true,
+ "example": 2
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": true
+ }
+ },
+ "schema": {
+ "type": "boolean"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/nginx/certificates/certID/download/get.json b/backend/schema/paths/nginx/certificates/certID/download/get.json
new file mode 100644
index 0000000000..4b858cae75
--- /dev/null
+++ b/backend/schema/paths/nginx/certificates/certID/download/get.json
@@ -0,0 +1,35 @@
+{
+ "operationId": "downloadCertificate",
+ "summary": "Downloads a Certificate",
+ "tags": ["Certificates"],
+ "security": [
+ {
+ "BearerAuth": ["certificates"]
+ }
+ ],
+ "parameters": [
+ {
+ "in": "path",
+ "name": "certID",
+ "schema": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "required": true,
+ "example": 1
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/zip": {
+ "schema": {
+ "type": "string",
+ "format": "binary"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/nginx/certificates/certID/get.json b/backend/schema/paths/nginx/certificates/certID/get.json
new file mode 100644
index 0000000000..22317b337f
--- /dev/null
+++ b/backend/schema/paths/nginx/certificates/certID/get.json
@@ -0,0 +1,53 @@
+{
+ "operationId": "getCertificate",
+ "summary": "Get a Certificate",
+ "tags": ["Certificates"],
+ "security": [
+ {
+ "BearerAuth": ["certificates"]
+ }
+ ],
+ "parameters": [
+ {
+ "in": "path",
+ "name": "certID",
+ "schema": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "required": true,
+ "example": 1
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": {
+ "id": 4,
+ "created_on": "2024-10-09T05:31:58.000Z",
+ "modified_on": "2024-10-09T05:32:11.000Z",
+ "owner_user_id": 1,
+ "provider": "letsencrypt",
+ "nice_name": "test.example.com",
+ "domain_names": ["test.example.com"],
+ "expires_on": "2025-01-07T04:34:18.000Z",
+ "meta": {
+ "letsencrypt_email": "jc@jc21.com",
+ "letsencrypt_agree": true,
+ "dns_challenge": false
+ }
+ }
+ }
+ },
+ "schema": {
+ "$ref": "../../../../components/certificate-object.json"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/nginx/certificates/certID/renew/post.json b/backend/schema/paths/nginx/certificates/certID/renew/post.json
new file mode 100644
index 0000000000..ef4d20e5b1
--- /dev/null
+++ b/backend/schema/paths/nginx/certificates/certID/renew/post.json
@@ -0,0 +1,54 @@
+{
+ "operationId": "renewCertificate",
+ "summary": "Renews a Certificate",
+ "tags": ["Certificates"],
+ "security": [
+ {
+ "BearerAuth": ["certificates"]
+ }
+ ],
+ "parameters": [
+ {
+ "in": "path",
+ "name": "certID",
+ "schema": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "required": true,
+ "example": 1
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": {
+ "expires_on": "2025-01-07T06:41:58.000Z",
+ "modified_on": "2024-10-09T07:39:51.000Z",
+ "id": 4,
+ "created_on": "2024-10-09T05:31:58.000Z",
+ "owner_user_id": 1,
+ "is_deleted": false,
+ "provider": "letsencrypt",
+ "nice_name": "My Test Cert",
+ "domain_names": ["test.jc21.supernerd.pro"],
+ "meta": {
+ "letsencrypt_email": "jc@jc21.com",
+ "letsencrypt_agree": true,
+ "dns_challenge": false
+ }
+ }
+ }
+ },
+ "schema": {
+ "$ref": "../../../../../components/certificate-object.json"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/nginx/certificates/certID/upload/post.json b/backend/schema/paths/nginx/certificates/certID/upload/post.json
new file mode 100644
index 0000000000..f38b8102a4
--- /dev/null
+++ b/backend/schema/paths/nginx/certificates/certID/upload/post.json
@@ -0,0 +1,82 @@
+{
+ "operationId": "uploadCertificate",
+ "summary": "Uploads a custom Certificate",
+ "tags": ["Certificates"],
+ "security": [
+ {
+ "BearerAuth": ["certificates"]
+ }
+ ],
+ "parameters": [
+ {
+ "in": "path",
+ "name": "certID",
+ "schema": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "required": true,
+ "example": 1
+ }
+ ],
+ "requestBody": {
+ "description": "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": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": {
+ "certificate": "-----BEGIN CERTIFICATE-----\nMIIEYDCCAsigAwIBAgIRAPoSC0hvitb26ODMlsH6YbowDQYJKoZIhvcNAQELBQAw\ngZExHjAcBgNVBAoTFW1rY2VydCBkZXZlbG9wbWVudCBDQTEzMDEGA1UECwwqamN1\ncm5vd0BKYW1pZXMtTGFwdG9wLmxvY2FsIChKYW1pZSBDdXJub3cpMTowOAYDVQQD\nDDFta2NlcnQgamN1cm5vd0BKYW1pZXMtTGFwdG9wLmxvY2FsIChKYW1pZSBDdXJu\nb3cpMB4XDTI0MTAwOTA3MjIxN1oXDTI3MDEwOTA3MjIxN1owXjEnMCUGA1UEChMe\nbWtjZXJ0IGRldmVsb3BtZW50IGNlcnRpZmljYXRlMTMwMQYDVQQLDCpqY3Vybm93\nQEphbWllcy1MYXB0b3AubG9jYWwgKEphbWllIEN1cm5vdykwggEiMA0GCSqGSIb3\nDQEBAQUAA4IBDwAwggEKAoIBAQC1n9j9C5Bes1ndqACDckERauxXVNKCnUlUM1bu\nGBx1xc+j2e2Ar23wUJJuWBY18VfT8yqfqVDktO2wrbmvZvLuPmXePOKbIKS+XXh+\n2NG9L5bDG9rwGFCRXnbQj+GWCdMfzx14+CR1IHgeYz6Cv/Si2/LJPCh/CoBfM4hU\nQJON3lxAWrWBpdbZnKYMrxuPBRfW9OuzTbCVXToQoxRAHiOR9081Xn1WeoKr7kVB\nIa5UphlvWXa12w1YmUwJu7YndnJGIavLWeNCVc7ZEo+nS8Wr/4QWicatIWZXpVaE\nOPhRoeplQDxNWg5b/Q26rYoVd7PrCmRs7sVcH79XzGONeH1PAgMBAAGjZTBjMA4G\nA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATAfBgNVHSMEGDAWgBSB\n/vfmBUd4W7CvyEMl7YpMVQs8vTAbBgNVHREEFDASghB0ZXN0LmV4YW1wbGUuY29t\nMA0GCSqGSIb3DQEBCwUAA4IBgQASwON/jPAHzcARSenY0ZGY1m5OVTYoQ/JWH0oy\nl8SyFCQFEXt7UHDD/eTtLT0vMyc190nP57P8lTnZGf7hSinZz1B1d6V4cmzxpk0s\nVXZT+irL6bJVJoMBHRpllKAhGULIo33baTrWFKA0oBuWx4AevSWKcLW5j87kEawn\nATCuMQ1I3ifR1mSlB7X8fb+vF+571q0NGuB3a42j6rdtXJ6SmH4+9B4qO0sfHDNt\nIImpLCH/tycDpcYrGSCn1QrekFG1bSEh+Bb9i8rqMDSDsYrTFPZTuOQ3EtjGni9u\nm+rEP3OyJg+md8c+0LVP7/UU4QWWnw3/Wolo5kSCxE8vNTFqi4GhVbdLnUtcIdTV\nXxuR6cKyW87Snj1a0nG76ZLclt/akxDhtzqeV60BO0p8pmiev8frp+E94wFNYCmp\n1cr3CnMEGRaficLSDFC6EBENzlZW2BQT6OMIV+g0NBgSyQe39s2zcdEl5+SzDVuw\nhp8bJUp/QN7pnOVCDbjTQ+HVMXw=\n-----END CERTIFICATE-----\n",
+ "certificate_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC1n9j9C5Bes1nd\nqACDckERauxXVNKCnUlUM1buGBx1xc+j2e2Ar23wUJJuWBY18VfT8yqfqVDktO2w\nrbmvZvLuPmXePOKbIKS+XXh+2NG9L5bDG9rwGFCRXnbQj+GWCdMfzx14+CR1IHge\nYz6Cv/Si2/LJPCh/CoBfM4hUQJON3lxAWrWBpdbZnKYMrxuPBRfW9OuzTbCVXToQ\noxRAHiOR9081Xn1WeoKr7kVBIa5UphlvWXa12w1YmUwJu7YndnJGIavLWeNCVc7Z\nEo+nS8Wr/4QWicatIWZXpVaEOPhRoeplQDxNWg5b/Q26rYoVd7PrCmRs7sVcH79X\nzGONeH1PAgMBAAECggEAANb3Wtwl07pCjRrMvc7WbC0xYIn82yu8/g2qtjkYUJcU\nia5lQbYN7RGCS85Oc/tkq48xQEG5JQWNH8b918jDEMTrFab0aUEyYcru1q9L8PL6\nYHaNgZSrMrDcHcS8h0QOXNRJT5jeGkiHJaTR0irvB526tqF3knbK9yW22KTfycUe\na0Z9voKn5xRk1DCbHi/nk2EpT7xnjeQeLFaTIRXbS68omkr4YGhwWm5OizoyEGZu\nW0Zum5BkQyMr6kor3wdxOTG97ske2rcyvvHi+ErnwL0xBv0qY0Dhe8DpuXpDezqw\no72yY8h31Fu84i7sAj24YuE5Df8DozItFXQpkgbQ6QKBgQDPrufhvIFm2S/MzBdW\nH8JxY7CJlJPyxOvc1NIl9RczQGAQR90kx52cgIcuIGEG6/wJ/xnGfMmW40F0DnQ+\nN+oLgB9SFxeLkRb7s9Z/8N3uIN8JJFYcerEOiRQeN2BXEEWJ7bUThNtsVrAcKoUh\nELsDmnHW/3V+GKwhd0vpk842+wKBgQDf4PGLG9PTE5tlAoyHFodJRd2RhTJQkwsU\nMDNjLJ+KecLv+Nl+QiJhoflG1ccqtSFlBSCG067CDQ5LV0xm3mLJ7pfJoMgjcq31\nqjEmX4Ls91GuVOPtbwst3yFKjsHaSoKB5fBvWRcKFpBUezM7Qcw2JP3+dQT+bQIq\ncMTkRWDSvQKBgQDOdCQFDjxg/lR7NQOZ1PaZe61aBz5P3pxNqa7ClvMaOsuEQ7w9\nvMYcdtRq8TsjA2JImbSI0TIg8gb2FQxPcYwTJKl+FICOeIwtaSg5hTtJZpnxX5LO\nutTaC0DZjNkTk5RdOdWA8tihyUdGqKoxJY2TVmwGe2rUEDjFB++J4inkEwKBgB6V\ng0nmtkxanFrzOzFlMXwgEEHF+Xaqb9QFNa/xs6XeNnREAapO7JV75Cr6H2hFMFe1\nmJjyqCgYUoCWX3iaHtLJRnEkBtNY4kzyQB6m46LtsnnnXO/dwKA2oDyoPfFNRoDq\nYatEd3JIXNU9s2T/+x7WdOBjKhh72dTkbPFmTPDdAoGAU6rlPBevqOFdObYxdPq8\nEQWu44xqky3Mf5sBpOwtu6rqCYuziLiN7K4sjN5GD5mb1cEU+oS92ZiNcUQ7MFXk\n8yTYZ7U0VcXyAcpYreWwE8thmb0BohJBr+Mp3wLTx32x0HKdO6vpUa0d35LUTUmM\nRrKmPK/msHKK/sVHiL+NFqo=\n-----END PRIVATE KEY-----\n"
+ }
+ }
+ },
+ "schema": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": ["certificate", "certificate_key"],
+ "properties": {
+ "certificate": {
+ "type": "string",
+ "minLength": 1
+ },
+ "certificate_key": {
+ "type": "string",
+ "minLength": 1
+ },
+ "intermediate_certificate": {
+ "type": "string",
+ "minLength": 1
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/nginx/certificates/get.json b/backend/schema/paths/nginx/certificates/get.json
new file mode 100644
index 0000000000..2f4b556afe
--- /dev/null
+++ b/backend/schema/paths/nginx/certificates/get.json
@@ -0,0 +1,54 @@
+{
+ "operationId": "getCertificates",
+ "summary": "Get all certificates",
+ "tags": ["Certificates"],
+ "security": [
+ {
+ "BearerAuth": ["certificates"]
+ }
+ ],
+ "parameters": [
+ {
+ "in": "query",
+ "name": "expand",
+ "description": "Expansions",
+ "schema": {
+ "type": "string",
+ "enum": ["owner"]
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": [
+ {
+ "id": 4,
+ "created_on": "2024-10-09T05:31:58.000Z",
+ "modified_on": "2024-10-09T05:32:11.000Z",
+ "owner_user_id": 1,
+ "provider": "letsencrypt",
+ "nice_name": "test.example.com",
+ "domain_names": ["test.example.com"],
+ "expires_on": "2025-01-07T04:34:18.000Z",
+ "meta": {
+ "letsencrypt_email": "jc@jc21.com",
+ "letsencrypt_agree": true,
+ "dns_challenge": false
+ }
+ }
+ ]
+ }
+ },
+ "schema": {
+ "$ref": "../../../components/certificate-list.json"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/nginx/certificates/post.json b/backend/schema/paths/nginx/certificates/post.json
new file mode 100644
index 0000000000..5a3306c22f
--- /dev/null
+++ b/backend/schema/paths/nginx/certificates/post.json
@@ -0,0 +1,97 @@
+{
+ "operationId": "createCertificate",
+ "summary": "Create a Certificate",
+ "tags": ["Certificates"],
+ "security": [
+ {
+ "BearerAuth": ["certificates"]
+ }
+ ],
+ "requestBody": {
+ "description": "Certificate Payload",
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": ["provider"],
+ "properties": {
+ "provider": {
+ "$ref": "../../../components/certificate-object.json#/properties/provider"
+ },
+ "nice_name": {
+ "$ref": "../../../components/certificate-object.json#/properties/nice_name"
+ },
+ "domain_names": {
+ "$ref": "../../../components/certificate-object.json#/properties/domain_names"
+ },
+ "meta": {
+ "$ref": "../../../components/certificate-object.json#/properties/meta"
+ }
+ }
+ }
+ }
+ }
+ },
+ "responses": {
+ "201": {
+ "description": "201 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": {
+ "expires_on": "2025-01-07 04:30:17",
+ "modified_on": "2024-10-09 05:28:51",
+ "id": 5,
+ "created_on": "2024-10-09 05:28:35",
+ "owner_user_id": 1,
+ "is_deleted": false,
+ "provider": "letsencrypt",
+ "nice_name": "test.example.com",
+ "domain_names": ["test.example.com"],
+ "meta": {
+ "letsencrypt_email": "jc@jc21.com",
+ "letsencrypt_agree": true,
+ "dns_challenge": false,
+ "letsencrypt_certificate": {
+ "cn": "test.example.com",
+ "issuer": "C = US, O = Let's Encrypt, CN = E5",
+ "dates": {
+ "from": 1728448218,
+ "to": 1736224217
+ }
+ }
+ }
+ }
+ }
+ },
+ "schema": {
+ "$ref": "../../../components/certificate-object.json"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "400 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": {
+ "error": {
+ "code": 400,
+ "message": "Domains are invalid"
+ }
+ }
+ }
+ },
+ "schema": {
+ "$ref": "../../../components/error.json"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/nginx/certificates/test-http/get.json b/backend/schema/paths/nginx/certificates/test-http/get.json
new file mode 100644
index 0000000000..2b9a8dd3b0
--- /dev/null
+++ b/backend/schema/paths/nginx/certificates/test-http/get.json
@@ -0,0 +1,40 @@
+{
+ "operationId": "testHttpReach",
+ "summary": "Test HTTP Reachability",
+ "tags": ["Certificates"],
+ "security": [
+ {
+ "BearerAuth": ["certificates"]
+ }
+ ],
+ "parameters": [
+ {
+ "in": "query",
+ "name": "domains",
+ "description": "Expansions",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "example": "[\"test.example.ord\",\"test.example.com\",\"nonexistent.example.com\"]"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": {
+ "test.example.org": "ok",
+ "test.example.com": "other:Invalid domain or IP",
+ "nonexistent.example.com": "404"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/nginx/certificates/validate/post.json b/backend/schema/paths/nginx/certificates/validate/post.json
new file mode 100644
index 0000000000..21eb325ef2
--- /dev/null
+++ b/backend/schema/paths/nginx/certificates/validate/post.json
@@ -0,0 +1,114 @@
+{
+ "operationId": "validateCertificates",
+ "summary": "Validates given Custom Certificates",
+ "tags": ["Certificates"],
+ "security": [
+ {
+ "BearerAuth": ["certificates"]
+ }
+ ],
+ "requestBody": {
+ "description": "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": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": {
+ "certificate": {
+ "cn": "mkcert",
+ "issuer": "O = mkcert development CA, OU = jc@jc-Laptop.local (John Doe), CN = mkcert jc@jc-Laptop.local (John Doe)",
+ "dates": {
+ "from": 1728458537,
+ "to": 1799479337
+ }
+ },
+ "certificate_key": true
+ }
+ }
+ },
+ "schema": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": ["certificate", "certificate_key"],
+ "properties": {
+ "certificate": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": ["cn", "issuer", "dates"],
+ "properties": {
+ "cn": {
+ "type": "string"
+ },
+ "issuer": {
+ "type": "string"
+ },
+ "dates": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": ["from", "to"],
+ "properties": {
+ "from": {
+ "type": "integer"
+ },
+ "to": {
+ "type": "integer"
+ }
+ }
+ }
+ }
+ },
+ "certificate_key": {
+ "type": "boolean"
+ }
+ }
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "400 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": {
+ "error": {
+ "code": 400,
+ "message": "Certificate is not valid"
+ }
+ }
+ }
+ },
+ "schema": {
+ "$ref": "../../../../components/error.json"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/nginx/dead-hosts/get.json b/backend/schema/paths/nginx/dead-hosts/get.json
new file mode 100644
index 0000000000..8a11a3f66d
--- /dev/null
+++ b/backend/schema/paths/nginx/dead-hosts/get.json
@@ -0,0 +1,57 @@
+{
+ "operationId": "getDeadHosts",
+ "summary": "Get all 404 hosts",
+ "tags": ["404 Hosts"],
+ "security": [
+ {
+ "BearerAuth": ["dead_hosts"]
+ }
+ ],
+ "parameters": [
+ {
+ "in": "query",
+ "name": "expand",
+ "description": "Expansions",
+ "schema": {
+ "type": "string",
+ "enum": ["owner", "certificate"]
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": [
+ {
+ "id": 1,
+ "created_on": "2024-10-09T01:38:52.000Z",
+ "modified_on": "2024-10-09T01:38:52.000Z",
+ "owner_user_id": 1,
+ "domain_names": ["test.example.com"],
+ "certificate_id": 0,
+ "ssl_forced": false,
+ "advanced_config": "",
+ "meta": {
+ "nginx_online": true,
+ "nginx_err": null
+ },
+ "http2_support": false,
+ "enabled": true,
+ "hsts_enabled": false,
+ "hsts_subdomains": false
+ }
+ ]
+ }
+ },
+ "schema": {
+ "$ref": "../../../components/dead-host-list.json"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/nginx/dead-hosts/hostID/delete.json b/backend/schema/paths/nginx/dead-hosts/hostID/delete.json
new file mode 100644
index 0000000000..f3aa81a5b0
--- /dev/null
+++ b/backend/schema/paths/nginx/dead-hosts/hostID/delete.json
@@ -0,0 +1,39 @@
+{
+ "operationId": "deleteDeadHost",
+ "summary": "Delete a 404 Host",
+ "tags": ["404 Hosts"],
+ "security": [
+ {
+ "BearerAuth": ["dead_hosts"]
+ }
+ ],
+ "parameters": [
+ {
+ "in": "path",
+ "name": "hostID",
+ "schema": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "required": true,
+ "example": 2
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": true
+ }
+ },
+ "schema": {
+ "type": "boolean"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/nginx/dead-hosts/hostID/disable/post.json b/backend/schema/paths/nginx/dead-hosts/hostID/disable/post.json
new file mode 100644
index 0000000000..2cdcecf4b4
--- /dev/null
+++ b/backend/schema/paths/nginx/dead-hosts/hostID/disable/post.json
@@ -0,0 +1,59 @@
+{
+ "operationId": "disableDeadHost",
+ "summary": "Disable a 404 Host",
+ "tags": ["404 Hosts"],
+ "security": [
+ {
+ "BearerAuth": ["dead_hosts"]
+ }
+ ],
+ "parameters": [
+ {
+ "in": "path",
+ "name": "hostID",
+ "schema": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "required": true,
+ "example": 2
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": true
+ }
+ },
+ "schema": {
+ "type": "boolean"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "400 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": {
+ "error": {
+ "code": 400,
+ "message": "Host is already disabled"
+ }
+ }
+ }
+ },
+ "schema": {
+ "$ref": "../../../../../components/error.json"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/nginx/dead-hosts/hostID/enable/post.json b/backend/schema/paths/nginx/dead-hosts/hostID/enable/post.json
new file mode 100644
index 0000000000..ca3ce9faec
--- /dev/null
+++ b/backend/schema/paths/nginx/dead-hosts/hostID/enable/post.json
@@ -0,0 +1,59 @@
+{
+ "operationId": "enableDeadHost",
+ "summary": "Enable a 404 Host",
+ "tags": ["404 Hosts"],
+ "security": [
+ {
+ "BearerAuth": ["dead_hosts"]
+ }
+ ],
+ "parameters": [
+ {
+ "in": "path",
+ "name": "hostID",
+ "schema": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "required": true,
+ "example": 2
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": true
+ }
+ },
+ "schema": {
+ "type": "boolean"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "400 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": {
+ "error": {
+ "code": 400,
+ "message": "Host is already enabled"
+ }
+ }
+ }
+ },
+ "schema": {
+ "$ref": "../../../../../components/error.json"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/nginx/dead-hosts/hostID/get.json b/backend/schema/paths/nginx/dead-hosts/hostID/get.json
new file mode 100644
index 0000000000..47e2f8b125
--- /dev/null
+++ b/backend/schema/paths/nginx/dead-hosts/hostID/get.json
@@ -0,0 +1,56 @@
+{
+ "operationId": "getDeadHost",
+ "summary": "Get a 404 Host",
+ "tags": ["404 Hosts"],
+ "security": [
+ {
+ "BearerAuth": ["dead_hosts"]
+ }
+ ],
+ "parameters": [
+ {
+ "in": "path",
+ "name": "hostID",
+ "schema": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "required": true,
+ "example": 1
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": {
+ "id": 1,
+ "created_on": "2024-10-09T01:38:52.000Z",
+ "modified_on": "2024-10-09T01:38:52.000Z",
+ "owner_user_id": 1,
+ "domain_names": ["test.example.com"],
+ "certificate_id": 0,
+ "ssl_forced": false,
+ "advanced_config": "",
+ "meta": {
+ "nginx_online": true,
+ "nginx_err": null
+ },
+ "http2_support": false,
+ "enabled": true,
+ "hsts_enabled": false,
+ "hsts_subdomains": false
+ }
+ }
+ },
+ "schema": {
+ "$ref": "../../../../components/dead-host-object.json"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/nginx/dead-hosts/hostID/put.json b/backend/schema/paths/nginx/dead-hosts/hostID/put.json
new file mode 100644
index 0000000000..f9505ed48e
--- /dev/null
+++ b/backend/schema/paths/nginx/dead-hosts/hostID/put.json
@@ -0,0 +1,108 @@
+{
+ "operationId": "updateDeadHost",
+ "summary": "Update a 404 Host",
+ "tags": ["404 Hosts"],
+ "security": [
+ {
+ "BearerAuth": ["dead_hosts"]
+ }
+ ],
+ "parameters": [
+ {
+ "in": "path",
+ "name": "hostID",
+ "schema": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "required": true,
+ "example": 2
+ }
+ ],
+ "requestBody": {
+ "description": "404 Host Payload",
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "additionalProperties": false,
+ "minProperties": 1,
+ "properties": {
+ "domain_names": {
+ "$ref": "../../../../components/dead-host-object.json#/properties/domain_names"
+ },
+ "certificate_id": {
+ "$ref": "../../../../components/dead-host-object.json#/properties/certificate_id"
+ },
+ "ssl_forced": {
+ "$ref": "../../../../components/dead-host-object.json#/properties/ssl_forced"
+ },
+ "hsts_enabled": {
+ "$ref": "../../../../components/dead-host-object.json#/properties/hsts_enabled"
+ },
+ "hsts_subdomains": {
+ "$ref": "../../../../components/dead-host-object.json#/properties/hsts_subdomains"
+ },
+ "http2_support": {
+ "$ref": "../../../../components/dead-host-object.json#/properties/http2_support"
+ },
+ "advanced_config": {
+ "$ref": "../../../../components/dead-host-object.json#/properties/advanced_config"
+ },
+ "meta": {
+ "$ref": "../../../../components/dead-host-object.json#/properties/meta"
+ }
+ }
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": {
+ "id": 1,
+ "created_on": "2024-10-09T01:38:52.000Z",
+ "modified_on": "2024-10-09T01:46:06.000Z",
+ "owner_user_id": 1,
+ "domain_names": ["test.example.com"],
+ "certificate_id": 0,
+ "ssl_forced": false,
+ "advanced_config": "",
+ "meta": {
+ "nginx_online": true,
+ "nginx_err": null
+ },
+ "http2_support": false,
+ "enabled": true,
+ "hsts_enabled": false,
+ "hsts_subdomains": false,
+ "owner": {
+ "id": 1,
+ "created_on": "2024-10-09T00:59:56.000Z",
+ "modified_on": "2024-10-09T00:59:56.000Z",
+ "is_deleted": false,
+ "is_disabled": false,
+ "email": "admin@example.com",
+ "name": "Administrator",
+ "nickname": "Admin",
+ "avatar": "",
+ "roles": ["admin"]
+ },
+ "certificate": null
+ }
+ }
+ },
+ "schema": {
+ "$ref": "../../../../components/dead-host-object.json"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/nginx/dead-hosts/post.json b/backend/schema/paths/nginx/dead-hosts/post.json
new file mode 100644
index 0000000000..c8bbb6932c
--- /dev/null
+++ b/backend/schema/paths/nginx/dead-hosts/post.json
@@ -0,0 +1,93 @@
+{
+ "operationId": "create404Host",
+ "summary": "Create a 404 Host",
+ "tags": ["404 Hosts"],
+ "security": [
+ {
+ "BearerAuth": ["dead_hosts"]
+ }
+ ],
+ "requestBody": {
+ "description": "404 Host Payload",
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": ["domain_names"],
+ "properties": {
+ "domain_names": {
+ "$ref": "../../../components/dead-host-object.json#/properties/domain_names"
+ },
+ "certificate_id": {
+ "$ref": "../../../components/dead-host-object.json#/properties/certificate_id"
+ },
+ "ssl_forced": {
+ "$ref": "../../../components/dead-host-object.json#/properties/ssl_forced"
+ },
+ "hsts_enabled": {
+ "$ref": "../../../components/dead-host-object.json#/properties/hsts_enabled"
+ },
+ "hsts_subdomains": {
+ "$ref": "../../../components/dead-host-object.json#/properties/hsts_subdomains"
+ },
+ "http2_support": {
+ "$ref": "../../../components/dead-host-object.json#/properties/http2_support"
+ },
+ "advanced_config": {
+ "$ref": "../../../components/dead-host-object.json#/properties/advanced_config"
+ },
+ "meta": {
+ "$ref": "../../../components/dead-host-object.json#/properties/meta"
+ }
+ }
+ }
+ }
+ }
+ },
+ "responses": {
+ "201": {
+ "description": "201 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": {
+ "id": 1,
+ "created_on": "2024-10-09T01:38:52.000Z",
+ "modified_on": "2024-10-09T01:38:52.000Z",
+ "owner_user_id": 1,
+ "domain_names": ["test.example.com"],
+ "certificate_id": 0,
+ "ssl_forced": false,
+ "advanced_config": "",
+ "meta": {},
+ "http2_support": false,
+ "enabled": true,
+ "hsts_enabled": false,
+ "hsts_subdomains": false,
+ "certificate": null,
+ "owner": {
+ "id": 1,
+ "created_on": "2024-10-09T00:59:56.000Z",
+ "modified_on": "2024-10-09T00:59:56.000Z",
+ "is_deleted": false,
+ "is_disabled": false,
+ "email": "admin@example.com",
+ "name": "Administrator",
+ "nickname": "Admin",
+ "avatar": "",
+ "roles": ["admin"]
+ }
+ }
+ }
+ },
+ "schema": {
+ "$ref": "../../../components/dead-host-object.json"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/nginx/proxy-hosts/get.json b/backend/schema/paths/nginx/proxy-hosts/get.json
new file mode 100644
index 0000000000..1d9f633512
--- /dev/null
+++ b/backend/schema/paths/nginx/proxy-hosts/get.json
@@ -0,0 +1,65 @@
+{
+ "operationId": "getProxyHosts",
+ "summary": "Get all proxy hosts",
+ "tags": ["Proxy Hosts"],
+ "security": [
+ {
+ "BearerAuth": ["proxy_hosts"]
+ }
+ ],
+ "parameters": [
+ {
+ "in": "query",
+ "name": "expand",
+ "description": "Expansions",
+ "schema": {
+ "type": "string",
+ "enum": ["access_list", "owner", "certificate"]
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": [
+ {
+ "id": 1,
+ "created_on": "2024-10-08T23:23:03.000Z",
+ "modified_on": "2024-10-08T23:23:04.000Z",
+ "owner_user_id": 1,
+ "domain_names": ["test.example.com"],
+ "forward_host": "127.0.0.1",
+ "forward_port": 8989,
+ "access_list_id": 0,
+ "certificate_id": 0,
+ "ssl_forced": false,
+ "caching_enabled": false,
+ "block_exploits": false,
+ "advanced_config": "",
+ "meta": {
+ "nginx_online": true,
+ "nginx_err": null
+ },
+ "allow_websocket_upgrade": false,
+ "http2_support": false,
+ "forward_scheme": "http",
+ "enabled": true,
+ "locations": null,
+ "hsts_enabled": false,
+ "hsts_subdomains": false
+ }
+ ]
+ }
+ },
+ "schema": {
+ "$ref": "../../../components/proxy-host-list.json"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/nginx/proxy-hosts/hostID/delete.json b/backend/schema/paths/nginx/proxy-hosts/hostID/delete.json
new file mode 100644
index 0000000000..991ef0e9e3
--- /dev/null
+++ b/backend/schema/paths/nginx/proxy-hosts/hostID/delete.json
@@ -0,0 +1,39 @@
+{
+ "operationId": "deleteProxyHost",
+ "summary": "Delete a Proxy Host",
+ "tags": ["Proxy Hosts"],
+ "security": [
+ {
+ "BearerAuth": ["proxy_hosts"]
+ }
+ ],
+ "parameters": [
+ {
+ "in": "path",
+ "name": "hostID",
+ "schema": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "required": true,
+ "example": 2
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": true
+ }
+ },
+ "schema": {
+ "type": "boolean"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/nginx/proxy-hosts/hostID/disable/post.json b/backend/schema/paths/nginx/proxy-hosts/hostID/disable/post.json
new file mode 100644
index 0000000000..54ff8a6632
--- /dev/null
+++ b/backend/schema/paths/nginx/proxy-hosts/hostID/disable/post.json
@@ -0,0 +1,59 @@
+{
+ "operationId": "disableProxyHost",
+ "summary": "Disable a Proxy Host",
+ "tags": ["Proxy Hosts"],
+ "security": [
+ {
+ "BearerAuth": ["proxy_hosts"]
+ }
+ ],
+ "parameters": [
+ {
+ "in": "path",
+ "name": "hostID",
+ "schema": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "required": true,
+ "example": 2
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": true
+ }
+ },
+ "schema": {
+ "type": "boolean"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "400 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": {
+ "error": {
+ "code": 400,
+ "message": "Host is already disabled"
+ }
+ }
+ }
+ },
+ "schema": {
+ "$ref": "../../../../../components/error.json"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/nginx/proxy-hosts/hostID/enable/post.json b/backend/schema/paths/nginx/proxy-hosts/hostID/enable/post.json
new file mode 100644
index 0000000000..9f052de052
--- /dev/null
+++ b/backend/schema/paths/nginx/proxy-hosts/hostID/enable/post.json
@@ -0,0 +1,59 @@
+{
+ "operationId": "enableProxyHost",
+ "summary": "Enable a Proxy Host",
+ "tags": ["Proxy Hosts"],
+ "security": [
+ {
+ "BearerAuth": ["proxy_hosts"]
+ }
+ ],
+ "parameters": [
+ {
+ "in": "path",
+ "name": "hostID",
+ "schema": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "required": true,
+ "example": 2
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": true
+ }
+ },
+ "schema": {
+ "type": "boolean"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "400 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": {
+ "error": {
+ "code": 400,
+ "message": "Host is already enabled"
+ }
+ }
+ }
+ },
+ "schema": {
+ "$ref": "../../../../../components/error.json"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/nginx/proxy-hosts/hostID/get.json b/backend/schema/paths/nginx/proxy-hosts/hostID/get.json
new file mode 100644
index 0000000000..5e10a9cfd1
--- /dev/null
+++ b/backend/schema/paths/nginx/proxy-hosts/hostID/get.json
@@ -0,0 +1,64 @@
+{
+ "operationId": "getProxyHost",
+ "summary": "Get a Proxy Host",
+ "tags": ["Proxy Hosts"],
+ "security": [
+ {
+ "BearerAuth": ["proxy_hosts"]
+ }
+ ],
+ "parameters": [
+ {
+ "in": "path",
+ "name": "hostID",
+ "schema": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "required": true,
+ "example": 1
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": {
+ "id": 1,
+ "created_on": "2024-10-08T23:23:03.000Z",
+ "modified_on": "2024-10-08T23:26:38.000Z",
+ "owner_user_id": 1,
+ "domain_names": ["test.example.com"],
+ "forward_host": "192.168.0.10",
+ "forward_port": 8989,
+ "access_list_id": 0,
+ "certificate_id": 0,
+ "ssl_forced": false,
+ "caching_enabled": false,
+ "block_exploits": false,
+ "advanced_config": "",
+ "meta": {
+ "nginx_online": true,
+ "nginx_err": null
+ },
+ "allow_websocket_upgrade": false,
+ "http2_support": false,
+ "forward_scheme": "http",
+ "enabled": true,
+ "locations": null,
+ "hsts_enabled": false,
+ "hsts_subdomains": false
+ }
+ }
+ },
+ "schema": {
+ "$ref": "../../../../components/proxy-host-object.json"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/nginx/proxy-hosts/hostID/put.json b/backend/schema/paths/nginx/proxy-hosts/hostID/put.json
new file mode 100644
index 0000000000..5cab6e7520
--- /dev/null
+++ b/backend/schema/paths/nginx/proxy-hosts/hostID/put.json
@@ -0,0 +1,143 @@
+{
+ "operationId": "updateProxyHost",
+ "summary": "Update a Proxy Host",
+ "tags": ["Proxy Hosts"],
+ "security": [
+ {
+ "BearerAuth": ["proxy_hosts"]
+ }
+ ],
+ "parameters": [
+ {
+ "in": "path",
+ "name": "hostID",
+ "schema": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "required": true,
+ "example": 2
+ }
+ ],
+ "requestBody": {
+ "description": "Proxy Host Payload",
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "additionalProperties": false,
+ "minProperties": 1,
+ "properties": {
+ "domain_names": {
+ "$ref": "../../../../components/proxy-host-object.json#/properties/domain_names"
+ },
+ "forward_scheme": {
+ "$ref": "../../../../components/proxy-host-object.json#/properties/forward_scheme"
+ },
+ "forward_host": {
+ "$ref": "../../../../components/proxy-host-object.json#/properties/forward_host"
+ },
+ "forward_port": {
+ "$ref": "../../../../components/proxy-host-object.json#/properties/forward_port"
+ },
+ "certificate_id": {
+ "$ref": "../../../../components/proxy-host-object.json#/properties/certificate_id"
+ },
+ "ssl_forced": {
+ "$ref": "../../../../components/proxy-host-object.json#/properties/ssl_forced"
+ },
+ "hsts_enabled": {
+ "$ref": "../../../../components/proxy-host-object.json#/properties/hsts_enabled"
+ },
+ "hsts_subdomains": {
+ "$ref": "../../../../components/proxy-host-object.json#/properties/hsts_subdomains"
+ },
+ "http2_support": {
+ "$ref": "../../../../components/proxy-host-object.json#/properties/http2_support"
+ },
+ "block_exploits": {
+ "$ref": "../../../../components/proxy-host-object.json#/properties/block_exploits"
+ },
+ "caching_enabled": {
+ "$ref": "../../../../components/proxy-host-object.json#/properties/caching_enabled"
+ },
+ "allow_websocket_upgrade": {
+ "$ref": "../../../../components/proxy-host-object.json#/properties/allow_websocket_upgrade"
+ },
+ "access_list_id": {
+ "$ref": "../../../../components/proxy-host-object.json#/properties/access_list_id"
+ },
+ "advanced_config": {
+ "$ref": "../../../../components/proxy-host-object.json#/properties/advanced_config"
+ },
+ "enabled": {
+ "$ref": "../../../../components/proxy-host-object.json#/properties/enabled"
+ },
+ "meta": {
+ "$ref": "../../../../components/proxy-host-object.json#/properties/meta"
+ },
+ "locations": {
+ "$ref": "../../../../components/proxy-host-object.json#/properties/locations"
+ }
+ }
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": {
+ "id": 1,
+ "created_on": "2024-10-08T23:23:03.000Z",
+ "modified_on": "2024-10-08T23:26:37.000Z",
+ "owner_user_id": 1,
+ "domain_names": ["test.example.com"],
+ "forward_host": "192.168.0.10",
+ "forward_port": 8989,
+ "access_list_id": 0,
+ "certificate_id": 0,
+ "ssl_forced": false,
+ "caching_enabled": false,
+ "block_exploits": false,
+ "advanced_config": "",
+ "meta": {
+ "nginx_online": true,
+ "nginx_err": null
+ },
+ "allow_websocket_upgrade": false,
+ "http2_support": false,
+ "forward_scheme": "http",
+ "enabled": true,
+ "hsts_enabled": false,
+ "hsts_subdomains": false,
+ "owner": {
+ "id": 1,
+ "created_on": "2024-10-07T22:43:55.000Z",
+ "modified_on": "2024-10-08T12:52:54.000Z",
+ "is_deleted": false,
+ "is_disabled": false,
+ "email": "admin@example.com",
+ "name": "Administrator",
+ "nickname": "some guy",
+ "avatar": "//www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?default=mm",
+ "roles": ["admin"]
+ },
+ "certificate": null,
+ "access_list": null
+ }
+ }
+ },
+ "schema": {
+ "$ref": "../../../../components/proxy-host-object.json"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/nginx/proxy-hosts/post.json b/backend/schema/paths/nginx/proxy-hosts/post.json
new file mode 100644
index 0000000000..85455fb6b6
--- /dev/null
+++ b/backend/schema/paths/nginx/proxy-hosts/post.json
@@ -0,0 +1,128 @@
+{
+ "operationId": "createProxyHost",
+ "summary": "Create a Proxy Host",
+ "tags": ["Proxy Hosts"],
+ "security": [
+ {
+ "BearerAuth": ["proxy_hosts"]
+ }
+ ],
+ "requestBody": {
+ "description": "Proxy Host Payload",
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": ["domain_names", "forward_scheme", "forward_host", "forward_port"],
+ "properties": {
+ "domain_names": {
+ "$ref": "../../../components/proxy-host-object.json#/properties/domain_names"
+ },
+ "forward_scheme": {
+ "$ref": "../../../components/proxy-host-object.json#/properties/forward_scheme"
+ },
+ "forward_host": {
+ "$ref": "../../../components/proxy-host-object.json#/properties/forward_host"
+ },
+ "forward_port": {
+ "$ref": "../../../components/proxy-host-object.json#/properties/forward_port"
+ },
+ "certificate_id": {
+ "$ref": "../../../components/proxy-host-object.json#/properties/certificate_id"
+ },
+ "ssl_forced": {
+ "$ref": "../../../components/proxy-host-object.json#/properties/ssl_forced"
+ },
+ "hsts_enabled": {
+ "$ref": "../../../components/proxy-host-object.json#/properties/hsts_enabled"
+ },
+ "hsts_subdomains": {
+ "$ref": "../../../components/proxy-host-object.json#/properties/hsts_subdomains"
+ },
+ "http2_support": {
+ "$ref": "../../../components/proxy-host-object.json#/properties/http2_support"
+ },
+ "block_exploits": {
+ "$ref": "../../../components/proxy-host-object.json#/properties/block_exploits"
+ },
+ "caching_enabled": {
+ "$ref": "../../../components/proxy-host-object.json#/properties/caching_enabled"
+ },
+ "allow_websocket_upgrade": {
+ "$ref": "../../../components/proxy-host-object.json#/properties/allow_websocket_upgrade"
+ },
+ "access_list_id": {
+ "$ref": "../../../components/proxy-host-object.json#/properties/access_list_id"
+ },
+ "advanced_config": {
+ "$ref": "../../../components/proxy-host-object.json#/properties/advanced_config"
+ },
+ "enabled": {
+ "$ref": "../../../components/proxy-host-object.json#/properties/enabled"
+ },
+ "meta": {
+ "$ref": "../../../components/proxy-host-object.json#/properties/meta"
+ },
+ "locations": {
+ "$ref": "../../../components/proxy-host-object.json#/properties/locations"
+ }
+ }
+ }
+ }
+ }
+ },
+ "responses": {
+ "201": {
+ "description": "201 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": {
+ "id": 1,
+ "created_on": "2024-10-08T23:23:03.000Z",
+ "modified_on": "2024-10-08T23:23:03.000Z",
+ "owner_user_id": 1,
+ "domain_names": ["test.example.com"],
+ "forward_host": "127.0.0.1",
+ "forward_port": 8989,
+ "access_list_id": 0,
+ "certificate_id": 0,
+ "ssl_forced": false,
+ "caching_enabled": false,
+ "block_exploits": false,
+ "advanced_config": "",
+ "meta": {},
+ "allow_websocket_upgrade": false,
+ "http2_support": false,
+ "forward_scheme": "http",
+ "enabled": true,
+ "hsts_enabled": false,
+ "hsts_subdomains": false,
+ "certificate": null,
+ "owner": {
+ "id": 1,
+ "created_on": "2024-10-07T22:43:55.000Z",
+ "modified_on": "2024-10-08T12:52:54.000Z",
+ "is_deleted": false,
+ "is_disabled": false,
+ "email": "admin@example.com",
+ "name": "Administrator",
+ "nickname": "some guy",
+ "avatar": "//www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?default=mm",
+ "roles": ["admin"]
+ },
+ "access_list": null
+ }
+ }
+ },
+ "schema": {
+ "$ref": "../../../components/proxy-host-object.json"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/nginx/redirection-hosts/get.json b/backend/schema/paths/nginx/redirection-hosts/get.json
new file mode 100644
index 0000000000..0b35e0fc4a
--- /dev/null
+++ b/backend/schema/paths/nginx/redirection-hosts/get.json
@@ -0,0 +1,62 @@
+{
+ "operationId": "getRedirectionHosts",
+ "summary": "Get all Redirection hosts",
+ "tags": ["Redirection Hosts"],
+ "security": [
+ {
+ "BearerAuth": ["redirection_hosts"]
+ }
+ ],
+ "parameters": [
+ {
+ "in": "query",
+ "name": "expand",
+ "description": "Expansions",
+ "schema": {
+ "type": "string",
+ "enum": ["owner", "certificate"]
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": [
+ {
+ "id": 1,
+ "created_on": "2024-10-09T01:13:12.000Z",
+ "modified_on": "2024-10-09T01:13:13.000Z",
+ "owner_user_id": 1,
+ "domain_names": ["test.example.com"],
+ "forward_domain_name": "something-else.com",
+ "preserve_path": false,
+ "certificate_id": 0,
+ "ssl_forced": false,
+ "block_exploits": false,
+ "advanced_config": "",
+ "meta": {
+ "nginx_online": true,
+ "nginx_err": null
+ },
+ "http2_support": false,
+ "enabled": true,
+ "hsts_enabled": false,
+ "hsts_subdomains": false,
+ "forward_scheme": "http",
+ "forward_http_code": 301
+ }
+ ]
+ }
+ },
+ "schema": {
+ "$ref": "../../../components/redirection-host-list.json"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/nginx/redirection-hosts/hostID/delete.json b/backend/schema/paths/nginx/redirection-hosts/hostID/delete.json
new file mode 100644
index 0000000000..7330f36239
--- /dev/null
+++ b/backend/schema/paths/nginx/redirection-hosts/hostID/delete.json
@@ -0,0 +1,39 @@
+{
+ "operationId": "deleteRedirectionHost",
+ "summary": "Delete a Redirection Host",
+ "tags": ["Redirection Hosts"],
+ "security": [
+ {
+ "BearerAuth": ["redirection_hosts"]
+ }
+ ],
+ "parameters": [
+ {
+ "in": "path",
+ "name": "hostID",
+ "schema": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "required": true,
+ "example": 2
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": true
+ }
+ },
+ "schema": {
+ "type": "boolean"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/nginx/redirection-hosts/hostID/disable/post.json b/backend/schema/paths/nginx/redirection-hosts/hostID/disable/post.json
new file mode 100644
index 0000000000..8433220d5f
--- /dev/null
+++ b/backend/schema/paths/nginx/redirection-hosts/hostID/disable/post.json
@@ -0,0 +1,59 @@
+{
+ "operationId": "disableRedirectionHost",
+ "summary": "Disable a Redirection Host",
+ "tags": ["Redirection Hosts"],
+ "security": [
+ {
+ "BearerAuth": ["redirection_hosts"]
+ }
+ ],
+ "parameters": [
+ {
+ "in": "path",
+ "name": "hostID",
+ "schema": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "required": true,
+ "example": 2
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": true
+ }
+ },
+ "schema": {
+ "type": "boolean"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "400 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": {
+ "error": {
+ "code": 400,
+ "message": "Host is already disabled"
+ }
+ }
+ }
+ },
+ "schema": {
+ "$ref": "../../../../../components/error.json"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/nginx/redirection-hosts/hostID/enable/post.json b/backend/schema/paths/nginx/redirection-hosts/hostID/enable/post.json
new file mode 100644
index 0000000000..bef53436b7
--- /dev/null
+++ b/backend/schema/paths/nginx/redirection-hosts/hostID/enable/post.json
@@ -0,0 +1,59 @@
+{
+ "operationId": "enableRedirectionHost",
+ "summary": "Enable a Redirection Host",
+ "tags": ["Redirection Hosts"],
+ "security": [
+ {
+ "BearerAuth": ["redirection_hosts"]
+ }
+ ],
+ "parameters": [
+ {
+ "in": "path",
+ "name": "hostID",
+ "schema": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "required": true,
+ "example": 2
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": true
+ }
+ },
+ "schema": {
+ "type": "boolean"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "400 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": {
+ "error": {
+ "code": 400,
+ "message": "Host is already enabled"
+ }
+ }
+ }
+ },
+ "schema": {
+ "$ref": "../../../../../components/error.json"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/nginx/redirection-hosts/hostID/get.json b/backend/schema/paths/nginx/redirection-hosts/hostID/get.json
new file mode 100644
index 0000000000..d780f874b0
--- /dev/null
+++ b/backend/schema/paths/nginx/redirection-hosts/hostID/get.json
@@ -0,0 +1,61 @@
+{
+ "operationId": "getRedirectionHost",
+ "summary": "Get a Redirection Host",
+ "tags": ["Redirection Hosts"],
+ "security": [
+ {
+ "BearerAuth": ["redirection_hosts"]
+ }
+ ],
+ "parameters": [
+ {
+ "in": "path",
+ "name": "hostID",
+ "schema": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "required": true,
+ "example": 1
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": {
+ "id": 1,
+ "created_on": "2024-10-09T01:13:12.000Z",
+ "modified_on": "2024-10-09T01:13:13.000Z",
+ "owner_user_id": 1,
+ "domain_names": ["test.example.com"],
+ "forward_domain_name": "something-else.com",
+ "preserve_path": false,
+ "certificate_id": 0,
+ "ssl_forced": false,
+ "block_exploits": false,
+ "advanced_config": "",
+ "meta": {
+ "nginx_online": true,
+ "nginx_err": null
+ },
+ "http2_support": false,
+ "enabled": true,
+ "hsts_enabled": false,
+ "hsts_subdomains": false,
+ "forward_scheme": "http",
+ "forward_http_code": 301
+ }
+ }
+ },
+ "schema": {
+ "$ref": "../../../../components/redirection-host-object.json"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/nginx/redirection-hosts/hostID/put.json b/backend/schema/paths/nginx/redirection-hosts/hostID/put.json
new file mode 100644
index 0000000000..fd97cbfa8f
--- /dev/null
+++ b/backend/schema/paths/nginx/redirection-hosts/hostID/put.json
@@ -0,0 +1,128 @@
+{
+ "operationId": "updateRedirectionHost",
+ "summary": "Update a Redirection Host",
+ "tags": ["Redirection Hosts"],
+ "security": [
+ {
+ "BearerAuth": ["redirection_hosts"]
+ }
+ ],
+ "parameters": [
+ {
+ "in": "path",
+ "name": "hostID",
+ "schema": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "required": true,
+ "example": 2
+ }
+ ],
+ "requestBody": {
+ "description": "Redirection Host Payload",
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "additionalProperties": false,
+ "minProperties": 1,
+ "properties": {
+ "domain_names": {
+ "$ref": "../../../../components/redirection-host-object.json#/properties/domain_names"
+ },
+ "forward_http_code": {
+ "$ref": "../../../../components/redirection-host-object.json#/properties/forward_http_code"
+ },
+ "forward_scheme": {
+ "$ref": "../../../../components/redirection-host-object.json#/properties/forward_scheme"
+ },
+ "forward_domain_name": {
+ "$ref": "../../../../components/redirection-host-object.json#/properties/forward_domain_name"
+ },
+ "preserve_path": {
+ "$ref": "../../../../components/redirection-host-object.json#/properties/preserve_path"
+ },
+ "certificate_id": {
+ "$ref": "../../../../components/redirection-host-object.json#/properties/certificate_id"
+ },
+ "ssl_forced": {
+ "$ref": "../../../../components/redirection-host-object.json#/properties/ssl_forced"
+ },
+ "hsts_enabled": {
+ "$ref": "../../../../components/redirection-host-object.json#/properties/hsts_enabled"
+ },
+ "hsts_subdomains": {
+ "$ref": "../../../../components/redirection-host-object.json#/properties/hsts_subdomains"
+ },
+ "http2_support": {
+ "$ref": "../../../../components/redirection-host-object.json#/properties/http2_support"
+ },
+ "block_exploits": {
+ "$ref": "../../../../components/redirection-host-object.json#/properties/block_exploits"
+ },
+ "advanced_config": {
+ "$ref": "../../../../components/redirection-host-object.json#/properties/advanced_config"
+ },
+ "meta": {
+ "$ref": "../../../../components/redirection-host-object.json#/properties/meta"
+ }
+ }
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": {
+ "id": 1,
+ "created_on": "2024-10-09T01:13:12.000Z",
+ "modified_on": "2024-10-09T01:18:11.000Z",
+ "owner_user_id": 1,
+ "domain_names": ["test.example.com"],
+ "forward_domain_name": "something-else.com",
+ "preserve_path": false,
+ "certificate_id": 0,
+ "ssl_forced": false,
+ "block_exploits": false,
+ "advanced_config": "",
+ "meta": {
+ "nginx_online": true,
+ "nginx_err": null
+ },
+ "http2_support": false,
+ "enabled": true,
+ "hsts_enabled": false,
+ "hsts_subdomains": false,
+ "forward_scheme": "http",
+ "forward_http_code": 301,
+ "owner": {
+ "id": 1,
+ "created_on": "2024-10-09T00:59:56.000Z",
+ "modified_on": "2024-10-09T00:59:56.000Z",
+ "is_deleted": false,
+ "is_disabled": false,
+ "email": "admin@example.com",
+ "name": "Administrator",
+ "nickname": "Admin",
+ "avatar": "",
+ "roles": ["admin"]
+ },
+ "certificate": null
+ }
+ }
+ },
+ "schema": {
+ "$ref": "../../../../components/redirection-host-object.json"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/nginx/redirection-hosts/post.json b/backend/schema/paths/nginx/redirection-hosts/post.json
new file mode 100644
index 0000000000..5bfde2c388
--- /dev/null
+++ b/backend/schema/paths/nginx/redirection-hosts/post.json
@@ -0,0 +1,113 @@
+{
+ "operationId": "createRedirectionHost",
+ "summary": "Create a Redirection Host",
+ "tags": ["Redirection Hosts"],
+ "security": [
+ {
+ "BearerAuth": ["redirection_hosts"]
+ }
+ ],
+ "requestBody": {
+ "description": "Redirection Host Payload",
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": ["domain_names", "forward_scheme", "forward_http_code", "forward_domain_name"],
+ "properties": {
+ "domain_names": {
+ "$ref": "../../../components/redirection-host-object.json#/properties/domain_names"
+ },
+ "forward_http_code": {
+ "$ref": "../../../components/redirection-host-object.json#/properties/forward_http_code"
+ },
+ "forward_scheme": {
+ "$ref": "../../../components/redirection-host-object.json#/properties/forward_scheme"
+ },
+ "forward_domain_name": {
+ "$ref": "../../../components/redirection-host-object.json#/properties/forward_domain_name"
+ },
+ "preserve_path": {
+ "$ref": "../../../components/redirection-host-object.json#/properties/preserve_path"
+ },
+ "certificate_id": {
+ "$ref": "../../../components/redirection-host-object.json#/properties/certificate_id"
+ },
+ "ssl_forced": {
+ "$ref": "../../../components/redirection-host-object.json#/properties/ssl_forced"
+ },
+ "hsts_enabled": {
+ "$ref": "../../../components/redirection-host-object.json#/properties/hsts_enabled"
+ },
+ "hsts_subdomains": {
+ "$ref": "../../../components/redirection-host-object.json#/properties/hsts_subdomains"
+ },
+ "http2_support": {
+ "$ref": "../../../components/redirection-host-object.json#/properties/http2_support"
+ },
+ "block_exploits": {
+ "$ref": "../../../components/redirection-host-object.json#/properties/block_exploits"
+ },
+ "advanced_config": {
+ "$ref": "../../../components/redirection-host-object.json#/properties/advanced_config"
+ },
+ "meta": {
+ "$ref": "../../../components/redirection-host-object.json#/properties/meta"
+ }
+ }
+ }
+ }
+ }
+ },
+ "responses": {
+ "201": {
+ "description": "201 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": {
+ "id": 1,
+ "created_on": "2024-10-09T01:13:12.000Z",
+ "modified_on": "2024-10-09T01:13:12.000Z",
+ "owner_user_id": 1,
+ "domain_names": ["test.example.com"],
+ "forward_domain_name": "something-else.com",
+ "preserve_path": false,
+ "certificate_id": 0,
+ "ssl_forced": false,
+ "block_exploits": false,
+ "advanced_config": "",
+ "meta": {},
+ "http2_support": false,
+ "enabled": true,
+ "hsts_enabled": false,
+ "hsts_subdomains": false,
+ "forward_scheme": "http",
+ "forward_http_code": 301,
+ "certificate": null,
+ "owner": {
+ "id": 1,
+ "created_on": "2024-10-09T00:59:56.000Z",
+ "modified_on": "2024-10-09T00:59:56.000Z",
+ "is_deleted": false,
+ "is_disabled": false,
+ "email": "admin@example.com",
+ "name": "Administrator",
+ "nickname": "Admin",
+ "avatar": "",
+ "roles": ["admin"]
+ }
+ }
+ }
+ },
+ "schema": {
+ "$ref": "../../../components/redirection-host-object.json"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/nginx/streams/get.json b/backend/schema/paths/nginx/streams/get.json
new file mode 100644
index 0000000000..596afc6e74
--- /dev/null
+++ b/backend/schema/paths/nginx/streams/get.json
@@ -0,0 +1,55 @@
+{
+ "operationId": "getStreams",
+ "summary": "Get all streams",
+ "tags": ["Streams"],
+ "security": [
+ {
+ "BearerAuth": ["streams"]
+ }
+ ],
+ "parameters": [
+ {
+ "in": "query",
+ "name": "expand",
+ "description": "Expansions",
+ "schema": {
+ "type": "string",
+ "enum": ["access_list", "owner", "certificate"]
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": [
+ {
+ "id": 1,
+ "created_on": "2024-10-09T02:33:45.000Z",
+ "modified_on": "2024-10-09T02:33:45.000Z",
+ "owner_user_id": 1,
+ "incoming_port": 9090,
+ "forwarding_host": "router.internal",
+ "forwarding_port": 80,
+ "tcp_forwarding": true,
+ "udp_forwarding": false,
+ "meta": {
+ "nginx_online": true,
+ "nginx_err": null
+ },
+ "enabled": true
+ }
+ ]
+ }
+ },
+ "schema": {
+ "$ref": "../../../components/stream-list.json"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/nginx/streams/post.json b/backend/schema/paths/nginx/streams/post.json
new file mode 100644
index 0000000000..3e3cd55e67
--- /dev/null
+++ b/backend/schema/paths/nginx/streams/post.json
@@ -0,0 +1,95 @@
+{
+ "operationId": "createStream",
+ "summary": "Create a Stream",
+ "tags": ["Streams"],
+ "security": [
+ {
+ "BearerAuth": ["streams"]
+ }
+ ],
+ "requestBody": {
+ "description": "Stream Payload",
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": ["incoming_port", "forwarding_host", "forwarding_port"],
+ "properties": {
+ "incoming_port": {
+ "$ref": "../../../components/stream-object.json#/properties/incoming_port"
+ },
+ "forwarding_host": {
+ "$ref": "../../../components/stream-object.json#/properties/forwarding_host"
+ },
+ "forwarding_port": {
+ "$ref": "../../../components/stream-object.json#/properties/forwarding_port"
+ },
+ "tcp_forwarding": {
+ "$ref": "../../../components/stream-object.json#/properties/tcp_forwarding"
+ },
+ "udp_forwarding": {
+ "$ref": "../../../components/stream-object.json#/properties/udp_forwarding"
+ },
+ "domain_names": {
+ "$ref": "../../../components/stream-object.json#/properties/domain_names"
+ },
+ "certificate_id": {
+ "$ref": "../../../components/stream-object.json#/properties/certificate_id"
+ },
+ "meta": {
+ "$ref": "../../../components/stream-object.json#/properties/meta"
+ }
+ }
+ }
+ }
+ }
+ },
+ "responses": {
+ "201": {
+ "description": "201 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": {
+ "id": 1,
+ "created_on": "2024-10-09T02:33:45.000Z",
+ "modified_on": "2024-10-09T02:33:45.000Z",
+ "owner_user_id": 1,
+ "incoming_port": 9090,
+ "forwarding_host": "router.internal",
+ "forwarding_port": 80,
+ "tcp_forwarding": true,
+ "udp_forwarding": false,
+ "meta": {
+ "nginx_online": true,
+ "nginx_err": null
+ },
+ "enabled": true,
+ "owner": {
+ "id": 1,
+ "created_on": "2024-10-09T02:33:16.000Z",
+ "modified_on": "2024-10-09T02:33:16.000Z",
+ "is_deleted": false,
+ "is_disabled": false,
+ "email": "admin@example.com",
+ "name": "Administrator",
+ "nickname": "Admin",
+ "avatar": "",
+ "roles": ["admin"]
+ },
+ "certificate_id": 0,
+ "certificate": null
+ }
+ }
+ },
+ "schema": {
+ "$ref": "../../../components/stream-object.json"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/nginx/streams/streamID/delete.json b/backend/schema/paths/nginx/streams/streamID/delete.json
new file mode 100644
index 0000000000..3a96852582
--- /dev/null
+++ b/backend/schema/paths/nginx/streams/streamID/delete.json
@@ -0,0 +1,39 @@
+{
+ "operationId": "deleteStream",
+ "summary": "Delete a Stream",
+ "tags": ["Streams"],
+ "security": [
+ {
+ "BearerAuth": ["streams"]
+ }
+ ],
+ "parameters": [
+ {
+ "in": "path",
+ "name": "streamID",
+ "schema": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "required": true,
+ "example": 2
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": true
+ }
+ },
+ "schema": {
+ "type": "boolean"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/nginx/streams/streamID/disable/post.json b/backend/schema/paths/nginx/streams/streamID/disable/post.json
new file mode 100644
index 0000000000..d1c1b1c84a
--- /dev/null
+++ b/backend/schema/paths/nginx/streams/streamID/disable/post.json
@@ -0,0 +1,59 @@
+{
+ "operationId": "disableStream",
+ "summary": "Disable a Stream",
+ "tags": ["Streams"],
+ "security": [
+ {
+ "BearerAuth": ["streams"]
+ }
+ ],
+ "parameters": [
+ {
+ "in": "path",
+ "name": "streamID",
+ "schema": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "required": true,
+ "example": 2
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": true
+ }
+ },
+ "schema": {
+ "type": "boolean"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "400 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": {
+ "error": {
+ "code": 400,
+ "message": "Host is already disabled"
+ }
+ }
+ }
+ },
+ "schema": {
+ "$ref": "../../../../../components/error.json"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/nginx/streams/streamID/enable/post.json b/backend/schema/paths/nginx/streams/streamID/enable/post.json
new file mode 100644
index 0000000000..dc914f5f27
--- /dev/null
+++ b/backend/schema/paths/nginx/streams/streamID/enable/post.json
@@ -0,0 +1,59 @@
+{
+ "operationId": "enableStream",
+ "summary": "Enable a Stream",
+ "tags": ["Streams"],
+ "security": [
+ {
+ "BearerAuth": ["streams"]
+ }
+ ],
+ "parameters": [
+ {
+ "in": "path",
+ "name": "streamID",
+ "schema": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "required": true,
+ "example": 2
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": true
+ }
+ },
+ "schema": {
+ "type": "boolean"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "400 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": {
+ "error": {
+ "code": 400,
+ "message": "Host is already enabled"
+ }
+ }
+ }
+ },
+ "schema": {
+ "$ref": "../../../../../components/error.json"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/nginx/streams/streamID/get.json b/backend/schema/paths/nginx/streams/streamID/get.json
new file mode 100644
index 0000000000..6547656dfc
--- /dev/null
+++ b/backend/schema/paths/nginx/streams/streamID/get.json
@@ -0,0 +1,54 @@
+{
+ "operationId": "getStream",
+ "summary": "Get a Stream",
+ "tags": ["Streams"],
+ "security": [
+ {
+ "BearerAuth": ["streams"]
+ }
+ ],
+ "parameters": [
+ {
+ "in": "path",
+ "name": "streamID",
+ "schema": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "required": true,
+ "example": 2
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": {
+ "id": 1,
+ "created_on": "2024-10-09T02:33:45.000Z",
+ "modified_on": "2024-10-09T02:33:45.000Z",
+ "owner_user_id": 1,
+ "incoming_port": 9090,
+ "forwarding_host": "router.internal",
+ "forwarding_port": 80,
+ "tcp_forwarding": true,
+ "udp_forwarding": false,
+ "meta": {
+ "nginx_online": true,
+ "nginx_err": null
+ },
+ "enabled": true
+ }
+ }
+ },
+ "schema": {
+ "$ref": "../../../../components/stream-object.json"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/nginx/streams/streamID/put.json b/backend/schema/paths/nginx/streams/streamID/put.json
new file mode 100644
index 0000000000..8fa64cdf65
--- /dev/null
+++ b/backend/schema/paths/nginx/streams/streamID/put.json
@@ -0,0 +1,107 @@
+{
+ "operationId": "updateStream",
+ "summary": "Update a Stream",
+ "tags": ["Streams"],
+ "security": [
+ {
+ "BearerAuth": ["streams"]
+ }
+ ],
+ "parameters": [
+ {
+ "in": "path",
+ "name": "streamID",
+ "schema": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "required": true,
+ "example": 2
+ }
+ ],
+ "requestBody": {
+ "description": "Stream Payload",
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "additionalProperties": false,
+ "minProperties": 1,
+ "properties": {
+ "incoming_port": {
+ "$ref": "../../../../components/stream-object.json#/properties/incoming_port"
+ },
+ "forwarding_host": {
+ "$ref": "../../../../components/stream-object.json#/properties/forwarding_host"
+ },
+ "forwarding_port": {
+ "$ref": "../../../../components/stream-object.json#/properties/forwarding_port"
+ },
+ "tcp_forwarding": {
+ "$ref": "../../../../components/stream-object.json#/properties/tcp_forwarding"
+ },
+ "udp_forwarding": {
+ "$ref": "../../../../components/stream-object.json#/properties/udp_forwarding"
+ },
+ "domain_names": {
+ "$ref": "../../../../components/stream-object.json#/properties/domain_names"
+ },
+ "certificate_id": {
+ "$ref": "../../../../components/stream-object.json#/properties/certificate_id"
+ },
+ "meta": {
+ "$ref": "../../../../components/stream-object.json#/properties/meta"
+ }
+ }
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": {
+ "id": 1,
+ "created_on": "2024-10-08T23:23:03.000Z",
+ "modified_on": "2024-10-08T23:26:37.000Z",
+ "owner_user_id": 1,
+ "incoming_port": 9090,
+ "forwarding_host": "router.internal",
+ "forwarding_port": 80,
+ "tcp_forwarding": true,
+ "udp_forwarding": false,
+ "meta": {
+ "nginx_online": true,
+ "nginx_err": null
+ },
+ "enabled": true,
+ "owner": {
+ "id": 1,
+ "created_on": "2024-10-07T22:43:55.000Z",
+ "modified_on": "2024-10-08T12:52:54.000Z",
+ "is_deleted": false,
+ "is_disabled": false,
+ "email": "admin@example.com",
+ "name": "Administrator",
+ "nickname": "some guy",
+ "avatar": "//www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?default=mm",
+ "roles": ["admin"]
+ },
+ "certificate": null,
+ "access_list": null
+ }
+ }
+ },
+ "schema": {
+ "$ref": "../../../../components/stream-object.json"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/reports/hosts/get.json b/backend/schema/paths/reports/hosts/get.json
new file mode 100644
index 0000000000..a40ddc723c
--- /dev/null
+++ b/backend/schema/paths/reports/hosts/get.json
@@ -0,0 +1,50 @@
+{
+ "operationId": "reportsHosts",
+ "summary": "Report on Host Statistics",
+ "tags": ["Reports"],
+ "security": [
+ {
+ "BearerAuth": ["reports"]
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": {
+ "proxy": 20,
+ "redirection": 1,
+ "stream": 0,
+ "dead": 1
+ }
+ }
+ },
+ "schema": {
+ "type": "object",
+ "properties": {
+ "proxy": {
+ "type": "integer",
+ "description": "Proxy Hosts Count"
+ },
+ "redirection": {
+ "type": "integer",
+ "description": "Redirection Hosts Count"
+ },
+ "stream": {
+ "type": "integer",
+ "description": "Streams Count"
+ },
+ "dead": {
+ "type": "integer",
+ "description": "404 Hosts Count"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/schema/get.json b/backend/schema/paths/schema/get.json
new file mode 100644
index 0000000000..d435b00429
--- /dev/null
+++ b/backend/schema/paths/schema/get.json
@@ -0,0 +1,10 @@
+{
+ "operationId": "schema",
+ "summary": "Returns this swagger API schema",
+ "tags": ["Public"],
+ "responses": {
+ "200": {
+ "description": "200 response"
+ }
+ }
+}
diff --git a/backend/schema/paths/settings/get.json b/backend/schema/paths/settings/get.json
new file mode 100644
index 0000000000..5d148d8af4
--- /dev/null
+++ b/backend/schema/paths/settings/get.json
@@ -0,0 +1,35 @@
+{
+ "operationId": "getSettings",
+ "summary": "Get all settings",
+ "tags": ["Settings"],
+ "security": [
+ {
+ "BearerAuth": ["settings"]
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": [
+ {
+ "id": "default-site",
+ "name": "Default Site",
+ "description": "What to show when Nginx is hit with an unknown Host",
+ "value": "congratulations",
+ "meta": {}
+ }
+ ]
+ }
+ },
+ "schema": {
+ "$ref": "../../components/setting-list.json"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/settings/settingID/get.json b/backend/schema/paths/settings/settingID/get.json
new file mode 100644
index 0000000000..405b976d28
--- /dev/null
+++ b/backend/schema/paths/settings/settingID/get.json
@@ -0,0 +1,46 @@
+{
+ "operationId": "getSetting",
+ "summary": "Get a setting",
+ "tags": ["Settings"],
+ "security": [
+ {
+ "BearerAuth": ["settings"]
+ }
+ ],
+ "parameters": [
+ {
+ "in": "path",
+ "name": "settingID",
+ "schema": {
+ "type": "string",
+ "minLength": 1
+ },
+ "required": true,
+ "description": "Setting ID",
+ "example": "default-site"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": {
+ "id": "default-site",
+ "name": "Default Site",
+ "description": "What to show when Nginx is hit with an unknown Host",
+ "value": "congratulations",
+ "meta": {}
+ }
+ }
+ },
+ "schema": {
+ "$ref": "../../../components/setting-object.json"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/settings/settingID/put.json b/backend/schema/paths/settings/settingID/put.json
new file mode 100644
index 0000000000..4ca6242939
--- /dev/null
+++ b/backend/schema/paths/settings/settingID/put.json
@@ -0,0 +1,79 @@
+{
+ "operationId": "updateSetting",
+ "summary": "Update a setting",
+ "tags": ["Settings"],
+ "security": [
+ {
+ "BearerAuth": ["settings"]
+ }
+ ],
+ "parameters": [
+ {
+ "in": "path",
+ "name": "settingID",
+ "schema": {
+ "type": "string",
+ "minLength": 1,
+ "enum": ["default-site"]
+ },
+ "required": true,
+ "description": "Setting ID",
+ "example": "default-site"
+ }
+ ],
+ "requestBody": {
+ "description": "Setting Payload",
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "additionalProperties": false,
+ "minProperties": 1,
+ "properties": {
+ "value": {
+ "type": "string",
+ "minLength": 1,
+ "enum": ["congratulations", "404", "444", "redirect", "html"]
+ },
+ "meta": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "redirect": {
+ "type": "string"
+ },
+ "html": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": {
+ "id": "default-site",
+ "name": "Default Site",
+ "description": "What to show when Nginx is hit with an unknown Host",
+ "value": "congratulations",
+ "meta": {}
+ }
+ }
+ },
+ "schema": {
+ "$ref": "../../../components/setting-object.json"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/tokens/get.json b/backend/schema/paths/tokens/get.json
new file mode 100644
index 0000000000..859bc61a4f
--- /dev/null
+++ b/backend/schema/paths/tokens/get.json
@@ -0,0 +1,30 @@
+{
+ "operationId": "refreshToken",
+ "summary": "Refresh your access token",
+ "tags": ["Tokens"],
+ "security": [
+ {
+ "BearerAuth": ["tokens"]
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": {
+ "expires": 1566540510,
+ "token": "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.ey...xaHKYr3Kk6MvkUjcC4"
+ }
+ }
+ },
+ "schema": {
+ "$ref": "../../components/token-object.json"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/tokens/post.json b/backend/schema/paths/tokens/post.json
new file mode 100644
index 0000000000..dece6b6567
--- /dev/null
+++ b/backend/schema/paths/tokens/post.json
@@ -0,0 +1,55 @@
+{
+ "operationId": "requestToken",
+ "summary": "Request a new access token from credentials",
+ "tags": ["Tokens"],
+ "requestBody": {
+ "description": "Credentials Payload",
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "additionalProperties": false,
+ "properties": {
+ "identity": {
+ "minLength": 1,
+ "type": "string"
+ },
+ "scope": {
+ "minLength": 1,
+ "type": "string",
+ "enum": ["user"]
+ },
+ "secret": {
+ "minLength": 1,
+ "type": "string"
+ }
+ },
+ "required": ["identity", "secret"],
+ "type": "object"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": {
+ "result": {
+ "expires": 1566540510,
+ "token": "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.ey...xaHKYr3Kk6MvkUjcC4"
+ }
+ }
+ }
+ },
+ "schema": {
+ "$ref": "../../components/token-object.json"
+ }
+ }
+ },
+ "description": "200 response"
+ }
+ }
+}
diff --git a/backend/schema/paths/users/get.json b/backend/schema/paths/users/get.json
new file mode 100644
index 0000000000..3741530114
--- /dev/null
+++ b/backend/schema/paths/users/get.json
@@ -0,0 +1,74 @@
+{
+ "operationId": "getUsers",
+ "summary": "Get all users",
+ "tags": ["Users"],
+ "security": [
+ {
+ "BearerAuth": ["users"]
+ }
+ ],
+ "parameters": [
+ {
+ "in": "query",
+ "name": "expand",
+ "description": "Expansions",
+ "schema": {
+ "type": "string",
+ "enum": ["permissions"]
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": [
+ {
+ "id": 1,
+ "created_on": "2020-01-30T09:36:08.000Z",
+ "modified_on": "2020-01-30T09:41:04.000Z",
+ "is_disabled": false,
+ "email": "jc@jc21.com",
+ "name": "Jamie Curnow",
+ "nickname": "James",
+ "avatar": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm",
+ "roles": ["admin"]
+ }
+ ]
+ },
+ "withPermissions": {
+ "value": [
+ {
+ "id": 1,
+ "created_on": "2020-01-30T09:36:08.000Z",
+ "modified_on": "2020-01-30T09:41:04.000Z",
+ "is_disabled": false,
+ "email": "jc@jc21.com",
+ "name": "Jamie Curnow",
+ "nickname": "James",
+ "avatar": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm",
+ "roles": ["admin"],
+ "permissions": {
+ "visibility": "all",
+ "proxy_hosts": "manage",
+ "redirection_hosts": "manage",
+ "dead_hosts": "manage",
+ "streams": "manage",
+ "access_lists": "manage",
+ "certificates": "manage"
+ }
+ }
+ ]
+ }
+ },
+ "schema": {
+ "$ref": "../../components/user-list.json"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/users/post.json b/backend/schema/paths/users/post.json
new file mode 100644
index 0000000000..c0213fe05b
--- /dev/null
+++ b/backend/schema/paths/users/post.json
@@ -0,0 +1,88 @@
+{
+ "operationId": "createUser",
+ "summary": "Create a User",
+ "tags": ["Users"],
+ "security": [
+ {
+ "BearerAuth": ["users"]
+ }
+ ],
+ "requestBody": {
+ "description": "User Payload",
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": ["name", "nickname", "email"],
+ "properties": {
+ "name": {
+ "$ref": "../../components/user-object.json#/properties/name"
+ },
+ "nickname": {
+ "$ref": "../../components/user-object.json#/properties/nickname"
+ },
+ "email": {
+ "$ref": "../../components/user-object.json#/properties/email"
+ },
+ "roles": {
+ "$ref": "../../components/user-object.json#/properties/roles"
+ },
+ "is_disabled": {
+ "$ref": "../../components/user-object.json#/properties/is_disabled"
+ },
+ "auth": {
+ "type": "object",
+ "description": "Auth Credentials",
+ "example": {
+ "type": "password",
+ "secret": "bigredhorsebanana"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "responses": {
+ "201": {
+ "description": "201 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": {
+ "id": 2,
+ "created_on": "2020-01-30T09:41:04.000Z",
+ "modified_on": "2020-01-30T09:41:04.000Z",
+ "is_disabled": false,
+ "email": "jc@jc21.com",
+ "name": "Jamie Curnow",
+ "nickname": "James",
+ "avatar": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm",
+ "roles": ["admin"],
+ "permissions": {
+ "id": 3,
+ "created_on": "2020-01-30T09:41:04.000Z",
+ "modified_on": "2020-01-30T09:41:04.000Z",
+ "user_id": 2,
+ "visibility": "user",
+ "proxy_hosts": "manage",
+ "redirection_hosts": "manage",
+ "dead_hosts": "manage",
+ "streams": "manage",
+ "access_lists": "manage",
+ "certificates": "manage"
+ }
+ }
+ }
+ },
+ "schema": {
+ "$ref": "../../components/user-object.json"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/users/userID/auth/put.json b/backend/schema/paths/users/userID/auth/put.json
new file mode 100644
index 0000000000..a72f5617c4
--- /dev/null
+++ b/backend/schema/paths/users/userID/auth/put.json
@@ -0,0 +1,79 @@
+{
+ "operationId": "updateUserAuth",
+ "summary": "Update a User's Authentication",
+ "tags": ["Users"],
+ "security": [
+ {
+ "BearerAuth": ["users"]
+ }
+ ],
+ "parameters": [
+ {
+ "in": "path",
+ "name": "userID",
+ "schema": {
+ "oneOf": [
+ {
+ "type": "string",
+ "pattern": "^me$"
+ },
+ {
+ "type": "integer",
+ "minimum": 1
+ }
+ ]
+ },
+ "required": true,
+ "description": "User ID or 'me' for yourself",
+ "example": 2
+ }
+ ],
+ "requestBody": {
+ "description": "Auth Payload",
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "required": ["type", "secret"],
+ "properties": {
+ "type": {
+ "type": "string",
+ "pattern": "^password$",
+ "example": "password"
+ },
+ "current": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 64,
+ "example": "changeme"
+ },
+ "secret": {
+ "type": "string",
+ "minLength": 8,
+ "maxLength": 64,
+ "example": "mySuperN3wP@ssword!"
+ }
+ }
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": true
+ }
+ },
+ "schema": {
+ "type": "boolean"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/users/userID/delete.json b/backend/schema/paths/users/userID/delete.json
new file mode 100644
index 0000000000..7d4f36151d
--- /dev/null
+++ b/backend/schema/paths/users/userID/delete.json
@@ -0,0 +1,40 @@
+{
+ "operationId": "deleteUser",
+ "summary": "Delete a User",
+ "tags": ["Users"],
+ "security": [
+ {
+ "BearerAuth": ["users"]
+ }
+ ],
+ "parameters": [
+ {
+ "in": "path",
+ "name": "userID",
+ "schema": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "required": true,
+ "description": "User ID",
+ "example": 2
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": true
+ }
+ },
+ "schema": {
+ "type": "boolean"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/users/userID/get.json b/backend/schema/paths/users/userID/get.json
new file mode 100644
index 0000000000..cb8ac61b46
--- /dev/null
+++ b/backend/schema/paths/users/userID/get.json
@@ -0,0 +1,58 @@
+{
+ "operationId": "getUser",
+ "summary": "Get a user",
+ "tags": ["Users"],
+ "security": [
+ {
+ "BearerAuth": ["users"]
+ }
+ ],
+ "parameters": [
+ {
+ "in": "path",
+ "name": "userID",
+ "schema": {
+ "oneOf": [
+ {
+ "type": "string",
+ "pattern": "^me$"
+ },
+ {
+ "type": "integer",
+ "minimum": 1
+ }
+ ]
+ },
+ "required": true,
+ "description": "User ID or 'me' for yourself",
+ "example": 1
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": {
+ "id": 1,
+ "created_on": "2020-01-30T09:36:08.000Z",
+ "modified_on": "2020-01-30T09:41:04.000Z",
+ "is_disabled": false,
+ "email": "jc@jc21.com",
+ "name": "Jamie Curnow",
+ "nickname": "James",
+ "avatar": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm",
+ "roles": ["admin"]
+ }
+ }
+ },
+ "schema": {
+ "$ref": "../../../components/user-object.json"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/users/userID/login/post.json b/backend/schema/paths/users/userID/login/post.json
new file mode 100644
index 0000000000..6148d182b4
--- /dev/null
+++ b/backend/schema/paths/users/userID/login/post.json
@@ -0,0 +1,73 @@
+{
+ "operationId": "loginAsUser",
+ "summary": "Login as this user",
+ "tags": ["Users"],
+ "security": [
+ {
+ "BearerAuth": ["users"]
+ }
+ ],
+ "parameters": [
+ {
+ "in": "path",
+ "name": "userID",
+ "schema": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "required": true,
+ "description": "User ID",
+ "example": 2
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": {
+ "token": "eyJhbGciOiJSUzI1NiIsInR...16OjT8B3NLyXg",
+ "expires": "2020-01-31T10:56:23.239Z",
+ "user": {
+ "id": 1,
+ "created_on": "2020-01-30T10:43:44.000Z",
+ "modified_on": "2020-01-30T10:43:44.000Z",
+ "is_disabled": false,
+ "email": "jc@jc21.com",
+ "name": "Jamie Curnow",
+ "nickname": "James",
+ "avatar": "//www.gravatar.com/avatar/3c8d73f45fd8763f827b964c76e6032a?default=mm",
+ "roles": ["admin"]
+ }
+ }
+ }
+ },
+ "schema": {
+ "type": "object",
+ "description": "Login object",
+ "required": ["expires", "token", "user"],
+ "additionalProperties": false,
+ "properties": {
+ "expires": {
+ "description": "Token Expiry Unix Time",
+ "example": 1566540249,
+ "minimum": 1,
+ "type": "number"
+ },
+ "token": {
+ "description": "JWT Token",
+ "example": "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.ey...xaHKYr3Kk6MvkUjcC4",
+ "type": "string"
+ },
+ "user": {
+ "$ref": "../../../../components/user-object.json"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/users/userID/permissions/put.json b/backend/schema/paths/users/userID/permissions/put.json
new file mode 100644
index 0000000000..2dcd2aed71
--- /dev/null
+++ b/backend/schema/paths/users/userID/permissions/put.json
@@ -0,0 +1,51 @@
+{
+ "operationId": "updateUserPermissions",
+ "summary": "Update a User's Permissions",
+ "tags": ["Users"],
+ "security": [
+ {
+ "BearerAuth": ["users"]
+ }
+ ],
+ "parameters": [
+ {
+ "in": "path",
+ "name": "userID",
+ "schema": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "required": true,
+ "description": "User ID",
+ "example": 2
+ }
+ ],
+ "requestBody": {
+ "description": "Permissions Payload",
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "../../../../components/permission-object.json"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": true
+ }
+ },
+ "schema": {
+ "type": "boolean"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/paths/users/userID/put.json b/backend/schema/paths/users/userID/put.json
new file mode 100644
index 0000000000..60a6cd132d
--- /dev/null
+++ b/backend/schema/paths/users/userID/put.json
@@ -0,0 +1,88 @@
+{
+ "operationId": "updateUser",
+ "summary": "Update a User",
+ "tags": ["Users"],
+ "security": [
+ {
+ "BearerAuth": ["users"]
+ }
+ ],
+ "parameters": [
+ {
+ "in": "path",
+ "name": "userID",
+ "schema": {
+ "oneOf": [
+ {
+ "type": "string",
+ "pattern": "^me$"
+ },
+ {
+ "type": "integer",
+ "minimum": 1
+ }
+ ]
+ },
+ "required": true,
+ "description": "User ID or 'me' for yourself",
+ "example": 2
+ }
+ ],
+ "requestBody": {
+ "description": "User Payload",
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "additionalProperties": false,
+ "minProperties": 1,
+ "properties": {
+ "name": {
+ "$ref": "../../../components/user-object.json#/properties/name"
+ },
+ "nickname": {
+ "$ref": "../../../components/user-object.json#/properties/nickname"
+ },
+ "email": {
+ "$ref": "../../../components/user-object.json#/properties/email"
+ },
+ "roles": {
+ "$ref": "../../../components/user-object.json#/properties/roles"
+ },
+ "is_disabled": {
+ "$ref": "../../../components/user-object.json#/properties/is_disabled"
+ }
+ }
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "default": {
+ "value": {
+ "id": 2,
+ "created_on": "2020-01-30T09:36:08.000Z",
+ "modified_on": "2020-01-30T09:41:04.000Z",
+ "is_disabled": false,
+ "email": "jc@jc21.com",
+ "name": "Jamie Curnow",
+ "nickname": "James",
+ "avatar": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm",
+ "roles": ["admin"]
+ }
+ }
+ },
+ "schema": {
+ "$ref": "../../../components/user-object.json"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/backend/schema/swagger.json b/backend/schema/swagger.json
new file mode 100644
index 0000000000..5a0142bff7
--- /dev/null
+++ b/backend/schema/swagger.json
@@ -0,0 +1,265 @@
+{
+ "openapi": "3.1.0",
+ "info": {
+ "title": "Nginx Proxy Manager API",
+ "version": "2.x.x"
+ },
+ "servers": [
+ {
+ "url": "http://127.0.0.1:81/api"
+ }
+ ],
+ "paths": {
+ "/": {
+ "get": {
+ "$ref": "./paths/get.json"
+ }
+ },
+ "/audit-log": {
+ "get": {
+ "$ref": "./paths/audit-log/get.json"
+ }
+ },
+ "/nginx/access-lists": {
+ "get": {
+ "$ref": "./paths/nginx/access-lists/get.json"
+ },
+ "post": {
+ "$ref": "./paths/nginx/access-lists/post.json"
+ }
+ },
+ "/nginx/access-lists/{listID}": {
+ "get": {
+ "$ref": "./paths/nginx/access-lists/listID/get.json"
+ },
+ "put": {
+ "$ref": "./paths/nginx/access-lists/listID/put.json"
+ },
+ "delete": {
+ "$ref": "./paths/nginx/access-lists/listID/delete.json"
+ }
+ },
+ "/nginx/certificates": {
+ "get": {
+ "$ref": "./paths/nginx/certificates/get.json"
+ },
+ "post": {
+ "$ref": "./paths/nginx/certificates/post.json"
+ }
+ },
+ "/nginx/certificates/validate": {
+ "post": {
+ "$ref": "./paths/nginx/certificates/validate/post.json"
+ }
+ },
+ "/nginx/certificates/test-http": {
+ "get": {
+ "$ref": "./paths/nginx/certificates/test-http/get.json"
+ }
+ },
+ "/nginx/certificates/{certID}": {
+ "get": {
+ "$ref": "./paths/nginx/certificates/certID/get.json"
+ },
+ "delete": {
+ "$ref": "./paths/nginx/certificates/certID/delete.json"
+ }
+ },
+ "/nginx/certificates/{certID}/download": {
+ "get": {
+ "$ref": "./paths/nginx/certificates/certID/download/get.json"
+ }
+ },
+ "/nginx/certificates/{certID}/renew": {
+ "post": {
+ "$ref": "./paths/nginx/certificates/certID/renew/post.json"
+ }
+ },
+ "/nginx/certificates/{certID}/upload": {
+ "post": {
+ "$ref": "./paths/nginx/certificates/certID/upload/post.json"
+ }
+ },
+ "/nginx/proxy-hosts": {
+ "get": {
+ "$ref": "./paths/nginx/proxy-hosts/get.json"
+ },
+ "post": {
+ "$ref": "./paths/nginx/proxy-hosts/post.json"
+ }
+ },
+ "/nginx/proxy-hosts/{hostID}": {
+ "get": {
+ "$ref": "./paths/nginx/proxy-hosts/hostID/get.json"
+ },
+ "put": {
+ "$ref": "./paths/nginx/proxy-hosts/hostID/put.json"
+ },
+ "delete": {
+ "$ref": "./paths/nginx/proxy-hosts/hostID/delete.json"
+ }
+ },
+ "/nginx/proxy-hosts/{hostID}/enable": {
+ "post": {
+ "$ref": "./paths/nginx/proxy-hosts/hostID/enable/post.json"
+ }
+ },
+ "/nginx/proxy-hosts/{hostID}/disable": {
+ "post": {
+ "$ref": "./paths/nginx/proxy-hosts/hostID/disable/post.json"
+ }
+ },
+ "/nginx/redirection-hosts": {
+ "get": {
+ "$ref": "./paths/nginx/redirection-hosts/get.json"
+ },
+ "post": {
+ "$ref": "./paths/nginx/redirection-hosts/post.json"
+ }
+ },
+ "/nginx/redirection-hosts/{hostID}": {
+ "get": {
+ "$ref": "./paths/nginx/redirection-hosts/hostID/get.json"
+ },
+ "put": {
+ "$ref": "./paths/nginx/redirection-hosts/hostID/put.json"
+ },
+ "delete": {
+ "$ref": "./paths/nginx/redirection-hosts/hostID/delete.json"
+ }
+ },
+ "/nginx/redirection-hosts/{hostID}/enable": {
+ "post": {
+ "$ref": "./paths/nginx/redirection-hosts/hostID/enable/post.json"
+ }
+ },
+ "/nginx/redirection-hosts/{hostID}/disable": {
+ "post": {
+ "$ref": "./paths/nginx/redirection-hosts/hostID/disable/post.json"
+ }
+ },
+ "/nginx/dead-hosts": {
+ "get": {
+ "$ref": "./paths/nginx/dead-hosts/get.json"
+ },
+ "post": {
+ "$ref": "./paths/nginx/dead-hosts/post.json"
+ }
+ },
+ "/nginx/dead-hosts/{hostID}": {
+ "get": {
+ "$ref": "./paths/nginx/dead-hosts/hostID/get.json"
+ },
+ "put": {
+ "$ref": "./paths/nginx/dead-hosts/hostID/put.json"
+ },
+ "delete": {
+ "$ref": "./paths/nginx/dead-hosts/hostID/delete.json"
+ }
+ },
+ "/nginx/dead-hosts/{hostID}/enable": {
+ "post": {
+ "$ref": "./paths/nginx/dead-hosts/hostID/enable/post.json"
+ }
+ },
+ "/nginx/dead-hosts/{hostID}/disable": {
+ "post": {
+ "$ref": "./paths/nginx/dead-hosts/hostID/disable/post.json"
+ }
+ },
+ "/nginx/streams": {
+ "get": {
+ "$ref": "./paths/nginx/streams/get.json"
+ },
+ "post": {
+ "$ref": "./paths/nginx/streams/post.json"
+ }
+ },
+ "/nginx/streams/{streamID}": {
+ "get": {
+ "$ref": "./paths/nginx/streams/streamID/get.json"
+ },
+ "put": {
+ "$ref": "./paths/nginx/streams/streamID/put.json"
+ },
+ "delete": {
+ "$ref": "./paths/nginx/streams/streamID/delete.json"
+ }
+ },
+ "/nginx/streams/{streamID}/enable": {
+ "post": {
+ "$ref": "./paths/nginx/streams/streamID/enable/post.json"
+ }
+ },
+ "/nginx/streams/{streamID}/disable": {
+ "post": {
+ "$ref": "./paths/nginx/streams/streamID/disable/post.json"
+ }
+ },
+ "/reports/hosts": {
+ "get": {
+ "$ref": "./paths/reports/hosts/get.json"
+ }
+ },
+ "/schema": {
+ "get": {
+ "$ref": "./paths/schema/get.json"
+ }
+ },
+ "/settings": {
+ "get": {
+ "$ref": "./paths/settings/get.json"
+ }
+ },
+ "/settings/{settingID}": {
+ "get": {
+ "$ref": "./paths/settings/settingID/get.json"
+ },
+ "put": {
+ "$ref": "./paths/settings/settingID/put.json"
+ }
+ },
+ "/tokens": {
+ "get": {
+ "$ref": "./paths/tokens/get.json"
+ },
+ "post": {
+ "$ref": "./paths/tokens/post.json"
+ }
+ },
+ "/users": {
+ "get": {
+ "$ref": "./paths/users/get.json"
+ },
+ "post": {
+ "$ref": "./paths/users/post.json"
+ }
+ },
+ "/users/{userID}": {
+ "get": {
+ "$ref": "./paths/users/userID/get.json"
+ },
+ "put": {
+ "$ref": "./paths/users/userID/put.json"
+ },
+ "delete": {
+ "$ref": "./paths/users/userID/delete.json"
+ }
+ },
+ "/users/{userID}/auth": {
+ "put": {
+ "$ref": "./paths/users/userID/auth/put.json"
+ }
+ },
+ "/users/{userID}/permissions": {
+ "put": {
+ "$ref": "./paths/users/userID/permissions/put.json"
+ }
+ },
+ "/users/{userID}/login": {
+ "post": {
+ "$ref": "./paths/users/userID/login/post.json"
+ }
+ }
+ }
+}
diff --git a/backend/templates/_location.conf b/backend/templates/_location.conf
index b75632278b..fcc7d12104 100644
--- a/backend/templates/_location.conf
+++ b/backend/templates/_location.conf
@@ -6,7 +6,12 @@
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Real-IP $remote_addr;
- proxy_pass {{ forward_scheme }}://{{ forward_host }}:{{ forward_port }}{{ forward_path }};
+
+ set $proxy_forward_scheme {{ forward_scheme }};
+ set $proxy_server "{{ forward_host }}";
+ set $proxy_port {{ forward_port }};
+
+ proxy_pass $proxy_forward_scheme://$proxy_server:$proxy_port{{ forward_path }};
{% include "_access.conf" %}
{% include "_assets.conf" %}
diff --git a/backend/validate-schema.js b/backend/validate-schema.js
new file mode 100644
index 0000000000..71a05c8184
--- /dev/null
+++ b/backend/validate-schema.js
@@ -0,0 +1,16 @@
+const SwaggerParser = require('@apidevtools/swagger-parser');
+const chalk = require('chalk');
+const schema = require('./schema');
+const log = console.log;
+
+schema.getCompiledSchema().then(async (swaggerJSON) => {
+ try {
+ const api = await SwaggerParser.validate(swaggerJSON);
+ console.log('API name: %s, Version: %s', api.info.title, api.info.version);
+ log(chalk.green('❯ Schema is valid'));
+ } catch (e) {
+ console.error(e);
+ log(chalk.red('❯', e.message), '\n');
+ process.exit(1);
+ }
+});
diff --git a/backend/yarn.lock b/backend/yarn.lock
index af20954969..5441a511c0 100644
--- a/backend/yarn.lock
+++ b/backend/yarn.lock
@@ -2,15 +2,47 @@
# yarn lockfile v1
-"@apidevtools/json-schema-ref-parser@8.0.0":
- version "8.0.0"
- resolved "https://registry.yarnpkg.com/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-8.0.0.tgz#9eb749499b3f8d919e90bb141e4b6f67aee4692d"
- integrity sha512-n4YBtwQhdpLto1BaUCyAeflizmIbaloGShsPyRtFf5qdFJxfssj+GgLavczgKJFa3Bq+3St2CKcpRJdjtB4EBw==
+"@apidevtools/json-schema-ref-parser@9.0.6":
+ version "9.0.6"
+ resolved "https://registry.yarnpkg.com/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.0.6.tgz#5d9000a3ac1fd25404da886da6b266adcd99cf1c"
+ integrity sha512-M3YgsLjI0lZxvrpeGVk9Ap032W6TPQkH6pRAZz81Ac3WUNF79VQooAFnp8umjvVzUmD93NkogxEwbSce7qMsUg==
dependencies:
- "@jsdevtools/ono" "^7.1.0"
+ "@jsdevtools/ono" "^7.1.3"
call-me-maybe "^1.0.1"
js-yaml "^3.13.1"
+"@apidevtools/json-schema-ref-parser@^11.7.0":
+ version "11.7.0"
+ resolved "https://registry.yarnpkg.com/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-11.7.0.tgz#228d72018a0e7cbee744b677eaa01a8968f302d9"
+ integrity sha512-pRrmXMCwnmrkS3MLgAIW5dXRzeTv6GLjkjb4HmxNnvAKXN1Nfzp4KmGADBQvlVUcqi+a5D+hfGDLLnd5NnYxog==
+ dependencies:
+ "@jsdevtools/ono" "^7.1.3"
+ "@types/json-schema" "^7.0.15"
+ js-yaml "^4.1.0"
+
+"@apidevtools/openapi-schemas@^2.1.0":
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz#9fa08017fb59d80538812f03fc7cac5992caaa17"
+ integrity sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==
+
+"@apidevtools/swagger-methods@^3.0.2":
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz#b789a362e055b0340d04712eafe7027ddc1ac267"
+ integrity sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==
+
+"@apidevtools/swagger-parser@^10.1.0":
+ version "10.1.0"
+ resolved "https://registry.yarnpkg.com/@apidevtools/swagger-parser/-/swagger-parser-10.1.0.tgz#a987d71e5be61feb623203be0c96e5985b192ab6"
+ integrity sha512-9Kt7EuS/7WbMAUv2gSziqjvxwDbFSg3Xeyfuj5laUODX8o/k/CpsAKiQ8W7/R88eXFTMbJYg6+7uAmOWNKmwnw==
+ dependencies:
+ "@apidevtools/json-schema-ref-parser" "9.0.6"
+ "@apidevtools/openapi-schemas" "^2.1.0"
+ "@apidevtools/swagger-methods" "^3.0.2"
+ "@jsdevtools/ono" "^7.1.3"
+ ajv "^8.6.3"
+ ajv-draft-04 "^1.0.0"
+ call-me-maybe "^1.0.1"
+
"@eslint-community/eslint-utils@^4.2.0":
version "4.3.0"
resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.3.0.tgz#a556790523a351b4e47e9d385f47265eaaf9780a"
@@ -67,7 +99,7 @@
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
-"@jsdevtools/ono@^7.1.0":
+"@jsdevtools/ono@^7.1.3":
version "7.1.3"
resolved "https://registry.yarnpkg.com/@jsdevtools/ono/-/ono-7.1.3.tgz#9df03bbd7c696a5c58885c34aa06da41c8543796"
integrity sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==
@@ -146,6 +178,11 @@
resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==
+"@types/json-schema@^7.0.15":
+ version "7.0.15"
+ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
+ integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==
+
abbrev@1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
@@ -193,7 +230,12 @@ aggregate-error@^3.0.0:
clean-stack "^2.0.0"
indent-string "^4.0.0"
-ajv@^6.10.0, ajv@^6.12.0, ajv@^6.12.4:
+ajv-draft-04@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz#3b64761b268ba0b9e668f0b41ba53fce0ad77fc8"
+ integrity sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==
+
+ajv@^6.10.0, ajv@^6.12.4:
version "6.12.6"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
@@ -203,6 +245,16 @@ ajv@^6.10.0, ajv@^6.12.0, ajv@^6.12.4:
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"
+ajv@^8.17.1, ajv@^8.6.3:
+ version "8.17.1"
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6"
+ integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==
+ dependencies:
+ fast-deep-equal "^3.1.3"
+ fast-uri "^3.0.1"
+ json-schema-traverse "^1.0.0"
+ require-from-string "^2.0.2"
+
ajv@^8.6.2:
version "8.12.0"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1"
@@ -360,6 +412,11 @@ async@^3.2.0:
resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c"
integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==
+aws-ssl-profiles@^1.1.1:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz#157dd77e9f19b1d123678e93f120e6f193022641"
+ integrity sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==
+
balanced-match@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
@@ -383,11 +440,6 @@ bcrypt@^5.0.0:
node-addon-api "^3.0.0"
node-pre-gyp "0.15.0"
-bignumber.js@9.0.0:
- version "9.0.0"
- resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.0.tgz#805880f84a329b5eac6e7cb6f8274b6d82bdf075"
- integrity sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==
-
binary-extensions@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.1.0.tgz#30fa40c9e7fe07dbc895678cd287024dea241dd9"
@@ -407,10 +459,10 @@ blueimp-md5@^2.16.0:
resolved "https://registry.yarnpkg.com/blueimp-md5/-/blueimp-md5-2.17.0.tgz#f4fcac088b115f7b4045f19f5da59e9d01b1bb96"
integrity sha512-x5PKJHY5rHQYaADj6NwPUR2QRCUVSggPzrUKkeENpj871o9l9IefJbO2jkT5UvYykeOK9dx0VmkIo6dZ+vThYw==
-body-parser@1.20.2, body-parser@^1.19.0:
- version "1.20.2"
- resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd"
- integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==
+body-parser@1.20.3, body-parser@^1.20.3:
+ version "1.20.3"
+ resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6"
+ integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==
dependencies:
bytes "3.1.2"
content-type "~1.0.5"
@@ -420,7 +472,7 @@ body-parser@1.20.2, body-parser@^1.19.0:
http-errors "2.0.0"
iconv-lite "0.4.24"
on-finished "2.4.1"
- qs "6.11.0"
+ qs "6.13.0"
raw-body "2.5.2"
type-is "~1.6.18"
unpipe "1.0.0"
@@ -552,6 +604,14 @@ camelcase@^5.0.0, camelcase@^5.3.1:
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
+chalk@4.1.2, chalk@^4.0.0:
+ version "4.1.2"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
+ integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
+ dependencies:
+ ansi-styles "^4.1.0"
+ supports-color "^7.1.0"
+
chalk@^2.3.2:
version "2.4.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
@@ -569,14 +629,6 @@ chalk@^3.0.0:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
-chalk@^4.0.0:
- version "4.1.2"
- resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
- integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
- dependencies:
- ansi-styles "^4.1.0"
- supports-color "^7.1.0"
-
chokidar@^3.2.2:
version "3.4.1"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.1.tgz#e905bdecf10eaa0a0b1db0c664481cc4cbc22ba1"
@@ -858,6 +910,11 @@ delegates@^1.0.0:
resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=
+denque@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/denque/-/denque-2.1.0.tgz#e93e1a6569fb5e66f16a3c2a2964617d349d6ab1"
+ integrity sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==
+
depd@2.0.0, depd@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
@@ -936,6 +993,11 @@ encodeurl@~1.0.2:
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
+encodeurl@~2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58"
+ integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==
+
encoding@^0.1.12:
version "0.1.13"
resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9"
@@ -1133,37 +1195,37 @@ express-fileupload@^1.1.9:
dependencies:
busboy "^0.3.1"
-express@^4.19.2:
- version "4.19.2"
- resolved "https://registry.yarnpkg.com/express/-/express-4.19.2.tgz#e25437827a3aa7f2a827bc8171bbbb664a356465"
- integrity sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==
+express@^4.20.0:
+ version "4.20.0"
+ resolved "https://registry.yarnpkg.com/express/-/express-4.20.0.tgz#f1d08e591fcec770c07be4767af8eb9bcfd67c48"
+ integrity sha512-pLdae7I6QqShF5PnNTCVn4hI91Dx0Grkn2+IAsMTgMIKuQVte2dN9PeGSSAME2FR8anOhVA62QDIUaWVfEXVLw==
dependencies:
accepts "~1.3.8"
array-flatten "1.1.1"
- body-parser "1.20.2"
+ body-parser "1.20.3"
content-disposition "0.5.4"
content-type "~1.0.4"
cookie "0.6.0"
cookie-signature "1.0.6"
debug "2.6.9"
depd "2.0.0"
- encodeurl "~1.0.2"
+ encodeurl "~2.0.0"
escape-html "~1.0.3"
etag "~1.8.1"
finalhandler "1.2.0"
fresh "0.5.2"
http-errors "2.0.0"
- merge-descriptors "1.0.1"
+ merge-descriptors "1.0.3"
methods "~1.1.2"
on-finished "2.4.1"
parseurl "~1.3.3"
- path-to-regexp "0.1.7"
+ path-to-regexp "0.1.10"
proxy-addr "~2.0.7"
qs "6.11.0"
range-parser "~1.2.1"
safe-buffer "5.2.1"
- send "0.18.0"
- serve-static "1.15.0"
+ send "0.19.0"
+ serve-static "1.16.0"
setprototypeof "1.2.0"
statuses "2.0.1"
type-is "~1.6.18"
@@ -1185,6 +1247,11 @@ fast-levenshtein@^2.0.6:
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==
+fast-uri@^3.0.1:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.2.tgz#d78b298cf70fd3b752fd951175a3da6a7b48f024"
+ integrity sha512-GR6f0hD7XXyNJa25Tb9BuIdN0tdr+0BMi6/CJPH3wJO1JjNG3n/VsSw38AwRdKZABm8lGbPfakLRkYzx2V9row==
+
fastq@^1.6.0:
version "1.15.0"
resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a"
@@ -1354,6 +1421,13 @@ gauge@~2.7.3:
strip-ansi "^3.0.1"
wide-align "^1.1.0"
+generate-function@^2.3.1:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.3.1.tgz#f069617690c10c868e73b8465746764f97c3479f"
+ integrity sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==
+ dependencies:
+ is-property "^1.0.2"
+
get-caller-file@^2.0.1:
version "2.0.5"
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
@@ -1598,7 +1672,7 @@ iconv-lite@0.4.24, iconv-lite@^0.4.4:
dependencies:
safer-buffer ">= 2.1.2 < 3"
-iconv-lite@^0.6.2:
+iconv-lite@^0.6.2, iconv-lite@^0.6.3:
version "0.6.3"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==
@@ -1801,6 +1875,11 @@ is-path-inside@^3.0.3:
resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283"
integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==
+is-property@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84"
+ integrity sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==
+
is-stream@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3"
@@ -1856,13 +1935,6 @@ json-parse-better-errors@^1.0.1:
resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==
-json-schema-ref-parser@^8.0.0:
- version "8.0.0"
- resolved "https://registry.yarnpkg.com/json-schema-ref-parser/-/json-schema-ref-parser-8.0.0.tgz#7c758fac2cf822c05e837abd0a13f8fa2c15ffd4"
- integrity sha512-2P4icmNkZLrBr6oa5gSZaDSol/oaBHYkoP/8dsw63E54NnHGRhhiFuy9yFoxPuSm+uHKmeGxAAWMDF16SCHhcQ==
- dependencies:
- "@apidevtools/json-schema-ref-parser" "8.0.0"
-
json-schema-traverse@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
@@ -2028,6 +2100,11 @@ lodash@^4.17.21:
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
+long@^5.2.1:
+ version "5.2.3"
+ resolved "https://registry.yarnpkg.com/long/-/long-5.2.3.tgz#a3ba97f3877cf1d778eccbcb048525ebb77499e1"
+ integrity sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==
+
lowercase-keys@^1.0.0, lowercase-keys@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f"
@@ -2045,6 +2122,16 @@ lru-cache@^6.0.0:
dependencies:
yallist "^4.0.0"
+lru-cache@^7.14.1:
+ version "7.18.3"
+ resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89"
+ integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==
+
+lru-cache@^8.0.0:
+ version "8.0.5"
+ resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-8.0.5.tgz#983fe337f3e176667f8e567cfcce7cb064ea214e"
+ integrity sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA==
+
make-dir@^3.0.0, make-dir@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
@@ -2079,10 +2166,10 @@ media-typer@0.3.0:
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
-merge-descriptors@1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
- integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=
+merge-descriptors@1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5"
+ integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==
methods@~1.1.2:
version "1.1.2"
@@ -2248,15 +2335,27 @@ ms@2.1.3, ms@^2.0.0:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
-mysql@^2.18.1:
- version "2.18.1"
- resolved "https://registry.yarnpkg.com/mysql/-/mysql-2.18.1.tgz#2254143855c5a8c73825e4522baf2ea021766717"
- integrity sha512-Bca+gk2YWmqp2Uf6k5NFEurwY/0td0cpebAucFpY/3jhrwrVGuxU2uQFCHjU19SJfje0yQvi+rVWdq78hR5lig==
+mysql2@^3.11.1:
+ version "3.11.1"
+ resolved "https://registry.yarnpkg.com/mysql2/-/mysql2-3.11.1.tgz#edfb856e2176fcf43d2cc066dd4959e9fc76ea85"
+ integrity sha512-Oc8Zffd0gpIJnJ/NOMp6IiiJJDdWc7nmWpS+UE3K9feTpYia8XkbgL6EaOJYz52f6+2pAoC0eAQqUzal4lnNGQ==
+ dependencies:
+ aws-ssl-profiles "^1.1.1"
+ denque "^2.1.0"
+ generate-function "^2.3.1"
+ iconv-lite "^0.6.3"
+ long "^5.2.1"
+ lru-cache "^8.0.0"
+ named-placeholders "^1.1.3"
+ seq-queue "^0.0.5"
+ sqlstring "^2.3.2"
+
+named-placeholders@^1.1.3:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/named-placeholders/-/named-placeholders-1.1.3.tgz#df595799a36654da55dda6152ba7a137ad1d9351"
+ integrity sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==
dependencies:
- bignumber.js "9.0.0"
- readable-stream "2.3.7"
- safe-buffer "5.1.2"
- sqlstring "2.3.1"
+ lru-cache "^7.14.1"
natural-compare@^1.4.0:
version "1.4.0"
@@ -2623,10 +2722,10 @@ path-parse@^1.0.7:
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
-path-to-regexp@0.1.7:
- version "0.1.7"
- resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
- integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=
+path-to-regexp@0.1.10:
+ version "0.1.10"
+ resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.10.tgz#67e9108c5c0551b9e5326064387de4763c4d5f8b"
+ integrity sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==
path@^0.12.7:
version "0.12.7"
@@ -2742,6 +2841,13 @@ qs@6.11.0:
dependencies:
side-channel "^1.0.4"
+qs@6.13.0:
+ version "6.13.0"
+ resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906"
+ integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==
+ dependencies:
+ side-channel "^1.0.6"
+
querystring@0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
@@ -2777,7 +2883,7 @@ rc@^1.2.7, rc@^1.2.8:
minimist "^1.2.0"
strip-json-comments "~2.0.1"
-readable-stream@2.3.7, readable-stream@^2.0.0, readable-stream@^2.0.5, readable-stream@^2.0.6:
+readable-stream@^2.0.0, readable-stream@^2.0.5, readable-stream@^2.0.6:
version "2.3.7"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
@@ -2969,10 +3075,34 @@ send@0.18.0:
range-parser "~1.2.1"
statuses "2.0.1"
-serve-static@1.15.0:
- version "1.15.0"
- resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540"
- integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==
+send@0.19.0:
+ version "0.19.0"
+ resolved "https://registry.yarnpkg.com/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8"
+ integrity sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==
+ dependencies:
+ debug "2.6.9"
+ depd "2.0.0"
+ destroy "1.2.0"
+ encodeurl "~1.0.2"
+ escape-html "~1.0.3"
+ etag "~1.8.1"
+ fresh "0.5.2"
+ http-errors "2.0.0"
+ mime "1.6.0"
+ ms "2.1.3"
+ on-finished "2.4.1"
+ range-parser "~1.2.1"
+ statuses "2.0.1"
+
+seq-queue@^0.0.5:
+ version "0.0.5"
+ resolved "https://registry.yarnpkg.com/seq-queue/-/seq-queue-0.0.5.tgz#d56812e1c017a6e4e7c3e3a37a1da6d78dd3c93e"
+ integrity sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==
+
+serve-static@1.16.0:
+ version "1.16.0"
+ resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.16.0.tgz#2bf4ed49f8af311b519c46f272bf6ac3baf38a92"
+ integrity sha512-pDLK8zwl2eKaYrs8mrPZBJua4hMplRWJ1tIFksVC3FtBEBnl8dxgeHtsaMS8DhS9i4fLObaon6ABoc4/hQGdPA==
dependencies:
encodeurl "~1.0.2"
escape-html "~1.0.3"
@@ -3013,7 +3143,7 @@ shebang-regex@^3.0.0:
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
-side-channel@^1.0.4:
+side-channel@^1.0.4, side-channel@^1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2"
integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==
@@ -3080,10 +3210,10 @@ sqlite3@5.1.6:
optionalDependencies:
node-gyp "8.x"
-sqlstring@2.3.1:
- version "2.3.1"
- resolved "https://registry.yarnpkg.com/sqlstring/-/sqlstring-2.3.1.tgz#475393ff9e91479aea62dcaf0ca3d14983a7fb40"
- integrity sha1-R1OT/56RR5rqYtyvDKPRSYOn+0A=
+sqlstring@^2.3.2:
+ version "2.3.3"
+ resolved "https://registry.yarnpkg.com/sqlstring/-/sqlstring-2.3.3.tgz#2ddc21f03bce2c387ed60680e739922c65751d0c"
+ integrity sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==
ssri@^8.0.0, ssri@^8.0.1:
version "8.0.1"
diff --git a/docker/Dockerfile b/docker/Dockerfile
index 799ee2a63a..0603e2ded8 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -3,6 +3,8 @@
# This file assumes that the frontend has been built using ./scripts/frontend-build
+FROM nginxproxymanager/testca AS testca
+FROM letsencrypt/pebble AS pebbleca
FROM nginxproxymanager/nginx-full:certbot-node
ARG TARGETPLATFORM
@@ -45,6 +47,8 @@ RUN yarn install \
# add late to limit cache-busting by modifications
COPY docker/rootfs /
+COPY --from=pebbleca /test/certs/pebble.minica.pem /etc/ssl/certs/pebble.minica.pem
+COPY --from=testca /home/step/certs/root_ca.crt /etc/ssl/certs/NginxProxyManager.crt
# Remove frontend service not required for prod, dev nginx config as well
RUN rm -rf /etc/s6-overlay/s6-rc.d/user/contents.d/frontend /etc/nginx/conf.d/dev.conf \
diff --git a/docker/dev/Dockerfile b/docker/dev/Dockerfile
index 3c21849cdb..bb4ac6d441 100644
--- a/docker/dev/Dockerfile
+++ b/docker/dev/Dockerfile
@@ -1,7 +1,10 @@
+FROM nginxproxymanager/testca AS testca
+FROM letsencrypt/pebble AS pebbleca
FROM nginxproxymanager/nginx-full:certbot-node
LABEL maintainer="Jamie Curnow