Compare commits

...

469 Commits

Author SHA1 Message Date
Jamie Curnow
5e66d677f1
Adds test for dashboard endpoints
All checks were successful
Close stale issues and PRs / stale (push) Successful in 18s
2025-03-24 14:34:45 +10:00
jc21
79d28f03d0
Merge pull request #4346 from Sander0542/feature/security-schemes-component
All checks were successful
Close stale issues and PRs / stale (push) Successful in 4s
API Schema Improvements
2025-02-07 12:39:49 +10:00
Jamie Curnow
c4df89df1f
Fix dashboard loading loop and freezing the page 2025-02-06 13:38:47 +10:00
jc21
34c703f8b4
Merge branch 'master' into develop 2025-02-06 08:52:55 +10:00
Jamie Curnow
0a05d8f0ad
Bump version 2025-02-06 08:39:03 +10:00
jc21
0a9141fad5
Merge pull request #4208 from badkeyy/feature/add-zone-edit-certbot-plugin
Add ZoneEdit certbot plugin
2025-02-06 08:33:11 +10:00
jc21
42836774b7
Merge branch 'develop' into feature/add-zone-edit-certbot-plugin 2025-02-06 08:33:01 +10:00
jc21
2a07544f58
Merge pull request #4235 from FabianK3/update-domainoffensive-certbot-plugin
Update DomainOffensive certbot plugin
2025-02-06 08:30:09 +10:00
jc21
dc9d884743
Merge pull request #4292 from icaksh/patch-1
feat: change htpasswd to openssl
2025-02-06 08:29:15 +10:00
jc21
0d5d2b1b7c
Merge pull request #4283 from badkeyy/feature/show-active-host-in-cert-list
SSL Certificates: Show if cert is in use on host
2025-02-06 07:43:12 +10:00
Sander Jochems
df48b835c4
Update order to match others 2025-02-05 22:20:21 +01:00
Sander Jochems
8a1557154a
Add certificate fields to boolFields 2025-02-05 22:15:12 +01:00
Sander Jochems
a6af5ec2c7
Remove certificate as required from proxy host 2025-02-05 18:18:50 +01:00
Sander Jochems
14d7c35fd7
Fix whitespaces 2025-02-05 17:31:09 +01:00
Sander Jochems
cfcf78aaee
Set bearer auth security component 2025-02-05 17:29:40 +01:00
jc21
3a01b2c84f
Merge pull request #4334 from nwagenmakers/mijn-host-patch
All checks were successful
Close stale issues and PRs / stale (push) Successful in 4s
Update certbot-dns-plugins.json (mijn-host)
2025-02-05 20:36:06 +10:00
jc21
e1c84a5c10
Merge pull request #4338 from Sander0542/fix/token-expires-type
Fix type for token.expires
2025-02-05 20:35:33 +10:00
jc21
c56c95a59a
Merge pull request #4344 from NginxProxyManager/stream-ssl
SSL for Streams - 2025
2025-02-05 18:22:51 +10:00
Jamie Curnow
6a60627833
Cypress test for Streams
and updated cypress + packages
2025-02-05 16:02:17 +10:00
Jamie Curnow
b4793d3c16
Adds testssl.sh and mkcert to cypress stack 2025-02-05 08:10:11 +10:00
Jamie Curnow
68a7803513
Fix api schema after merging latest changes 2025-02-04 17:55:28 +10:00
jbowring
2657af97cf
Fix stream update not persisting 2025-02-04 17:14:07 +10:00
jbowring
4452f014b9
Fix whitespace in nginx stream config 2025-02-04 17:14:07 +10:00
jbowring
cd80cc8e4d
Add certificate to streams database model 2025-02-04 17:14:04 +10:00
jbowring
ee4250d770
Add SSL column to streams table UI 2025-02-04 17:12:05 +10:00
jbowring
3dbc70faa6
Add SSL tab to stream UI 2025-02-04 17:12:04 +10:00
jbowring
3091c21cae
Add SSL certificate to TCP streams if certificate in database 2025-02-04 17:12:04 +10:00
Sander Jochems
57cd2a1919
Fix type for token.expires 2025-02-03 21:47:41 +01:00
nwagenmakers
ad5936c530
Update certbot-dns-plugins.json (mijn-host)
Updated credentials hint/text in mijn-host plugin entry
2025-02-01 13:10:53 +01:00
jc21
498109addb
Merge pull request #4310 from NginxProxyManager/dependabot/npm_and_yarn/docs/vite-5.4.14
All checks were successful
Close stale issues and PRs / stale (push) Successful in 3s
Bump vite from 5.4.8 to 5.4.14 in /docs
2025-01-28 18:08:46 +10:00
jc21
3f3aacd7ec
Merge pull request #4274 from Dim145/develop
[Postgres] fix error in access_list get
2025-01-28 14:03:07 +10:00
dependabot[bot]
bb4ecf812d
Bump vite from 5.4.8 to 5.4.14 in /docs
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.4.8 to 5.4.14.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v5.4.14/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v5.4.14/packages/vite)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-22 07:09:04 +00:00
Julian Gassner
c05f9695d0
Merge branch 'develop' into feature/add-zone-edit-certbot-plugin 2025-01-15 15:37:53 +01:00
Julian Gassner
6343b398f0 Add --no-deps 2025-01-15 14:36:38 +00:00
icaksh
59362b7477 feat: change htpasswd to openssl 2025-01-12 19:16:38 +07:00
Julian Gassner
aedaaa18e0 Fix whitespace 2025-01-10 05:20:28 +01:00
Julian Gassner
080bd0b749 Added status of certificates to the certificate list and show on which domain names the certificates are in use 2025-01-10 05:15:22 +01:00
Jamie Curnow
9687e9e450
Use previous version of powerdns image, newer version is broken
All checks were successful
Close stale issues and PRs / stale (push) Successful in 3s
2025-01-07 10:30:08 +10:00
Jamie Curnow
5a234bb88c
Fix incorrect test folder in ci results 2025-01-07 08:13:04 +10:00
jc21
4de4b65036
Merge pull request #4252 from GergelyGombai/develop
Add Gcore DNS Provider
2025-01-07 07:54:44 +10:00
dim145
f1c97c7c36 fix: add missing group_by clause for access_list get 2025-01-03 00:39:29 +01:00
jc21
b4f49969d6
Merge pull request #4261 from NginxProxyManager/develop
v2.12.2
2024-12-29 14:40:05 +10:00
jc21
ec12d8f9bf
Merge pull request #4148 from Medan-rfz/develop
Added certbot plugin for Beget DNS service
2024-12-29 14:00:51 +10:00
jc21
e50e3def9d
Merge pull request #4169 from andrew-codechimp/bump-porkbun
Bump certbot-dns-porkbun
2024-12-29 14:00:18 +10:00
jc21
6415f284f9
Merge pull request #4256 from bigcat26/develop
upgrade certbot-dns-aliyun plugin from 0.38.1 to 2.0.0
2024-12-29 13:52:03 +10:00
Chris Xiong
98e5997f0a
upgrade certbot-dns-aliyun plugin from 0.38.1 to 2.0.0 2024-12-26 09:51:28 +08:00
Jamie Curnow
fc30a92bd4
Open port for authentik in dev
All checks were successful
Close stale issues and PRs / stale (push) Successful in 4s
2024-12-24 18:19:52 +10:00
Jamie Curnow
e2011ee45c
Bump version 2024-12-24 17:51:25 +10:00
jc21
1406e75c2c
Merge pull request #4254 from NginxProxyManager/postgres
Postgres
2024-12-24 17:24:05 +10:00
Jamie Curnow
ca3ee98c68
Postgres Support
- Combines #4086 and #4087 PRs
- Adds authentik in CI stack
2024-12-24 16:48:48 +10:00
jc21
f90d839ebe
Merge pull request #4246 from JanzenJohn/develop
Remove infinite requests loop
2024-12-24 08:16:48 +10:00
jc21
be5278f31e
Merge pull request #4247 from miguelangel-nubla/patch-1
Add custom configuration to 404 hosts
2024-12-24 08:15:55 +10:00
ComradeBlin
73110d5e1e Update Gcore apikey format
I managed to mis-write the format in my previous commit
2024-12-22 01:44:52 +01:00
ComradeBlin
356b98bf7e Add Gcore DNS Provider 2024-12-22 01:02:47 +01:00
Miguel Angel Nubla
3eecf7a38b
Add custom configuration to 404 hosts 2024-12-20 01:03:21 +01:00
Miguel Angel Nubla
7f9240dda7
Add custom configuration to dead_host.conf 2024-12-20 00:59:26 +01:00
John Janzen
f537619ffe Revert "Change onRender function to always update the dashboard stats"
This reverts commit d26e8c1d0c44a5fbeb1264f8fe713bdac0f5e703.

This reopens #4204 (which i can't reproduce sadly)

The reverted commit is responsible for an infinite loop of requests to /hosts, which makes buttons unresponsive on the main page
another way to invalidate the cache needs to be found

this infinite requests loop happens on d26e8c1d0c44a5fbeb1264f8fe713bdac0f5e703
and on the docker image
`nginxproxymanager/nginx-proxy-manager-dev:pr-4206`

the docker image is attaced to the pr #4206 which merges the commit
2024-12-19 16:16:03 +01:00
jc21
805968aac6
Merge pull request #4185 from muescha/patch-1
All checks were successful
Close stale issues and PRs / stale (push) Successful in 4s
Update index.md: add link to Proxmox VE Helper-Scripts
2024-12-17 07:59:45 +10:00
jc21
2a4093c1b8
Merge pull request #4215 from TECH7Fox/patch-1
Add hostingnl DNS Challenge provider
2024-12-17 07:57:43 +10:00
jc21
ae2ac8a733
Merge pull request #4230 from NginxProxyManager/dependabot/npm_and_yarn/docs/nanoid-3.3.8
Bump nanoid from 3.3.7 to 3.3.8 in /docs
2024-12-17 07:52:24 +10:00
FabianK3
5d087f1256 Update DomainOffensive certbot plugin 2024-12-15 11:35:58 +01:00
dependabot[bot]
c6eca2578e
Bump nanoid from 3.3.7 to 3.3.8 in /docs
Bumps [nanoid](https://github.com/ai/nanoid) from 3.3.7 to 3.3.8.
- [Release notes](https://github.com/ai/nanoid/releases)
- [Changelog](https://github.com/ai/nanoid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ai/nanoid/compare/3.3.7...3.3.8)

---
updated-dependencies:
- dependency-name: nanoid
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-14 10:02:55 +00:00
Jordy Kuhne
56033bee9c
Add hostingnl 2024-12-08 15:23:37 +01:00
Medan-rfz
c6630e87bb Update version 'certbot-beget-plugin' & fix credentials content 2024-12-07 15:01:57 +04:00
Medan
d6b98f51b0
Merge branch 'NginxProxyManager:develop' into develop 2024-12-07 14:27:29 +04:00
Julian Gassner
1e322804ce Add ZoneEdit certbot plugin 2024-12-04 16:47:36 +01:00
jc21
b3de76c945
Merge pull request #4192 from badkeyy/bugfix/fix-user-edit-email-format-check
All checks were successful
Close stale issues and PRs / stale (push) Successful in 4s
Enforce email format when editing user
2024-12-04 14:50:42 +10:00
jc21
fcf4117f8e
Merge pull request #4206 from badkeyy/bugfix/update-dashboard-stats-on-change
Update the dashboard stats every time the dashboard is shown
2024-12-04 13:08:21 +10:00
Julian Gassner
d26e8c1d0c Change onRender function to always update the dashboard stats 2024-12-04 03:45:56 +01:00
Julian Gassner
19ed4c1212 Change click to submit 2024-12-04 03:08:49 +01:00
Julian Gassner
03018d252b
Merge branch 'NginxProxyManager:develop' into bugfix/fix-user-edit-email-format-check 2024-12-04 01:58:08 +01:00
jc21
8351dd41f6
Merge pull request #4199 from NginxProxyManager/dependabot/npm_and_yarn/test/cross-spawn-7.0.6
All checks were successful
Close stale issues and PRs / stale (push) Successful in 4s
Bump cross-spawn from 7.0.3 to 7.0.6 in /test
2024-12-02 10:45:00 +10:00
jc21
97212f2686
Merge pull request #4123 from NginxProxyManager/dependabot/npm_and_yarn/frontend/elliptic-6.6.0
Bump elliptic from 6.5.7 to 6.6.0 in /frontend
2024-12-02 10:44:20 +10:00
dependabot[bot]
fe068a8b51
Bump cross-spawn from 7.0.3 to 7.0.6 in /test
Bumps [cross-spawn](https://github.com/moxystudio/node-cross-spawn) from 7.0.3 to 7.0.6.
- [Changelog](https://github.com/moxystudio/node-cross-spawn/blob/master/CHANGELOG.md)
- [Commits](https://github.com/moxystudio/node-cross-spawn/compare/v7.0.3...v7.0.6)

---
updated-dependencies:
- dependency-name: cross-spawn
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-01 22:49:09 +00:00
jc21
61e2bde98f
Merge pull request #4184 from NginxProxyManager/dependabot/npm_and_yarn/backend/cross-spawn-7.0.6
Bump cross-spawn from 7.0.3 to 7.0.6 in /backend
2024-12-02 08:48:08 +10:00
Julian Gassner
81c9038929 Refactor user form structure 2024-11-27 18:27:11 +01:00
jc21
4ea50ca40c
Merge pull request #4126 from jonasrdl/remove-deprecated-version-line
All checks were successful
Close stale issues and PRs / stale (push) Successful in 4s
docs(setup): Remove deprecated version from docker-compose.yml
2024-11-26 07:37:41 +10:00
jc21
53ed12bcf2
Merge pull request #4163 from Jasparigus/stream_error_correction
Fix Container Bootloop if Stream is used for http/https ports
2024-11-26 07:37:14 +10:00
jc21
cb3e4ed59c
Merge pull request #4137 from irexyc/add-woff2-asset
Add woff2 format to assets.conf for Cache Assets
2024-11-26 07:35:57 +10:00
jc21
b20dc5eade
Merge pull request #4167 from NginxProxyManager/dependabot/npm_and_yarn/test/eslint/plugin-kit-0.2.3
Bump @eslint/plugin-kit from 0.2.0 to 0.2.3 in /test
2024-11-26 07:35:10 +10:00
jc21
586afc0c91
Merge pull request #4187 from kerstenremco/avatar
Fix entries of a deleted user break the UI
2024-11-26 07:31:03 +10:00
Remco Kersten
93ea17a9bb Fix entries of a deleted user break the UI 2024-11-25 20:37:49 +01:00
Muescha
151160a834
Update index.md: add link to Proxmox VE Helper-Scripts
Update index.md: add link to Proxmox VE Helper-Scripts
2024-11-24 20:10:17 +01:00
dependabot[bot]
2075f98cad
Bump cross-spawn from 7.0.3 to 7.0.6 in /backend
Bumps [cross-spawn](https://github.com/moxystudio/node-cross-spawn) from 7.0.3 to 7.0.6.
- [Changelog](https://github.com/moxystudio/node-cross-spawn/blob/master/CHANGELOG.md)
- [Commits](https://github.com/moxystudio/node-cross-spawn/compare/v7.0.3...v7.0.6)

---
updated-dependencies:
- dependency-name: cross-spawn
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-24 03:36:44 +00:00
jc21
07a4e5791f
Merge pull request #4179 from tametsi/develop
All checks were successful
Close stale issues and PRs / stale (push) Successful in 4s
Return generic auth error to prevent user enumeration attacks
2024-11-23 22:39:37 +10:00
tametsi
640a1eeb68
Return generic auth error to prevent user enumeration attacks
On invalid user/password error the error message "Invalid email or password" is returned.
Thereby, no information about the existence of the user is given.
2024-11-22 10:37:09 +01:00
Andrew Jackson
126d3d44ca
Bump certbot-dns-porkbun 2024-11-17 10:44:29 +00:00
dependabot[bot]
20646e7bb5
Bump @eslint/plugin-kit from 0.2.0 to 0.2.3 in /test
Bumps [@eslint/plugin-kit](https://github.com/eslint/rewrite) from 0.2.0 to 0.2.3.
- [Release notes](https://github.com/eslint/rewrite/releases)
- [Changelog](https://github.com/eslint/rewrite/blob/main/release-please-config.json)
- [Commits](https://github.com/eslint/rewrite/compare/core-v0.2.0...plugin-kit-v0.2.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-15 21:19:05 +00:00
Jasper Stubbe
87998a03ce Fix bootloop if stream is used for http/https port 2024-11-14 11:39:48 -08:00
Medan-rfz
a56342c76a Fix credentials 2024-11-10 19:23:28 +04:00
Medan-rfz
4c89379671 Update version 'certbot-beget-plugin' 2024-11-10 18:31:07 +04:00
Medan-rfz
10b9a49274 Update version 'certbot-beget-plugin' 2024-11-10 16:16:45 +04:00
Medan-rfz
595a742c40 Change beget plugin 2024-11-10 15:09:41 +04:00
Medan-rfz
c171752137 Added certbot plugin for Beget DNS service 2024-11-08 02:29:38 +04:00
irexyc
a0b26b9e98 Add woff2 format to assets.conf for Cache Assets 2024-11-04 20:01:39 +08:00
Jonas Riedel
d6791f4e38 docs(setup): Remove deprecated version from docker-compose.yml 2024-10-31 11:25:38 +01:00
dependabot[bot]
62c94f3099
Bump elliptic from 6.5.7 to 6.6.0 in /frontend
Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.7 to 6.6.0.
- [Commits](https://github.com/indutny/elliptic/compare/v6.5.7...v6.6.0)

---
updated-dependencies:
- dependency-name: elliptic
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-31 02:19:58 +00:00
jc21
25a26d6175
Merge pull request #4112 from prospo/develop
All checks were successful
Close stale issues and PRs / stale (push) Successful in 4s
feat: Add leaseweb to certbot-dns-plugins
2024-10-30 14:40:20 +10:00
jc21
17246e418f
Merge pull request #4118 from mitossoft-rd/patch-1
Remove variable usage from proxy_pass directive to fix resolution issues
2024-10-30 14:39:48 +10:00
mitossoft-rd
f7d3ca0b07
Cleaning unused variable. 2024-10-28 15:18:54 +03:00
mitossoft-rd
a55de386e7
Fix URL format 2024-10-28 15:15:08 +03:00
mitossoft-rd
e9d4f5b827
Remove variable usage from proxy_pass directive to fix resolution issues
By using a static URL, the backend server can be accessed reliably, avoiding the common 404 errors or "no resolver defined" issues seen when variables are used.
2024-10-28 02:59:23 +03:00
Emil
1c1cee3836 feat: Add leaseweb to certbot-dns-plugins 2024-10-25 13:25:09 +00:00
jc21
eaf6335694
Merge pull request #4106 from dreik/develop
All checks were successful
Close stale issues and PRs / stale (push) Successful in 4s
http2 directive migration
2024-10-25 08:53:08 +10:00
jc21
ffe05ebd41
Merge pull request #4108 from chrismaffey/patch-2
Update put.json
2024-10-25 08:06:50 +10:00
Chris Maffey
2e9a4f1aed
Update put.json
Password can be left blank for updates.  Otherwise you have to reenter the password every time you save the auth list
2024-10-24 17:29:16 +13:00
jc21
d17c85e4c8
Merge pull request #4107 from chrismaffey/patch-1
Update _access.conf
2024-10-24 11:31:12 +10:00
Chris Maffey
dad8d0ca00
Update _access.conf
the pass_auth and satisfy_any properties and now boolean true/false, they do not == 1 so the switching in this template breaks
2024-10-24 14:04:17 +13:00
Sergey 'dreik' Kolesnik
d7e0558a35 http2 directive
to reduce warns in logs
2024-10-24 01:30:14 +03:00
jc21
ee41bb5562
Merge pull request #4078 from Guiorgy/patch-1
All checks were successful
Close stale issues and PRs / stale (push) Successful in 4s
normalize indentations in certbot-dns-plugins.json
2024-10-22 10:14:31 +10:00
jc21
0cf6b9caa4
Merge pull request #4084 from ttodua/patch-1
doc(site) - default credentials change
2024-10-22 10:14:11 +10:00
T. Todua
68a9baf206
minor 2024-10-18 15:35:15 +04:00
T. Todua
d92421d098
doc(site) - default credentials change 2024-10-18 15:33:32 +04:00
Guiorgy
96c58b203e
normalize indentations in certbot-dns-plugins.json 2024-10-17 15:34:04 +04:00
Jamie Curnow
d499e2bfef
Push PR and github branch builds to separate docker image
All checks were successful
Close stale issues and PRs / stale (push) Successful in 4s
2024-10-17 10:00:12 +10:00
jc21
5084cb7296
Merge pull request #4077 from NginxProxyManager/develop
v2.12.1
2024-10-17 09:49:07 +10:00
Jamie Curnow
2f9e062718
bump version 2024-10-17 09:05:25 +10:00
Jamie Curnow
edbed1af90
Adds tests for settings endpoints
and reenables dns cert test
and fixes problems with schema
2024-10-17 08:48:47 +10:00
jc21
8497022e41
Merge pull request #4076 from Nephiel/4074-fix-1
All checks were successful
Close stale issues and PRs / stale (push) Successful in 5s
Fix schema validation errors
2024-10-17 07:07:05 +10:00
Nephiel
fa2c814fcb
Fix schema validation in Default Site
Should solve error `data/value must match exactly one schema in oneOf` when setting the Default Site to 404 or 444. #4074
2024-10-16 19:09:14 +00:00
Nephiel
d96a3987c0
Fix forward_scheme validation in Redirection Host
Should solve error `data/forward_scheme must be equal to one of the allowed values` when configuring a Redirection Host with scheme set to `auto`. #4074
2024-10-16 19:04:50 +00:00
jc21
e677bfa2e8
Merge pull request #4073 from NginxProxyManager/develop
v2.12.0
2024-10-16 15:41:55 +10:00
Jamie Curnow
fe2d8895d6
Cypress test for http and dns cert provision 2024-10-16 14:53:57 +10:00
Jamie Curnow
5bdc05878f
Fix issues with certbot command when using LE_SERVER 2024-10-16 11:23:58 +10:00
Jamie Curnow
929ac3bd7c
Adds env var to set certbot acme server
this is required for test suite to use dns certbot request
without talking to live or staging letsencrypt servers or
production level dns providers. This is a backwards port
from the v3 branch and opens the door for a full certificate
cypress test
2024-10-16 11:06:29 +10:00
Jamie Curnow
f48e1b46a8
Updated swagger cypress package,
which works with proxies
2024-10-16 08:32:49 +10:00
Jamie Curnow
351ba8dacd
More tests for certificates, fixed schema problems 2024-10-16 08:32:49 +10:00
jc21
3b89d5f380
Merge pull request #4068 from Hadatko/fixWedosParamDescription
All checks were successful
Close stale issues and PRs / stale (push) Successful in 5s
fixed wedos password description
2024-10-15 10:23:33 +10:00
Dusan Cervenka
e5aa880ec4 fixed wedos password description
Signed-off-by: Dusan Cervenka <cervenka.dusan@gmail.com>
2024-10-15 01:58:15 +02:00
Jamie Curnow
7322d35bd7
Fix CI
All checks were successful
Close stale issues and PRs / stale (push) Successful in 4s
2024-10-14 07:39:50 +10:00
Jamie Curnow
81b89185f2
Squid ci fixes
All checks were successful
Close stale issues and PRs / stale (push) Successful in 4s
2024-10-13 22:15:18 +10:00
Jamie Curnow
f2bb8f2b3d
Squid ci fixes 2024-10-13 22:04:07 +10:00
Jamie Curnow
b01817bc7f
Adds squid to dev/CI stacks
- for testing forwarded ip address later
2024-10-13 21:54:58 +10:00
Jamie Curnow
5aeb99b856
Version bump
All checks were successful
Close stale issues and PRs / stale (push) Successful in 4s
2024-10-11 15:28:24 +10:00
jc21
e7e4003d15
Merge pull request #4053 from NginxProxyManager/master
Master
2024-10-11 15:26:06 +10:00
jc21
78f3e7281b
Merge pull request #4015 from NginxProxyManager/dependabot/npm_and_yarn/backend/express-4.20.0
Bump express from 4.19.2 to 4.20.0 in /backend
2024-10-11 15:18:36 +10:00
jc21
c9d97aff38
Merge pull request #4052 from NginxProxyManager/dependabot/npm_and_yarn/test/braces-3.0.3
Bump braces from 3.0.2 to 3.0.3 in /test
2024-10-11 15:18:15 +10:00
jc21
9813071e76
Merge pull request #3864 from ROSEBikesGmbH/egobude-add-edge-dns-by-akamai
Add Edge DNS by Akamai
2024-10-11 14:16:39 +10:00
jc21
d7a7fa3496
Merge pull request #3907 from rockenstein-AG/develop
Add rockenstein AG DNS Plugin
2024-10-11 14:14:49 +10:00
jc21
2e72f253a0
Merge pull request #3910 from rafaelncarvalho/patch-1
Update Bootstrap to 3.4.1
2024-10-11 14:14:20 +10:00
jc21
ac47eab23b
Merge pull request #3942 from cqhtyi/patch-1
Update nginx-proxy-manager
2024-10-11 14:13:31 +10:00
jc21
0bfa6c9d4f
Merge pull request #3973 from ddshd/proxy-add-set
Add set directives for proxied paths to keep nginx from crashing if upstream is down
2024-10-11 14:08:39 +10:00
dependabot[bot]
f71de7474d
Bump express from 4.19.2 to 4.20.0 in /backend
Bumps [express](https://github.com/expressjs/express) from 4.19.2 to 4.20.0.
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/master/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.19.2...4.20.0)

---
updated-dependencies:
- dependency-name: express
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-11 04:06:52 +00:00
dependabot[bot]
3a2617e6bf
Bump braces from 3.0.2 to 3.0.3 in /test
Bumps [braces](https://github.com/micromatch/braces) from 3.0.2 to 3.0.3.
- [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/braces/compare/3.0.2...3.0.3)

---
updated-dependencies:
- dependency-name: braces
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-11 04:06:40 +00:00
jc21
6c6722464d
Merge pull request #4051 from NginxProxyManager/dependabot/npm_and_yarn/frontend/elliptic-6.5.7
Bump elliptic from 6.5.4 to 6.5.7 in /frontend
2024-10-11 14:05:42 +10:00
jc21
02a7b43932
Merge pull request #3991 from nlynzaad/develop_mysql2
swap mysql library and knex client for mysql2
2024-10-11 14:05:26 +10:00
jc21
42a5bb6af3
Merge pull request #3988 from vggscqq/patch-1
Added active24 DNS provider
2024-10-11 14:04:41 +10:00
Jamie Curnow
a08d18bdb2
Remove broken script 2024-10-11 14:04:24 +10:00
jc21
d2d104b723
Merge pull request #4020 from RafaelSchridi/develop
Add mijn.host dns plugin
2024-10-11 13:27:57 +10:00
jc21
e0352ecc48
Merge pull request #4016 from NginxProxyManager/dependabot/npm_and_yarn/backend/body-parser-1.20.3
Bump body-parser from 1.20.2 to 1.20.3 in /backend
2024-10-11 13:27:14 +10:00
Jamie Curnow
4e035f285d
Update deps in docs 2024-10-11 13:26:00 +10:00
jc21
b046bb3229
Merge pull request #4044 from mokkin/patch-1
version is obsolete now
2024-10-11 13:24:24 +10:00
dependabot[bot]
304899e604
Bump elliptic from 6.5.4 to 6.5.7 in /frontend
Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.4 to 6.5.7.
- [Commits](https://github.com/indutny/elliptic/compare/v6.5.4...v6.5.7)

---
updated-dependencies:
- dependency-name: elliptic
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-11 03:22:32 +00:00
jc21
e525b5470e
Merge pull request #4018 from NginxProxyManager/dependabot/npm_and_yarn/docs/rollup-4.22.4
Bump rollup from 4.17.2 to 4.22.4 in /docs
2024-10-11 13:22:21 +10:00
jc21
aacb2302bf
Merge pull request #4049 from NginxProxyManager/cve-fixes
CVE fixes and other API work
2024-10-11 13:21:28 +10:00
Jamie Curnow
d21403ca1e
Move docker login in pipeline 2024-10-11 12:57:40 +10:00
Jamie Curnow
c39d5433bc
Fix CVE-2024-46256 and CVE-2024-46257
- Schema validate against bad domain characters
- Integration test for CVE POC examples
- Cypress rewrite of plugins for file upload
2024-10-11 11:31:57 +10:00
Jamie Curnow
7c97516de6
Fix schema issue with cors 2024-10-10 16:31:19 +10:00
Jamie Curnow
4572b205c9
Openapi Schema improvements
- Return proper booleans in api responses
- Update jsonschemavalidation to latest draft
2024-10-10 15:53:11 +10:00
mokkin
6f7963ee08
version is obsolete now 2024-10-09 23:47:07 +02:00
Jamie Curnow
dfe2588523
Refactor API Schema and validation
- /schema now returns full openapi/swagger schema
- That schema is used to validate incoming requests
- And used as a contract in future integration tests
- Moved route files up one level
- Fixed incorrect 404 reponses when getting objects
- Fixed saving new objects and passing jsonschemavalidation
2024-10-09 18:05:15 +10:00
Rafaël Schridi
a8f1f7f017
Add mijn.host dns plugin 2024-09-25 22:37:13 +02:00
dependabot[bot]
e401095707
Bump rollup from 4.17.2 to 4.22.4 in /docs
Bumps [rollup](https://github.com/rollup/rollup) from 4.17.2 to 4.22.4.
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.17.2...v4.22.4)

---
updated-dependencies:
- dependency-name: rollup
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-24 00:54:26 +00:00
dependabot[bot]
d69cb26157
Bump body-parser from 1.20.2 to 1.20.3 in /backend
Bumps [body-parser](https://github.com/expressjs/body-parser) from 1.20.2 to 1.20.3.
- [Release notes](https://github.com/expressjs/body-parser/releases)
- [Changelog](https://github.com/expressjs/body-parser/blob/master/HISTORY.md)
- [Commits](https://github.com/expressjs/body-parser/compare/1.20.2...1.20.3)

---
updated-dependencies:
- dependency-name: body-parser
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-21 08:52:30 +00:00
Nico Lynzaad
48a9f5f9db swop mysql library and knex client for mysql2 2024-09-10 23:08:02 +02:00
vggscqq
ca9eeb5118
Added quotation around TOKEN variable. Made Active24 start from capital letter in UI 2024-09-09 11:53:16 +02:00
vggscqq
a03009056c
Added active24 DNS provider 2024-09-09 11:06:47 +02:00
Dhrumil Shah
554d1ff2b6
Add set directives for proxies to keep from crashing if upstream is down 2024-09-04 00:07:43 -04:00
CoffeeCHN
0042726477
Update nginx-proxy-manager
Fix Nginx not restarting correctly.
2024-08-20 15:36:21 +08:00
Rafael Carvalho
ed5d87b021
Update Bootstrap to 3.4.1
Fixes:

CVE-2018-20676
CVE-2019-8331
CVE-2018-20677
CVE-2018-14042
CVE-2016-10735
CVE-2018-14040
2024-08-01 17:09:33 -03:00
rag-franky
894cd25534
Add "rockenstein" as dns provider 2024-07-31 11:04:20 +02:00
Benedikt Schmitz
4446e2f760
Add Edge DNS by Akamai
Add Edge DNS by Akamai
2024-07-09 11:22:54 +02:00
jc21
35d7a3a407
Merge pull request #3847 from NginxProxyManager/develop
v2.11.3
2024-07-01 21:37:42 +10:00
jc21
63d06da8a8
Merge branch 'master' into develop
All checks were successful
Close stale issues and PRs / stale (push) Successful in 4s
2024-07-01 16:12:21 +10:00
Jamie Curnow
b5a0d74654
Bump version 2024-07-01 16:09:33 +10:00
Jamie Curnow
99cce7e2b0
Fix command injection when passing bash commands into the dns provider configuration
- Use built in node functions to write the file
- And to delete the file
2024-07-01 16:08:01 +10:00
jc21
120d50e5c0
Merge pull request #3766 from kroegerama/kroegerama-patch-1
Add include for `root_top.conf` in the nginx.conf
2024-07-01 15:23:43 +10:00
jc21
5454fd61b3
Merge pull request #3781 from jinhei/patch-1
Remove spaces around Cloudflare API Credential
2024-07-01 15:22:43 +10:00
jc21
b33012705b
Merge pull request #3790 from DavidLievrouw/initial_admin
Read initial admin email and password from env vars
2024-07-01 15:22:15 +10:00
jc21
e948b60194
Merge pull request #3809 from NginxProxyManager/dependabot/npm_and_yarn/backend/braces-3.0.3
Bump braces from 3.0.2 to 3.0.3 in /backend
2024-07-01 15:20:48 +10:00
jc21
7913c9a07d
Merge pull request #3827 from Hadatko/feature/addWedosDns
Add wedos dns
2024-07-01 15:20:23 +10:00
jc21
d1c23b6286
Merge pull request #3833 from NginxProxyManager/dependabot/npm_and_yarn/backend/glob-parent-5.1.2
Bump glob-parent from 5.1.1 to 5.1.2 in /backend
2024-07-01 15:19:39 +10:00
jc21
c7e2946dbf
Merge pull request #3837 from Allesanddro/patch-1
Update README.md
2024-07-01 15:19:28 +10:00
jc21
8936402229
Merge pull request #3843 from jay-lab/feature/fix-syntax-cause-err
Fix syntax that causes errors (generateKeys log)
2024-07-01 15:19:00 +10:00
An Seongjin
001c77e686
Fix syntax that causes errors (generateKeys log) 2024-06-30 22:27:54 +09:00
Dusan Cervenka
5578e825b1 Update version
Signed-off-by: Dusan Cervenka <cervenka.dusan@gmail.com>
2024-06-29 21:30:27 +02:00
S.S
c93656a7a1
Update README.md
In 2020, the concept of a single compose specification was introduced, removing the need for versioning.
2024-06-28 20:04:31 +02:00
dependabot[bot]
50aeae234f
Bump glob-parent from 5.1.1 to 5.1.2 in /backend
Bumps [glob-parent](https://github.com/gulpjs/glob-parent) from 5.1.1 to 5.1.2.
- [Release notes](https://github.com/gulpjs/glob-parent/releases)
- [Changelog](https://github.com/gulpjs/glob-parent/blob/main/CHANGELOG.md)
- [Commits](https://github.com/gulpjs/glob-parent/compare/v5.1.1...v5.1.2)

---
updated-dependencies:
- dependency-name: glob-parent
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-27 18:24:09 +00:00
Dusan Cervenka
a5c06c1a34 Add wedos dns
Signed-off-by: Dusan Cervenka <cervenka.dusan@gmail.com>
2024-06-25 23:26:50 +02:00
jc21
51414ced3a
Merge pull request #3810 from Brendon-Mendicino/change_log_format_location
All checks were successful
Close stale issues and PRs / stale (push) Successful in 4s
Changing `log_format proxy` default location
2024-06-25 10:17:04 +10:00
jc21
5e35e538af
Merge pull request #3815 from alexjsp/alex/hover-dns-plugin
Add Hover.com DNS plugin
2024-06-25 10:16:03 +10:00
Alex Stevenson-Price
13fec42d1f
Add Hover.com DNS plugin 2024-06-20 11:47:50 +01:00
Brendon Mendicino
b4560d7dde feat: changing log_format proxy default location
This is useful when some user would want to change the default
log format for each of the service, without the need of creating a
new `log_format custom` and changing the `access_log` for each
service.
2024-06-16 15:44:52 +02:00
dependabot[bot]
6f9eed8a61
Bump braces from 3.0.2 to 3.0.3 in /backend
Bumps [braces](https://github.com/micromatch/braces) from 3.0.2 to 3.0.3.
- [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/braces/compare/3.0.2...3.0.3)

---
updated-dependencies:
- dependency-name: braces
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-16 10:41:32 +00:00
David Lievrouw
d66e4e03e6 #3790 Attempt to make ci happy. 2024-06-03 13:44:08 +02:00
David Lievrouw
1d19c29bb0 Read initial admin email and password from env vars. 2024-06-03 13:32:23 +02:00
Nicholas Fong
e20a11de4a
Remove spaces around cloudflare api credential 2024-05-28 23:32:03 -04:00
Jamie Curnow
d3a654b546
Fix flakey CI due to full stack network determination 2024-05-23 08:12:51 +10:00
Jamie Curnow
bed387ebd4 Small fix for CI cleanup 2024-05-21 13:16:53 +10:00
Jamie Curnow
6ac9a82279 Major update to cypress
- Updated cypress
- Ground work for testing DNS certs in CI
2024-05-21 12:53:07 +10:00
kroegerama
ef23e796ec
update advanced config documentation
describe the `root_top.conf` file and add a snippet for enabling the geoip2 module
2024-05-20 10:35:36 +02:00
jc21
3754a569ba
Merge pull request #3729 from clhey/custom_proxy
move advanced_config section of /app/templates/_location.conf to top of default config
2024-05-20 13:53:09 +10:00
jc21
b383f46656
Merge pull request #3764 from ransbachm/develop
Fix Cloudflare DNS Auth
2024-05-20 13:46:39 +10:00
kroegerama
3ce477d350
add include for root_top.conf in the nginx.conf
Allow custom configuration of the root config in the top of the file. This can be used to load modules, which is not possible at the end of the config file.
There is already a `http_top.conf`, so `root_top.conf` is a logical addition.
2024-05-19 15:53:02 +02:00
ransbachm
516b4d991c
Pin version as requested by dep 2024-05-18 22:49:48 +02:00
jc21
12d77e3ab6
Merge pull request #3747 from NginxProxyManager/develop
Docs migration from vuepress to vitepress
2024-05-11 00:33:13 +10:00
Jamie Curnow
8d80af3a26
Fix CI 2024-05-11 00:15:44 +10:00
Jamie Curnow
1f45e6a5e9
Fix unescaped character in CI 2024-05-11 00:14:05 +10:00
Jamie Curnow
dcb9628c36
CI improvement: move docs upload to separate build 2024-05-11 00:13:11 +10:00
jc21
029b184398
Merge branch 'master' into develop 2024-05-10 23:31:28 +10:00
Jamie Curnow
2422587530
Updates to docs FAQ 2024-05-10 23:28:36 +10:00
Jamie Curnow
4ee940d3dc
Fix missing feature on docs homepage 2024-05-10 23:10:55 +10:00
Jamie Curnow
47dddc548b
Migrate from vuepress to vitepress for docs site 2024-05-10 23:00:27 +10:00
jc21
256a667e2c
Merge pull request #3733 from NginxProxyManager/develop
v2.11.2
2024-05-02 09:43:20 +10:00
jc21
79cd0c5294
Merge branch 'master' into develop 2024-05-02 08:40:10 +10:00
Jamie Curnow
09a03edfd7
Bump version 2024-05-02 08:21:32 +10:00
jc21
35f0fe745d
Merge pull request #3569 from NginxProxyManager/dependabot/npm_and_yarn/backend/ip-2.0.1
Bump ip from 2.0.0 to 2.0.1 in /backend
2024-05-02 08:19:02 +10:00
jc21
f1e433714e
Merge pull request #3571 from NginxProxyManager/dependabot/npm_and_yarn/docs/ip-2.0.1
Bump ip from 2.0.0 to 2.0.1 in /docs
2024-05-02 08:18:51 +10:00
jc21
035eaed0a4
Merge pull request #3600 from sdaqo/patch-1
Update certbot-dns-duckdns version (fix #2994)
2024-05-02 08:18:32 +10:00
jc21
4b100a384d
Merge pull request #3679 from jdolderer/fix/update-certbot-dns-strato
fix: update certbot-dns-strato to latest version
2024-05-02 08:17:02 +10:00
jc21
c5c5fa0a5a
Merge pull request #3691 from Fuechslein/fix/certbot-dns-infomaniak
Update certbot-dns-infomaniak
2024-05-02 08:16:45 +10:00
clhey
280bac8b43 advanced config move to top of default confg 2024-04-28 18:18:38 +08:00
jc21
02aefa50cd
Merge pull request #3617 from woodmichl/fix-slow-startup
replaced chown with find -not -user -execdir chown
2024-04-19 21:00:31 +10:00
jc21
4d91cfc397
Merge pull request #3639 from wolviex/develop
Update certbot-dns-goddaddy
2024-04-19 20:59:09 +10:00
jc21
79a453f2fe
Merge pull request #3642 from leinelissen/fix/certbot-dns-transip
fix: update certbot-dns-transip to latest version
2024-04-19 20:56:59 +10:00
jc21
c62c09569d
Merge pull request #3643 from starsoccer/patch-1
Add DNS multi
2024-04-19 20:12:04 +10:00
jc21
09bcf4010c
Merge pull request #3660 from NginxProxyManager/dependabot/npm_and_yarn/backend/express-4.19.2
Bump express from 4.17.3 to 4.19.2 in /backend
2024-04-19 20:10:35 +10:00
jc21
6aeade6c98
Merge pull request #3676 from NginxProxyManager/dependabot/npm_and_yarn/docs/vite-5.0.13
Bump vite from 5.0.12 to 5.0.13 in /docs
2024-04-19 20:10:24 +10:00
jc21
8655b7d2db
Merge pull request #3697 from NginxProxyManager/dependabot/npm_and_yarn/frontend/tar-6.2.1
Bump tar from 6.1.11 to 6.2.1 in /frontend
2024-04-19 20:06:59 +10:00
jc21
2d929dffa8
Merge pull request #3698 from NginxProxyManager/dependabot/npm_and_yarn/docs/tar-6.2.1
Bump tar from 6.2.0 to 6.2.1 in /docs
2024-04-19 20:06:44 +10:00
dependabot[bot]
52eaa042d8
Bump tar from 6.2.0 to 6.2.1 in /docs
Bumps [tar](https://github.com/isaacs/node-tar) from 6.2.0 to 6.2.1.
- [Release notes](https://github.com/isaacs/node-tar/releases)
- [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md)
- [Commits](https://github.com/isaacs/node-tar/compare/v6.2.0...v6.2.1)

---
updated-dependencies:
- dependency-name: tar
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-11 14:00:31 +00:00
dependabot[bot]
b35aa50b88
Bump tar from 6.1.11 to 6.2.1 in /frontend
Bumps [tar](https://github.com/isaacs/node-tar) from 6.1.11 to 6.2.1.
- [Release notes](https://github.com/isaacs/node-tar/releases)
- [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md)
- [Commits](https://github.com/isaacs/node-tar/compare/v6.1.11...v6.2.1)

---
updated-dependencies:
- dependency-name: tar
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-10 21:18:28 +00:00
Fuechslein
c575a706b5
Update certbot-dns-plugins.json
Latest plugin version with several fixes
2024-04-09 10:12:55 +02:00
Jakob Dolderer
587b97c2d3
fix: update certbot-dns-strato to latest version 2024-04-04 12:27:16 +02:00
dependabot[bot]
317003beda
Bump vite from 5.0.12 to 5.0.13 in /docs
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.0.12 to 5.0.13.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v5.0.13/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v5.0.13/packages/vite)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-03 18:36:32 +00:00
dependabot[bot]
5a761236c5
Bump express from 4.17.3 to 4.19.2 in /backend
Bumps [express](https://github.com/expressjs/express) from 4.17.3 to 4.19.2.
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/master/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.17.3...4.19.2)

---
updated-dependencies:
- dependency-name: express
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-28 02:21:37 +00:00
starsoccer
b135527347
Fix version key 2024-03-20 11:58:47 -04:00
starsoccer
abca9cc89c
Add DNS multi
Credit to original PR #2402
2024-03-20 11:19:47 -04:00
Lei Nelissen
6721923601
fix: update certbot-dns-transip to latest version 2024-03-20 11:07:29 +01:00
Joe Manifold
a88f77c1a5
Update certbot-dns-plugins.json
Pinned certbot-dns-godaddy at 2.8.0
2024-03-19 10:18:41 -07:00
woodmichl
a5b21d0306
replaxed chown with find -not -user ... chown
chown -R tries to chown all files. find -not -user -execdir only chowns files not owned by PUID
2024-03-10 01:55:18 +01:00
sdaqo
8eab8d71f2
Update duckdns version 2024-03-03 20:57:53 +01:00
dependabot[bot]
d06572bb5f
Bump ip from 2.0.0 to 2.0.1 in /docs
Bumps [ip](https://github.com/indutny/node-ip) from 2.0.0 to 2.0.1.
- [Commits](https://github.com/indutny/node-ip/compare/v2.0.0...v2.0.1)

---
updated-dependencies:
- dependency-name: ip
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-27 01:45:05 +00:00
jc21
d40f9e06fc
Merge pull request #3479 from NginxProxyManager/dependabot/npm_and_yarn/docs/vite-5.0.12
Bump vite from 5.0.11 to 5.0.12 in /docs
2024-02-27 11:44:04 +10:00
jc21
69ec017a53
Merge pull request #3513 from setrin/websupport-fix
Updated certbot-dns-websupport plugin to 2.0.1 #3447
2024-02-27 11:43:47 +10:00
jc21
fa67f257ef
Merge pull request #3526 from eltociear/patch-1
Update README.md
2024-02-27 11:43:24 +10:00
jc21
0dcd648c9d
Merge pull request #3531 from hywax/develop
Add DNS Provider TimeWeb Cloud
2024-02-27 11:41:56 +10:00
jc21
c989a282e3
Merge pull request #3532 from Habbie/jwt-not-gpg
the generated keys appear to be for JWT, not GPG
2024-02-27 11:41:28 +10:00
jc21
5aff969c04
Merge pull request #3554 from bricas/develop
Add FreeDNS certbot plugin
2024-02-27 11:38:12 +10:00
jc21
bfbf7519ec
Merge pull request #3560 from drachul/develop
Adding easyDNS provider.
2024-02-27 11:37:51 +10:00
jc21
bf36c7966a
Merge pull request #3570 from NginxProxyManager/dependabot/npm_and_yarn/frontend/ip-2.0.1
Bump ip from 2.0.0 to 2.0.1 in /frontend
2024-02-27 11:36:18 +10:00
jc21
63cd9ba08f
Merge pull request #3581 from davidindra/increase-max-domains-count
Fix: increase max number of domains to 100 (match with Let's Encrypt)
2024-02-27 11:36:04 +10:00
jc21
e3d4882c3d
Merge pull request #3583 from michto01/patch-1
Update README.md to support Podman
2024-02-27 11:35:23 +10:00
jc21
3e1b73143e
Merge pull request #3584 from timob/develop
Access-List fix so that nginx config is loaded after configuration happens
2024-02-27 11:34:52 +10:00
David Indra
10ece3548d Fixing "the map directive is not allowed here" at the validation stage (see https://github.com/NginxProxyManager/nginx-proxy-manager/pull/3478) 2024-02-27 00:42:58 +01:00
Tim
0503a6af75 Fix so that nginx config is loaded after configuration happens
M	backend/internal/access-list.js
2024-02-26 10:04:25 +11:00
Tomas Michalek
55d765e785
Update README.md to support Podman
Podman by default doesn't except the not fully qualified image urls. This commit adds the domain (docker.io) in order to resolve this issue.
2024-02-25 22:38:50 +01:00
David Indra
1fb9a75a33 Fix: increase max number of domains to 100 2024-02-23 15:37:32 +01:00
dependabot[bot]
9c2e838d61
Bump ip from 2.0.0 to 2.0.1 in /frontend
Bumps [ip](https://github.com/indutny/node-ip) from 2.0.0 to 2.0.1.
- [Commits](https://github.com/indutny/node-ip/compare/v2.0.0...v2.0.1)

---
updated-dependencies:
- dependency-name: ip
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-21 03:12:49 +00:00
dependabot[bot]
c55e47aacf
Bump ip from 2.0.0 to 2.0.1 in /backend
Bumps [ip](https://github.com/indutny/node-ip) from 2.0.0 to 2.0.1.
- [Commits](https://github.com/indutny/node-ip/compare/v2.0.0...v2.0.1)

---
updated-dependencies:
- dependency-name: ip
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-20 21:49:54 +00:00
Derek Rachul
40d81d6e44 Adding easyDNS provider. 2024-02-17 12:32:05 -08:00
Brian Cassidy
1c84eaac02
Add FreeDNS certbot plugin
Info from #2352 and https://github.com/schleuss/certbot_dns_freedns
2024-02-15 23:43:53 -04:00
Alexander
577954ef8c
Bump version DNS Provider TimeWeb Cloud 2024-02-08 03:20:53 +05:00
Peter van Dijk
f0c75641d8 the generated keys appear to be for JWT, not GPG 2024-02-07 12:44:37 +01:00
hywax
e42e2acf12 Add DNS Provider TimeWeb Cloud 2024-02-07 13:12:20 +05:00
Ikko Eltociear Ashimine
eaa11fe460
Update README.md
a -> an
2024-02-04 18:50:50 +09:00
Setrin
5b53825ccb Fixed certbot-dns-websupport plugin name 2024-01-30 22:46:05 +01:00
Setrin
a94660120f Renamed certbot-dns-websupportsk plugin to certbot-dns-websupport & updatedcredentials 2024-01-30 22:17:33 +01:00
Setrin
39f4836485 Updated certbot-dns-webstorm plugin to 2.0.1 2024-01-30 20:57:19 +01:00
jc21
aec30207da
Merge pull request #3483 from NginxProxyManager/develop
v2.11.1
2024-01-21 21:17:30 +10:00
jc21
209c1b3334
Merge branch 'master' into develop 2024-01-21 21:16:30 +10:00
Jamie Curnow
58138fbac4
Bump version 2024-01-21 21:13:03 +10:00
Jamie Curnow
da820db4e1
Fix startup hang due to unresolved promise
Affects instances where there are certs but none
of them are dns validated
2024-01-21 20:48:53 +10:00
dependabot[bot]
47b868bfc6
Bump vite from 5.0.11 to 5.0.12 in /docs
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.0.11 to 5.0.12.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v5.0.12/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v5.0.12/packages/vite)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-19 22:36:30 +00:00
jc21
89a405f60c
Merge pull request #3466 from NginxProxyManager/develop
v2.11.0
2024-01-19 10:52:34 +10:00
Jamie Curnow
0353051436
Prevent installing same plugin over and over 2024-01-18 16:06:09 +10:00
Jamie Curnow
a3630a6286
Fix unused var 2024-01-18 15:17:27 +10:00
Jamie Curnow
10d9760242
Refactor certbot plugin install for setup 2024-01-18 15:13:16 +10:00
jc21
c722eb1cea
Merge branch 'master' into develop 2024-01-18 13:21:39 +10:00
Jamie Curnow
0472abacd2
Remove test file 2024-01-18 13:20:03 +10:00
Jamie Curnow
a2e85ceed8
Use certbot version for godaddy plugin, relates to #3165 2024-01-18 13:08:16 +10:00
Jamie Curnow
cddd6fb985
certbot-dns-cloudns update 0.6.0 from PR #3459 by @existful 2024-01-18 13:01:05 +10:00
Jamie Curnow
db23c9a52f
Refactor certbot plugins install
- Added a script to install every single plugin, used in development and debugging
- Improved certbot plugin install commands
- Adjusted some version for plugins to install properly
- It's noted that some plugins require deps that do not match other plugins,
  however these use cases should be extremely rare
2024-01-18 12:26:55 +10:00
Jamie Curnow
8646cb5a19
Allow stale action to run manually 2024-01-16 07:57:38 +10:00
Jamie Curnow
fe0c04610f
Add stale github action and set a wide limit 2024-01-16 07:53:02 +10:00
jc21
9f16dae2ff
Merge pull request #3258 from iBobik/patch-1
Removed /etc/letsencrypt from explicit volumes
2024-01-15 09:12:44 +10:00
Jamie Curnow
00264bcfb2
Mount letsencrypt folder in CI 2024-01-15 08:18:48 +10:00
Jamie Curnow
834fb1a361
Add missing args to certbot command, was causing failures in rovokation 2024-01-12 17:04:55 +10:00
jc21
1be87f48c1
Merge pull request #3392 from stevecrozz/auto-renew-uses-bulitin-renew
Make auto-renew use built-in renew function
2024-01-12 12:15:37 +10:00
Stephen Crosby
9c54d1b718 Provide the token model for certificate renewal 2024-01-10 20:08:36 -08:00
Stephen Crosby
f7d1c490b3 Run renews sequentially 2024-01-10 20:08:36 -08:00
Stephen Crosby
fe4bd9fed6 Make auto-renew use built-in renew function 2024-01-10 20:08:29 -08:00
jc21
58ef9a688e
Merge pull request #3445 from tilalx/develop
Update the vuepress config.js to fix pr/3395
2024-01-10 20:43:33 +10:00
tilalx
d19ebf5925 Update the config.js to fix pr/3395 2024-01-10 11:06:40 +01:00
jc21
96fc6a20bb
Merge pull request #3444 from NginxProxyManager/bookworm-base
Use nginxproxymanager/nginx-full image base
2024-01-10 13:18:10 +10:00
Jamie Curnow
e69684919c
Use nginxproxymanager/nginx-full image base
which has been updated with bookworm, python 3.8, certbot 2.8.0 and node 20

Moved rootfs scripts as /bin is a symlink in bookworm
2024-01-10 12:59:51 +10:00
Jamie Curnow
be39253a6f
No need to use berry yarn for docs
as the ci image uses latest yarn
2024-01-10 09:39:25 +10:00
Jamie Curnow
30772a48bd
Fix jenkinsfile after messy merge - again 2024-01-10 09:29:05 +10:00
Jamie Curnow
33c867895c
Fix jenkinsfile after messy merge 2024-01-10 09:24:45 +10:00
Jamie Curnow
a7fe687bae
Fix permission recursiveness 2024-01-10 09:22:34 +10:00
jc21
4028120f55
Merge pull request #3395 from tilalx/develop
upgrade docs to vuepress v2.0.0-rc and implement dark mode
2024-01-10 09:21:10 +10:00
tilalx
d1119ec63f revert change 2024-01-09 09:35:16 +01:00
tilalx
4c906283df try to set the yarn version in jenkins 2024-01-09 09:35:15 +01:00
tilalx
8ec0c76f51 update docs-build and add yarn.lock 2024-01-09 09:31:39 +01:00
tilalx
c70f65d349 upgrade to v2.0.0-rc and implement dark mode 2024-01-09 09:27:34 +01:00
Jamie Curnow
883a272b0a
Bump version 2024-01-09 11:30:50 +10:00
Jamie Curnow
6aee2bbcba
Fix race condition with integration network 2024-01-09 10:57:47 +10:00
Jamie Curnow
025fc9776b
Pre-build cypress images before runnings integration tests 2024-01-09 10:32:58 +10:00
Jamie Curnow
b699f05f47
Run integration tests in parallel 2024-01-09 10:25:10 +10:00
Jamie Curnow
f7c87f63bd
Updated CI to run some things in parallel 2024-01-09 10:05:19 +10:00
Jamie Curnow
e4ef095254
Deploy develop docs in CI, updated readme 2024-01-09 08:36:32 +10:00
jc21
09d5e2c94f
Merge pull request #3360 from DarioViva42/hsts-only-with-https
only add hsts header with https.
2024-01-09 08:16:01 +10:00
jc21
459b7a2223
Merge pull request #3361 from timob/improve-container-start
Improve container startup time
2024-01-09 08:15:33 +10:00
jc21
9c813bcce3
Merge pull request #3437 from Encephala/fix-logrotate-docs
Fix typo in logrotate config path
2024-01-09 07:41:36 +10:00
jc21
b8596ac01c
Merge pull request #3367 from ej52/develop
chore: update Proxmox Scripts link
2024-01-09 07:40:50 +10:00
Encephala
082c4e1008 Fix typo in logrotate config path 2024-01-08 16:14:27 +01:00
jc21
2273eae6ee
Merge pull request #3436 from NginxProxyManager/dependabot/npm_and_yarn/docs/babel/traverse-7.23.7
Bump @babel/traverse from 7.11.0 to 7.23.7 in /docs
2024-01-08 11:16:53 +10:00
jc21
997e9d431b
Merge pull request #2924 from benhubert/2153_add-support-for-dns-hurricane-electric
added support for dns.he.net certbot plugin #2153
2024-01-08 10:49:27 +10:00
dependabot[bot]
b3564b6d4b
Bump @babel/traverse from 7.11.0 to 7.23.7 in /docs
Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.11.0 to 7.23.7.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.23.7/packages/babel-traverse)

---
updated-dependencies:
- dependency-name: "@babel/traverse"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-08 00:35:43 +00:00
jc21
4e27cdabc4
Merge pull request #3263 from NginxProxyManager/dependabot/npm_and_yarn/frontend/babel/traverse-7.23.2
Bump @babel/traverse from 7.11.0 to 7.23.2 in /frontend
2024-01-08 10:34:03 +10:00
jc21
965873adc5
Merge pull request #3377 from jlesage/http2-support-fix
Fixed issue where the HTTP2 support was always enabled in nginx config
2024-01-08 10:33:47 +10:00
jc21
5de95a8c90
Merge pull request #3382 from r3na/patch-1
fix: increasing maxOptions (amount of domains) to 30
2024-01-08 10:26:01 +10:00
jc21
fa557d8159
Merge pull request #3387 from clord/clord/update-vultr
chore: bump version of vultr certbot
2024-01-08 10:19:58 +10:00
jc21
bc8211a6a9
Merge pull request #3388 from jlesage/reachability-test-fix
Fixes for the server reachability test.
2024-01-08 10:19:27 +10:00
jc21
1c498f84ad
Merge pull request #3399 from hywax/patch-1
Fix proxmox scripts link
2024-01-08 10:13:06 +10:00
jc21
ea6e9757e3
Merge pull request #3401 from JeremieA/certbot-dns-gandi-1.5.0
Update certbot-dns-plugins.js for gandi
2024-01-08 10:12:50 +10:00
jc21
1308ae42c2
Merge pull request #3408 from arussell/certbot-dns-plesk
Add support for certbot-dns-plesk
2024-01-08 10:12:24 +10:00
jc21
7be548575b
Merge pull request #3422 from Encephala/logrotate-docs
Add documentation on customising logrotate config
2024-01-08 10:12:03 +10:00
jc21
c6aab8d4e6
Merge pull request #3427 from Encephala/bump-year
Update year to 2024 in footer
2024-01-08 10:10:44 +10:00
Encephala
da55e93183 Update year to 2024 in footer 2024-01-03 16:48:58 +01:00
Encephala
af475ab5d4 Add documentation on customising logrotate config 2023-12-30 15:23:17 +01:00
Aaron B. Russell
7d85463dae
Add support for certbot-dns-plesk 2023-12-21 16:07:34 +00:00
Jeremie Allard
13d4f98fdb Update certbot-dns-plugins.js for gandi (deprecation of Apikey in favor of personal tokens) 2023-12-20 12:19:17 +01:00
Jocelyn Le Sage
388fff84f2 Fixes for the server reachability test.
- Do not apply HTTPs redirection for challenge used by the test.
- Set the `User-Agent` to avoid 403 answer from site24x7.com.
- Handle JSON parsing failure of the received body.
- Better handling of different error cases.
2023-12-19 17:22:33 -05:00
Alexander
49a765516c
Fix proxmox scripts link 2023-12-19 18:37:50 +05:00
Christopher Lord
27bc8c4e33
use same formatting 2023-12-13 15:15:02 -07:00
Christopher Lord
881a067aff
update to latest vultr certbot plugin
closes https://github.com/NginxProxyManager/nginx-proxy-manager/issues/3234
2023-12-13 15:11:56 -07:00
Renan Duarte
1975e4a151
fix: updating maxItems (schema/definitions) to 30 2023-12-12 12:45:35 +01:00
Renan Duarte
4704bd6a38 Merge branch 'develop' into patch-1 2023-12-12 12:38:42 +01:00
Renan Duarte
ca56e0483f
fix: updating default maxOptions to 30 (dead) 2023-12-12 12:37:06 +01:00
Renan Duarte
3b8cb86d72
fix: updating default maxOptions to 30 (redirection) 2023-12-12 12:36:32 +01:00
Renan Duarte
5165de4a91
fix: updating default maxOptions to 30 (proxy) 2023-12-12 12:36:05 +01:00
Renan Duarte
1ab3575c68
fix: increasing maxOptions (amount of domains) to 30 2023-12-12 09:39:28 +01:00
Jocelyn Le Sage
ccf9cce825 Fixed issue where the HTTP2 support was always enabled in nginx config, no matter what the user configured. 2023-12-09 11:16:37 -05:00
Elton Renda
3ad2188f78 chore: upddate Proxmox Scripts link 2023-12-04 10:31:26 +00:00
Tim O'Brien
33dbffb974 Improve container startup time
See https://github.com/NginxProxyManager/nginx-proxy-manager/issues/2991

Removes uneeded file permission changes in rootfs certbot install. Tested installing custom DNS provider plugins for certbot, works correctly.
2023-12-02 14:56:48 +11:00
Dario Viva
289e438c59 only add hsts header with https.
fixes https://github.com/NginxProxyManager/nginx-proxy-manager/issues/1005
for more information look at: https://websistent.com/add-the-hsts-header-only-for-https-requests-nginx/
2023-12-02 03:26:34 +01:00
Jamie Curnow
e08a4d4490
Update mariadb example to auto upgrade from latest image 2023-11-28 08:27:11 +10:00
jc21
d1d1819677
Merge pull request #3281 from nmatton/patch-1
update docker-compose execution
2023-11-22 10:06:55 +10:00
jc21
4e0768d56c
Merge pull request #3289 from NginxProxyManager/dependabot/npm_and_yarn/frontend/browserify-sign-4.2.2
Bump browserify-sign from 4.2.1 to 4.2.2 in /frontend
2023-11-22 10:06:30 +10:00
jc21
3666364418
Merge pull request #3290 from NginxProxyManager/dependabot/npm_and_yarn/docs/browserify-sign-4.2.2
Bump browserify-sign from 4.2.1 to 4.2.2 in /docs
2023-11-22 10:06:19 +10:00
jc21
9052502a17
Merge pull request #3293 from xiaoxinpro/develop
Replace the description string on the default-site page with i18n
2023-11-09 07:37:13 +10:00
jc21
b608d3392d
Merge pull request #3312 from AngusC222/develop
min/max ports added for Streams
2023-11-09 07:36:05 +10:00
Jamie Curnow
edb81ecce0
Fix CI branch names being incorrectly replaced 2023-11-09 07:35:24 +10:00
AngusC222
e24181936f
min/max ports added 2023-11-08 12:09:36 +00:00
chishin
940d06cac9 Replace the 'default-site' variable 'description' with the 'i18n' configuration 2023-10-29 10:50:45 +08:00
chishin
134902d127 Add a default-site-description string 2023-10-29 10:43:57 +08:00
dependabot[bot]
2df4620d05
Bump browserify-sign from 4.2.1 to 4.2.2 in /docs
Bumps [browserify-sign](https://github.com/crypto-browserify/browserify-sign) from 4.2.1 to 4.2.2.
- [Changelog](https://github.com/browserify/browserify-sign/blob/main/CHANGELOG.md)
- [Commits](https://github.com/crypto-browserify/browserify-sign/compare/v4.2.1...v4.2.2)

---
updated-dependencies:
- dependency-name: browserify-sign
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-27 13:36:54 +00:00
dependabot[bot]
f41b1069ae
Bump browserify-sign from 4.2.1 to 4.2.2 in /frontend
Bumps [browserify-sign](https://github.com/crypto-browserify/browserify-sign) from 4.2.1 to 4.2.2.
- [Changelog](https://github.com/browserify/browserify-sign/blob/main/CHANGELOG.md)
- [Commits](https://github.com/crypto-browserify/browserify-sign/compare/v4.2.1...v4.2.2)

---
updated-dependencies:
- dependency-name: browserify-sign
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-27 13:36:29 +00:00
nmatton
004a93fbc3
update docker-compose execution
As of Jun 2023, the docker-compose command has been deprecated in favor of the compose plugin.

https://docs.docker.com/compose/install/linux/
2023-10-24 22:47:42 +02:00
dependabot[bot]
2d9f04edcd
Bump @babel/traverse from 7.11.0 to 7.23.2 in /frontend
Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.11.0 to 7.23.2.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.23.2/packages/babel-traverse)

---
updated-dependencies:
- dependency-name: "@babel/traverse"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-17 03:33:12 +00:00
Jamie Curnow
53dbe258a5
Fix CI compose project name not allowing dots 2023-10-17 11:57:14 +10:00
Honza Pobořil
e4ba22f0f8
Removed /etc/letsencrypt from explicit volumes
So it can be moved in other images using this as a base.

Fixes #3170
2023-10-15 08:55:36 +02:00
jc21
3197de41de
Merge pull request #3155 from devedse/develop
Added force renewal + --dns-duckdns-no-txt-restore
2023-10-03 18:54:02 +10:00
jc21
0f7be7987b
Merge pull request #3188 from AngusC222/develop
minimum/maximum ports added on frontend
2023-10-03 18:50:37 +10:00
jc21
853c48dff6
Merge pull request #3190 from OpenSourceSimon/patch-1
Add robots noindex meta tag to prevent indexing
2023-10-03 18:49:56 +10:00
jc21
410c3484ab
Merge pull request #3194 from zhzy0077/patch-1
certbot-dns-tencentcloud should be 2.0.2 or above.
2023-10-03 18:49:13 +10:00
jc21
44e9f377f9
Merge pull request #3212 from FlixMa/develop
Strato Certbot Plugin: 2FA and International Site Support
2023-10-03 18:48:42 +10:00
jc21
0f3b76f607
Merge pull request #3219 from FibreTTP/logrotate-perms
Make logrotate use the proper user and group.
2023-10-03 18:48:01 +10:00
FibreTTP
f426e64569 Add warning comment about changing the default user name and group name 2023-09-27 16:12:33 +10:00
FibreTTP
4867db078c Remove explicit user and group - add su directive for default user (npm). 2023-09-27 14:58:19 +10:00
FibreTTP
6b565e628f Change perms on logrotated logs to npm user 2023-09-27 14:25:04 +10:00
Felix Maaß
881d70502b
Add description for Strato 2FA and International Site Support
Tell users how to configure...
+ Two Factor authentication
+ Custom API Endpoint (mostly for international hosts like strato.es/strato.nl)
2023-09-24 19:17:53 +02:00
Zhiyuan Zheng
62e4edddf0
Update certbot-dns-plugins.js 2023-09-13 12:01:15 +08:00
Simon
4b9c02cc0c
Add robots noindex meta tag to prevent indexing 2023-09-10 12:08:28 +02:00
AngusC222
5af834e40b
mix/max ports 2023-09-09 13:44:16 +01:00
Devedse
6f8db95249 Added force renewal + --dns-duckdns-no-txt-restore 2023-08-24 13:21:01 +02:00
jc21
fe93cb3474
Merge pull request #3089 from NginxProxyManager/develop
v2.10.4
2023-08-02 11:44:02 +10:00
Jamie Curnow
fa851b61da
Bump version 2023-07-31 07:25:09 +10:00
jc21
3333a32612
Merge pull request #2971 from wolviex/certbot-dnsplugin-user-site-fix
drop --user on pip install dns plugin
2023-07-31 07:21:18 +10:00
jc21
9a79fce498
Merge pull request #3078 from andycandy-de/patch-1
Corrected docker-compose.yml
2023-07-26 10:27:30 +10:00
andycandy-de
b1180f5077
Corrected docker-compose.yml
The mysql folder should not be mounted to the npm container!
2023-07-25 18:00:48 +02:00
jc21
5454352fe5
Merge pull request #2929 from FlixMa/develop
Add strato.de to certbot dns plugins
2023-07-20 12:25:37 +10:00
jc21
aee93a2f6f
Merge pull request #2932 from nietzscheanic/patch-1
Fix for ignored ssl_protocols and ssl_ciphers directive in conf.d/inc…
2023-07-20 12:25:09 +10:00
jc21
f38cb5b500
Merge pull request #2942 from wrouesnel/444_default_support
Add support for nginx 444 default response
2023-07-20 12:23:57 +10:00
jc21
f1b7156c89
Merge pull request #3000 from xrh0905/xrh0905-patch-sed
Fix device or resource busy when patching IPv6 settings
2023-07-20 12:17:34 +10:00
jc21
98465cf1b0
Merge pull request #3018 from NginxProxyManager/dependabot/npm_and_yarn/docs/semver-7.5.2
Bump semver from 7.3.2 to 7.5.2 in /docs
2023-07-20 12:16:11 +10:00
jc21
137e865b66
Merge pull request #3069 from lug-gh/develop
update year 2022 -> 2023 in footer
2023-07-20 12:16:01 +10:00
lug-gh
e740fb4064
update year 2022 -> 2023 2023-07-19 13:27:17 +02:00
jc21
f91f0ee8db
Merge pull request #3044 from 6twenty/2741-suppress-s6-supervise-disk-writes
Fix #2741 - Prevent excessive disk writes by only adding frontend service when in development
2023-07-19 13:09:12 +10:00
Mike Fulcher
1c9f751512 Fix path to frontend service 2023-07-19 14:05:57 +12:00
dependabot[bot]
a602bdd514
Bump semver from 7.3.2 to 7.5.2 in /docs
Bumps [semver](https://github.com/npm/node-semver) from 7.3.2 to 7.5.2.
- [Release notes](https://github.com/npm/node-semver/releases)
- [Changelog](https://github.com/npm/node-semver/blob/main/CHANGELOG.md)
- [Commits](https://github.com/npm/node-semver/compare/v7.3.2...v7.5.2)

---
updated-dependencies:
- dependency-name: semver
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-19 00:02:51 +00:00
jc21
f7b2be68cc
Merge pull request #3048 from NginxProxyManager/dependabot/npm_and_yarn/docs/tough-cookie-4.1.3
Bump tough-cookie from 4.0.0 to 4.1.3 in /docs
2023-07-19 10:02:40 +10:00
jc21
ab4586fc6b
Merge pull request #3049 from deftdawg/patch-1
Add bunny.net DNS to DNS challenges
2023-07-19 10:02:29 +10:00
jc21
a984a68065
Merge pull request #3051 from NginxProxyManager/dependabot/npm_and_yarn/backend/semver-5.7.2
Bump semver from 5.7.1 to 5.7.2 in /backend
2023-07-19 10:02:04 +10:00
jc21
52875fca6e
Merge pull request #3053 from NginxProxyManager/dependabot/npm_and_yarn/test/semver-7.5.4
Bump semver from 7.3.2 to 7.5.4 in /test
2023-07-19 10:01:55 +10:00
jc21
63b50fcd95
Merge pull request #3054 from NginxProxyManager/dependabot/npm_and_yarn/frontend/semver-5.7.2
Bump semver from 5.7.1 to 5.7.2 in /frontend
2023-07-19 10:01:47 +10:00
jc21
5ab4aea03f
Merge pull request #3065 from NginxProxyManager/dependabot/npm_and_yarn/test/word-wrap-1.2.4
Bump word-wrap from 1.2.3 to 1.2.4 in /test
2023-07-19 10:01:40 +10:00
jc21
d73135378e
Merge pull request #3066 from NginxProxyManager/dependabot/npm_and_yarn/backend/word-wrap-1.2.4
Bump word-wrap from 1.2.3 to 1.2.4 in /backend
2023-07-19 10:01:30 +10:00
jc21
e19d685cb6
Merge pull request #3067 from NginxProxyManager/dependabot/npm_and_yarn/frontend/word-wrap-1.2.4
Bump word-wrap from 1.2.3 to 1.2.4 in /frontend
2023-07-19 10:01:20 +10:00
dependabot[bot]
c8caaa56d9
Bump word-wrap from 1.2.3 to 1.2.4 in /backend
Bumps [word-wrap](https://github.com/jonschlinkert/word-wrap) from 1.2.3 to 1.2.4.
- [Release notes](https://github.com/jonschlinkert/word-wrap/releases)
- [Commits](https://github.com/jonschlinkert/word-wrap/compare/1.2.3...1.2.4)

---
updated-dependencies:
- dependency-name: word-wrap
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-18 20:59:11 +00:00
dependabot[bot]
11a98f4c12
Bump word-wrap from 1.2.3 to 1.2.4 in /frontend
Bumps [word-wrap](https://github.com/jonschlinkert/word-wrap) from 1.2.3 to 1.2.4.
- [Release notes](https://github.com/jonschlinkert/word-wrap/releases)
- [Commits](https://github.com/jonschlinkert/word-wrap/compare/1.2.3...1.2.4)

---
updated-dependencies:
- dependency-name: word-wrap
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-18 20:59:11 +00:00
dependabot[bot]
4a85d4ac4e
Bump word-wrap from 1.2.3 to 1.2.4 in /test
Bumps [word-wrap](https://github.com/jonschlinkert/word-wrap) from 1.2.3 to 1.2.4.
- [Release notes](https://github.com/jonschlinkert/word-wrap/releases)
- [Commits](https://github.com/jonschlinkert/word-wrap/compare/1.2.3...1.2.4)

---
updated-dependencies:
- dependency-name: word-wrap
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-18 20:59:08 +00:00
dependabot[bot]
3138ba46ce
Bump semver from 5.7.1 to 5.7.2 in /frontend
Bumps [semver](https://github.com/npm/node-semver) from 5.7.1 to 5.7.2.
- [Release notes](https://github.com/npm/node-semver/releases)
- [Changelog](https://github.com/npm/node-semver/blob/v5.7.2/CHANGELOG.md)
- [Commits](https://github.com/npm/node-semver/compare/v5.7.1...v5.7.2)

---
updated-dependencies:
- dependency-name: semver
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-12 05:37:17 +00:00
dependabot[bot]
cdd0b2e6d3
Bump semver from 7.3.2 to 7.5.4 in /test
Bumps [semver](https://github.com/npm/node-semver) from 7.3.2 to 7.5.4.
- [Release notes](https://github.com/npm/node-semver/releases)
- [Changelog](https://github.com/npm/node-semver/blob/main/CHANGELOG.md)
- [Commits](https://github.com/npm/node-semver/compare/v7.3.2...v7.5.4)

---
updated-dependencies:
- dependency-name: semver
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-12 02:34:02 +00:00
dependabot[bot]
f458730d87
Bump semver from 5.7.1 to 5.7.2 in /backend
Bumps [semver](https://github.com/npm/node-semver) from 5.7.1 to 5.7.2.
- [Release notes](https://github.com/npm/node-semver/releases)
- [Changelog](https://github.com/npm/node-semver/blob/v5.7.2/CHANGELOG.md)
- [Commits](https://github.com/npm/node-semver/compare/v5.7.1...v5.7.2)

---
updated-dependencies:
- dependency-name: semver
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-11 02:29:25 +00:00
deftdawg
d20873dcbb
Add bunny.net DNS to DNS challenges
- Add support for bunny.net DNS challenges using @mwt's certbot-dns-bunny plugin.
2023-07-08 22:48:54 -04:00
dependabot[bot]
d1e9407e4d
Bump tough-cookie from 4.0.0 to 4.1.3 in /docs
Bumps [tough-cookie](https://github.com/salesforce/tough-cookie) from 4.0.0 to 4.1.3.
- [Release notes](https://github.com/salesforce/tough-cookie/releases)
- [Changelog](https://github.com/salesforce/tough-cookie/blob/master/CHANGELOG.md)
- [Commits](https://github.com/salesforce/tough-cookie/compare/v4.0.0...v4.1.3)

---
updated-dependencies:
- dependency-name: tough-cookie
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-08 14:17:45 +00:00
xrh0905
63ee69f432
Fix device or resource busy when patching IPv6 settings 2023-06-15 11:17:02 +08:00
Joe Manifold
f39e527680
drop --user on pip install dns plugin godaddy
Do not install dns_plugin into the user site because it will lack sys.path precedence to urllib3 in /opt/certbot/lib/python3.7/site-packages
2023-06-01 11:02:06 -07:00
Will Rouesnel
2dd4434ceb
Add support for nginx 444 default response
The default nginx 444 response drops the inbound connection without
sending any response to the client.
2023-05-22 11:59:50 +10:00
nietzscheanic
81054631f9
Fix for ignored ssl_protocols and ssl_ciphers directive in conf.d/include/ssl-ciphers.conf
nginx only uses the `ssl_protocols` directive in the `server{}` block of the first processed host config, which is the default config in `/etc/nginx/conf.d/default.conf`. in version `v2.9.20` the default ssl site was dropped by using `ssl_reject_handshake on` in the default host config. but beside the include of `conf.d/include/ssl-ciphers.conf` was removed from the default host config. that's why `tlsv1.3` isn't applied by default anymore, same thing with the defined cipher suites. npm is so broken since `2023-03-16`.

commit that broke the config -> a7f0c3b730
2023-05-19 14:13:29 +02:00
Felix Maaß
53d61bd626
Try to fix linter error in certbot plugin definitions. 2023-05-18 14:14:38 +02:00
Felix Maaß
847e879b3f
Update certbot-dns-plugins.js
Add dns wildcard certificate support for strato.de using the provided certbot plugin
2023-05-18 13:44:52 +02:00
Benjamin Hubert
4c59400731 added support for dns.he.net certbot plugin #2153 2023-05-16 22:38:43 +02:00
jc21
824c837a38
Merge pull request #2906 from NginxProxyManager/develop
Fix certbot plugins install when using PUID/PGID
2023-05-10 14:40:15 +10:00
jc21
2a06384a4a
Merge branch 'master' into develop 2023-05-10 14:40:06 +10:00
Jamie Curnow
05307aa253
Fix certbot plugins install when using PUID/PGID 2023-05-10 14:39:08 +10:00
jc21
3d2406ac3d
Merge pull request #2905 from NginxProxyManager/develop
v2.10.3
2023-05-10 14:09:04 +10:00
Jamie Curnow
0127dc7f03
Bump version 2023-05-10 11:32:22 +10:00
jc21
4349d42636
Merge pull request #2904 from NginxProxyManager/s6-verbose
Fixes for s6 timeout at startup
2023-05-10 11:31:17 +10:00
Jamie Curnow
4b6f9d9419
Remove s6 service timeout 2023-05-10 09:57:24 +10:00
Jamie Curnow
c3f019c911
Test ipv6 disabled in ci 2023-05-09 08:19:09 +10:00
Jamie Curnow
ecf0290203
Update s6-overlay 2023-05-09 08:15:44 +10:00
Jamie Curnow
4f41fe0c95
Update s6-overlay 2023-05-05 08:46:54 +10:00
Jamie Curnow
c3735fdbbb
Missed a file that was explicit verbose 2023-05-04 12:30:27 +10:00
Jamie Curnow
c432c34fb3
Small refactor of user/groups and add checks during startup. Only use -x in bash scripts when DEBUG=true set in env vars 2023-05-04 10:03:06 +10:00
Jamie Curnow
a1245bc161
Split up ownership to indentify point of failure 2023-05-04 08:27:38 +10:00
Jamie Curnow
db4ab1d548
Verbose debugging of s6 scripts 2023-05-03 16:01:27 +10:00
jc21
86ddd9c83c
Merge pull request #2784 from NginxProxyManager/develop
v2.10.2
2023-03-31 09:37:08 +10:00
jc21
67208e43cc
Merge branch 'master' into develop 2023-03-31 08:27:00 +10:00
Jamie Curnow
ddf80302c6
Bump version 2023-03-31 08:25:45 +10:00
jc21
5f2576946d
Merge pull request #2783 from NginxProxyManager/uidgid
Make PUID and PGID optional
2023-03-31 08:25:07 +10:00
Jamie Curnow
9fe07fa6c3
Update documentation 2023-03-30 15:37:59 +10:00
Jamie Curnow
d9b9af543e
Fix text replacement whoops 2023-03-30 15:03:57 +10:00
Jamie Curnow
eb2e2e0478
Throw in a docker restart during testing phase 2023-03-30 14:44:15 +10:00
Jamie Curnow
9225d5d442
Tweak test 2023-03-30 13:00:22 +10:00
Jamie Curnow
308a7149ed
Tweak test 2023-03-30 12:55:20 +10:00
Jamie Curnow
8a4a7d0caf
Allow 201 as success in test result 2023-03-30 12:51:26 +10:00
Jamie Curnow
5d03ede100
Add test for creating a host 2023-03-30 12:44:28 +10:00
Jamie Curnow
4a86bb42cc
Different approach, always create npmuser
even if the user id is zero, and then we'll always use it
2023-03-30 11:19:16 +10:00
Jamie Curnow
dad8561ea1
Use numbers for permissions in case npmuser doesn't exist 2023-03-30 10:20:20 +10:00
Jamie Curnow
56a92e5c0e
Run as root by default
Optionally run as another user/group only if
the env vars are specified. Should give flexibility
to those who need to run processes as root and open ports
without having to request additional priveleges
2023-03-30 09:04:37 +10:00
Jamie Curnow
9d672f5813
Own this nginx folder too 2023-03-29 14:04:48 +10:00
Jamie Curnow
d5ed70dbb6
Own this nginx folder too 2023-03-29 14:03:58 +10:00
jc21
c197e66d62
Merge pull request #2764 from NginxProxyManager/develop
v2.10.1
2023-03-29 08:54:30 +10:00
Jamie Curnow
91cf3c8873
Tweaks to docker compose ci after updates 2023-03-29 08:24:28 +10:00
Jamie Curnow
7f5e0414ac
Bump version 2023-03-29 07:22:15 +10:00
Jamie Curnow
d179887c15
Another fix for #2734, only chown parts of /etc/nginx 2023-03-28 10:39:26 +10:00
Jamie Curnow
35abb4d7ae
Execute permissions missing on script 2023-03-28 09:33:30 +10:00
Jamie Curnow
61b290e220
Chown each folder on separately
Really not sure why this fixes #2734 however it does actually
help the ownership script succeed specifically on arm7/raspbian
2023-03-28 08:50:10 +10:00
jc21
e1bcef6e5c
Merge pull request #2749 from NginxProxyManager/develop
v2.10.0
2023-03-27 12:17:07 +10:00
jc21
81f51f9e2d
Merge branch 'master' into develop 2023-03-27 08:29:08 +10:00
Jamie Curnow
661953db25
Bump version 2023-03-27 08:26:42 +10:00
jc21
065c2dac42
Merge pull request #2721 from NginxProxyManager/docker-user-group
Docker users and groups, refactor configuration
2023-03-27 08:19:57 +10:00
Jamie Curnow
c40e48e678
Fix docker restart because user already exists 2023-03-23 10:21:34 +10:00
Jamie Curnow
124cb18e17
Fix renewing certs because of permission errors 2023-03-22 13:40:36 +10:00
Jamie Curnow
5ac9dc0758
Attempt to set HOME for npmuser backend 2023-03-22 13:00:26 +10:00
Jamie Curnow
9a799d51ce
Optimize docker image a bit 2023-03-22 09:42:16 +10:00
Jamie Curnow
77eb618758
Fix pip installs running as non-root user 2023-03-22 09:41:59 +10:00
Jamie Curnow
79fedfcea4
Use consistent docker-compose file version in docs 2023-03-22 09:41:19 +10:00
Jamie Curnow
8fdb8ac853
Update docs 2023-03-21 18:26:28 +10:00
Jamie Curnow
4fdc80be01
Fix logical error with keys and mysql config 2023-03-21 17:59:27 +10:00
Jamie Curnow
f8e6c8d018
Fix mistake with debug output 2023-03-21 17:49:39 +10:00
Jamie Curnow
c3469de61b
Linting fixes 2023-03-21 17:11:16 +10:00
Jamie Curnow
ea61b15a40
don't zip log files anymore 2023-03-21 16:59:36 +10:00
Jamie Curnow
60175e6d8c
Updates for ci stack 2023-03-21 16:56:45 +10:00
Jamie Curnow
2a07445005
Refactor configuration
- No longer use config npm package
- Prefer config from env vars, though still has support for config file
- No longer writes a config file for database config
- Writes keys to a new file in /data folder
- Removes a lot of cruft and improves config understanding
2023-03-21 16:53:39 +10:00
Jamie Curnow
dad3e1da7c
Adds support to run processes as a user/group, defined
with PUID and PGID environment variables

- Detects if image is run with a user in docker command and fails if so
- Adds s6 prepare scripts for adding a 'npmuser'
- Split up and refactor the s6 prepare scripts
- Runs nginx and backend node as 'npmuser'
- Changes ownership of files required at startup
2023-03-20 16:56:52 +10:00
323 changed files with 14775 additions and 19783 deletions

21
.github/workflows/stale.yml vendored Normal file
View File

@ -0,0 +1,21 @@
name: 'Close stale issues and PRs'
on:
schedule:
- cron: '30 1 * * *'
workflow_dispatch:
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v9
with:
stale-issue-label: 'stale'
stale-pr-label: 'stale'
stale-issue-message: 'Issue is now considered stale. If you want to keep it open, please comment :+1:'
stale-pr-message: 'PR is now considered stale. If you want to keep it open, please comment :+1:'
close-issue-message: 'Issue was closed due to inactivity.'
close-pr-message: 'PR was closed due to inactivity.'
days-before-stale: 182
days-before-close: 365
operations-per-run: 50

4
.gitignore vendored
View File

@ -3,3 +3,7 @@
._*
.vscode
certbot-help.txt
test/node_modules
*/node_modules
docker/dev/dnsrouter-config.json.tmp
docker/dev/resolv.conf

View File

@ -1 +1 @@
2.9.22
2.12.3

295
Jenkinsfile vendored
View File

@ -14,16 +14,12 @@ pipeline {
ansiColor('xterm')
}
environment {
IMAGE = "nginx-proxy-manager"
IMAGE = 'nginx-proxy-manager'
BUILD_VERSION = getVersion()
MAJOR_VERSION = "2"
BRANCH_LOWER = "${BRANCH_NAME.toLowerCase().replaceAll('/', '-')}"
COMPOSE_PROJECT_NAME = "npm_${BRANCH_LOWER}_${BUILD_NUMBER}"
COMPOSE_FILE = 'docker/docker-compose.ci.yml'
MAJOR_VERSION = '2'
BRANCH_LOWER = "${BRANCH_NAME.toLowerCase().replaceAll('\\\\', '-').replaceAll('/', '-').replaceAll('\\.', '-')}"
BUILDX_NAME = "npm_${BRANCH_LOWER}_${BUILD_NUMBER}"
COMPOSE_INTERACTIVE_NO_CLI = 1
BUILDX_NAME = "${COMPOSE_PROJECT_NAME}"
DOCS_BUCKET = 'jc21-npm-site'
DOCS_CDN = 'EN1G6DEWZUTDT'
}
stages {
stage('Environment') {
@ -47,7 +43,7 @@ pipeline {
steps {
script {
// Defaults to the Branch name, which is applies to all branches AND pr's
buildxPushTags = "-t docker.io/jc21/${IMAGE}:github-${BRANCH_LOWER}"
buildxPushTags = "-t docker.io/nginxproxymanager/${IMAGE}-dev:${BRANCH_LOWER}"
}
}
}
@ -60,101 +56,153 @@ pipeline {
sh 'sed -i -E "s/(version-)[0-9]+\\.[0-9]+\\.[0-9]+(-green)/\\1${BUILD_VERSION}\\2/" README.md'
}
}
}
}
stage('Build and Test') {
steps {
script {
// Frontend and Backend
def shStatusCode = sh(label: 'Checking and Building', returnStatus: true, script: '''
set -e
./scripts/ci/frontend-build > ${WORKSPACE}/tmp-sh-build 2>&1
./scripts/ci/test-and-build > ${WORKSPACE}/tmp-sh-build 2>&1
''')
shOutput = readFile "${env.WORKSPACE}/tmp-sh-build"
if (shStatusCode != 0) {
error "${shOutput}"
stage('Docker Login') {
steps {
withCredentials([usernamePassword(credentialsId: 'jc21-dockerhub', passwordVariable: 'dpass', usernameVariable: 'duser')]) {
sh 'docker login -u "${duser}" -p "${dpass}"'
}
}
}
}
post {
always {
sh 'rm -f ${WORKSPACE}/tmp-sh-build'
}
failure {
npmGithubPrComment("CI Error:\n\n```\n${shOutput}\n```", true)
}
}
}
stage('Integration Tests Sqlite') {
steps {
// Bring up a stack
sh 'docker-compose up -d fullstack-sqlite'
sh './scripts/wait-healthy $(docker-compose ps -q fullstack-sqlite) 120'
// Run tests
sh 'rm -rf test/results'
sh 'docker-compose up cypress-sqlite'
// Get results
sh 'docker cp -L "$(docker-compose ps -q cypress-sqlite):/test/results" test/'
}
post {
always {
// Dumps to analyze later
sh 'mkdir -p debug'
sh 'docker-compose logs fullstack-sqlite | gzip > debug/docker_fullstack_sqlite.log.gz'
sh 'docker-compose logs db | gzip > debug/docker_db.log.gz'
// Cypress videos and screenshot artifacts
dir(path: 'test/results') {
archiveArtifacts allowEmptyArchive: true, artifacts: '**/*', excludes: '**/*.xml'
stage('Builds') {
parallel {
stage('Project') {
steps {
script {
// Frontend and Backend
def shStatusCode = sh(label: 'Checking and Building', returnStatus: true, script: '''
set -e
./scripts/ci/frontend-build > ${WORKSPACE}/tmp-sh-build 2>&1
./scripts/ci/test-and-build > ${WORKSPACE}/tmp-sh-build 2>&1
''')
shOutput = readFile "${env.WORKSPACE}/tmp-sh-build"
if (shStatusCode != 0) {
error "${shOutput}"
}
}
}
junit 'test/results/junit/*'
}
}
}
stage('Integration Tests Mysql') {
steps {
// Bring up a stack
sh 'docker-compose up -d fullstack-mysql'
sh './scripts/wait-healthy $(docker-compose ps -q fullstack-mysql) 120'
// Run tests
sh 'rm -rf test/results'
sh 'docker-compose up cypress-mysql'
// Get results
sh 'docker cp -L "$(docker-compose ps -q cypress-mysql):/test/results" test/'
}
post {
always {
// Dumps to analyze later
sh 'mkdir -p debug'
sh 'docker-compose logs fullstack-mysql | gzip > debug/docker_fullstack_mysql.log.gz'
sh 'docker-compose logs db | gzip > debug/docker_db.log.gz'
// Cypress videos and screenshot artifacts
dir(path: 'test/results') {
archiveArtifacts allowEmptyArchive: true, artifacts: '**/*', excludes: '**/*.xml'
post {
always {
sh 'rm -f ${WORKSPACE}/tmp-sh-build'
}
failure {
npmGithubPrComment("CI Error:\n\n```\n${shOutput}\n```", true)
}
}
}
stage('Docs') {
steps {
dir(path: 'docs') {
sh 'yarn install'
sh 'yarn build'
}
}
junit 'test/results/junit/*'
}
}
}
stage('Docs') {
stage('Test Sqlite') {
environment {
COMPOSE_PROJECT_NAME = "npm_${BRANCH_LOWER}_${BUILD_NUMBER}_sqlite"
COMPOSE_FILE = 'docker/docker-compose.ci.yml:docker/docker-compose.ci.sqlite.yml'
}
when {
not {
equals expected: 'UNSTABLE', actual: currentBuild.result
}
}
steps {
dir(path: 'docs') {
sh 'yarn install'
sh 'yarn build'
sh 'rm -rf ./test/results/junit/*'
sh './scripts/ci/fulltest-cypress'
}
post {
always {
// Dumps to analyze later
sh 'mkdir -p debug/sqlite'
sh 'docker logs $(docker-compose ps --all -q fullstack) > debug/sqlite/docker_fullstack.log 2>&1'
sh 'docker logs $(docker-compose ps --all -q stepca) > debug/sqlite/docker_stepca.log 2>&1'
sh 'docker logs $(docker-compose ps --all -q pdns) > debug/sqlite/docker_pdns.log 2>&1'
sh 'docker logs $(docker-compose ps --all -q pdns-db) > debug/sqlite/docker_pdns-db.log 2>&1'
sh 'docker logs $(docker-compose ps --all -q dnsrouter) > debug/sqlite/docker_dnsrouter.log 2>&1'
junit 'test/results/junit/*'
sh 'docker-compose down --remove-orphans --volumes -t 30 || true'
}
dir(path: 'docs/.vuepress/dist') {
sh 'tar -czf ../../docs.tgz *'
unstable {
dir(path: 'test/results') {
archiveArtifacts(allowEmptyArchive: true, artifacts: '**/*', excludes: '**/*.xml')
}
}
}
}
stage('Test Mysql') {
environment {
COMPOSE_PROJECT_NAME = "npm_${BRANCH_LOWER}_${BUILD_NUMBER}_mysql"
COMPOSE_FILE = 'docker/docker-compose.ci.yml:docker/docker-compose.ci.mysql.yml'
}
when {
not {
equals expected: 'UNSTABLE', actual: currentBuild.result
}
}
steps {
sh 'rm -rf ./test/results/junit/*'
sh './scripts/ci/fulltest-cypress'
}
post {
always {
// Dumps to analyze later
sh 'mkdir -p debug/mysql'
sh 'docker logs $(docker-compose ps --all -q fullstack) > debug/mysql/docker_fullstack.log 2>&1'
sh 'docker logs $(docker-compose ps --all -q stepca) > debug/mysql/docker_stepca.log 2>&1'
sh 'docker logs $(docker-compose ps --all -q pdns) > debug/mysql/docker_pdns.log 2>&1'
sh 'docker logs $(docker-compose ps --all -q pdns-db) > debug/mysql/docker_pdns-db.log 2>&1'
sh 'docker logs $(docker-compose ps --all -q dnsrouter) > debug/mysql/docker_dnsrouter.log 2>&1'
junit 'test/results/junit/*'
sh 'docker-compose down --remove-orphans --volumes -t 30 || true'
}
unstable {
dir(path: 'test/results') {
archiveArtifacts(allowEmptyArchive: true, artifacts: '**/*', excludes: '**/*.xml')
}
}
}
}
stage('Test Postgres') {
environment {
COMPOSE_PROJECT_NAME = "npm_${BRANCH_LOWER}_${BUILD_NUMBER}_postgres"
COMPOSE_FILE = 'docker/docker-compose.ci.yml:docker/docker-compose.ci.postgres.yml'
}
when {
not {
equals expected: 'UNSTABLE', actual: currentBuild.result
}
}
steps {
sh 'rm -rf ./test/results/junit/*'
sh './scripts/ci/fulltest-cypress'
}
post {
always {
// Dumps to analyze later
sh 'mkdir -p debug/postgres'
sh 'docker logs $(docker-compose ps --all -q fullstack) > debug/postgres/docker_fullstack.log 2>&1'
sh 'docker logs $(docker-compose ps --all -q stepca) > debug/postgres/docker_stepca.log 2>&1'
sh 'docker logs $(docker-compose ps --all -q pdns) > debug/postgres/docker_pdns.log 2>&1'
sh 'docker logs $(docker-compose ps --all -q pdns-db) > debug/postgres/docker_pdns-db.log 2>&1'
sh 'docker logs $(docker-compose ps --all -q dnsrouter) > debug/postgres/docker_dnsrouter.log 2>&1'
sh 'docker logs $(docker-compose ps --all -q db-postgres) > debug/postgres/docker_db-postgres.log 2>&1'
sh 'docker logs $(docker-compose ps --all -q authentik) > debug/postgres/docker_authentik.log 2>&1'
sh 'docker logs $(docker-compose ps --all -q authentik-redis) > debug/postgres/docker_authentik-redis.log 2>&1'
sh 'docker logs $(docker-compose ps --all -q authentik-ldap) > debug/postgres/docker_authentik-ldap.log 2>&1'
archiveArtifacts(artifacts: 'docs/docs.tgz', allowEmptyArchive: false)
junit 'test/results/junit/*'
sh 'docker-compose down --remove-orphans --volumes -t 30 || true'
}
unstable {
dir(path: 'test/results') {
archiveArtifacts(allowEmptyArchive: true, artifacts: '**/*', excludes: '**/*.xml')
}
}
}
}
stage('MultiArch Build') {
@ -164,60 +212,59 @@ 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 Deploy') {
when {
allOf {
branch 'master'
not {
equals expected: 'UNSTABLE', actual: currentBuild.result
stage('Docs / Comment') {
parallel {
stage('Docs Job') {
when {
allOf {
branch pattern: "^(develop|master)\$", comparator: "REGEXP"
not {
equals expected: 'UNSTABLE', actual: currentBuild.result
}
}
}
steps {
build wait: false, job: 'nginx-proxy-manager-docs', parameters: [string(name: 'docs_branch', value: "$BRANCH_NAME")]
}
}
}
steps {
npmDocsRelease("$DOCS_BUCKET", "$DOCS_CDN")
}
}
stage('PR Comment') {
when {
allOf {
changeRequest()
not {
equals expected: 'UNSTABLE', actual: currentBuild.result
stage('PR Comment') {
when {
allOf {
changeRequest()
not {
equals expected: 'UNSTABLE', actual: currentBuild.result
}
}
}
steps {
script {
npmGithubPrComment("""Docker Image for build ${BUILD_NUMBER} is available on
[DockerHub](https://cloud.docker.com/repository/docker/nginxproxymanager/${IMAGE}-dev)
as `nginxproxymanager/${IMAGE}-dev:${BRANCH_LOWER}`
**Note:** ensure you backup your NPM instance before testing this image! Especially if there are database changes
**Note:** this is a different docker image namespace than the official image
""", true)
}
}
}
}
steps {
script {
npmGithubPrComment("Docker Image for build ${BUILD_NUMBER} is available on [DockerHub](https://cloud.docker.com/repository/docker/jc21/${IMAGE}) as `jc21/${IMAGE}:github-${BRANCH_LOWER}`\n\n**Note:** ensure you backup your NPM instance before testing this PR image! Especially if this PR contains database changes.", true)
}
}
}
}
post {
always {
sh 'docker-compose down --remove-orphans --volumes -t 30'
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"'
sh 'docker run --rm -v "$(pwd):/data" jc21/ci-tools chown -R "$(id -u):$(id -g)" /data'
printResult(true)
}
failure {
archiveArtifacts(artifacts: 'debug/**.*', allowEmptyArchive: true)
juxtapose event: 'failure'
sh 'figlet "FAILURE"'
archiveArtifacts(artifacts: 'debug/**/*.*', allowEmptyArchive: true)
}
unstable {
archiveArtifacts(artifacts: 'debug/**.*', allowEmptyArchive: true)
juxtapose event: 'unstable'
sh 'figlet "UNSTABLE"'
archiveArtifacts(artifacts: 'debug/**/*.*', allowEmptyArchive: true)
}
}
}

View File

@ -1,7 +1,7 @@
<p align="center">
<img src="https://nginxproxymanager.com/github.png">
<br><br>
<img src="https://img.shields.io/badge/version-2.9.22-green.svg?style=for-the-badge">
<img src="https://img.shields.io/badge/version-2.12.3-green.svg?style=for-the-badge">
<a href="https://hub.docker.com/repository/docker/jc21/nginx-proxy-manager">
<img src="https://img.shields.io/docker/stars/jc21/nginx-proxy-manager.svg?style=for-the-badge">
</a>
@ -19,7 +19,7 @@ running at home or otherwise, including free SSL, without having to know too muc
## Project Goal
I created this project to fill a personal need to provide users with a easy way to accomplish reverse
I created this project to fill a personal need to provide users with an easy way to accomplish reverse
proxying hosts with SSL termination and it had to be so easy that a monkey could do it. This goal hasn't changed.
While there might be advanced options they are optional and the project should be as simple as possible
so that the barrier for entry here is low.
@ -56,10 +56,9 @@ I won't go in to too much detail here but here are the basics for someone new to
2. Create a docker-compose.yml file similar to this:
```yml
version: '3'
services:
app:
image: 'jc21/nginx-proxy-manager:latest'
image: 'docker.io/jc21/nginx-proxy-manager:latest'
restart: unless-stopped
ports:
- '80:80'
@ -70,6 +69,8 @@ services:
- ./letsencrypt:/etc/letsencrypt
```
This is the bare minimum configuration required. See the [documentation](https://nginxproxymanager.com/setup/) for more.
3. Bring up your stack by running
```bash
@ -96,7 +97,18 @@ Password: changeme
Immediately after logging in with this default user you will be asked to modify your details and change your password.
## Contributors
## Contributing
All are welcome to create pull requests for this project, against the `develop` branch. Official releases are created from the `master` branch.
CI is used in this project. All PR's must pass before being considered. After passing,
docker builds for PR's are available on dockerhub for manual verifications.
Documentation within the `develop` branch is available for preview at
[https://develop.nginxproxymanager.com](https://develop.nginxproxymanager.com)
### Contributors
Special thanks to [all of our contributors](https://github.com/NginxProxyManager/nginx-proxy-manager/graphs/contributors).
@ -105,5 +117,4 @@ Special thanks to [all of our contributors](https://github.com/NginxProxyManager
1. [Found a bug?](https://github.com/NginxProxyManager/nginx-proxy-manager/issues)
2. [Discussions](https://github.com/NginxProxyManager/nginx-proxy-manager/discussions)
3. [Development Gitter](https://gitter.im/nginx-proxy-manager/community)
4. [Reddit](https://reddit.com/r/nginxproxymanager)
3. [Reddit](https://reddit.com/r/nginxproxymanager)

View File

@ -1,8 +0,0 @@
{
"editor.insertSpaces": false,
"editor.formatOnSave": true,
"files.trimTrailingWhitespace": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
}
}

View File

@ -2,6 +2,7 @@ const express = require('express');
const bodyParser = require('body-parser');
const fileUpload = require('express-fileupload');
const compression = require('compression');
const config = require('./lib/config');
const log = require('./logger').express;
/**
@ -24,7 +25,7 @@ app.enable('trust proxy', ['loopback', 'linklocal', 'uniquelocal']);
app.enable('strict routing');
// pretty print JSON when not live
if (process.env.NODE_ENV !== 'production') {
if (config.debug()) {
app.set('json spaces', 2);
}
@ -51,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
@ -65,7 +66,7 @@ app.use(function (err, req, res, next) {
}
};
if (process.env.NODE_ENV === 'development' || (req.baseUrl + req.path).includes('nginx/certificates')) {
if (config.debug() || (req.baseUrl + req.path).includes('nginx/certificates')) {
payload.debug = {
stack: typeof err.stack !== 'undefined' && err.stack ? err.stack.split('\n') : null,
previous: err.previous
@ -74,7 +75,7 @@ app.use(function (err, req, res, next) {
// Not every error is worth logging - but this is good for now until it gets annoying.
if (typeof err.stack !== 'undefined' && err.stack) {
if (process.env.NODE_ENV === 'development' || process.env.DEBUG) {
if (config.debug()) {
log.debug(err.stack);
} else if (typeof err.public == 'undefined' || !err.public) {
log.warn(err.message);

View File

@ -1,6 +1,6 @@
{
"database": {
"engine": "mysql",
"engine": "mysql2",
"host": "db",
"name": "npm",
"user": "npm",

View File

@ -1,33 +1,27 @@
const config = require('config');
const config = require('./lib/config');
if (!config.has('database')) {
throw new Error('Database config does not exist! Please read the instructions: https://github.com/jc21/nginx-proxy-manager/blob/master/doc/INSTALL.md');
throw new Error('Database config does not exist! Please read the instructions: https://nginxproxymanager.com/setup/');
}
function generateDbConfig() {
if (config.database.engine === 'knex-native') {
return config.database.knex;
} else
return {
client: config.database.engine,
connection: {
host: config.database.host,
user: config.database.user,
password: config.database.password,
database: config.database.name,
port: config.database.port
},
migrations: {
tableName: 'migrations'
}
};
const cfg = config.get('database');
if (cfg.engine === 'knex-native') {
return cfg.knex;
}
return {
client: cfg.engine,
connection: {
host: cfg.host,
user: cfg.user,
password: cfg.password,
database: cfg.name,
port: cfg.port
},
migrations: {
tableName: 'migrations'
}
};
}
let data = generateDbConfig();
if (typeof config.database.version !== 'undefined') {
data.version = config.database.version;
}
module.exports = require('knex')(data);
module.exports = require('knex')(generateDbConfig());

File diff suppressed because it is too large Load Diff

View File

@ -1,26 +1,20 @@
#!/usr/bin/env node
const schema = require('./schema');
const logger = require('./logger').global;
async function appStart () {
// Create config file db settings if environment variables have been set
await createDbConfigFromEnvironment();
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();
@ -37,95 +31,11 @@ async function appStart () {
});
})
.catch((err) => {
logger.error(err.message);
logger.error(err.message, err);
setTimeout(appStart, 1000);
});
}
async function createDbConfigFromEnvironment() {
return new Promise((resolve, reject) => {
const envMysqlHost = process.env.DB_MYSQL_HOST || null;
const envMysqlPort = process.env.DB_MYSQL_PORT || null;
const envMysqlUser = process.env.DB_MYSQL_USER || null;
const envMysqlName = process.env.DB_MYSQL_NAME || null;
let envSqliteFile = process.env.DB_SQLITE_FILE || null;
const fs = require('fs');
const filename = (process.env.NODE_CONFIG_DIR || './config') + '/' + (process.env.NODE_ENV || 'default') + '.json';
let configData = {};
try {
configData = require(filename);
} catch (err) {
// do nothing
}
if (configData.database && configData.database.engine && !configData.database.fromEnv) {
logger.info('Manual db configuration already exists, skipping config creation from environment variables');
resolve();
return;
}
if ((!envMysqlHost || !envMysqlPort || !envMysqlUser || !envMysqlName) && !envSqliteFile){
envSqliteFile = '/data/database.sqlite';
logger.info(`No valid environment variables for database provided, using default SQLite file '${envSqliteFile}'`);
}
if (envMysqlHost && envMysqlPort && envMysqlUser && envMysqlName) {
const newConfig = {
fromEnv: true,
engine: 'mysql',
host: envMysqlHost,
port: envMysqlPort,
user: envMysqlUser,
password: process.env.DB_MYSQL_PASSWORD,
name: envMysqlName,
};
if (JSON.stringify(configData.database) === JSON.stringify(newConfig)) {
// Config is unchanged, skip overwrite
resolve();
return;
}
logger.info('Generating MySQL knex configuration from environment variables');
configData.database = newConfig;
} else {
const newConfig = {
fromEnv: true,
engine: 'knex-native',
knex: {
client: 'sqlite3',
connection: {
filename: envSqliteFile
},
useNullAsDefault: true
}
};
if (JSON.stringify(configData.database) === JSON.stringify(newConfig)) {
// Config is unchanged, skip overwrite
resolve();
return;
}
logger.info('Generating SQLite knex configuration');
configData.database = newConfig;
}
// Write config
fs.writeFile(filename, JSON.stringify(configData, null, 2), (err) => {
if (err) {
logger.error('Could not write db config to config file: ' + filename);
reject(err);
} else {
logger.debug('Wrote db configuration to config file: ' + filename);
resolve();
}
});
});
}
try {
appStart();
} catch (err) {

View File

@ -81,7 +81,7 @@ const internalAccessList = {
return internalAccessList.build(row)
.then(() => {
if (row.proxy_host_count) {
if (parseInt(row.proxy_host_count, 10)) {
return internalNginx.bulkGenerateConfigs('proxy_host', row.proxy_hosts);
}
})
@ -204,7 +204,6 @@ const internalAccessList = {
});
}
})
.then(internalNginx.reload)
.then(() => {
// Add to audit log
return internalAuditLog.add(access, {
@ -224,10 +223,10 @@ const internalAccessList = {
.then((row) => {
return internalAccessList.build(row)
.then(() => {
if (row.proxy_host_count) {
if (parseInt(row.proxy_host_count, 10)) {
return internalNginx.bulkGenerateConfigs('proxy_host', row.proxy_hosts);
}
})
}).then(internalNginx.reload)
.then(() => {
return internalAccessList.maskItems(row);
});
@ -253,9 +252,13 @@ const internalAccessList = {
let query = accessListModel
.query()
.select('access_list.*', accessListModel.raw('COUNT(proxy_host.id) as proxy_host_count'))
.joinRaw('LEFT JOIN `proxy_host` ON `proxy_host`.`access_list_id` = `access_list`.`id` AND `proxy_host`.`is_deleted` = 0')
.leftJoin('proxy_host', function() {
this.on('proxy_host.access_list_id', '=', 'access_list.id')
.andOn('proxy_host.is_deleted', '=', 0);
})
.where('access_list.is_deleted', 0)
.andWhere('access_list.id', data.id)
.groupBy('access_list.id')
.allowGraph('[owner,items,clients,proxy_hosts.[certificate,access_list.[clients,items]]]')
.first();
@ -270,7 +273,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) {
@ -297,7 +300,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);
}
@ -374,7 +377,10 @@ const internalAccessList = {
let query = accessListModel
.query()
.select('access_list.*', accessListModel.raw('COUNT(proxy_host.id) as proxy_host_count'))
.joinRaw('LEFT JOIN `proxy_host` ON `proxy_host`.`access_list_id` = `access_list`.`id` AND `proxy_host`.`is_deleted` = 0')
.leftJoin('proxy_host', function() {
this.on('proxy_host.access_list_id', '=', 'access_list.id')
.andOn('proxy_host.is_deleted', '=', 0);
})
.where('access_list.is_deleted', 0)
.groupBy('access_list.id')
.allowGraph('[owner,items,clients]')
@ -502,8 +508,13 @@ const internalAccessList = {
if (typeof item.password !== 'undefined' && item.password.length) {
logger.info('Adding: ' + item.username);
utils.execFile('/usr/bin/htpasswd', ['-b', htpasswd_file, item.username, item.password])
.then((/*result*/) => {
utils.execFile('openssl', ['passwd', '-apr1', item.password])
.then((res) => {
try {
fs.appendFileSync(htpasswd_file, item.username + ':' + res + '\n', {encoding: 'utf8'});
} catch (err) {
reject(err);
}
next();
})
.catch((err) => {

View File

@ -1,5 +1,6 @@
const error = require('../lib/error');
const auditLogModel = require('../models/audit-log');
const error = require('../lib/error');
const auditLogModel = require('../models/audit-log');
const {castJsonIfNeed} = require('../lib/helpers');
const internalAuditLog = {
@ -22,9 +23,9 @@ const internalAuditLog = {
.allowGraph('[user]');
// Query is used for searching
if (typeof search_query === 'string') {
if (typeof search_query === 'string' && search_query.length > 0) {
query.where(function () {
this.where('meta', 'like', '%' + search_query + '%');
this.where(castJsonIfNeed('meta'), 'like', '%' + search_query + '%');
});
}

View File

@ -1,33 +1,40 @@
const _ = require('lodash');
const fs = require('fs');
const https = require('https');
const tempWrite = require('temp-write');
const moment = require('moment');
const logger = require('../logger').ssl;
const error = require('../lib/error');
const utils = require('../lib/utils');
const certificateModel = require('../models/certificate');
const dnsPlugins = require('../global/certbot-dns-plugins');
const internalAuditLog = require('./audit-log');
const internalNginx = require('./nginx');
const internalHost = require('./host');
const letsencryptStaging = process.env.NODE_ENV !== 'production';
const _ = require('lodash');
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 letsencryptStaging = config.useLetsencryptStaging();
const letsencryptServer = config.useLetsencryptServer();
const letsencryptConfig = '/etc/letsencrypt.ini';
const certbotCommand = 'certbot';
const archiver = require('archiver');
const path = require('path');
const { isArray } = require('lodash');
function omissions() {
return ['is_deleted'];
return ['is_deleted', 'owner.is_deleted'];
}
const internalCertificate = {
allowedSslFiles: ['certificate', 'certificate_key', 'intermediate_certificate'],
intervalTimeout: 1000 * 60 * 60, // 1 hour
interval: null,
intervalProcessing: false,
allowedSslFiles: ['certificate', 'certificate_key', 'intermediate_certificate'],
intervalTimeout: 1000 * 60 * 60, // 1 hour
interval: null,
intervalProcessing: false,
renewBeforeExpirationBy: [30, 'days'],
initTimer: () => {
logger.info('Let\'s Encrypt Renewal Timer initialized');
@ -42,60 +49,51 @@ const internalCertificate = {
processExpiringHosts: () => {
if (!internalCertificate.intervalProcessing) {
internalCertificate.intervalProcessing = true;
logger.info('Renewing SSL certs close to expiry...');
logger.info('Renewing SSL certs expiring within ' + internalCertificate.renewBeforeExpirationBy[0] + ' ' + internalCertificate.renewBeforeExpirationBy[1] + ' ...');
const cmd = certbotCommand + ' renew --non-interactive --quiet ' +
'--config "' + letsencryptConfig + '" ' +
'--preferred-challenges "dns,http" ' +
'--disable-hook-validation ' +
(letsencryptStaging ? '--staging' : '');
const expirationThreshold = moment().add(internalCertificate.renewBeforeExpirationBy[0], internalCertificate.renewBeforeExpirationBy[1]).format('YYYY-MM-DD HH:mm:ss');
return utils.exec(cmd)
.then((result) => {
if (result) {
logger.info('Renew Result: ' + result);
// Fetch all the letsencrypt certs from the db that will expire within the configured threshold
certificateModel
.query()
.where('is_deleted', 0)
.andWhere('provider', 'letsencrypt')
.andWhere('expires_on', '<', expirationThreshold)
.then((certificates) => {
if (!certificates || !certificates.length) {
return null;
}
return internalNginx.reload()
.then(() => {
logger.info('Renew Complete');
return result;
});
})
.then(() => {
// Now go and fetch all the letsencrypt certs from the db and query the files and update expiry times
return certificateModel
.query()
.where('is_deleted', 0)
.andWhere('provider', 'letsencrypt')
.then((certificates) => {
if (certificates && certificates.length) {
let promises = [];
certificates.map(function (certificate) {
promises.push(
internalCertificate.getCertificateInfoFromFile('/etc/letsencrypt/live/npm-' + certificate.id + '/fullchain.pem')
.then((cert_info) => {
return certificateModel
.query()
.where('id', certificate.id)
.andWhere('provider', 'letsencrypt')
.patch({
expires_on: moment(cert_info.dates.to, 'X').format('YYYY-MM-DD HH:mm:ss')
});
})
.catch((err) => {
// Don't want to stop the train here, just log the error
logger.error(err.message);
})
);
});
return Promise.all(promises);
}
});
/**
* Renews must be run sequentially or we'll get an error 'Another
* instance of Certbot is already running.'
*/
let sequence = Promise.resolve();
certificates.forEach(function (certificate) {
sequence = sequence.then(() =>
internalCertificate
.renew(
{
can: () =>
Promise.resolve({
permission_visibility: 'all',
}),
token: new tokenModel(),
},
{ id: certificate.id },
)
.catch((err) => {
// Don't want to stop the train here, just log the error
logger.error(err.message);
}),
);
});
return sequence;
})
.then(() => {
logger.info('Completed SSL cert renew process');
internalCertificate.intervalProcessing = false;
})
.catch((err) => {
@ -211,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, {
@ -314,6 +313,9 @@ const internalCertificate = {
.where('is_deleted', 0)
.andWhere('id', data.id)
.allowGraph('[owner]')
.allowGraph('[proxy_hosts]')
.allowGraph('[redirection_hosts]')
.allowGraph('[dead_hosts]')
.first();
if (access_data.permission_visibility !== 'all') {
@ -327,7 +329,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
@ -416,7 +418,7 @@ const internalCertificate = {
return internalCertificate.get(access, {id: data.id});
})
.then((row) => {
if (!row) {
if (!row || !row.id) {
throw new error.ItemNotFoundError(data.id);
}
@ -465,6 +467,9 @@ const internalCertificate = {
.where('is_deleted', 0)
.groupBy('id')
.allowGraph('[owner]')
.allowGraph('[proxy_hosts]')
.allowGraph('[redirection_hosts]')
.allowGraph('[dead_hosts]')
.orderBy('nice_name', 'ASC');
if (access_data.permission_visibility !== 'all') {
@ -734,29 +739,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');
@ -831,15 +836,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 + '" ' +
'--cert-name "npm-' + certificate.id + '" ' +
const cmd = `${certbotCommand} certonly ` +
`--config '${letsencryptConfig}' ` +
'--work-dir "/tmp/letsencrypt-lib" ' +
'--logs-dir "/tmp/letsencrypt-log" ' +
`--cert-name "npm-${certificate.id}" ` +
'--agree-tos ' +
'--authenticator webroot ' +
'--email "' + certificate.meta.letsencrypt_email + '" ' +
`--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);
@ -852,72 +860,65 @@ const internalCertificate = {
/**
* @param {Object} certificate the certificate row
* @param {String} dns_provider the dns provider name (key used in `certbot-dns-plugins.js`)
* @param {String} dns_provider the dns provider name (key used in `certbot-dns-plugins.json`)
* @param {String | null} credentials the content of this providers credentials file
* @param {String} propagation_seconds the cloudflare api token
* @param {String} propagation_seconds
* @returns {Promise}
*/
requestLetsEncryptSslWithDnsChallenge: (certificate) => {
const dns_plugin = dnsPlugins[certificate.meta.dns_provider];
if (!dns_plugin) {
throw Error(`Unknown DNS provider '${certificate.meta.dns_provider}'`);
}
logger.info(`Requesting Let'sEncrypt certificates via ${dns_plugin.display_name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);
requestLetsEncryptSslWithDnsChallenge: async (certificate) => {
await certbot.installPlugin(certificate.meta.dns_provider);
const dnsPlugin = dnsPlugins[certificate.meta.dns_provider];
logger.info(`Requesting Let'sEncrypt certificates via ${dnsPlugin.name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);
const credentialsLocation = '/etc/letsencrypt/credentials/credentials-' + certificate.id;
// Escape single quotes and backslashes
const escapedCredentials = certificate.meta.dns_provider_credentials.replaceAll('\'', '\\\'').replaceAll('\\', '\\\\');
const credentialsCmd = 'mkdir -p /etc/letsencrypt/credentials 2> /dev/null; echo \'' + escapedCredentials + '\' > \'' + credentialsLocation + '\' && chmod 600 \'' + credentialsLocation + '\'';
// we call `. /opt/certbot/bin/activate` (`.` is alternative to `source` in dash) to access certbot venv
let prepareCmd = '. /opt/certbot/bin/activate && pip install ' + dns_plugin.package_name + (dns_plugin.version_requirement || '') + ' ' + dns_plugin.dependencies + ' && deactivate';
fs.mkdirSync('/etc/letsencrypt/credentials', { recursive: true });
fs.writeFileSync(credentialsLocation, certificate.meta.dns_provider_credentials, {mode: 0o600});
// Whether the plugin has a --<name>-credentials argument
const hasConfigArg = certificate.meta.dns_provider !== 'route53';
let mainCmd = certbotCommand + ' certonly ' +
'--config "' + letsencryptConfig + '" ' +
'--cert-name "npm-' + certificate.id + '" ' +
`--config '${letsencryptConfig}' ` +
'--work-dir "/tmp/letsencrypt-lib" ' +
'--logs-dir "/tmp/letsencrypt-log" ' +
`--cert-name 'npm-${certificate.id}' ` +
'--agree-tos ' +
'--email "' + certificate.meta.letsencrypt_email + '" ' +
'--domains "' + certificate.domain_names.join(',') + '" ' +
'--authenticator ' + dns_plugin.full_plugin_name + ' ' +
`--email '${certificate.meta.letsencrypt_email}' ` +
`--domains '${certificate.domain_names.join(',')}' ` +
`--authenticator '${dnsPlugin.full_plugin_name}' ` +
(
hasConfigArg
? '--' + dns_plugin.full_plugin_name + '-credentials "' + credentialsLocation + '"'
? `--${dnsPlugin.full_plugin_name}-credentials '${credentialsLocation}' `
: ''
) +
(
certificate.meta.propagation_seconds !== undefined
? ' --' + dns_plugin.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') {
mainCmd = 'AWS_CONFIG_FILE=\'' + credentialsLocation + '\' ' + mainCmd;
}
logger.info('Command:', `${credentialsCmd} && ${prepareCmd} && ${mainCmd}`);
if (certificate.meta.dns_provider === 'duckdns') {
mainCmd = mainCmd + ' --dns-duckdns-no-txt-restore';
}
return utils.exec(credentialsCmd)
.then(() => {
return utils.exec(prepareCmd)
.then(() => {
return utils.exec(mainCmd)
.then(async (result) => {
logger.info(result);
return result;
});
});
}).catch(async (err) => {
// Don't fail if file does not exist
const delete_credentialsCmd = `rm -f '${credentialsLocation}' || true`;
await utils.exec(delete_credentialsCmd);
throw err;
});
logger.info('Command:', mainCmd);
try {
const result = await utils.exec(mainCmd);
logger.info(result);
return result;
} catch (err) {
// Don't fail if file does not exist, so no need for action in the callback
fs.unlink(credentialsLocation, () => {});
throw err;
}
},
@ -973,12 +974,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 + '" ' +
'--cert-name "npm-' + certificate.id + '" ' +
`--config '${letsencryptConfig}' ` +
'--work-dir "/tmp/letsencrypt-lib" ' +
'--logs-dir "/tmp/letsencrypt-log" ' +
`--cert-name 'npm-${certificate.id}' ` +
'--preferred-challenges "dns,http" ' +
'--no-random-sleep-on-renew ' +
'--disable-hook-validation ' +
(letsencryptStaging ? '--staging' : '');
(letsencryptServer !== null ? `--server '${letsencryptServer}' ` : '') +
(letsencryptStaging && letsencryptServer === null ? '--staging ' : '');
logger.info('Command:', cmd);
@ -994,20 +998,23 @@ const internalCertificate = {
* @returns {Promise}
*/
renewLetsEncryptSslWithDnsChallenge: (certificate) => {
const dns_plugin = dnsPlugins[certificate.meta.dns_provider];
const dnsPlugin = dnsPlugins[certificate.meta.dns_provider];
if (!dns_plugin) {
if (!dnsPlugin) {
throw Error(`Unknown DNS provider '${certificate.meta.dns_provider}'`);
}
logger.info(`Renewing Let'sEncrypt certificates via ${dns_plugin.display_name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);
logger.info(`Renewing Let'sEncrypt certificates via ${dnsPlugin.name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);
let mainCmd = certbotCommand + ' renew ' +
'--config "' + letsencryptConfig + '" ' +
'--cert-name "npm-' + certificate.id + '" ' +
let mainCmd = certbotCommand + ' renew --force-renewal ' +
`--config "${letsencryptConfig}" ` +
'--work-dir "/tmp/letsencrypt-lib" ' +
'--logs-dir "/tmp/letsencrypt-log" ' +
`--cert-name 'npm-${certificate.id}' ` +
'--disable-hook-validation ' +
'--no-random-sleep-on-renew ' +
(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') {
@ -1033,10 +1040,13 @@ const internalCertificate = {
logger.info('Revoking Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
const mainCmd = certbotCommand + ' revoke ' +
'--config "' + letsencryptConfig + '" ' +
'--cert-path "/etc/letsencrypt/live/npm-' + certificate.id + '/fullchain.pem" ' +
`--config '${letsencryptConfig}' ` +
'--work-dir "/tmp/letsencrypt-lib" ' +
'--logs-dir "/tmp/letsencrypt-log" ' +
`--cert-path '/etc/letsencrypt/live/npm-${certificate.id}/fullchain.pem' ` +
'--delete-after-revoke ' +
(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`;
@ -1151,6 +1161,7 @@ const internalCertificate = {
const options = {
method: 'POST',
headers: {
'User-Agent': 'Mozilla/5.0',
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(formBody)
}
@ -1163,12 +1174,22 @@ const internalCertificate = {
res.on('data', (chunk) => responseBody = responseBody + chunk);
res.on('end', function () {
const parsedBody = JSON.parse(responseBody + '');
if (res.statusCode !== 200) {
logger.warn(`Failed to test HTTP challenge for domain ${domain}`, res);
try {
const parsedBody = JSON.parse(responseBody + '');
if (res.statusCode !== 200) {
logger.warn(`Failed to test HTTP challenge for domain ${domain} because HTTP status code ${res.statusCode} was returned: ${parsedBody.message}`);
resolve(undefined);
} else {
resolve(parsedBody);
}
} catch (err) {
if (res.statusCode !== 200) {
logger.warn(`Failed to test HTTP challenge for domain ${domain} because HTTP status code ${res.statusCode} was returned`);
} else {
logger.warn(`Failed to test HTTP challenge for domain ${domain} because response failed to be parsed: ${err.message}`);
}
resolve(undefined);
}
resolve(parsedBody);
});
});
@ -1182,6 +1203,9 @@ const internalCertificate = {
if (!result) {
// Some error occurred while trying to get the data
return 'failed';
} else if (result.error) {
logger.info(`HTTP challenge test failed for domain ${domain} because error was returned: ${result.error.msg}`);
return `other:${result.error.msg}`;
} else if (`${result.responsecode}` === '200' && result.htmlresponse === 'Success') {
// Server exists and has responded with the correct data
return 'ok';

View File

@ -6,6 +6,7 @@ const internalHost = require('./host');
const internalNginx = require('./nginx');
const internalAuditLog = require('./audit-log');
const internalCertificate = require('./certificate');
const {castJsonIfNeed} = require('../lib/helpers');
function omissions () {
return ['is_deleted'];
@ -48,6 +49,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 +240,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 +264,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 +312,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 +358,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');
@ -403,16 +410,16 @@ const internalDeadHost = {
.where('is_deleted', 0)
.groupBy('id')
.allowGraph('[owner,certificate]')
.orderBy('domain_names', 'ASC');
.orderBy(castJsonIfNeed('domain_names'), 'ASC');
if (access_data.permission_visibility !== 'all') {
query.andWhere('owner_user_id', access.token.getUserId(1));
}
// Query is used for searching
if (typeof search_query === 'string') {
if (typeof search_query === 'string' && search_query.length > 0) {
query.where(function () {
this.where('domain_names', 'like', '%' + search_query + '%');
this.where(castJsonIfNeed('domain_names'), 'like', '%' + search_query + '%');
});
}

View File

@ -2,6 +2,7 @@ const _ = require('lodash');
const proxyHostModel = require('../models/proxy_host');
const redirectionHostModel = require('../models/redirection_host');
const deadHostModel = require('../models/dead_host');
const {castJsonIfNeed} = require('../lib/helpers');
const internalHost = {
@ -17,7 +18,7 @@ const internalHost = {
cleanSslHstsData: function (data, existing_data) {
existing_data = existing_data === undefined ? {} : existing_data;
let combined_data = _.assign({}, existing_data, data);
const combined_data = _.assign({}, existing_data, data);
if (!combined_data.certificate_id) {
combined_data.ssl_forced = false;
@ -73,7 +74,7 @@ const internalHost = {
* @returns {Promise}
*/
getHostsWithDomains: function (domain_names) {
let promises = [
const promises = [
proxyHostModel
.query()
.where('is_deleted', 0),
@ -125,19 +126,19 @@ const internalHost = {
* @returns {Promise}
*/
isHostnameTaken: function (hostname, ignore_type, ignore_id) {
let promises = [
const promises = [
proxyHostModel
.query()
.where('is_deleted', 0)
.andWhere('domain_names', 'like', '%' + hostname + '%'),
.andWhere(castJsonIfNeed('domain_names'), 'like', '%' + hostname + '%'),
redirectionHostModel
.query()
.where('is_deleted', 0)
.andWhere('domain_names', 'like', '%' + hostname + '%'),
.andWhere(castJsonIfNeed('domain_names'), 'like', '%' + hostname + '%'),
deadHostModel
.query()
.where('is_deleted', 0)
.andWhere('domain_names', 'like', '%' + hostname + '%')
.andWhere(castJsonIfNeed('domain_names'), 'like', '%' + hostname + '%')
];
return Promise.all(promises)

View File

@ -1,9 +1,9 @@
const _ = require('lodash');
const fs = require('fs');
const logger = require('../logger').nginx;
const utils = require('../lib/utils');
const error = require('../lib/error');
const debug_mode = process.env.NODE_ENV !== 'production' || !!process.env.DEBUG;
const _ = require('lodash');
const fs = require('fs');
const logger = require('../logger').nginx;
const config = require('../lib/config');
const utils = require('../lib/utils');
const error = require('../lib/error');
const internalNginx = {
@ -65,7 +65,7 @@ const internalNginx = {
}
});
if (debug_mode) {
if (config.debug()) {
logger.error('Nginx test failed:', valid_lines.join('\n'));
}
@ -101,7 +101,7 @@ const internalNginx = {
* @returns {Promise}
*/
test: () => {
if (debug_mode) {
if (config.debug()) {
logger.info('Testing Nginx configuration');
}
@ -181,10 +181,12 @@ 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 (debug_mode) {
if (config.debug()) {
logger.info('Generating ' + nice_host_type + ' Config:', JSON.stringify(host, null, 2));
}
@ -239,7 +241,7 @@ const internalNginx = {
.then((config_text) => {
fs.writeFileSync(filename, config_text, {encoding: 'utf8'});
if (debug_mode) {
if (config.debug()) {
logger.success('Wrote config:', filename, config_text);
}
@ -249,7 +251,7 @@ const internalNginx = {
resolve(true);
})
.catch((err) => {
if (debug_mode) {
if (config.debug()) {
logger.warn('Could not write ' + filename + ':', err.message);
}
@ -268,7 +270,7 @@ const internalNginx = {
* @returns {Promise}
*/
generateLetsEncryptRequestConfig: (certificate) => {
if (debug_mode) {
if (config.debug()) {
logger.info('Generating LetsEncrypt Request Config:', certificate);
}
@ -292,14 +294,14 @@ const internalNginx = {
.then((config_text) => {
fs.writeFileSync(filename, config_text, {encoding: 'utf8'});
if (debug_mode) {
if (config.debug()) {
logger.success('Wrote config:', filename, config_text);
}
resolve(true);
})
.catch((err) => {
if (debug_mode) {
if (config.debug()) {
logger.warn('Could not write ' + filename + ':', err.message);
}
@ -416,8 +418,8 @@ const internalNginx = {
* @param {string} config
* @returns {boolean}
*/
advancedConfigHasDefaultLocation: function (config) {
return !!config.match(/^(?:.*;)?\s*?location\s*?\/\s*?{/im);
advancedConfigHasDefaultLocation: function (cfg) {
return !!cfg.match(/^(?:.*;)?\s*?location\s*?\/\s*?{/im);
},
/**

View File

@ -6,9 +6,10 @@ const internalHost = require('./host');
const internalNginx = require('./nginx');
const internalAuditLog = require('./audit-log');
const internalCertificate = require('./certificate');
const {castJsonIfNeed} = require('../lib/helpers');
function omissions () {
return ['is_deleted'];
return ['is_deleted', 'owner.is_deleted'];
}
const internalProxyHost = {
@ -48,6 +49,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)
@ -225,7 +232,7 @@ const internalProxyHost = {
.query()
.where('is_deleted', 0)
.andWhere('id', data.id)
.allowGraph('[owner,access_list,access_list.[clients,items],certificate]')
.allowGraph('[owner,access_list.[clients,items],certificate]')
.first();
if (access_data.permission_visibility !== 'all') {
@ -239,7 +246,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 +271,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 +319,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 +365,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');
@ -410,16 +417,16 @@ const internalProxyHost = {
.where('is_deleted', 0)
.groupBy('id')
.allowGraph('[owner,access_list,certificate]')
.orderBy('domain_names', 'ASC');
.orderBy(castJsonIfNeed('domain_names'), 'ASC');
if (access_data.permission_visibility !== 'all') {
query.andWhere('owner_user_id', access.token.getUserId(1));
}
// Query is used for searching
if (typeof search_query === 'string') {
if (typeof search_query === 'string' && search_query.length > 0) {
query.where(function () {
this.where('domain_names', 'like', '%' + search_query + '%');
this.where(castJsonIfNeed('domain_names'), 'like', `%${search_query}%`);
});
}

View File

@ -6,6 +6,7 @@ const internalHost = require('./host');
const internalNginx = require('./nginx');
const internalAuditLog = require('./audit-log');
const internalCertificate = require('./certificate');
const {castJsonIfNeed} = require('../lib/helpers');
function omissions () {
return ['is_deleted'];
@ -48,6 +49,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 +239,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 +264,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 +312,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 +358,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');
@ -403,16 +410,16 @@ const internalRedirectionHost = {
.where('is_deleted', 0)
.groupBy('id')
.allowGraph('[owner,certificate]')
.orderBy('domain_names', 'ASC');
.orderBy(castJsonIfNeed('domain_names'), 'ASC');
if (access_data.permission_visibility !== 'all') {
query.andWhere('owner_user_id', access.token.getUserId(1));
}
// Query is used for searching
if (typeof search_query === 'string') {
if (typeof search_query === 'string' && search_query.length > 0) {
query.where(function () {
this.where('domain_names', 'like', '%' + search_query + '%');
this.where(castJsonIfNeed('domain_names'), 'like', `%${search_query}%`);
});
}

View File

@ -1,12 +1,15 @@
const _ = require('lodash');
const error = require('../lib/error');
const utils = require('../lib/utils');
const streamModel = require('../models/stream');
const internalNginx = require('./nginx');
const internalAuditLog = require('./audit-log');
const _ = require('lodash');
const error = require('../lib/error');
const utils = require('../lib/utils');
const streamModel = require('../models/stream');
const internalNginx = require('./nginx');
const internalAuditLog = require('./audit-log');
const internalCertificate = require('./certificate');
const internalHost = require('./host');
const {castJsonIfNeed} = require('../lib/helpers');
function omissions () {
return ['is_deleted'];
return ['is_deleted', 'owner.is_deleted', 'certificate.is_deleted'];
}
const internalStream = {
@ -17,6 +20,12 @@ const internalStream = {
* @returns {Promise}
*/
create: (access, data) => {
const create_certificate = data.certificate_id === 'new';
if (create_certificate) {
delete data.certificate_id;
}
return access.can('streams:create', data)
.then((/*access_data*/) => {
// TODO: At this point the existing ports should have been checked
@ -26,16 +35,44 @@ const internalStream = {
data.meta = {};
}
// streams aren't routed by domain name so don't store domain names in the DB
let data_no_domains = structuredClone(data);
delete data_no_domains.domain_names;
return streamModel
.query()
.insertAndFetch(data)
.insertAndFetch(data_no_domains)
.then(utils.omitRow(omissions()));
})
.then((row) => {
if (create_certificate) {
return internalCertificate.createQuickCertificate(access, data)
.then((cert) => {
// update host with cert id
return internalStream.update(access, {
id: row.id,
certificate_id: cert.id
});
})
.then(() => {
return row;
});
} else {
return row;
}
})
.then((row) => {
// re-fetch with cert
return internalStream.get(access, {
id: row.id,
expand: ['certificate', 'owner']
});
})
.then((row) => {
// Configure nginx
return internalNginx.configure(streamModel, 'stream', row)
.then(() => {
return internalStream.get(access, {id: row.id, expand: ['owner']});
return row;
});
})
.then((row) => {
@ -59,6 +96,12 @@ const internalStream = {
* @return {Promise}
*/
update: (access, data) => {
const create_certificate = data.certificate_id === 'new';
if (create_certificate) {
delete data.certificate_id;
}
return access.can('streams:update', data.id)
.then((/*access_data*/) => {
// TODO: at this point the existing streams should have been checked
@ -70,16 +113,32 @@ const internalStream = {
throw new error.InternalValidationError('Stream could not be updated, IDs do not match: ' + row.id + ' !== ' + data.id);
}
if (create_certificate) {
return internalCertificate.createQuickCertificate(access, {
domain_names: data.domain_names || row.domain_names,
meta: _.assign({}, row.meta, data.meta)
})
.then((cert) => {
// update host with cert id
data.certificate_id = cert.id;
})
.then(() => {
return row;
});
} else {
return row;
}
})
.then((row) => {
// Add domain_names to the data in case it isn't there, so that the audit log renders correctly. The order is important here.
data = _.assign({}, {
domain_names: row.domain_names
}, data);
return streamModel
.query()
.patchAndFetchById(row.id, data)
.then(utils.omitRow(omissions()))
.then((saved_row) => {
return internalNginx.configure(streamModel, 'stream', saved_row)
.then(() => {
return internalStream.get(access, {id: row.id, expand: ['owner']});
});
})
.then((saved_row) => {
// Add to audit log
return internalAuditLog.add(access, {
@ -92,6 +151,17 @@ const internalStream = {
return saved_row;
});
});
})
.then(() => {
return internalStream.get(access, {id: data.id, expand: ['owner', 'certificate']})
.then((row) => {
return internalNginx.configure(streamModel, 'stream', row)
.then((new_meta) => {
row.meta = new_meta;
row = internalHost.cleanRowCertificateMeta(row);
return _.omit(row, omissions());
});
});
});
},
@ -114,7 +184,7 @@ const internalStream = {
.query()
.where('is_deleted', 0)
.andWhere('id', data.id)
.allowGraph('[owner]')
.allowGraph('[owner,certificate]')
.first();
if (access_data.permission_visibility !== 'all') {
@ -128,9 +198,10 @@ 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);
// Custom omissions
if (typeof data.omit !== 'undefined' && data.omit !== null) {
row = _.omit(row, data.omit);
@ -152,7 +223,7 @@ const internalStream = {
return internalStream.get(access, {id: data.id});
})
.then((row) => {
if (!row) {
if (!row || !row.id) {
throw new error.ItemNotFoundError(data.id);
}
@ -196,14 +267,14 @@ const internalStream = {
.then(() => {
return internalStream.get(access, {
id: data.id,
expand: ['owner']
expand: ['certificate', 'owner']
});
})
.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');
throw new error.ValidationError('Stream is already enabled');
}
row.enabled = 1;
@ -246,10 +317,10 @@ 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('Host is already disabled');
throw new error.ValidationError('Stream is already disabled');
}
row.enabled = 0;
@ -293,21 +364,21 @@ const internalStream = {
getAll: (access, expand, search_query) => {
return access.can('streams:list')
.then((access_data) => {
let query = streamModel
const query = streamModel
.query()
.where('is_deleted', 0)
.groupBy('id')
.allowGraph('[owner]')
.orderBy('incoming_port', 'ASC');
.allowGraph('[owner,certificate]')
.orderByRaw('CAST(incoming_port AS INTEGER) ASC');
if (access_data.permission_visibility !== 'all') {
query.andWhere('owner_user_id', access.token.getUserId(1));
}
// Query is used for searching
if (typeof search_query === 'string') {
if (typeof search_query === 'string' && search_query.length > 0) {
query.where(function () {
this.where('incoming_port', 'like', '%' + search_query + '%');
this.where(castJsonIfNeed('incoming_port'), 'like', `%${search_query}%`);
});
}
@ -316,6 +387,13 @@ const internalStream = {
}
return query.then(utils.omitRows(omissions()));
})
.then((rows) => {
if (typeof expand !== 'undefined' && expand !== null && expand.indexOf('certificate') !== -1) {
return internalHost.cleanAllRowsCertificateMeta(rows);
}
return rows;
});
},
@ -327,9 +405,9 @@ const internalStream = {
* @returns {Promise}
*/
getCount: (user_id, visibility) => {
let query = streamModel
const query = streamModel
.query()
.count('id as count')
.count('id AS count')
.where('is_deleted', 0);
if (visibility !== 'all') {

View File

@ -5,6 +5,8 @@ const authModel = require('../models/auth');
const helpers = require('../lib/helpers');
const TokenModel = require('../models/token');
const ERROR_MESSAGE_INVALID_AUTH = 'Invalid email or password';
module.exports = {
/**
@ -69,15 +71,15 @@ module.exports = {
};
});
} else {
throw new error.AuthError('Invalid password');
throw new error.AuthError(ERROR_MESSAGE_INVALID_AUTH);
}
});
} else {
throw new error.AuthError('No password auth for user');
throw new error.AuthError(ERROR_MESSAGE_INVALID_AUTH);
}
});
} else {
throw new error.AuthError('No relevant user found');
throw new error.AuthError(ERROR_MESSAGE_INVALID_AUTH);
}
});
},

View File

@ -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

View File

@ -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',

View File

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

View File

@ -1,5 +1,4 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "perms",
"definitions": {
"view": {

View File

@ -1,5 +1,4 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "roles",
"definitions": {
"admin": {

78
backend/lib/certbot.js Normal file
View File

@ -0,0 +1,78 @@
const dnsPlugins = require('../global/certbot-dns-plugins.json');
const utils = require('./utils');
const error = require('./error');
const logger = require('../logger').certbot;
const batchflow = require('batchflow');
const CERTBOT_VERSION_REPLACEMENT = '$(certbot --version | grep -Eo \'[0-9](\\.[0-9]+)+\')';
const certbot = {
/**
* @param {array} pluginKeys
*/
installPlugins: async function (pluginKeys) {
let hasErrors = false;
return new Promise((resolve, reject) => {
if (pluginKeys.length === 0) {
resolve();
return;
}
batchflow(pluginKeys).sequential()
.each((i, pluginKey, next) => {
certbot.installPlugin(pluginKey)
.then(() => {
next();
})
.catch((err) => {
hasErrors = true;
next(err);
});
})
.error((err) => {
logger.error(err.message);
})
.end(() => {
if (hasErrors) {
reject(new error.CommandError('Some plugins failed to install. Please check the logs above', 1));
} else {
resolve();
}
});
});
},
/**
* Installs a cerbot plugin given the key for the object from
* ../global/certbot-dns-plugins.json
*
* @param {string} pluginKey
* @returns {Object}
*/
installPlugin: async function (pluginKey) {
if (typeof dnsPlugins[pluginKey] === 'undefined') {
// throw Error(`Certbot plugin ${pluginKey} not found`);
throw new error.ItemNotFoundError(pluginKey);
}
const plugin = dnsPlugins[pluginKey];
logger.start(`Installing ${pluginKey}...`);
plugin.version = plugin.version.replace(/{{certbot-version}}/g, CERTBOT_VERSION_REPLACEMENT);
plugin.dependencies = plugin.dependencies.replace(/{{certbot-version}}/g, CERTBOT_VERSION_REPLACEMENT);
const cmd = '. /opt/certbot/bin/activate && pip install --no-cache-dir ' + plugin.dependencies + ' ' + plugin.package_name + plugin.version + ' ' + ' && deactivate';
return utils.exec(cmd)
.then((result) => {
logger.complete(`Installed ${pluginKey}`);
return result;
})
.catch((err) => {
throw err;
});
},
};
module.exports = certbot;

237
backend/lib/config.js Normal file
View File

@ -0,0 +1,237 @@
const fs = require('fs');
const NodeRSA = require('node-rsa');
const logger = require('../logger').global;
const keysFile = '/data/keys.json';
const mysqlEngine = 'mysql2';
const postgresEngine = 'pg';
const sqliteClientName = 'sqlite3';
let instance = null;
// 1. Load from config file first (not recommended anymore)
// 2. Use config env variables next
const configure = () => {
const filename = (process.env.NODE_CONFIG_DIR || './config') + '/' + (process.env.NODE_ENV || 'default') + '.json';
if (fs.existsSync(filename)) {
let configData;
try {
configData = require(filename);
} catch (_) {
// do nothing
}
if (configData && configData.database) {
logger.info(`Using configuration from file: ${filename}`);
instance = configData;
instance.keys = getKeys();
return;
}
}
const envMysqlHost = process.env.DB_MYSQL_HOST || null;
const envMysqlUser = process.env.DB_MYSQL_USER || null;
const envMysqlName = process.env.DB_MYSQL_NAME || null;
if (envMysqlHost && envMysqlUser && envMysqlName) {
// we have enough mysql creds to go with mysql
logger.info('Using MySQL configuration');
instance = {
database: {
engine: mysqlEngine,
host: envMysqlHost,
port: process.env.DB_MYSQL_PORT || 3306,
user: envMysqlUser,
password: process.env.DB_MYSQL_PASSWORD,
name: envMysqlName,
},
keys: getKeys(),
};
return;
}
const envPostgresHost = process.env.DB_POSTGRES_HOST || null;
const envPostgresUser = process.env.DB_POSTGRES_USER || null;
const envPostgresName = process.env.DB_POSTGRES_NAME || null;
if (envPostgresHost && envPostgresUser && envPostgresName) {
// we have enough postgres creds to go with postgres
logger.info('Using Postgres configuration');
instance = {
database: {
engine: postgresEngine,
host: envPostgresHost,
port: process.env.DB_POSTGRES_PORT || 5432,
user: envPostgresUser,
password: process.env.DB_POSTGRES_PASSWORD,
name: envPostgresName,
},
keys: getKeys(),
};
return;
}
const envSqliteFile = process.env.DB_SQLITE_FILE || '/data/database.sqlite';
logger.info(`Using Sqlite: ${envSqliteFile}`);
instance = {
database: {
engine: 'knex-native',
knex: {
client: sqliteClientName,
connection: {
filename: envSqliteFile
},
useNullAsDefault: true
}
},
keys: getKeys(),
};
};
const getKeys = () => {
// Get keys from file
if (!fs.existsSync(keysFile)) {
generateKeys();
} else if (process.env.DEBUG) {
logger.info('Keys file exists OK');
}
try {
return require(keysFile);
} catch (err) {
logger.error('Could not read JWT key pair from config file: ' + keysFile, err);
process.exit(1);
}
};
const generateKeys = () => {
logger.info('Creating a new JWT key pair...');
// Now create the keys and save them in the config.
const key = new NodeRSA({ b: 2048 });
key.generateKeyPair();
const keys = {
key: key.exportKey('private').toString(),
pub: key.exportKey('public').toString(),
};
// Write keys config
try {
fs.writeFileSync(keysFile, JSON.stringify(keys, null, 2));
} catch (err) {
logger.error('Could not write JWT key pair to config file: ' + keysFile + ': ' + err.message);
process.exit(1);
}
logger.info('Wrote JWT key pair to config file: ' + keysFile);
};
module.exports = {
/**
*
* @param {string} key ie: 'database' or 'database.engine'
* @returns {boolean}
*/
has: function(key) {
instance === null && configure();
const keys = key.split('.');
let level = instance;
let has = true;
keys.forEach((keyItem) =>{
if (typeof level[keyItem] === 'undefined') {
has = false;
} else {
level = level[keyItem];
}
});
return has;
},
/**
* Gets a specific key from the top level
*
* @param {string} key
* @returns {*}
*/
get: function (key) {
instance === null && configure();
if (key && typeof instance[key] !== 'undefined') {
return instance[key];
}
return instance;
},
/**
* Is this a sqlite configuration?
*
* @returns {boolean}
*/
isSqlite: function () {
instance === null && configure();
return instance.database.knex && instance.database.knex.client === sqliteClientName;
},
/**
* Is this a mysql configuration?
*
* @returns {boolean}
*/
isMysql: function () {
instance === null && configure();
return instance.database.engine === mysqlEngine;
},
/**
* Is this a postgres configuration?
*
* @returns {boolean}
*/
isPostgres: function () {
instance === null && configure();
return instance.database.engine === postgresEngine;
},
/**
* Are we running in debug mdoe?
*
* @returns {boolean}
*/
debug: function () {
return !!process.env.DEBUG;
},
/**
* Returns a public key
*
* @returns {string}
*/
getPublicKey: function () {
instance === null && configure();
return instance.keys.pub;
},
/**
* Returns a private key
*
* @returns {string}
*/
getPrivateKey: function () {
instance === null && configure();
return instance.keys.key;
},
/**
* @returns {boolean}
*/
useLetsencryptStaging: function () {
return !!process.env.LE_STAGING;
},
/**
* @returns {string|null}
*/
useLetsencryptServer: function () {
if (process.env.LE_SERVER) {
return process.env.LE_SERVER;
}
return null;
}
};

View File

@ -82,7 +82,16 @@ module.exports = {
this.message = message;
this.public = false;
this.status = 400;
}
},
CommandError: function (stdErr, code, previous) {
Error.captureStackTrace(this, this.constructor);
this.name = this.constructor.name;
this.previous = previous;
this.message = stdErr;
this.code = code;
this.public = false;
},
};
_.forEach(module.exports, function (error) {

View File

@ -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();
}
};

View File

@ -1,4 +1,6 @@
const moment = require('moment');
const moment = require('moment');
const {isPostgres} = require('./config');
const {ref} = require('objection');
module.exports = {
@ -27,6 +29,34 @@ 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;
},
/**
* Casts a column to json if using postgres
*
* @param {string} colName
* @returns {string|Objection.ReferenceBuilder}
*/
castJsonIfNeed: function (colName) {
return isPostgres() ? ref(colName).castText() : colName;
}
};

View File

@ -3,23 +3,27 @@ const exec = require('child_process').exec;
const execFile = require('child_process').execFile;
const { Liquid } = require('liquidjs');
const logger = require('../logger').global;
const error = require('./error');
module.exports = {
/**
* @param {String} cmd
* @returns {Promise}
*/
exec: function (cmd) {
return new Promise((resolve, reject) => {
exec(cmd, function (err, stdout, /*stderr*/) {
if (err && typeof err === 'object') {
reject(err);
exec: async function(cmd, options = {}) {
logger.debug('CMD:', cmd);
const { stdout, stderr } = await new Promise((resolve, reject) => {
const child = exec(cmd, options, (isError, stdout, stderr) => {
if (isError) {
reject(new error.CommandError(stderr, isError));
} else {
resolve(stdout.trim());
resolve({ stdout, stderr });
}
});
child.on('error', (e) => {
reject(new error.CommandError(stderr, 1, e));
});
});
return stdout;
},
/**
@ -28,7 +32,8 @@ module.exports = {
* @returns {Promise}
*/
execFile: function (cmd, args) {
logger.debug('CMD: ' + cmd + ' ' + (args ? args.join(' ') : ''));
// logger.debug('CMD: ' + cmd + ' ' + (args ? args.join(' ') : ''));
return new Promise((resolve, reject) => {
execFile(cmd, args, function (err, stdout, /*stderr*/) {
if (err && typeof err === 'object') {

View File

@ -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 (typeof payload === 'undefined') {
reject(new error.ValidationError('Payload is undefined'));
if (schema === null) {
reject(new error.ValidationError('Schema is undefined'));
return;
}
let validate = ajv.compile(schema);
let valid = validate(payload);
if (typeof payload === 'undefined') {
reject(new error.ValidationError('Payload is undefined'));
return;
}
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;

View File

@ -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, //process.env.NODE_ENV === 'development',
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;

View File

@ -7,6 +7,7 @@ module.exports = {
access: new Signale({scope: 'Access '}),
nginx: new Signale({scope: 'Nginx '}),
ssl: new Signale({scope: 'SSL '}),
certbot: new Signale({scope: 'Certbot '}),
import: new Signale({scope: 'Importer '}),
setup: new Signale({scope: 'Setup '}),
ip_ranges: new Signale({scope: 'IP Ranges'})

View File

@ -0,0 +1,38 @@
const migrate_name = 'stream_ssl';
const logger = require('../logger').migrate;
/**
* Migrate
*
* @see http://knexjs.org/#Schema
*
* @param {Object} knex
* @returns {Promise}
*/
exports.up = function (knex) {
logger.info('[' + migrate_name + '] Migrating Up...');
return knex.schema.table('stream', (table) => {
table.integer('certificate_id').notNull().unsigned().defaultTo(0);
})
.then(function () {
logger.info('[' + migrate_name + '] stream Table altered');
});
};
/**
* Undo Migrate
*
* @param {Object} knex
* @returns {Promise}
*/
exports.down = function (knex) {
logger.info('[' + migrate_name + '] Migrating Down...');
return knex.schema.table('stream', (table) => {
table.dropColumn('certificate_id');
})
.then(function () {
logger.info('[' + migrate_name + '] stream Table altered');
});
};

View File

@ -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';
}

View File

@ -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
*

View File

@ -1,13 +1,17 @@
// 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 now = require('./now_helper');
Model.knex(db);
const boolFields = [
'is_deleted',
];
class Certificate extends Model {
$beforeInsert () {
this.created_on = now();
@ -40,6 +44,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';
}
@ -53,6 +67,11 @@ class Certificate extends Model {
}
static get relationMappings () {
const ProxyHost = require('./proxy_host');
const DeadHost = require('./dead_host');
const User = require('./user');
const RedirectionHost = require('./redirection_host');
return {
owner: {
relation: Model.HasOneRelation,
@ -64,6 +83,39 @@ class Certificate extends Model {
modify: function (qb) {
qb.where('user.is_deleted', 0);
}
},
proxy_hosts: {
relation: Model.HasManyRelation,
modelClass: ProxyHost,
join: {
from: 'certificate.id',
to: 'proxy_host.certificate_id'
},
modify: function (qb) {
qb.where('proxy_host.is_deleted', 0);
}
},
dead_hosts: {
relation: Model.HasManyRelation,
modelClass: DeadHost,
join: {
from: 'certificate.id',
to: 'dead_host.certificate_id'
},
modify: function (qb) {
qb.where('dead_host.is_deleted', 0);
}
},
redirection_hosts: {
relation: Model.HasManyRelation,
modelClass: RedirectionHost,
join: {
from: 'certificate.id',
to: 'redirection_host.certificate_id'
},
modify: function (qb) {
qb.where('redirection_host.is_deleted', 0);
}
}
};
}

View File

@ -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,15 @@ const now = require('./now_helper');
Model.knex(db);
const boolFields = [
'is_deleted',
'ssl_forced',
'http2_support',
'enabled',
'hsts_enabled',
'hsts_subdomains',
];
class DeadHost extends Model {
$beforeInsert () {
this.created_on = now();
@ -36,6 +46,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';
}

View File

@ -1,11 +1,11 @@
const db = require('../db');
const config = require('config');
const config = require('../lib/config');
const Model = require('objection').Model;
Model.knex(db);
module.exports = function () {
if (config.database.knex && config.database.knex.client === 'sqlite3') {
if (config.isSqlite()) {
// eslint-disable-next-line
return Model.raw("datetime('now','localtime')");
}

View File

@ -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';
}

View File

@ -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,17 @@ const now = require('./now_helper');
Model.knex(db);
const boolFields = [
'is_deleted',
'enabled',
'preserve_path',
'ssl_forced',
'block_exploits',
'hsts_enabled',
'hsts_subdomains',
'http2_support',
];
class RedirectionHost extends Model {
$beforeInsert () {
this.created_on = now();
@ -37,6 +49,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';
}

View File

@ -1,13 +1,19 @@
// 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 Model = require('objection').Model;
const db = require('../db');
const helpers = require('../lib/helpers');
const User = require('./user');
const Certificate = require('./certificate');
const now = require('./now_helper');
Model.knex(db);
const boolFields = [
'is_deleted',
'enabled',
'tcp_forwarding',
'udp_forwarding',
];
class Stream extends Model {
$beforeInsert () {
this.created_on = now();
@ -23,6 +29,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';
}
@ -47,6 +63,17 @@ class Stream extends Model {
modify: function (qb) {
qb.where('user.is_deleted', 0);
}
},
certificate: {
relation: Model.HasOneRelation,
modelClass: Certificate,
join: {
from: 'stream.certificate_id',
to: 'certificate.id'
},
modify: function (qb) {
qb.where('certificate.is_deleted', 0);
}
}
};
}

View File

@ -6,44 +6,36 @@
const _ = require('lodash');
const jwt = require('jsonwebtoken');
const crypto = require('crypto');
const config = require('../lib/config');
const error = require('../lib/error');
const logger = require('../logger').global;
const ALGO = 'RS256';
let public_key = null;
let private_key = null;
function checkJWTKeyPair() {
if (!public_key || !private_key) {
let config = require('config');
public_key = config.get('jwt.pub');
private_key = config.get('jwt.key');
}
}
module.exports = function () {
let token_data = {};
let self = {
const self = {
/**
* @param {Object} payload
* @returns {Promise}
*/
create: (payload) => {
if (!config.getPrivateKey()) {
logger.error('Private key is empty!');
}
// sign with RSA SHA256
let options = {
const options = {
algorithm: ALGO,
expiresIn: payload.expiresIn || '1d'
};
payload.jti = crypto.randomBytes(12)
.toString('base64')
.substr(-8);
checkJWTKeyPair();
.substring(-8);
return new Promise((resolve, reject) => {
jwt.sign(payload, private_key, options, (err, token) => {
jwt.sign(payload, config.getPrivateKey(), options, (err, token) => {
if (err) {
reject(err);
} else {
@ -62,13 +54,15 @@ module.exports = function () {
* @returns {Promise}
*/
load: function (token) {
if (!config.getPublicKey()) {
logger.error('Public key is empty!');
}
return new Promise((resolve, reject) => {
checkJWTKeyPair();
try {
if (!token || token === null || token === 'null') {
reject(new error.AuthError('Empty token'));
} else {
jwt.verify(token, public_key, {ignoreExpiration: false, algorithms: [ALGO]}, (err, result) => {
jwt.verify(token, config.getPublicKey(), {ignoreExpiration: false, algorithms: [ALGO]}, (err, result) => {
if (err) {
if (err.name === 'TokenExpiredError') {
@ -132,7 +126,7 @@ module.exports = function () {
* @returns {Integer}
*/
getUserId: (default_value) => {
let attrs = self.get('attrs');
const attrs = self.get('attrs');
if (attrs && typeof attrs.id !== 'undefined' && attrs.id) {
return attrs.id;
}

View File

@ -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';
}

View File

@ -2,29 +2,28 @@
"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",
"config": "^3.3.1",
"express": "^4.17.3",
"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",
"nodemon": "^2.0.2",
"objection": "3.0.1",
"path": "^0.12.7",
"pg": "^8.13.1",
"signale": "1.4.0",
"sqlite3": "5.1.6",
"temp-write": "^4.0.0"
@ -36,8 +35,14 @@
"author": "Jamie Curnow <jc@jc21.com>",
"license": "MIT",
"devDependencies": {
"eslint": "^6.8.0",
"@apidevtools/swagger-parser": "^10.1.0",
"chalk": "4.1.2",
"eslint": "^8.36.0",
"eslint-plugin-align-assignments": "^1.1.2",
"nodemon": "^2.0.2",
"prettier": "^2.0.4"
},
"scripts": {
"validate-schema": "node validate-schema.js"
}
}

View File

@ -1,7 +1,7 @@
const express = require('express');
const validator = require('../../lib/validator');
const jwtdecode = require('../../lib/express/jwt-decode');
const internalAuditLog = require('../../internal/audit-log');
const validator = require('../lib/validator');
const jwtdecode = require('../lib/express/jwt-decode');
const internalAuditLog = require('../internal/audit-log');
let router = express.Router({
caseSensitive: true,
@ -14,7 +14,7 @@ let router = express.Router({
*/
router
.route('/')
.options((req, res) => {
.options((_, res) => {
res.sendStatus(204);
})
.all(jwtdecode())
@ -29,10 +29,10 @@ router
additionalProperties: false,
properties: {
expand: {
$ref: 'definitions#/definitions/expand'
$ref: 'common#/properties/expand'
},
query: {
$ref: 'definitions#/definitions/query'
$ref: 'common#/properties/query'
}
}
}, {

View File

@ -1,6 +1,6 @@
const express = require('express');
const pjson = require('../../package.json');
const error = require('../../lib/error');
const pjson = require('../package.json');
const error = require('../lib/error');
let router = express.Router({
caseSensitive: true,
@ -43,7 +43,7 @@ router.use('/nginx/certificates', require('./nginx/certificates'));
*
* ALL /api/*
*/
router.all(/(.+)/, function (req, res, next) {
router.all(/(.+)/, function (req, _, next) {
req.params.page = req.params['0'];
next(new error.ItemNotFoundError(req.params.page));
});

View File

@ -1,8 +1,9 @@
const express = require('express');
const validator = require('../../../lib/validator');
const jwtdecode = require('../../../lib/express/jwt-decode');
const internalAccessList = require('../../../internal/access-list');
const apiValidator = require('../../../lib/validator/api');
const validator = require('../../lib/validator');
const jwtdecode = require('../../lib/express/jwt-decode');
const apiValidator = require('../../lib/validator/api');
const internalAccessList = require('../../internal/access-list');
const schema = require('../../schema');
let router = express.Router({
caseSensitive: true,
@ -30,10 +31,10 @@ router
additionalProperties: false,
properties: {
expand: {
$ref: 'definitions#/definitions/expand'
$ref: 'common#/properties/expand'
},
query: {
$ref: 'definitions#/definitions/query'
$ref: 'common#/properties/query'
}
}
}, {
@ -56,7 +57,7 @@ router
* Create a new access-list
*/
.post((req, res, next) => {
apiValidator({$ref: 'endpoints/access-lists#/links/1/schema'}, req.body)
apiValidator(schema.getValidationSchema('/nginx/access-lists', 'post'), req.body)
.then((payload) => {
return internalAccessList.create(res.locals.access, payload);
})
@ -74,7 +75,7 @@ router
*/
router
.route('/:list_id')
.options((req, res) => {
.options((_, res) => {
res.sendStatus(204);
})
.all(jwtdecode())
@ -90,10 +91,10 @@ router
additionalProperties: false,
properties: {
list_id: {
$ref: 'definitions#/definitions/id'
$ref: 'common#/properties/id'
},
expand: {
$ref: 'definitions#/definitions/expand'
$ref: 'common#/properties/expand'
}
}
}, {
@ -119,7 +120,7 @@ router
* Update and existing access-list
*/
.put((req, res, next) => {
apiValidator({$ref: 'endpoints/access-lists#/links/2/schema'}, req.body)
apiValidator(schema.getValidationSchema('/nginx/access-lists/{listID}', 'put'), req.body)
.then((payload) => {
payload.id = parseInt(req.params.list_id, 10);
return internalAccessList.update(res.locals.access, payload);

View File

@ -1,8 +1,10 @@
const express = require('express');
const validator = require('../../../lib/validator');
const jwtdecode = require('../../../lib/express/jwt-decode');
const internalCertificate = require('../../../internal/certificate');
const apiValidator = require('../../../lib/validator/api');
const error = require('../../lib/error');
const validator = require('../../lib/validator');
const jwtdecode = require('../../lib/express/jwt-decode');
const apiValidator = require('../../lib/validator/api');
const internalCertificate = require('../../internal/certificate');
const schema = require('../../schema');
let router = express.Router({
caseSensitive: true,
@ -15,7 +17,7 @@ let router = express.Router({
*/
router
.route('/')
.options((req, res) => {
.options((_, res) => {
res.sendStatus(204);
})
.all(jwtdecode())
@ -30,10 +32,10 @@ router
additionalProperties: false,
properties: {
expand: {
$ref: 'definitions#/definitions/expand'
$ref: 'common#/properties/expand'
},
query: {
$ref: 'definitions#/definitions/query'
$ref: 'common#/properties/query'
}
}
}, {
@ -56,7 +58,7 @@ router
* Create a new certificate
*/
.post((req, res, next) => {
apiValidator({$ref: 'endpoints/certificates#/links/1/schema'}, req.body)
apiValidator(schema.getValidationSchema('/nginx/certificates', 'post'), req.body)
.then((payload) => {
req.setTimeout(900000); // 15 minutes timeout
return internalCertificate.create(res.locals.access, payload);
@ -75,17 +77,22 @@ router
*/
router
.route('/test-http')
.options((req, res) => {
.options((_, res) => {
res.sendStatus(204);
})
.all(jwtdecode())
/**
* GET /api/nginx/certificates/test-http
*
* Test HTTP challenge for domains
*/
/**
* GET /api/nginx/certificates/test-http
*
* Test HTTP challenge for domains
*/
.get((req, res, next) => {
if (req.query.domains === undefined) {
next(new error.ValidationError('Domains are required as query parameters'));
return;
}
internalCertificate.testHttpsChallenge(res.locals.access, JSON.parse(req.query.domains))
.then((result) => {
res.status(200)
@ -101,7 +108,7 @@ router
*/
router
.route('/:certificate_id')
.options((req, res) => {
.options((_, res) => {
res.sendStatus(204);
})
.all(jwtdecode())
@ -117,10 +124,10 @@ router
additionalProperties: false,
properties: {
certificate_id: {
$ref: 'definitions#/definitions/id'
$ref: 'common#/properties/id'
},
expand: {
$ref: 'definitions#/definitions/expand'
$ref: 'common#/properties/expand'
}
}
}, {
@ -140,24 +147,6 @@ router
.catch(next);
})
/**
* PUT /api/nginx/certificates/123
*
* Update and existing certificate
*/
.put((req, res, next) => {
apiValidator({$ref: 'endpoints/certificates#/links/2/schema'}, req.body)
.then((payload) => {
payload.id = parseInt(req.params.certificate_id, 10);
return internalCertificate.update(res.locals.access, payload);
})
.then((result) => {
res.status(200)
.send(result);
})
.catch(next);
})
/**
* DELETE /api/nginx/certificates/123
*
@ -179,7 +168,7 @@ router
*/
router
.route('/:certificate_id/upload')
.options((req, res) => {
.options((_, res) => {
res.sendStatus(204);
})
.all(jwtdecode())
@ -213,7 +202,7 @@ router
*/
router
.route('/:certificate_id/renew')
.options((req, res) => {
.options((_, res) => {
res.sendStatus(204);
})
.all(jwtdecode())
@ -270,7 +259,7 @@ router
*/
router
.route('/validate')
.options((req, res) => {
.options((_, res) => {
res.sendStatus(204);
})
.all(jwtdecode())

View File

@ -1,8 +1,9 @@
const express = require('express');
const validator = require('../../../lib/validator');
const jwtdecode = require('../../../lib/express/jwt-decode');
const internalDeadHost = require('../../../internal/dead-host');
const apiValidator = require('../../../lib/validator/api');
const validator = require('../../lib/validator');
const jwtdecode = require('../../lib/express/jwt-decode');
const apiValidator = require('../../lib/validator/api');
const internalDeadHost = require('../../internal/dead-host');
const schema = require('../../schema');
let router = express.Router({
caseSensitive: true,
@ -15,7 +16,7 @@ let router = express.Router({
*/
router
.route('/')
.options((req, res) => {
.options((_, res) => {
res.sendStatus(204);
})
.all(jwtdecode())
@ -30,10 +31,10 @@ router
additionalProperties: false,
properties: {
expand: {
$ref: 'definitions#/definitions/expand'
$ref: 'common#/properties/expand'
},
query: {
$ref: 'definitions#/definitions/query'
$ref: 'common#/properties/query'
}
}
}, {
@ -56,7 +57,7 @@ router
* Create a new dead-host
*/
.post((req, res, next) => {
apiValidator({$ref: 'endpoints/dead-hosts#/links/1/schema'}, req.body)
apiValidator(schema.getValidationSchema('/nginx/dead-hosts', 'post'), req.body)
.then((payload) => {
return internalDeadHost.create(res.locals.access, payload);
})
@ -90,10 +91,10 @@ router
additionalProperties: false,
properties: {
host_id: {
$ref: 'definitions#/definitions/id'
$ref: 'common#/properties/id'
},
expand: {
$ref: 'definitions#/definitions/expand'
$ref: 'common#/properties/expand'
}
}
}, {
@ -119,7 +120,7 @@ router
* Update and existing dead-host
*/
.put((req, res, next) => {
apiValidator({$ref: 'endpoints/dead-hosts#/links/2/schema'}, req.body)
apiValidator(schema.getValidationSchema('/nginx/dead-hosts/{hostID}', 'put'), req.body)
.then((payload) => {
payload.id = parseInt(req.params.host_id, 10);
return internalDeadHost.update(res.locals.access, payload);
@ -152,7 +153,7 @@ router
*/
router
.route('/:host_id/enable')
.options((req, res) => {
.options((_, res) => {
res.sendStatus(204);
})
.all(jwtdecode())
@ -176,7 +177,7 @@ router
*/
router
.route('/:host_id/disable')
.options((req, res) => {
.options((_, res) => {
res.sendStatus(204);
})
.all(jwtdecode())

View File

@ -1,8 +1,9 @@
const express = require('express');
const validator = require('../../../lib/validator');
const jwtdecode = require('../../../lib/express/jwt-decode');
const internalProxyHost = require('../../../internal/proxy-host');
const apiValidator = require('../../../lib/validator/api');
const validator = require('../../lib/validator');
const jwtdecode = require('../../lib/express/jwt-decode');
const apiValidator = require('../../lib/validator/api');
const internalProxyHost = require('../../internal/proxy-host');
const schema = require('../../schema');
let router = express.Router({
caseSensitive: true,
@ -30,10 +31,10 @@ router
additionalProperties: false,
properties: {
expand: {
$ref: 'definitions#/definitions/expand'
$ref: 'common#/properties/expand'
},
query: {
$ref: 'definitions#/definitions/query'
$ref: 'common#/properties/query'
}
}
}, {
@ -56,7 +57,7 @@ router
* Create a new proxy-host
*/
.post((req, res, next) => {
apiValidator({$ref: 'endpoints/proxy-hosts#/links/1/schema'}, req.body)
apiValidator(schema.getValidationSchema('/nginx/proxy-hosts', 'post'), req.body)
.then((payload) => {
return internalProxyHost.create(res.locals.access, payload);
})
@ -90,10 +91,10 @@ router
additionalProperties: false,
properties: {
host_id: {
$ref: 'definitions#/definitions/id'
$ref: 'common#/properties/id'
},
expand: {
$ref: 'definitions#/definitions/expand'
$ref: 'common#/properties/expand'
}
}
}, {
@ -119,7 +120,7 @@ router
* Update and existing proxy-host
*/
.put((req, res, next) => {
apiValidator({$ref: 'endpoints/proxy-hosts#/links/2/schema'}, req.body)
apiValidator(schema.getValidationSchema('/nginx/proxy-hosts/{hostID}', 'put'), req.body)
.then((payload) => {
payload.id = parseInt(req.params.host_id, 10);
return internalProxyHost.update(res.locals.access, payload);
@ -152,7 +153,7 @@ router
*/
router
.route('/:host_id/enable')
.options((req, res) => {
.options((_, res) => {
res.sendStatus(204);
})
.all(jwtdecode())
@ -176,7 +177,7 @@ router
*/
router
.route('/:host_id/disable')
.options((req, res) => {
.options((_, res) => {
res.sendStatus(204);
})
.all(jwtdecode())

View File

@ -1,8 +1,9 @@
const express = require('express');
const validator = require('../../../lib/validator');
const jwtdecode = require('../../../lib/express/jwt-decode');
const internalRedirectionHost = require('../../../internal/redirection-host');
const apiValidator = require('../../../lib/validator/api');
const validator = require('../../lib/validator');
const jwtdecode = require('../../lib/express/jwt-decode');
const apiValidator = require('../../lib/validator/api');
const internalRedirectionHost = require('../../internal/redirection-host');
const schema = require('../../schema');
let router = express.Router({
caseSensitive: true,
@ -30,10 +31,10 @@ router
additionalProperties: false,
properties: {
expand: {
$ref: 'definitions#/definitions/expand'
$ref: 'common#/properties/expand'
},
query: {
$ref: 'definitions#/definitions/query'
$ref: 'common#/properties/query'
}
}
}, {
@ -56,7 +57,7 @@ router
* Create a new redirection-host
*/
.post((req, res, next) => {
apiValidator({$ref: 'endpoints/redirection-hosts#/links/1/schema'}, req.body)
apiValidator(schema.getValidationSchema('/nginx/redirection-hosts', 'post'), req.body)
.then((payload) => {
return internalRedirectionHost.create(res.locals.access, payload);
})
@ -90,10 +91,10 @@ router
additionalProperties: false,
properties: {
host_id: {
$ref: 'definitions#/definitions/id'
$ref: 'common#/properties/id'
},
expand: {
$ref: 'definitions#/definitions/expand'
$ref: 'common#/properties/expand'
}
}
}, {
@ -119,7 +120,7 @@ router
* Update and existing redirection-host
*/
.put((req, res, next) => {
apiValidator({$ref: 'endpoints/redirection-hosts#/links/2/schema'}, req.body)
apiValidator(schema.getValidationSchema('/nginx/redirection-hosts/{hostID}', 'put'), req.body)
.then((payload) => {
payload.id = parseInt(req.params.host_id, 10);
return internalRedirectionHost.update(res.locals.access, payload);

View File

@ -1,8 +1,9 @@
const express = require('express');
const validator = require('../../../lib/validator');
const jwtdecode = require('../../../lib/express/jwt-decode');
const internalStream = require('../../../internal/stream');
const apiValidator = require('../../../lib/validator/api');
const validator = require('../../lib/validator');
const jwtdecode = require('../../lib/express/jwt-decode');
const apiValidator = require('../../lib/validator/api');
const internalStream = require('../../internal/stream');
const schema = require('../../schema');
let router = express.Router({
caseSensitive: true,
@ -30,10 +31,10 @@ router
additionalProperties: false,
properties: {
expand: {
$ref: 'definitions#/definitions/expand'
$ref: 'common#/properties/expand'
},
query: {
$ref: 'definitions#/definitions/query'
$ref: 'common#/properties/query'
}
}
}, {
@ -56,7 +57,7 @@ router
* Create a new stream
*/
.post((req, res, next) => {
apiValidator({$ref: 'endpoints/streams#/links/1/schema'}, req.body)
apiValidator(schema.getValidationSchema('/nginx/streams', 'post'), req.body)
.then((payload) => {
return internalStream.create(res.locals.access, payload);
})
@ -90,10 +91,10 @@ router
additionalProperties: false,
properties: {
stream_id: {
$ref: 'definitions#/definitions/id'
$ref: 'common#/properties/id'
},
expand: {
$ref: 'definitions#/definitions/expand'
$ref: 'common#/properties/expand'
}
}
}, {
@ -119,7 +120,7 @@ router
* Update and existing stream
*/
.put((req, res, next) => {
apiValidator({$ref: 'endpoints/streams#/links/2/schema'}, req.body)
apiValidator(schema.getValidationSchema('/nginx/streams/{streamID}', 'put'), req.body)
.then((payload) => {
payload.id = parseInt(req.params.stream_id, 10);
return internalStream.update(res.locals.access, payload);
@ -152,7 +153,7 @@ router
*/
router
.route('/:host_id/enable')
.options((req, res) => {
.options((_, res) => {
res.sendStatus(204);
})
.all(jwtdecode())
@ -176,7 +177,7 @@ router
*/
router
.route('/:host_id/disable')
.options((req, res) => {
.options((_, res) => {
res.sendStatus(204);
})
.all(jwtdecode())

View File

@ -1,6 +1,6 @@
const express = require('express');
const jwtdecode = require('../../lib/express/jwt-decode');
const internalReport = require('../../internal/report');
const jwtdecode = require('../lib/express/jwt-decode');
const internalReport = require('../internal/report');
let router = express.Router({
caseSensitive: true,
@ -10,14 +10,14 @@ let router = express.Router({
router
.route('/hosts')
.options((req, res) => {
.options((_, res) => {
res.sendStatus(204);
})
/**
* GET /reports/hosts
*/
.get(jwtdecode(), (req, res, next) => {
.get(jwtdecode(), (_, res, next) => {
internalReport.getHostsReport(res.locals.access)
.then((data) => {
res.status(200)

View File

@ -1,8 +1,8 @@
const express = require('express');
const swaggerJSON = require('../../doc/api.swagger.json');
const PACKAGE = require('../../package.json');
const express = require('express');
const schema = require('../schema');
const PACKAGE = require('../package.json');
let router = express.Router({
const router = express.Router({
caseSensitive: true,
strict: true,
mergeParams: true
@ -10,14 +10,16 @@ let router = express.Router({
router
.route('/')
.options((req, res) => {
.options((_, res) => {
res.sendStatus(204);
})
/**
* GET /schema
*/
.get((req, res/*, next*/) => {
.get(async (req, res) => {
let swaggerJSON = await schema.getCompiledSchema();
let proto = req.protocol;
if (typeof req.headers['x-forwarded-proto'] !== 'undefined' && req.headers['x-forwarded-proto']) {
proto = req.headers['x-forwarded-proto'];

View File

@ -1,8 +1,9 @@
const express = require('express');
const validator = require('../../lib/validator');
const jwtdecode = require('../../lib/express/jwt-decode');
const internalSetting = require('../../internal/setting');
const apiValidator = require('../../lib/validator/api');
const validator = require('../lib/validator');
const jwtdecode = require('../lib/express/jwt-decode');
const apiValidator = require('../lib/validator/api');
const internalSetting = require('../internal/setting');
const schema = require('../schema');
let router = express.Router({
caseSensitive: true,
@ -15,7 +16,7 @@ let router = express.Router({
*/
router
.route('/')
.options((req, res) => {
.options((_, res) => {
res.sendStatus(204);
})
.all(jwtdecode())
@ -25,7 +26,7 @@ router
*
* Retrieve all settings
*/
.get((req, res, next) => {
.get((_, res, next) => {
internalSetting.getAll(res.locals.access)
.then((rows) => {
res.status(200)
@ -41,7 +42,7 @@ router
*/
router
.route('/:setting_id')
.options((req, res) => {
.options((_, res) => {
res.sendStatus(204);
})
.all(jwtdecode())
@ -57,7 +58,8 @@ router
additionalProperties: false,
properties: {
setting_id: {
$ref: 'definitions#/definitions/setting_id'
type: 'string',
minLength: 1
}
}
}, {
@ -81,7 +83,7 @@ router
* Update and existing setting
*/
.put((req, res, next) => {
apiValidator({$ref: 'endpoints/settings#/links/1/schema'}, req.body)
apiValidator(schema.getValidationSchema('/settings/{settingID}', 'put'), req.body)
.then((payload) => {
payload.id = req.params.setting_id;
return internalSetting.update(res.locals.access, payload);

View File

@ -1,7 +1,8 @@
const express = require('express');
const jwtdecode = require('../../lib/express/jwt-decode');
const internalToken = require('../../internal/token');
const apiValidator = require('../../lib/validator/api');
const jwtdecode = require('../lib/express/jwt-decode');
const apiValidator = require('../lib/validator/api');
const internalToken = require('../internal/token');
const schema = require('../schema');
let router = express.Router({
caseSensitive: true,
@ -11,7 +12,7 @@ let router = express.Router({
router
.route('/')
.options((req, res) => {
.options((_, res) => {
res.sendStatus(204);
})
@ -39,11 +40,9 @@ router
*
* Create a new Token
*/
.post((req, res, next) => {
apiValidator({$ref: 'endpoints/tokens#/links/0/schema'}, req.body)
.then((payload) => {
return internalToken.getTokenFromEmail(payload);
})
.post(async (req, res, next) => {
apiValidator(schema.getValidationSchema('/tokens', 'post'), req.body)
.then(internalToken.getTokenFromEmail)
.then((data) => {
res.status(200)
.send(data);

View File

@ -1,9 +1,10 @@
const express = require('express');
const validator = require('../../lib/validator');
const jwtdecode = require('../../lib/express/jwt-decode');
const userIdFromMe = require('../../lib/express/user-id-from-me');
const internalUser = require('../../internal/user');
const apiValidator = require('../../lib/validator/api');
const validator = require('../lib/validator');
const jwtdecode = require('../lib/express/jwt-decode');
const userIdFromMe = require('../lib/express/user-id-from-me');
const internalUser = require('../internal/user');
const apiValidator = require('../lib/validator/api');
const schema = require('../schema');
let router = express.Router({
caseSensitive: true,
@ -16,7 +17,7 @@ let router = express.Router({
*/
router
.route('/')
.options((req, res) => {
.options((_, res) => {
res.sendStatus(204);
})
.all(jwtdecode())
@ -31,10 +32,10 @@ router
additionalProperties: false,
properties: {
expand: {
$ref: 'definitions#/definitions/expand'
$ref: 'common#/properties/expand'
},
query: {
$ref: 'definitions#/definitions/query'
$ref: 'common#/properties/query'
}
}
}, {
@ -48,7 +49,11 @@ router
res.status(200)
.send(users);
})
.catch(next);
.catch((err) => {
console.log(err);
next(err);
});
//.catch(next);
})
/**
@ -57,7 +62,7 @@ router
* Create a new User
*/
.post((req, res, next) => {
apiValidator({$ref: 'endpoints/users#/links/1/schema'}, req.body)
apiValidator(schema.getValidationSchema('/users', 'post'), req.body)
.then((payload) => {
return internalUser.create(res.locals.access, payload);
})
@ -75,7 +80,7 @@ router
*/
router
.route('/:user_id')
.options((req, res) => {
.options((_, res) => {
res.sendStatus(204);
})
.all(jwtdecode())
@ -92,10 +97,10 @@ router
additionalProperties: false,
properties: {
user_id: {
$ref: 'definitions#/definitions/id'
$ref: 'common#/properties/id'
},
expand: {
$ref: 'definitions#/definitions/expand'
$ref: 'common#/properties/expand'
}
}
}, {
@ -113,7 +118,10 @@ router
res.status(200)
.send(user);
})
.catch(next);
.catch((err) => {
console.log(err);
next(err);
});
})
/**
@ -122,7 +130,7 @@ router
* Update and existing user
*/
.put((req, res, next) => {
apiValidator({$ref: 'endpoints/users#/links/2/schema'}, req.body)
apiValidator(schema.getValidationSchema('/users/{userID}', 'put'), req.body)
.then((payload) => {
payload.id = req.params.user_id;
return internalUser.update(res.locals.access, payload);
@ -167,7 +175,7 @@ router
* Update password for a user
*/
.put((req, res, next) => {
apiValidator({$ref: 'endpoints/users#/links/4/schema'}, req.body)
apiValidator(schema.getValidationSchema('/users/{userID}/auth', 'put'), req.body)
.then((payload) => {
payload.id = req.params.user_id;
return internalUser.setPassword(res.locals.access, payload);
@ -198,7 +206,7 @@ router
* Set some or all permissions for a user
*/
.put((req, res, next) => {
apiValidator({$ref: 'endpoints/users#/links/5/schema'}, req.body)
apiValidator(schema.getValidationSchema('/users/{userID}/permissions', 'put'), req.body)
.then((payload) => {
payload.id = req.params.user_id;
return internalUser.setPermissions(res.locals.access, payload);
@ -217,7 +225,7 @@ router
*/
router
.route('/:user_id/login')
.options((req, res) => {
.options((_, res) => {
res.sendStatus(204);
})
.all(jwtdecode())

115
backend/schema/common.json Normal file
View File

@ -0,0 +1,115 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "common",
"type": "object",
"properties": {
"id": {
"description": "Unique identifier",
"readOnly": true,
"type": "integer",
"minimum": 1
},
"expand": {
"anyOf": [
{
"type": "null"
},
{
"type": "array",
"minItems": 1,
"items": {
"type": "string"
}
}
]
},
"query": {
"anyOf": [
{
"type": "null"
},
{
"type": "string",
"minLength": 1,
"maxLength": 255
}
]
},
"created_on": {
"description": "Date and time of creation",
"readOnly": true,
"type": "string"
},
"modified_on": {
"description": "Date and time of last update",
"readOnly": true,
"type": "string"
},
"user_id": {
"description": "User ID",
"type": "integer",
"minimum": 1
},
"certificate_id": {
"description": "Certificate ID",
"anyOf": [
{
"type": "integer",
"minimum": 0
},
{
"type": "string",
"pattern": "^new$"
}
]
},
"access_list_id": {
"description": "Access List ID",
"type": "integer",
"minimum": 0
},
"domain_names": {
"description": "Domain Names separated by a comma",
"type": "array",
"minItems": 1,
"maxItems": 100,
"uniqueItems": true,
"items": {
"type": "string",
"pattern": "^[^&| @!#%^();:/\\\\}{=+?<>,~`'\"]+$"
}
},
"enabled": {
"description": "Is Enabled",
"type": "boolean"
},
"ssl_forced": {
"description": "Is SSL Forced",
"type": "boolean"
},
"hsts_enabled": {
"description": "Is HSTS Enabled",
"type": "boolean"
},
"hsts_subdomains": {
"description": "Is HSTS applicable to all subdomains",
"type": "boolean"
},
"ssl_provider": {
"type": "string",
"pattern": "^(letsencrypt|other)$"
},
"http2_support": {
"description": "HTTP2 Protocol Support",
"type": "boolean"
},
"block_exploits": {
"description": "Should we block common exploits",
"type": "boolean"
},
"caching_enabled": {
"description": "Should we cache assets",
"type": "boolean"
}
}
}

View File

@ -0,0 +1,53 @@
{
"type": "object",
"description": "Access List object",
"required": ["id", "created_on", "modified_on", "owner_user_id", "name", "directive", "address", "satisfy_any", "pass_auth", "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"
},
"name": {
"type": "string",
"minLength": 1
},
"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"
}
}
}

View File

@ -0,0 +1,32 @@
{
"type": "object",
"description": "Audit Log object",
"required": ["id", "created_on", "modified_on", "user_id", "object_type", "object_id", "action", "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"
},
"user_id": {
"$ref": "../common.json#/properties/user_id"
},
"object_type": {
"type": "string"
},
"object_id": {
"$ref": "../common.json#/properties/id"
},
"action": {
"type": "string"
},
"meta": {
"type": "object"
}
}
}

View File

@ -0,0 +1,7 @@
{
"type": "array",
"description": "Certificates list",
"items": {
"$ref": "./certificate-object.json"
}
}

View File

@ -0,0 +1,81 @@
{
"type": "object",
"description": "Certificate object",
"required": ["id", "created_on", "modified_on", "owner_user_id", "provider", "nice_name", "domain_names", "expires_on", "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"
},
"provider": {
"$ref": "../common.json#/properties/ssl_provider"
},
"nice_name": {
"type": "string",
"description": "Nice Name for the custom certificate"
},
"domain_names": {
"description": "Domain Names separated by a comma",
"type": "array",
"maxItems": 100,
"uniqueItems": true,
"items": {
"type": "string",
"pattern": "^[^&| @!#%^();:/\\\\}{=+?<>,~`'\"]+$"
}
},
"expires_on": {
"description": "Date and time of expiration",
"readOnly": true,
"type": "string"
},
"owner": {
"$ref": "./user-object.json"
},
"meta": {
"type": "object",
"additionalProperties": false,
"properties": {
"certificate": {
"type": "string",
"minLength": 1
},
"certificate_key": {
"type": "string",
"minLength": 1
},
"dns_challenge": {
"type": "boolean"
},
"dns_provider": {
"type": "string"
},
"dns_provider_credentials": {
"type": "string"
},
"letsencrypt_agree": {
"type": "boolean"
},
"letsencrypt_certificate": {
"type": "object"
},
"letsencrypt_email": {
"type": "string"
},
"propagation_seconds": {
"type": "integer",
"minimum": 0
}
}
}
}
}

View File

@ -0,0 +1,7 @@
{
"type": "array",
"description": "404 Hosts list",
"items": {
"$ref": "./dead-host-object.json"
}
}

View File

@ -0,0 +1,47 @@
{
"type": "object",
"description": "404 Host object",
"required": ["id", "created_on", "modified_on", "owner_user_id", "domain_names", "certificate_id", "ssl_forced", "hsts_enabled", "hsts_subdomains", "http2_support", "advanced_config", "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"
},
"domain_names": {
"$ref": "../common.json#/properties/domain_names"
},
"certificate_id": {
"$ref": "../common.json#/properties/certificate_id"
},
"ssl_forced": {
"$ref": "../common.json#/properties/ssl_forced"
},
"hsts_enabled": {
"$ref": "../common.json#/properties/hsts_enabled"
},
"hsts_subdomains": {
"$ref": "../common.json#/properties/hsts_subdomains"
},
"http2_support": {
"$ref": "../common.json#/properties/http2_support"
},
"advanced_config": {
"type": "string"
},
"enabled": {
"$ref": "../common.json#/properties/enabled"
},
"meta": {
"type": "object"
}
}
}

View File

@ -0,0 +1,14 @@
{
"type": "object",
"description": "Error object",
"additionalProperties": false,
"required": ["code", "message"],
"properties": {
"code": {
"type": "integer"
},
"message": {
"type": "string"
}
}
}

View File

@ -0,0 +1,9 @@
{
"type": "object",
"description": "Error",
"properties": {
"error": {
"$ref": "./error-object.json"
}
}
}

View File

@ -0,0 +1,38 @@
{
"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
}
}
}
}
}

View File

@ -0,0 +1,41 @@
{
"type": "object",
"minProperties": 1,
"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"]
}
}
}

View File

@ -0,0 +1,7 @@
{
"type": "array",
"description": "Proxy Hosts list",
"items": {
"$ref": "./proxy-host-object.json"
}
}

View File

@ -0,0 +1,153 @@
{
"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"
],
"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"
},
"domain_names": {
"$ref": "../common.json#/properties/domain_names"
},
"forward_host": {
"type": "string",
"minLength": 1,
"maxLength": 255
},
"forward_port": {
"type": "integer",
"minimum": 1,
"maximum": 65535
},
"access_list_id": {
"$ref": "../common.json#/properties/access_list_id"
},
"certificate_id": {
"$ref": "../common.json#/properties/certificate_id"
},
"ssl_forced": {
"$ref": "../common.json#/properties/ssl_forced"
},
"caching_enabled": {
"$ref": "../common.json#/properties/caching_enabled"
},
"block_exploits": {
"$ref": "../common.json#/properties/block_exploits"
},
"advanced_config": {
"type": "string"
},
"meta": {
"type": "object"
},
"allow_websocket_upgrade": {
"description": "Allow Websocket Upgrade for all paths",
"example": true,
"type": "boolean"
},
"http2_support": {
"$ref": "../common.json#/properties/http2_support"
},
"forward_scheme": {
"type": "string",
"enum": ["http", "https"]
},
"enabled": {
"$ref": "../common.json#/properties/enabled"
},
"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": "#/properties/forward_scheme"
},
"forward_host": {
"$ref": "#/properties/forward_host"
},
"forward_port": {
"$ref": "#/properties/forward_port"
},
"forward_path": {
"type": "string"
},
"advanced_config": {
"type": "string"
}
}
}
},
"hsts_enabled": {
"$ref": "../common.json#/properties/hsts_enabled"
},
"hsts_subdomains": {
"$ref": "../common.json#/properties/hsts_subdomains"
},
"certificate": {
"oneOf": [
{
"type": "null"
},
{
"$ref": "./certificate-object.json"
}
]
},
"owner": {
"$ref": "./user-object.json"
},
"access_list": {
"oneOf": [
{
"type": "null"
},
{
"$ref": "./access-list-object.json"
}
]
}
}
}

View File

@ -0,0 +1,7 @@
{
"type": "array",
"description": "Redirection Hosts list",
"items": {
"$ref": "./redirection-host-object.json"
}
}

View File

@ -0,0 +1,72 @@
{
"type": "object",
"description": "Redirection Host object",
"required": ["id", "created_on", "modified_on", "owner_user_id", "domain_names", "forward_http_code", "forward_scheme", "forward_domain_name", "preserve_path", "certificate_id", "ssl_forced", "hsts_enabled", "hsts_subdomains", "http2_support", "block_exploits", "advanced_config", "enabled", "meta"],
"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"
},
"domain_names": {
"$ref": "../common.json#/properties/domain_names"
},
"forward_http_code": {
"description": "Redirect HTTP Status Code",
"example": 302,
"type": "integer",
"minimum": 300,
"maximum": 308
},
"forward_scheme": {
"type": "string",
"enum": ["auto", "http", "https"]
},
"forward_domain_name": {
"description": "Domain Name",
"example": "jc21.com",
"type": "string",
"pattern": "^(?:[^.*]+\\.?)+[^.]$"
},
"preserve_path": {
"description": "Should the path be preserved",
"example": true,
"type": "boolean"
},
"certificate_id": {
"$ref": "../common.json#/properties/certificate_id"
},
"ssl_forced": {
"$ref": "../common.json#/properties/ssl_forced"
},
"hsts_enabled": {
"$ref": "../common.json#/properties/hsts_enabled"
},
"hsts_subdomains": {
"$ref": "../common.json#/properties/hsts_subdomains"
},
"http2_support": {
"$ref": "../common.json#/properties/http2_support"
},
"block_exploits": {
"$ref": "../common.json#/properties/block_exploits"
},
"advanced_config": {
"type": "string"
},
"enabled": {
"$ref": "../common.json#/properties/enabled"
},
"meta": {
"type": "object"
}
}
}

View File

@ -0,0 +1,6 @@
{
"BearerAuth": {
"type": "http",
"scheme": "bearer"
}
}

View File

@ -0,0 +1,7 @@
{
"type": "array",
"description": "Setting list",
"items": {
"$ref": "./setting-object.json"
}
}

View File

@ -0,0 +1,56 @@
{
"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",
"anyOf": [
{
"type": "string",
"minLength": 1
},
{
"type": "integer"
},
{
"type": "object"
},
{
"type": "number"
},
{
"type": "array"
}
]
},
"meta": {
"description": "Extra metadata",
"example": {
"redirect": "http://example.com",
"html": "<h1>404</h1>"
},
"type": "object"
}
}
}

View File

@ -0,0 +1,7 @@
{
"type": "array",
"description": "Proxy Hosts list",
"items": {
"$ref": "./proxy-host-object.json"
}
}

View File

@ -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"
},
"enabled": {
"$ref": "../common.json#/properties/enabled"
},
"certificate_id": {
"$ref": "../common.json#/properties/certificate_id"
},
"meta": {
"type": "object"
},
"owner": {
"$ref": "./user-object.json"
},
"certificate": {
"oneOf": [
{
"type": "null"
},
{
"$ref": "./certificate-object.json"
}
]
}
}
}

View File

@ -0,0 +1,18 @@
{
"type": "object",
"description": "Token object",
"required": ["expires", "token"],
"additionalProperties": false,
"properties": {
"expires": {
"description": "Token Expiry ISO Time String",
"example": "2025-02-04T20:40:46.340Z",
"type": "string"
},
"token": {
"description": "JWT Token",
"example": "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.ey...xaHKYr3Kk6MvkUjcC4",
"type": "string"
}
}
}

View File

@ -0,0 +1,7 @@
{
"type": "array",
"description": "User list",
"items": {
"$ref": "./user-object.json"
}
}

View File

@ -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"
}
}
}
}

View File

@ -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": 15,
"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"
}
}
}

View File

@ -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"
}
}
]
}

View File

@ -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"
}
}
]
}

View File

@ -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"
}
}
]
}

View File

@ -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"
}
}
]
}

View File

@ -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"
}
}
]
}

View File

@ -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"
}
}
}

View File

@ -1,234 +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"
},
"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"
},
"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"
},
"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"
},
"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"
}
}
]
}

View File

@ -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"
}
}
}
}
]
}

View File

@ -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"
}
}
}

View File

@ -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"
}
}
}

41
backend/schema/index.js Normal file
View File

@ -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;
}
};

View File

@ -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"
}
}
}

View File

@ -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"
}
}
}
}
}
}

View File

@ -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"
}
}
}
}
}
}

View File

@ -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"
}
}
}
}
}
}

View File

@ -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"
}
}
}
}
}
}

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