Compare commits

...

178 Commits

Author SHA1 Message Date
301499dc52 Merge pull request #659 from jc21/develop
v2.6.1
2020-10-16 15:53:56 +10:00
5c2f13ed8e Merge branch 'master' into develop 2020-10-16 13:44:10 +10:00
e30ad81f69 Updated version 2020-10-16 13:43:13 +10:00
21f36f535f Don't spit out a ; if the preceeding value is empty 2020-10-16 13:41:08 +10:00
c14236823a Merge pull request #656 from chaptergy/fixes-custom-certificate-upload
Fixes custom certificate upload
2020-10-16 08:33:51 +10:00
551a9fe1c6 Fixes custom certificate upload 2020-10-15 14:58:05 +02:00
e3399e1035 Merge pull request #654 from jc21/develop
2.6.0 Release
2020-10-15 15:14:57 +10:00
c413b4af3f Added contributors 2020-10-15 14:06:21 +10:00
dbf5dec23b Bump version 2020-10-15 10:40:01 +10:00
10f0eb17d7 Fix linting errors 2020-10-15 10:33:51 +10:00
e3b680c351 Merge pull request #653 from jmwebslave/dont-pass-auth-header
Pass/Don't Pass Auth Header
2020-10-15 10:10:33 +10:00
0df0545777 Allows auth information from AccessList not to be passed to proxied hosts. Resolves issue #153.
Signed-off-by: James Morgan <jmorgan.au+github@gmail.com>
2020-10-15 10:23:09 +11:00
165bfc9f5f Merge pull request #607 from jc21/dependabot/npm_and_yarn/docs/node-forge-0.10.0
Bump node-forge from 0.9.1 to 0.10.0 in /docs
2020-10-15 08:34:14 +10:00
5830bd73b9 Merge pull request #608 from Philip-Mooney/master
Fix for access list getAll when not granted all permissions
2020-10-15 08:33:58 +10:00
3c4ce839b9 Merge pull request #635 from chaptergy/allow-more-dns-challenges
Allow DNS challenges not just for cloudflare
2020-10-14 19:12:15 +10:00
ac9f052309 Fixes linting errors 2020-10-14 09:55:45 +02:00
049e424957 Adds special case for Route53 2020-10-14 09:20:52 +02:00
07e78aec48 Adds error stack information in prod environment for certificates 2020-10-08 15:30:13 +02:00
3fec135fe5 Fixes ESlint formatting errors 2020-10-08 14:38:19 +02:00
867fe1322b Unifies directory structure in dev and prod containers 2020-10-08 13:38:20 +02:00
95208a50a7 Increases timeouts in front- and backend 2020-10-08 13:21:17 +02:00
514b13fcc2 Fixes build issues due to globally used file 2020-10-06 16:12:12 +02:00
4cbc1f5bbe Minor refactoring 2020-10-06 15:37:51 +02:00
64de36cdf2 Adds more DNS plugins 2020-10-06 15:16:45 +02:00
093b48ad7b Implements backend changes to allow more dns challenges 2020-10-06 14:52:06 +02:00
05f6a55a0b Adds frontend improvements and fixes 2020-10-06 14:49:02 +02:00
2523424f68 Updates dockerfiles 2020-10-05 01:04:18 +02:00
b81325d7bf Implements dns challenge provider selection in frontend 2020-10-05 01:04:06 +02:00
3e10b7b2b1 Fix for access list getAll when not granted all permissions 2020-09-19 22:16:16 +01:00
e5cb750015 Bump node-forge from 0.9.1 to 0.10.0 in /docs
Bumps [node-forge](https://github.com/digitalbazaar/forge) from 0.9.1 to 0.10.0.
- [Release notes](https://github.com/digitalbazaar/forge/releases)
- [Changelog](https://github.com/digitalbazaar/forge/blob/master/CHANGELOG.md)
- [Commits](https://github.com/digitalbazaar/forge/compare/0.9.1...0.10.0)

Signed-off-by: dependabot[bot] <support@github.com>
2020-09-17 23:42:28 +00:00
28f72086ec Merge pull request #592 from jc21/develop
v2.5.0
2020-09-04 09:07:47 +10:00
a6b9bd7b01 Version bump and contributors 2020-09-03 14:11:44 +10:00
2c5eac9dad Merge branch 'master' of github.com:jc21/nginx-proxy-manager into develop 2020-09-03 14:03:43 +10:00
87f61b8527 Merge pull request #572 from jipjan/features/dns-cloudflare
Add DNS CloudFlare with wildcard support
2020-09-03 14:01:05 +10:00
74bfe490c6 Merge pull request #587 from duhruh/bug/custom-ssl-inputs
Allow inputs to update
2020-09-03 13:53:17 +10:00
015167f34d Allow inputs to update 2020-08-29 20:24:51 -07:00
4bafc7ff1a Merge pull request #546 from jc21/dependabot/npm_and_yarn/docs/prismjs-1.21.0
Bump prismjs from 1.20.0 to 1.21.0 in /docs
2020-08-25 10:51:11 +10:00
bf8beb50b4 Merge pull request #559 from jlesage/remove-webroot-certbot-arg
Removed the hardcoded `--webroot` certbot argument to better support DNS challenge
2020-08-25 08:44:00 +10:00
e5034a34f5 Merge pull request #570 from jc21/dependabot/npm_and_yarn/backend/bcrypt-5.0.0
Bump bcrypt from 4.0.1 to 5.0.0 in /backend
2020-08-25 08:31:48 +10:00
a561605653 show in ssl certificates list that CloudFlare is used 2020-08-24 09:09:52 +00:00
e8596c1554 cloudflare DNS also possible while adding proxy, redirection and 404 2020-08-24 09:00:00 +00:00
ab67481e99 fix eslint errors 2020-08-23 18:56:25 +00:00
1b611e67c8 Merge commit 'c5aa2b9f771cbd4c78c239ed0791aeb8d9e4d2e4' into features/dns-cloudflare 2020-08-23 18:30:07 +00:00
c5aa2b9f77 add cloudflare renew and make revoke working for both by deleting unnecessary config command 2020-08-23 18:29:16 +00:00
cff6c4d1f5 - prevent wildcard generation when not using Cloudflare dns
- fix cloudflare token required logic
2020-08-23 16:48:14 +00:00
077cf75ef2 wildcard support 2020-08-23 13:24:20 +00:00
ff1770204c request via cloudflare dns working 2020-08-23 12:50:41 +00:00
b9a95840e0 add cloudflare dns option to letsencrypt via manual certificate 2020-08-23 11:40:41 +00:00
2d7576c57e add cloudflare dns also to dev docker file 2020-08-23 10:54:36 +00:00
251aac716a Add CloudFlare DNS plugin to certbot 2020-08-21 09:49:43 +02:00
6694a42270 Merge pull request #560 from jlesage/remove-from-unixtime
Removed usage of `FROM_UNIXTIME` mysql-specific function.
2020-08-21 14:21:40 +10:00
f78a4c6ad1 Bump bcrypt from 4.0.1 to 5.0.0 in /backend
Bumps [bcrypt](https://github.com/kelektiv/node.bcrypt.js) from 4.0.1 to 5.0.0.
- [Release notes](https://github.com/kelektiv/node.bcrypt.js/releases)
- [Changelog](https://github.com/kelektiv/node.bcrypt.js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/kelektiv/node.bcrypt.js/compare/v4.0.1...v5.0.0)

Signed-off-by: dependabot[bot] <support@github.com>
2020-08-20 17:01:00 +00:00
83fad8bcda Removed usage of FROM_UNIXTIME mysql-specific function.
This provide better interoperability with different databases (e.g. sqlite).
Fixes #557
2020-08-14 19:31:05 -04:00
f539e813aa Removed the hardcoded --webroot certbot argument to better support DNS challenge. Also, this option is already set in the default letsencrypt.ini. 2020-08-14 14:28:03 -04:00
5d65166777 Ignore local subnets for real IP determination 2020-08-12 09:32:40 +10:00
70346138a7 Bump prismjs from 1.20.0 to 1.21.0 in /docs
Bumps [prismjs](https://github.com/PrismJS/prism) from 1.20.0 to 1.21.0.
- [Release notes](https://github.com/PrismJS/prism/releases)
- [Changelog](https://github.com/PrismJS/prism/blob/master/CHANGELOG.md)
- [Commits](https://github.com/PrismJS/prism/compare/v1.20.0...v1.21.0)

Signed-off-by: dependabot[bot] <support@github.com>
2020-08-08 00:02:04 +00:00
d68656559c Merge pull request #544 from jlesage/sqlite-now-helper-fix
Fixed now_helper for sqlite (time is missing)
2020-08-07 08:37:00 +10:00
01660b5b80 Fixed now_helper for sqlite: it should also returns the time. 2020-08-06 17:16:22 -04:00
74010acd85 Merge pull request #543 from jc21/develop
v2.4.0
2020-08-06 16:00:10 +10:00
7c7d255172 Added another contributor 2020-08-06 14:46:19 +10:00
058f1e9835 Merge pull request #464 from vrenjith/patch-1
Update location-item.ejs - forward_host size increase to 200
2020-08-06 14:45:09 +10:00
b4fc629ec0 Bumped version 2020-08-06 14:43:34 +10:00
ae06b2da75 Updated deps and added contributor 2020-08-06 14:40:54 +10:00
54d423a11f Updated doc for sqlite 2020-08-06 14:27:29 +10:00
5da6c97a00 Pull cypress tests from correct location 2020-08-06 13:57:33 +10:00
bf2f13443f Cypress fixes 2020-08-06 12:47:24 +10:00
9ce4c3fe2f CI fix 2020-08-06 12:02:47 +10:00
4a07bf666d Added users cypress tests 2020-08-06 11:57:31 +10:00
5be46b4b20 Cypress fixes 2020-08-06 11:26:37 +10:00
7fd825b76b Use development config file in CI 2020-08-06 10:59:25 +10:00
b23d59dec7 Updated cypress to 4.12.1 2020-08-06 09:00:52 +10:00
492d450d26 Sqlite Tweaks
- Added cypress testing in CI for sqlite
- Cleaned up promises in setup
- Ensure check for settings is strict
2020-08-06 08:58:20 +10:00
04412f3624 Merge pull request #510 from tg44/multidb-re
Multidb - sqlite support
2020-08-06 08:33:00 +10:00
c41057b28a Revert builx push experiment 2020-07-31 09:28:45 +10:00
8312bc0100 Use same tags for experiment 2020-07-30 14:00:59 +10:00
85ac43bc5e Merge branch 'master' of github.com:jc21/nginx-proxy-manager into develop 2020-07-30 08:31:18 +10:00
d1a0780c7a Attempt to circumvent docker login token timeouts 2020-07-30 08:30:26 +10:00
f9b8d76527 Merge pull request #513 from jc21/dependabot/npm_and_yarn/frontend/lodash-4.17.19
Bump lodash from 4.17.15 to 4.17.19 in /frontend
2020-07-20 12:39:10 +10:00
26f00eeae4 Merge branch 'master' into dependabot/npm_and_yarn/frontend/lodash-4.17.19 2020-07-20 10:59:15 +10:00
1bc2df2178 Merge pull request #514 from jc21/dependabot/npm_and_yarn/docs/lodash-4.17.19
Bump lodash from 4.17.15 to 4.17.19 in /docs
2020-07-20 10:58:36 +10:00
8dfbcef198 Bump lodash from 4.17.15 to 4.17.19 in /docs
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.19.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.19)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-19 20:33:49 +00:00
6690b7735d sqlite3 and psql support 2020-07-19 20:04:29 +02:00
a9e7222e5e introduced now_helper for multidb capabilities 2020-07-19 20:03:53 +02:00
f8edeb2775 fixed migration and setup
more info: https://github.com/knex/knex/issues/2820
2020-07-19 20:02:20 +02:00
d1786fe159 Bump lodash from 4.17.15 to 4.17.19 in /frontend
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.19.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.19)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-19 15:20:28 +00:00
157a12fb7c Update location-item.ejs 2020-06-19 01:56:16 +05:30
3f723b1638 Merge pull request #454 from jc21/develop
v2.3.1
2020-06-09 09:47:31 +10:00
e2e9835d01 Version bump 2020-06-09 09:17:25 +10:00
7599617b67 Merge pull request #452 from jc21/dependabot/npm_and_yarn/docs/websocket-extensions-0.1.4
Bump websocket-extensions from 0.1.3 to 0.1.4 in /docs
2020-06-08 11:14:20 +10:00
18a5b11033 Bump websocket-extensions from 0.1.3 to 0.1.4 in /docs
Bumps [websocket-extensions](https://github.com/faye/websocket-extensions-node) from 0.1.3 to 0.1.4.
- [Release notes](https://github.com/faye/websocket-extensions-node/releases)
- [Changelog](https://github.com/faye/websocket-extensions-node/blob/master/CHANGELOG.md)
- [Commits](https://github.com/faye/websocket-extensions-node/compare/0.1.3...0.1.4)

Signed-off-by: dependabot[bot] <support@github.com>
2020-06-07 23:36:47 +00:00
fff31b0f34 Ensure we're using the latest node image 2020-06-03 10:30:29 +10:00
c02e30663a Revert last 2020-06-02 20:21:27 +10:00
4c6527cafc Ensure python2 is installed for frontend build 2020-06-02 20:09:27 +10:00
55bddb12e5 Merge pull request #435 from Subv/access_lists_ips
Don't use duplicate relations when eager-loading access list items and clients.
2020-06-02 19:42:27 +10:00
d95861e1fb Don't use duplicate relations when eager-loading access list items and clients.
This fixes an Objection warning: 'Duplicate relation "proxy_hosts" in a relation expression. You should use "a.[b, c]" instead of "[a.b, a.c]". This will cause an error in objection 2.0'.

It also fixes the access list clients not being properly eager-loaded when building the proxy host nginx configuration files. Closes #434
2020-05-29 20:29:34 -05:00
94754a5cb3 Revert CI debug 2020-05-28 20:26:16 +10:00
546f862236 Merge pull request #429 from jc21/develop
v2.3.0
2020-05-28 17:06:19 +10:00
f105e29e56 Merge branch 'master' into develop 2020-05-28 15:38:38 +10:00
5c15993d06 Contributors 7 wide 2020-05-28 13:31:41 +10:00
a369ea1080 Bump version 2020-05-28 13:29:55 +10:00
98068c0f57 Debug CI by leaving images alive 2020-05-28 13:26:36 +10:00
e0ef8683a2 Merge pull request #428 from jc21/openresty
Openresty base
2020-05-28 12:22:31 +10:00
66412a75f9 Revert to node base now that base has openresty 2020-05-28 09:25:29 +10:00
84d8fb0899 Merge pull request #403 from Indemnity83/empty-auth
Don't ask for username/password if none are defined
2020-05-28 09:18:50 +10:00
c631537dbe Don't wipe out nginx dir, keeps luajit 2020-05-27 10:38:00 +10:00
8d2f49541c Use OpenResty base image 2020-05-26 14:38:41 +10:00
55a28e3437 Merge branch 'develop' of github.com:jc21/nginx-proxy-manager into develop 2020-05-25 14:53:46 +10:00
67ea2d01c8 Added gitter, contributors 2020-05-25 14:53:35 +10:00
dab229e37c Merge pull request #406 from theraw/patch-1
set proper timeout.
2020-05-25 14:37:06 +10:00
7084473330 Merge pull request #416 from jc21/develop
v2.2.4
2020-05-21 16:52:16 +10:00
dd2e335fae Cypress 4.6.0 and tweaks to scripts 2020-05-21 16:11:19 +10:00
1ff87bbc12 Version bump 2020-05-21 15:09:51 +10:00
2ebfdcf0c9 Fix LE certs for IPv6 only domains Fixes 394 2020-05-20 22:21:26 +10:00
8ab161a3ee Merge branch 'develop' of github.com:jc21/nginx-proxy-manager into develop 2020-05-20 21:53:44 +10:00
e74b9617be Added product support github template 2020-05-20 21:40:54 +10:00
c3d88c83e3 Merge pull request #402 from Indemnity83/patch-2
Fix address validation rule to allow 'all' keyword
2020-05-20 21:16:11 +10:00
3e912a7474 Added FAQ to docs 2020-05-20 21:14:00 +10:00
0d726a1d83 Merge pull request #405 from Indemnity83/fix-satisfy
fix spelling of 'satisfy'
2020-05-20 20:44:38 +10:00
affabf065e set proper timeout. 2020-05-11 00:24:02 +02:00
e6ea77d263 fix spelling of 'satisfy'
Fixes #385
2020-05-09 18:01:43 -07:00
df73c2a458 skip auth check if no users defined 2020-05-09 15:51:11 -07:00
96c5c79aef Fix address validation rule to allow 'all' keyword
The rule was looking for the keyword 'any' but should have been looking for 'all' 

http://nginx.org/en/docs/http/ngx_http_access_module.html
2020-05-09 09:31:58 -07:00
64922f07ff Merge pull request #388 from jc21/dependabot/npm_and_yarn/frontend/jquery-3.5.0
Bump jquery from 3.4.1 to 3.5.0 in /frontend
2020-05-07 14:53:24 +10:00
bae21f3210 Merge pull request #397 from Indemnity83/patch-1
apply migration to correct table
2020-05-05 10:14:47 +10:00
0702a4e58e Fix incorrect var 2020-05-05 10:00:41 +10:00
31f1d304d6 apply migration to correct table 2020-05-04 16:56:26 -07:00
291a74c295 Bump jquery from 3.4.1 to 3.5.0 in /frontend
Bumps [jquery](https://github.com/jquery/jquery) from 3.4.1 to 3.5.0.
- [Release notes](https://github.com/jquery/jquery/releases)
- [Commits](https://github.com/jquery/jquery/compare/3.4.1...3.5.0)

Signed-off-by: dependabot[bot] <support@github.com>
2020-04-30 14:43:28 +00:00
c0e9d1eb2f Fix satisy typo 2020-04-22 11:11:20 +10:00
a7cabdde3a Merge pull request #376 from spalger/expand-forward-host-size
expand the maximum size of the forward_host
2020-04-17 08:59:55 +10:00
3af560c2d0 switch to 255 limit to match db 2020-04-16 15:14:49 -07:00
1d23d5c761 remove maxlength from html too 2020-04-16 15:13:28 -07:00
995db12f22 remove arbitrary length limit of forward_host 2020-04-16 14:00:22 -07:00
4c60bfb66b Merge pull request #370 from jc21/develop
v2.2.3
2020-04-15 15:06:56 +10:00
1716747047 Merge branch 'master' into develop 2020-04-15 14:19:07 +10:00
090b4d0388 Version bump 2020-04-15 14:18:27 +10:00
a9f068daa8 Merge pull request #360 from Indemnity83/ip-access-control
Client Access Lists
2020-04-15 08:29:40 +10:00
f5ee91aeb3 write access list to proxy host config 2020-04-13 23:32:00 -07:00
e2ee2cbf2d enforce a 'deny all' default rule
this ensures that an access list is 'secure by default' and requires the user to create exceptions or holes in the proection instead of building the wall entirely. This also means that we no longer require the user to input any username/passwords or client addressses and can avoid internal errors which generate unhelpful user errors.
2020-04-13 23:31:54 -07:00
dcf8364899 Merge pull request #368 from jc21/develop
Support ipv6 address as a origin header, hopefully fixes #149
2020-04-14 14:40:00 +10:00
b783602786 Support ipv6 address as a origin header, hopefully fixes #149 2020-04-14 13:01:13 +10:00
005e64eb9f valite auth/access rules in backend 2020-04-13 19:23:55 -07:00
e9e5d293cc expand address format
now accepts CIDR notation, IPv6 or the string 'any'
2020-04-13 19:16:18 -07:00
a57255350f Merge pull request #365 from jc21/develop
Develop
2020-04-14 09:10:45 +10:00
781442bf1e Merge pull request #361 from Xantios/fix-bad-gateway
Fixes #310 Clarification on the docs
2020-04-14 09:09:39 +10:00
604bd2c576 Merge pull request #358 from dpanesso/dev-formatting
Documentation formatting
2020-04-14 08:37:23 +10:00
d9e1e1bbb7 Fixes #310 Clarification on the docs 2020-04-11 13:03:15 +02:00
907e9e182d remove testing cruft 2020-04-11 00:42:58 -07:00
0f238a5021 add satisfy configuration to the ui 2020-04-11 00:26:54 -07:00
8d432bd60a refine the UI labeling 2020-04-10 20:22:01 -07:00
fd932c7678 fix bugs preventing client rules from being updated 2020-04-10 17:42:44 -07:00
46a9f5cb96 add basic functionality to front end 2020-04-10 17:33:14 -07:00
f990d3f674 add access list clients to back-end 2020-04-10 16:38:54 -07:00
4a6de8deee Documentation formatting on advanced configuration page 2020-04-10 00:57:45 -05:00
9a7a216b23 Merge pull request #352 from jc21/develop
Develop
2020-04-07 12:09:17 +10:00
fccaaaae4d Merge branch 'master' into develop 2020-04-07 12:09:09 +10:00
a882b0be82 Merge branch 'develop' of github.com:jc21/nginx-proxy-manager into develop 2020-04-07 12:06:55 +10:00
db7bbab768 Updated npm deps 2020-04-07 12:06:36 +10:00
030e553549 Merge pull request #351 from jc21/develop
v2.2.2 Release
2020-04-07 12:01:48 +10:00
8b0ca8e367 Merge branch 'master' into develop 2020-04-07 11:23:03 +10:00
83b2b07200 Version bump 2020-04-07 10:45:45 +10:00
bdb591af9e - Add ability to disable ipv6, fixes #312
- Added ipv6 listening to hosts when configured, fixes #236 and #149
- Added documentation about disabling ipv6
- Updated npm packages
2020-04-07 10:43:19 +10:00
2993a08777 Merge pull request #349 from jc21/develop
v2.2.1
2020-04-06 10:23:45 +10:00
2a2d3d57ec Version bump 2020-04-06 09:13:40 +10:00
33c2c131c2 Merge pull request #345 from IronTooch/master
Documentation update
2020-04-06 08:33:02 +10:00
e4286c96a7 Merge pull request #342 from damianog/patch-1
Update proxy_host.conf
2020-04-06 08:32:48 +10:00
2d9486b6fd Merge pull request #347 from jc21/dependabot/npm_and_yarn/test/acorn-7.1.1
Bump acorn from 7.1.0 to 7.1.1 in /test
2020-04-06 07:55:40 +10:00
632ee2d0bd Merge pull request #348 from jc21/dependabot/npm_and_yarn/backend/acorn-7.1.1
Bump acorn from 7.1.0 to 7.1.1 in /backend
2020-04-06 07:36:16 +10:00
b09f201819 Bump acorn from 7.1.0 to 7.1.1 in /backend
Bumps [acorn](https://github.com/acornjs/acorn) from 7.1.0 to 7.1.1.
- [Release notes](https://github.com/acornjs/acorn/releases)
- [Commits](https://github.com/acornjs/acorn/compare/7.1.0...7.1.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-04-04 12:29:15 +00:00
baaf39c23d Bump acorn from 7.1.0 to 7.1.1 in /test
Bumps [acorn](https://github.com/acornjs/acorn) from 7.1.0 to 7.1.1.
- [Release notes](https://github.com/acornjs/acorn/releases)
- [Commits](https://github.com/acornjs/acorn/compare/7.1.0...7.1.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-04-04 12:28:57 +00:00
b7467c10e8 Update README.md 2020-04-01 23:44:24 -04:00
701ef18606 Update README.md 2020-04-01 23:42:58 -04:00
3e7d2b216b Update proxy_host.conf
FIX as explained on #340
2020-03-27 19:01:21 +01:00
41f16c20b6 Bump minimist from 1.2.0 to 1.2.2 in /docs
Bumps [minimist](https://github.com/substack/minimist) from 1.2.0 to 1.2.2.
- [Release notes](https://github.com/substack/minimist/releases)
- [Commits](https://github.com/substack/minimist/compare/1.2.0...1.2.2)

Signed-off-by: dependabot[bot] <support@github.com>
2020-03-19 09:53:16 +10:00
96bc0b53c3 Manually applying #296 - nickname required in ui 2020-03-12 08:46:18 +10:00
b80baa78ef Updated readme (#323)
* Tweaks and backend vscode settings

* Version bump

* Updated Icon to be more vibrant

* New logo and new login screen layout, version bump

* New documentation!

* Use CI to update package versions

because I was sick of changing it everywhere

* Generate docs

* Docs upload

* Fix pipeline

* Fix pipeline

* Update readme version before generating docs

* Testing docs deploy

* Fix pipeline

* Updated CI link

* Fix docs upload

* Docs upload fixes

* Fix s3 upload grrr

* Docs tweaks

* Updated readme

* Updated screenshots

* Updated cdn id for docs

* Updated github image

* Nicer readme header

Co-authored-by: Jamie Curnow <jamiec@benon.com>
2020-03-12 08:43:50 +10:00
ce88e0745d Updated cdn id for docs (#322)
* Tweaks and backend vscode settings

* Version bump

* Updated Icon to be more vibrant

* New logo and new login screen layout, version bump

* New documentation!

* Use CI to update package versions

because I was sick of changing it everywhere

* Generate docs

* Docs upload

* Fix pipeline

* Fix pipeline

* Update readme version before generating docs

* Testing docs deploy

* Fix pipeline

* Updated CI link

* Fix docs upload

* Docs upload fixes

* Fix s3 upload grrr

* Docs tweaks

* Updated readme

* Updated screenshots

* Updated cdn id for docs

Co-authored-by: Jamie Curnow <jamiec@benon.com>
2020-03-12 08:36:01 +10:00
256bd2336f v2.2.0 New Brand (#319)
* Tweaks and backend vscode settings

* Version bump

* Updated Icon to be more vibrant

* New logo and new login screen layout, version bump

* New documentation!

* Use CI to update package versions

because I was sick of changing it everywhere

* Generate docs

* Docs upload

* Fix pipeline

* Fix pipeline

* Update readme version before generating docs

* Testing docs deploy

* Fix pipeline

* Updated CI link

* Fix docs upload

* Docs upload fixes

* Fix s3 upload grrr

* Docs tweaks

* Updated readme

* Updated screenshots

Co-authored-by: Jamie Curnow <jamiec@benon.com>
2020-03-11 16:54:10 +10:00
1b6993ee70 Added better Build Badge 2020-03-10 09:59:34 +10:00
149 changed files with 17826 additions and 3706 deletions

View File

@ -0,0 +1,16 @@
---
name: Product Support
about: Need help configuring the software?
title: ''
labels: product-support
assignees: ''
---
**Checklist**
- Please read the [setup instructions](https://nginxproxymanager.com/setup/)
- Please read the [FAQ](https://nginxproxymanager.com/faq/)
**What is troubling you?**
_Clear and concise description of what you're trying to do and what isn't working for you_

2
.gitignore vendored
View File

@ -2,4 +2,4 @@
.idea
._*
.vscode
certbot-help.txt

View File

@ -0,0 +1,11 @@
{
"database": {
"engine": "knex-native",
"knex": {
"client": "sqlite3",
"connection": {
"filename": "/data/database.sqlite"
}
}
}
}

View File

@ -1 +1 @@
2.1.2
2.6.1

169
Jenkinsfile vendored
View File

@ -5,6 +5,7 @@ pipeline {
options {
buildDiscarder(logRotator(numToKeepStr: '5'))
disableConcurrentBuilds()
ansiColor('xterm')
}
environment {
IMAGE = "nginx-proxy-manager"
@ -42,60 +43,64 @@ pipeline {
}
}
}
stage('Versions') {
steps {
sh 'cat frontend/package.json | jq --arg BUILD_VERSION "${BUILD_VERSION}" \'.version = $BUILD_VERSION\' | sponge frontend/package.json'
sh 'echo -e "\\E[1;36mFrontend Version is:\\E[1;33m $(cat frontend/package.json | jq -r .version)\\E[0m"'
sh 'cat backend/package.json | jq --arg BUILD_VERSION "${BUILD_VERSION}" \'.version = $BUILD_VERSION\' | sponge backend/package.json'
sh 'echo -e "\\E[1;36mBackend Version is:\\E[1;33m $(cat backend/package.json | jq -r .version)\\E[0m"'
sh 'sed -i -E "s/(version-)[0-9]+\\.[0-9]+\\.[0-9]+(-green)/\\1${BUILD_VERSION}\\2/" README.md'
}
}
}
}
stage('Frontend') {
steps {
ansiColor('xterm') {
sh './scripts/frontend-build'
}
sh './scripts/frontend-build'
}
}
stage('Backend') {
steps {
ansiColor('xterm') {
echo 'Checking Syntax ...'
// See: https://github.com/yarnpkg/yarn/issues/3254
sh '''docker run --rm \\
-v "$(pwd)/backend:/app" \\
-w /app \\
node:latest \\
sh -c "yarn install && yarn eslint . && rm -rf node_modules"
'''
echo 'Checking Syntax ...'
// See: https://github.com/yarnpkg/yarn/issues/3254
sh '''docker run --rm \\
-v "$(pwd)/backend:/app" \\
-v "$(pwd)/global:/app/global" \\
-w /app \\
node:latest \\
sh -c "yarn install && yarn eslint . && rm -rf node_modules"
'''
echo 'Docker Build ...'
sh '''docker build --pull --no-cache --squash --compress \\
-t "${IMAGE}:ci-${BUILD_NUMBER}" \\
-f docker/Dockerfile \\
--build-arg TARGETPLATFORM=linux/amd64 \\
--build-arg BUILDPLATFORM=linux/amd64 \\
--build-arg BUILD_VERSION="${BUILD_VERSION}" \\
--build-arg BUILD_COMMIT="${BUILD_COMMIT}" \\
--build-arg BUILD_DATE="$(date '+%Y-%m-%d %T %Z')" \\
.
'''
}
echo 'Docker Build ...'
sh '''docker build --pull --no-cache --squash --compress \\
-t "${IMAGE}:ci-${BUILD_NUMBER}" \\
-f docker/Dockerfile \\
--build-arg TARGETPLATFORM=linux/amd64 \\
--build-arg BUILDPLATFORM=linux/amd64 \\
--build-arg BUILD_VERSION="${BUILD_VERSION}" \\
--build-arg BUILD_COMMIT="${BUILD_COMMIT}" \\
--build-arg BUILD_DATE="$(date '+%Y-%m-%d %T %Z')" \\
.
'''
}
}
stage('Test') {
stage('Integration Tests Sqlite') {
steps {
ansiColor('xterm') {
// Bring up a stack
sh 'docker-compose up -d fullstack'
sh './scripts/wait-healthy $(docker-compose ps -q fullstack) 120'
// 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'
// Get results
sh 'docker cp -L "$(docker-compose ps -q cypress):/results" test/'
}
// 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 | gzip > debug/docker_fullstack.log.gz'
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') {
@ -105,6 +110,51 @@ pipeline {
}
}
}
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'
}
junit 'test/results/junit/*'
}
}
}
stage('Docs') {
when {
not {
equals expected: 'UNSTABLE', actual: currentBuild.result
}
}
steps {
dir(path: 'docs') {
sh 'yarn install'
sh 'yarn build'
}
dir(path: 'docs/.vuepress/dist') {
sh 'tar -czf ../../docs.tgz *'
}
archiveArtifacts(artifacts: 'docs/docs.tgz', allowEmptyArchive: false)
}
}
stage('MultiArch Build') {
when {
not {
@ -112,14 +162,45 @@ pipeline {
}
}
steps {
ansiColor('xterm') {
withCredentials([usernamePassword(credentialsId: 'jc21-dockerhub', passwordVariable: 'dpass', usernameVariable: 'duser')]) {
sh "docker login -u '${duser}' -p '${dpass}'"
// Buildx with push
sh "./scripts/buildx --push ${BUILDX_PUSH_TAGS}"
withCredentials([usernamePassword(credentialsId: 'jc21-dockerhub', passwordVariable: 'dpass', usernameVariable: 'duser')]) {
// Docker Login
sh "docker login -u '${duser}' -p '${dpass}'"
// Buildx with push from cache
sh "./scripts/buildx --push ${BUILDX_PUSH_TAGS}"
}
}
}
stage('Docs Deploy') {
when {
allOf {
branch 'master'
not {
equals expected: 'UNSTABLE', actual: currentBuild.result
}
}
}
steps {
withCredentials([[$class: 'AmazonWebServicesCredentialsBinding', accessKeyVariable: 'AWS_ACCESS_KEY_ID', credentialsId: 'npm-s3-docs', secretKeyVariable: 'AWS_SECRET_ACCESS_KEY']]) {
sh """docker run --rm \\
--name \${COMPOSE_PROJECT_NAME}-docs-upload \\
-e S3_BUCKET=jc21-npm-site \\
-e AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID \\
-e AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY \\
-v \$(pwd):/app \\
-w /app \\
jc21/ci-tools \\
scripts/docs-upload /app/docs/.vuepress/dist/
"""
sh """docker run --rm \\
--name \${COMPOSE_PROJECT_NAME}-docs-invalidate \\
-e AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID \\
-e AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY \\
jc21/ci-tools \\
aws cloudfront create-invalidation --distribution-id EN1G6DEWZUTDT --paths '/*'
"""
}
}
}
stage('PR Comment') {
when {
@ -131,10 +212,8 @@ pipeline {
}
}
steps {
ansiColor('xterm') {
script {
def comment = pullRequest.comment("Docker Image for build ${BUILD_NUMBER} is available on [DockerHub](https://cloud.docker.com/repository/docker/jc21/${IMAGE}) as `jc21/${IMAGE}:github-${BRANCH_LOWER}`")
}
script {
def comment = pullRequest.comment("Docker Image for build ${BUILD_NUMBER} is available on [DockerHub](https://cloud.docker.com/repository/docker/jc21/${IMAGE}) as `jc21/${IMAGE}:github-${BRANCH_LOWER}`")
}
}
}

238
README.md
View File

@ -1,16 +1,27 @@
![Nginx Proxy Manager](https://public.jc21.com/nginx-proxy-manager/github.png "Nginx Proxy Manager")
# Nginx Proxy Manager
![Version](https://img.shields.io/badge/version-2.1.2-green.svg?style=for-the-badge)
![Stars](https://img.shields.io/docker/stars/jc21/nginx-proxy-manager.svg?style=for-the-badge)
![Pulls](https://img.shields.io/docker/pulls/jc21/nginx-proxy-manager.svg?style=for-the-badge)
[![Build Status](https://ci.nginxproxymanager.jc21.com/buildStatus/icon?job=nginx-proxy-manager%2Fmaster&style=flat-square)](https://ci.nginxproxymanager.jc21.com/job/nginx-proxy-manager/job/master/)
<p align="center">
<img src="https://nginxproxymanager.com/github.png">
<br><br>
<img src="https://img.shields.io/badge/version-2.6.1-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>
<a href="https://hub.docker.com/repository/docker/jc21/nginx-proxy-manager">
<img src="https://img.shields.io/docker/pulls/jc21/nginx-proxy-manager.svg?style=for-the-badge">
</a>
<a href="https://ci.nginxproxymanager.com/blue/organizations/jenkins/nginx-proxy-manager/branches/">
<img src="https://img.shields.io/jenkins/build?jobUrl=https%3A%2F%2Fci.nginxproxymanager.com%2Fjob%2Fnginx-proxy-manager%2Fjob%2Fmaster&style=for-the-badge">
</a>
<a href="https://gitter.im/nginx-proxy-manager/community">
<img alt="Gitter" src="https://img.shields.io/gitter/room/nginx-proxy-manager/community?style=for-the-badge">
</a>
</p>
This project comes as a pre-built docker image that enables you to easily forward to your websites
running at home or otherwise, including free SSL, without having to know too much about Nginx or Letsencrypt.
- [Quick Setup](https://nginxproxymanager.com#quick-setup)
- [Full Setup](https://nginxproxymanager.com/setup/)
- [Screenshots](https://nginxproxymanager.com/screenshots/)
## Project Goal
@ -32,54 +43,6 @@ so that the barrier for entry here is low.
- User management, permissions and audit log
## Screenshots
[![Login](https://public.jc21.com/nginx-proxy-manager/v2/small/login.jpg "Login")](https://public.jc21.com/nginx-proxy-manager/v2/large/login.jpg)
[![Dashboard](https://public.jc21.com/nginx-proxy-manager/v2/small/dashboard.jpg "Dashboard")](https://public.jc21.com/nginx-proxy-manager/v2/large/dashboard.jpg)
[![Proxy Hosts](https://public.jc21.com/nginx-proxy-manager/v2/small/proxy-hosts.jpg "Proxy Hosts")](https://public.jc21.com/nginx-proxy-manager/v2/large/proxy-hosts.jpg)
[![Proxy Host Settings](https://public.jc21.com/nginx-proxy-manager/v2/small/proxy-hosts-new1.jpg "Proxy Host Settings")](https://public.jc21.com/nginx-proxy-manager/v2/large/proxy-hosts-new1.jpg)
[![Proxy Host SSL](https://public.jc21.com/nginx-proxy-manager/v2/small/proxy-hosts-new2.jpg "Proxy Host SSL")](https://public.jc21.com/nginx-proxy-manager/v2/large/proxy-hosts-new2.jpg)
[![Redirection Hosts](https://public.jc21.com/nginx-proxy-manager/v2/small/redirection-hosts.jpg "Redirection Hosts")](https://public.jc21.com/nginx-proxy-manager/v2/large/redirection-hosts.jpg)
[![Redirection Host Settings](https://public.jc21.com/nginx-proxy-manager/v2/small/redirection-hosts-new1.jpg "Redirection Host Settings")](https://public.jc21.com/nginx-proxy-manager/v2/large/redirection-hosts-new1.jpg)
[![Streams](https://public.jc21.com/nginx-proxy-manager/v2/small/streams.jpg "Streams")](https://public.jc21.com/nginx-proxy-manager/v2/large/streams.jpg)
[![Stream Settings](https://public.jc21.com/nginx-proxy-manager/v2/small/streams-new1.jpg "Stream Settings")](https://public.jc21.com/nginx-proxy-manager/v2/large/streams-new1.jpg)
[![404 Hosts](https://public.jc21.com/nginx-proxy-manager/v2/small/dead-hosts.jpg "404 Hosts")](https://public.jc21.com/nginx-proxy-manager/v2/large/dead-hosts.jpg)
[![404 Host Settings](https://public.jc21.com/nginx-proxy-manager/v2/small/dead-hosts-new1.jpg "404 Host Settings")](https://public.jc21.com/nginx-proxy-manager/v2/large/dead-hosts-new1.jpg)
[![Certificates](https://public.jc21.com/nginx-proxy-manager/v2/small/certificates.jpg "Certificates")](https://public.jc21.com/nginx-proxy-manager/v2/large/certificates.jpg)
[![Lets Encrypt Certificates](https://public.jc21.com/nginx-proxy-manager/v2/small/certificates-new1.jpg "Lets Encrypt Certificates")](https://public.jc21.com/nginx-proxy-manager/v2/large/certificates-new1.jpg)
[![Custom Certificates](https://public.jc21.com/nginx-proxy-manager/v2/small/certificates-new2.jpg "Custom Certificates")](https://public.jc21.com/nginx-proxy-manager/v2/large/certificates-new2.jpg)
[![Access Lists](https://public.jc21.com/nginx-proxy-manager/v2/small/access-lists.jpg "Access Lists")](https://public.jc21.com/nginx-proxy-manager/v2/large/access-lists.jpg)
[![Access List Users](https://public.jc21.com/nginx-proxy-manager/v2/small/access-lists-new1.jpg "Access List Users")](https://public.jc21.com/nginx-proxy-manager/v2/large/access-lists-new1.jpg)
[![Users](https://public.jc21.com/nginx-proxy-manager/v2/small/users.jpg "Users")](https://public.jc21.com/nginx-proxy-manager/v2/large/users.jpg)
[![User Permissions](https://public.jc21.com/nginx-proxy-manager/v2/small/users-permissions.jpg "User Permissions")](https://public.jc21.com/nginx-proxy-manager/v2/large/users-permissions.jpg)
[![Audit Log](https://public.jc21.com/nginx-proxy-manager/v2/small/audit-log.jpg "Audit Log")](https://public.jc21.com/nginx-proxy-manager/v2/large/audit-log.jpg)
## Getting started
Please consult the [installation instructions](doc/INSTALL.md) for a complete guide or
if you just want to get up and running in the quickest time possible, grab all the files in the `doc/example/` folder and run `docker-compose up -d`
## Administration
When your docker container is running, connect to it on port `81` for the admin interface.
[http://localhost:81](http://localhost:81)
Note: Requesting SSL Certificates won't work until this project is accessible from the outside world, as explained below.
### Default Administrator User
```
Email: admin@example.com
Password: changeme
```
Immediately after logging in with this default user you will be asked to modify your details and change your password.
## Hosting your home network
I won't go in to too much detail here but here are the basics for someone new to this self-hosted world.
@ -90,12 +53,159 @@ I won't go in to too much detail here but here are the basics for someone new to
4. Use the Nginx Proxy Manager as your gateway to forward to your other web based services
## Nginx Proxy Manager in the wild
## Contributors
As this software gains popularity it's common to see it integrated with other platforms. Please be aware that unless specifically mentioned in the documenation of those
integrations, they are *not supported* by me and any donation links on the pages of those integrations will not come to me even though it looks like it.
Special thanks to the following contributors:
Known integrations:
- [HomeAssistant Hass.io plugin](https://github.com/hassio-addons/addon-nginx-proxy-manager)
- [UnRaid / Synology](https://github.com/jlesage/docker-nginx-proxy-manager)
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<table>
<tr>
<td align="center">
<a href="https://github.com/Subv">
<img src="https://avatars1.githubusercontent.com/u/357072?s=460&u=d8adcdc91d749ae53e177973ed9b6bb6c4c894a3&v=4" width="80px;" alt=""/>
<br /><sub><b>Sebastian Valle</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/Indemnity83">
<img src="https://avatars3.githubusercontent.com/u/35218?s=460&u=7082004ff35138157c868d7d9c683ccebfce5968&v=4" width="80px;" alt=""/>
<br /><sub><b>Kyle Klaus</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/theraw">
<img src="https://avatars1.githubusercontent.com/u/32969774?s=460&u=6b359971e15685fb0359e6a8c065a399b40dc228&v=4" width="80px;" alt=""/>
<br /><sub><b>ƬHE ЯAW</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/spalger">
<img src="https://avatars2.githubusercontent.com/u/1329312?s=400&u=565223e38f1c052afb4c5dcca3fcf1c63ba17ae7&v=4" width="80px;" alt=""/>
<br /><sub><b>Spencer</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/Xantios">
<img src="https://avatars3.githubusercontent.com/u/1507836?s=460&v=4" width="80px;" alt=""/>
<br /><sub><b>Xantios Krugor</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/dpanesso">
<img src="https://avatars2.githubusercontent.com/u/2687121?s=460&v=4" width="80px;" alt=""/>
<br /><sub><b>David Panesso</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/IronTooch">
<img src="https://avatars3.githubusercontent.com/u/27360514?s=460&u=69bf854a6647c55725f62ecb8d39249c6c0b2602&v=4" width="80px;" alt=""/>
<br /><sub><b>IronTooch</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center">
<a href="https://github.com/damianog">
<img src="https://avatars1.githubusercontent.com/u/2786682?s=460&u=76c6136fae797abb76b951cd8a246dcaecaf21af&v=4" width="80px;" alt=""/>
<br /><sub><b>Damiano</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/tfmm">
<img src="https://avatars3.githubusercontent.com/u/6880538?s=460&u=ce0160821cc4aa802df8395200f2d4956a5bc541&v=4" width="80px;" alt=""/>
<br /><sub><b>Russ</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/margaale">
<img src="https://avatars3.githubusercontent.com/u/20794934?s=460&v=4" width="80px;" alt=""/>
<br /><sub><b>Marcelo Castagna</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/Steven-Harris">
<img src="https://avatars2.githubusercontent.com/u/7720242?s=460&v=4" width="80px;" alt=""/>
<br /><sub><b>Steven Harris</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/jlesage">
<img src="https://avatars0.githubusercontent.com/u/1791123?s=460&v=4" width="80px;" alt=""/>
<br /><sub><b>Jocelyn Le Sage</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/cmer">
<img src="https://avatars0.githubusercontent.com/u/412?s=460&u=67dd8b2e3661bfd6f68ec1eaa5b9821bd8a321cd&v=4" width="80px;" alt=""/>
<br /><sub><b>Carl Mercier</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/the1ts">
<img src="https://avatars1.githubusercontent.com/u/84956?s=460&v=4" width="80px;" alt=""/>
<br /><sub><b>Paul Mansfield</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center">
<a href="https://github.com/OhHeyAlan">
<img src="https://avatars0.githubusercontent.com/u/11955126?s=460&u=fbaa5a1a4f73ef8960132c703349bfd037fe2630&v=4" width="80px;" alt=""/>
<br /><sub><b>OhHeyAlan</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/dogmatic69">
<img src="https://avatars2.githubusercontent.com/u/94674?s=460&u=ca7647de53145c6283b6373ade5dc94ba99347db&v=4" width="80px;" alt=""/>
<br /><sub><b>Carl Sutton</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/tg44">
<img src="https://avatars0.githubusercontent.com/u/31839?s=460&u=ad32f4cadfef5e5fb09cdfa4b7b7b36a99ba6811&v=4" width="80px;" alt=""/>
<br /><sub><b>Gergő Törcsvári</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/vrenjith">
<img src="https://avatars3.githubusercontent.com/u/2093241?s=460&u=96ce93a9bebabdd0a60a2dc96cd093a41d5edaba&v=4" width="80px;" alt=""/>
<br /><sub><b>vrenjith</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/duhruh">
<img src="https://avatars2.githubusercontent.com/u/1133969?s=460&u=c0691e6131ec6d516416c1c6fcedb5034f877bbe&v=4" width="80px;" alt=""/>
<br /><sub><b>David Rivera</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/jipjan">
<img src="https://avatars2.githubusercontent.com/u/1384618?s=460&v=4" width="80px;" alt=""/>
<br /><sub><b>Jaap-Jan de Wit</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/jmwebslave">
<img src="https://avatars2.githubusercontent.com/u/6118262?s=460&u=7db409c47135b1e141c366bbb03ed9fae6ac2638&v=4" width="80px;" alt=""/>
<br /><sub><b>James Morgan</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center">
<a href="https://github.com/chaptergy">
<img src="https://avatars2.githubusercontent.com/u/26956711?s=460&u=7d9adebabb6b4e7af7cb05d98d751087a372304b&v=4" width="80px;" alt=""/>
<br /><sub><b>chaptergy</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/Philip-Mooney">
<img src="https://avatars0.githubusercontent.com/u/48624631?s=460&v=4" width="80px;" alt=""/>
<br /><sub><b>Philip Mooney</b></sub>
</a>
</td>
</tr>
</table>
<!-- markdownlint-enable -->
<!-- prettier-ignore-end -->

2
backend/.gitignore vendored
View File

@ -4,3 +4,5 @@ yarn-error.log
tmp
certbot.log
node_modules
core.*

View File

@ -66,7 +66,7 @@ app.use(function (err, req, res, next) {
}
};
if (process.env.NODE_ENV === 'development') {
if (process.env.NODE_ENV === 'development' || (req.baseUrl + req.path).includes('nginx/certificates')) {
payload.debug = {
stack: typeof err.stack !== 'undefined' && err.stack ? err.stack.split('\n') : null,
previous: err.previous

View File

@ -0,0 +1,26 @@
{
"database": {
"engine": "knex-native",
"knex": {
"client": "sqlite3",
"connection": {
"filename": "/app/config/mydb.sqlite"
},
"pool": {
"min": 0,
"max": 1,
"createTimeoutMillis": 3000,
"acquireTimeoutMillis": 30000,
"idleTimeoutMillis": 30000,
"reapIntervalMillis": 1000,
"createRetryIntervalMillis": 100,
"propagateCreateError": false
},
"migrations": {
"tableName": "migrations",
"stub": "src/backend/lib/migrate_template.js",
"directory": "src/backend/migrations"
}
}
}
}

View File

@ -4,19 +4,27 @@ 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');
}
let data = {
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'
}
};
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'
}
};
}
let data = generateDbConfig();
if (typeof config.database.version !== 'undefined') {
data.version = config.database.version;

View File

@ -1,14 +1,15 @@
const _ = require('lodash');
const fs = require('fs');
const batchflow = require('batchflow');
const logger = require('../logger').access;
const error = require('../lib/error');
const accessListModel = require('../models/access_list');
const accessListAuthModel = require('../models/access_list_auth');
const proxyHostModel = require('../models/proxy_host');
const internalAuditLog = require('./audit-log');
const internalNginx = require('./nginx');
const utils = require('../lib/utils');
const _ = require('lodash');
const fs = require('fs');
const batchflow = require('batchflow');
const logger = require('../logger').access;
const error = require('../lib/error');
const accessListModel = require('../models/access_list');
const accessListAuthModel = require('../models/access_list_auth');
const accessListClientModel = require('../models/access_list_client');
const proxyHostModel = require('../models/proxy_host');
const internalAuditLog = require('./audit-log');
const internalNginx = require('./nginx');
const utils = require('../lib/utils');
function omissions () {
return ['is_deleted'];
@ -29,14 +30,17 @@ const internalAccessList = {
.omit(omissions())
.insertAndFetch({
name: data.name,
satisfy_any: data.satisfy_any,
pass_auth: data.pass_auth,
owner_user_id: access.token.getUserId(1)
});
})
.then((row) => {
data.id = row.id;
// Now add the items
let promises = [];
// Now add the items
data.items.map((item) => {
promises.push(accessListAuthModel
.query()
@ -48,13 +52,27 @@ const internalAccessList = {
);
});
// Now add the clients
if (typeof data.clients !== 'undefined' && data.clients) {
data.clients.map((client) => {
promises.push(accessListClientModel
.query()
.insert({
access_list_id: row.id,
address: client.address,
directive: client.directive
})
);
});
}
return Promise.all(promises);
})
.then(() => {
// re-fetch with expansions
return internalAccessList.get(access, {
id: data.id,
expand: ['owner', 'items']
expand: ['owner', 'items', 'clients', 'proxy_hosts.access_list.[clients,items]']
}, true /* <- skip masking */);
})
.then((row) => {
@ -64,7 +82,7 @@ const internalAccessList = {
return internalAccessList.build(row)
.then(() => {
if (row.proxy_host_count) {
return internalNginx.reload();
return internalNginx.bulkGenerateConfigs('proxy_host', row.proxy_hosts);
}
})
.then(() => {
@ -109,7 +127,9 @@ const internalAccessList = {
.query()
.where({id: data.id})
.patch({
name: data.name
name: data.name,
satisfy_any: data.satisfy_any,
pass_auth: data.pass_auth,
});
}
})
@ -153,6 +173,38 @@ const internalAccessList = {
});
}
})
.then(() => {
// Check for clients and add/update/remove them
if (typeof data.clients !== 'undefined' && data.clients) {
let promises = [];
data.clients.map(function (client) {
if (client.address) {
promises.push(accessListClientModel
.query()
.insert({
access_list_id: data.id,
address: client.address,
directive: client.directive
})
);
}
});
let query = accessListClientModel
.query()
.delete()
.where('access_list_id', data.id);
return query
.then(() => {
// Add new items
if (promises.length) {
return Promise.all(promises);
}
});
}
})
.then(() => {
// Add to audit log
return internalAuditLog.add(access, {
@ -166,14 +218,14 @@ const internalAccessList = {
// re-fetch with expansions
return internalAccessList.get(access, {
id: data.id,
expand: ['owner', 'items']
expand: ['owner', 'items', 'clients', 'proxy_hosts.access_list.[clients,items]']
}, true /* <- skip masking */);
})
.then((row) => {
return internalAccessList.build(row)
.then(() => {
if (row.proxy_host_count) {
return internalNginx.reload();
return internalNginx.bulkGenerateConfigs('proxy_host', row.proxy_hosts);
}
})
.then(() => {
@ -204,7 +256,7 @@ const internalAccessList = {
.joinRaw('LEFT JOIN `proxy_host` ON `proxy_host`.`access_list_id` = `access_list`.`id` AND `proxy_host`.`is_deleted` = 0')
.where('access_list.is_deleted', 0)
.andWhere('access_list.id', data.id)
.allowEager('[owner,items,proxy_hosts]')
.allowEager('[owner,items,clients,proxy_hosts.[*, access_list.[clients,items]]]')
.omit(['access_list.is_deleted'])
.first();
@ -246,7 +298,7 @@ const internalAccessList = {
delete: (access, data) => {
return access.can('access_lists:delete', data.id)
.then(() => {
return internalAccessList.get(access, {id: data.id, expand: ['proxy_hosts', 'items']});
return internalAccessList.get(access, {id: data.id, expand: ['proxy_hosts', 'items', 'clients']});
})
.then((row) => {
if (!row) {
@ -330,11 +382,11 @@ const internalAccessList = {
.where('access_list.is_deleted', 0)
.groupBy('access_list.id')
.omit(['access_list.is_deleted'])
.allowEager('[owner,items]')
.allowEager('[owner,items,clients]')
.orderBy('access_list.name', 'ASC');
if (access_data.permission_visibility !== 'all') {
query.andWhere('owner_user_id', access.token.getUserId(1));
query.andWhere('access_list.owner_user_id', access.token.getUserId(1));
}
// Query is used for searching

View File

@ -13,6 +13,7 @@ const internalNginx = require('./nginx');
const internalHost = require('./host');
const certbot_command = '/usr/bin/certbot';
const le_config = '/etc/letsencrypt.ini';
const dns_plugins = require('../global/certbot-dns-plugins');
function omissions() {
return ['is_deleted'];
@ -77,7 +78,7 @@ const internalCertificate = {
.where('id', certificate.id)
.andWhere('provider', 'letsencrypt')
.patch({
expires_on: certificateModel.raw('FROM_UNIXTIME(' + cert_info.dates.to + ')')
expires_on: moment(cert_info.dates.to, 'X').format('YYYY-MM-DD HH:mm:ss')
});
})
.catch((err) => {
@ -141,36 +142,60 @@ const internalCertificate = {
});
})
.then((in_use_result) => {
// 3. Generate the LE config
return internalNginx.generateLetsEncryptRequestConfig(certificate)
.then(internalNginx.reload)
.then(() => {
// With DNS challenge no config is needed, so skip 3 and 5.
if (certificate.meta.dns_challenge) {
return internalNginx.reload().then(() => {
// 4. Request cert
return internalCertificate.requestLetsEncryptSsl(certificate);
return internalCertificate.requestLetsEncryptSslWithDnsChallenge(certificate);
})
.then(() => {
// 5. Remove LE config
return internalNginx.deleteLetsEncryptRequestConfig(certificate);
})
.then(internalNginx.reload)
.then(() => {
// 6. Re-instate previously disabled hosts
return internalCertificate.enableInUseHosts(in_use_result);
})
.then(() => {
return certificate;
})
.catch((err) => {
// In the event of failure, revert things and throw err back
return internalNginx.deleteLetsEncryptRequestConfig(certificate)
.then(() => {
return internalCertificate.enableInUseHosts(in_use_result);
})
.then(internalNginx.reload)
.then(() => {
throw err;
});
});
.then(internalNginx.reload)
.then(() => {
// 6. Re-instate previously disabled hosts
return internalCertificate.enableInUseHosts(in_use_result);
})
.then(() => {
return certificate;
})
.catch((err) => {
// In the event of failure, revert things and throw err back
return internalCertificate.enableInUseHosts(in_use_result)
.then(internalNginx.reload)
.then(() => {
throw err;
});
});
} else {
// 3. Generate the LE config
return internalNginx.generateLetsEncryptRequestConfig(certificate)
.then(internalNginx.reload)
.then(() => {
// 4. Request cert
return internalCertificate.requestLetsEncryptSsl(certificate);
})
.then(() => {
// 5. Remove LE config
return internalNginx.deleteLetsEncryptRequestConfig(certificate);
})
.then(internalNginx.reload)
.then(() => {
// 6. Re-instate previously disabled hosts
return internalCertificate.enableInUseHosts(in_use_result);
})
.then(() => {
return certificate;
})
.catch((err) => {
// In the event of failure, revert things and throw err back
return internalNginx.deleteLetsEncryptRequestConfig(certificate)
.then(() => {
return internalCertificate.enableInUseHosts(in_use_result);
})
.then(internalNginx.reload)
.then(() => {
throw err;
});
});
}
})
.then(() => {
// At this point, the letsencrypt cert should exist on disk.
@ -180,7 +205,7 @@ const internalCertificate = {
return certificateModel
.query()
.patchAndFetchById(certificate.id, {
expires_on: certificateModel.raw('FROM_UNIXTIME(' + cert_info.dates.to + ')')
expires_on: moment(cert_info.dates.to, 'X').format('YYYY-MM-DD HH:mm:ss')
})
.then((saved_row) => {
// Add cert data for audit log
@ -558,7 +583,7 @@ const internalCertificate = {
// TODO: This uses a mysql only raw function that won't translate to postgres
return internalCertificate.update(access, {
id: data.id,
expires_on: certificateModel.raw('FROM_UNIXTIME(' + validations.certificate.dates.to + ')'),
expires_on: moment(validations.certificate.dates.to, 'X').format('YYYY-MM-DD HH:mm:ss'),
domain_names: [validations.certificate.cn],
meta: _.clone(row.meta) // Prevent the update method from changing this value that we'll use later
})
@ -733,7 +758,6 @@ const internalCertificate = {
'--agree-tos ' +
'--email "' + certificate.meta.letsencrypt_email + '" ' +
'--preferred-challenges "dns,http" ' +
'--webroot ' +
'--domains "' + certificate.domain_names.join(',') + '" ' +
(le_staging ? '--staging' : '');
@ -748,6 +772,74 @@ 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 | null} credentials the content of this providers credentials file
* @param {String} propagation_seconds the cloudflare api token
* @returns {Promise}
*/
requestLetsEncryptSslWithDnsChallenge: (certificate) => {
const dns_plugin = dns_plugins[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(', ')}`);
const credentials_loc = '/etc/letsencrypt/credentials-' + certificate.id;
const credentials_cmd = 'echo \'' + certificate.meta.dns_provider_credentials.replace('\'', '\\\'') + '\' > \'' + credentials_loc + '\' && chmod 600 \'' + credentials_loc + '\'';
const prepare_cmd = 'pip3 install ' + dns_plugin.package_name + '==' + dns_plugin.package_version;
// Whether the plugin has a --<name>-credentials argument
const has_config_arg = certificate.meta.dns_provider !== 'route53';
let main_cmd =
certbot_command + ' certonly --non-interactive ' +
'--cert-name "npm-' + certificate.id + '" ' +
'--agree-tos ' +
'--email "' + certificate.meta.letsencrypt_email + '" ' +
'--domains "' + certificate.domain_names.join(',') + '" ' +
'--authenticator ' + dns_plugin.full_plugin_name + ' ' +
(
has_config_arg
? '--' + dns_plugin.full_plugin_name + '-credentials "' + credentials_loc + '"'
: ''
) +
(
certificate.meta.propagation_seconds !== undefined
? ' --' + dns_plugin.full_plugin_name + '-propagation-seconds ' + certificate.meta.propagation_seconds
: ''
) +
(le_staging ? ' --staging' : '');
// Prepend the path to the credentials file as an environment variable
if (certificate.meta.dns_provider === 'route53') {
main_cmd = 'AWS_CONFIG_FILE=\'' + credentials_loc + '\' ' + main_cmd;
}
const teardown_cmd = `rm '${credentials_loc}'`;
if (debug_mode) {
logger.info('Command:', `${credentials_cmd} && ${prepare_cmd} && ${main_cmd} && ${teardown_cmd}`);
}
return utils.exec(credentials_cmd)
.then(() => {
return utils.exec(prepare_cmd)
.then(() => {
return utils.exec(main_cmd)
.then(async (result) => {
await utils.exec(teardown_cmd);
logger.info(result);
return result;
});
});
});
},
/**
* @param {Access} access
* @param {Object} data
@ -761,7 +853,9 @@ const internalCertificate = {
})
.then((certificate) => {
if (certificate.provider === 'letsencrypt') {
return internalCertificate.renewLetsEncryptSsl(certificate)
let renewMethod = certificate.meta.dns_challenge ? internalCertificate.renewLetsEncryptSslWithDnsChallenge : internalCertificate.renewLetsEncryptSsl;
return renewMethod(certificate)
.then(() => {
return internalCertificate.getCertificateInfoFromFile('/etc/letsencrypt/live/npm-' + certificate.id + '/fullchain.pem');
})
@ -769,7 +863,7 @@ const internalCertificate = {
return certificateModel
.query()
.patchAndFetchById(certificate.id, {
expires_on: certificateModel.raw('FROM_UNIXTIME(' + cert_info.dates.to + ')')
expires_on: moment(cert_info.dates.to, 'X').format('YYYY-MM-DD HH:mm:ss')
});
})
.then((updated_certificate) => {
@ -815,6 +909,54 @@ const internalCertificate = {
});
},
/**
* @param {Object} certificate the certificate row
* @returns {Promise}
*/
renewLetsEncryptSslWithDnsChallenge: (certificate) => {
const dns_plugin = dns_plugins[certificate.meta.dns_provider];
if (!dns_plugin) {
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(', ')}`);
const credentials_loc = '/etc/letsencrypt/credentials-' + certificate.id;
const credentials_cmd = 'echo \'' + certificate.meta.dns_provider_credentials.replace('\'', '\\\'') + '\' > \'' + credentials_loc + '\' && chmod 600 \'' + credentials_loc + '\'';
const prepare_cmd = 'pip3 install ' + dns_plugin.package_name + '==' + dns_plugin.package_version;
let main_cmd =
certbot_command + ' renew --non-interactive ' +
'--cert-name "npm-' + certificate.id + '" ' +
'--disable-hook-validation' +
(le_staging ? ' --staging' : '');
// Prepend the path to the credentials file as an environment variable
if (certificate.meta.dns_provider === 'route53') {
main_cmd = 'AWS_CONFIG_FILE=\'' + credentials_loc + '\' ' + main_cmd;
}
const teardown_cmd = `rm '${credentials_loc}'`;
if (debug_mode) {
logger.info('Command:', `${credentials_cmd} && ${prepare_cmd} && ${main_cmd} && ${teardown_cmd}`);
}
return utils.exec(credentials_cmd)
.then(() => {
return utils.exec(prepare_cmd)
.then(() => {
return utils.exec(main_cmd)
.then(async (result) => {
await utils.exec(teardown_cmd);
logger.info(result);
return result;
});
});
});
},
/**
* @param {Object} certificate the certificate row
* @param {Boolean} [throw_errors]
@ -824,7 +966,6 @@ const internalCertificate = {
logger.info('Revoking Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
let cmd = certbot_command + ' revoke --non-interactive ' +
'--config "' + le_config + '" ' +
'--cert-path "/etc/letsencrypt/live/npm-' + certificate.id + '/fullchain.pem" ' +
'--delete-after-revoke ' +
(le_staging ? '--staging' : '');

View File

@ -224,6 +224,9 @@ const internalNginx = {
locationsPromise = Promise.resolve();
}
// Set the IPv6 setting for the host
host.ipv6 = internalNginx.ipv6Enabled();
locationsPromise.then(() => {
renderEngine
.parseAndRender(template, host)
@ -270,6 +273,7 @@ const internalNginx = {
return new Promise((resolve, reject) => {
let template = null;
let filename = '/data/nginx/temp/letsencrypt_' + certificate.id + '.conf';
try {
template = fs.readFileSync(__dirname + '/../templates/letsencrypt-request.conf', {encoding: 'utf8'});
} catch (err) {
@ -277,6 +281,8 @@ const internalNginx = {
return;
}
certificate.ipv6 = internalNginx.ipv6Enabled();
renderEngine
.parseAndRender(template, certificate)
.then((config_text) => {
@ -396,6 +402,18 @@ const internalNginx = {
*/
advancedConfigHasDefaultLocation: function (config) {
return !!config.match(/^(?:.*;)?\s*?location\s*?\/\s*?{/im);
},
/**
* @returns {boolean}
*/
ipv6Enabled: function () {
if (typeof process.env.DISABLE_IPV6 !== 'undefined') {
const disabled = process.env.DISABLE_IPV6.toLowerCase();
return !(disabled === 'on' || disabled === 'true' || disabled === '1' || disabled === 'yes');
}
return true;
}
};

View File

@ -73,7 +73,7 @@ const internalProxyHost = {
// re-fetch with cert
return internalProxyHost.get(access, {
id: row.id,
expand: ['certificate', 'owner', 'access_list']
expand: ['certificate', 'owner', 'access_list.[clients,items]']
});
})
.then((row) => {
@ -186,7 +186,7 @@ const internalProxyHost = {
.then(() => {
return internalProxyHost.get(access, {
id: data.id,
expand: ['owner', 'certificate', 'access_list']
expand: ['owner', 'certificate', 'access_list.[clients,items]']
})
.then((row) => {
// Configure nginx
@ -219,7 +219,7 @@ const internalProxyHost = {
.query()
.where('is_deleted', 0)
.andWhere('id', data.id)
.allowEager('[owner,access_list,certificate]')
.allowEager('[owner,access_list,access_list.[clients,items],certificate]')
.first();
if (access_data.permission_visibility !== 'all') {

View File

@ -4,11 +4,21 @@ 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({
type: 'string',
pattern: '^[a-z\\-]+:\\/\\/(?:[\\w\\-\\.]+(:[0-9]+)?/?)?$'
}, req.headers.origin)
validator(originSchema, req.headers.origin)
.then(function () {
res.set({
'Access-Control-Allow-Origin': req.headers.origin,

View File

@ -22,22 +22,6 @@ exports.up = function (knex/*, Promise*/) {
})
.then(() => {
logger.info('[' + migrate_name + '] setting Table created');
// TODO: add settings
let settingModel = require('../models/setting');
return settingModel
.query()
.insert({
id: 'default-site',
name: 'Default Site',
description: 'What to show when Nginx is hit with an unknown Host',
value: 'congratulations',
meta: {}
});
})
.then(() => {
logger.info('[' + migrate_name + '] Default settings added');
});
};

View File

@ -0,0 +1,53 @@
const migrate_name = 'access_list_client';
const logger = require('../logger').migrate;
/**
* Migrate
*
* @see http://knexjs.org/#Schema
*
* @param {Object} knex
* @param {Promise} Promise
* @returns {Promise}
*/
exports.up = function (knex/*, Promise*/) {
logger.info('[' + migrate_name + '] Migrating Up...');
return knex.schema.createTable('access_list_client', (table) => {
table.increments().primary();
table.dateTime('created_on').notNull();
table.dateTime('modified_on').notNull();
table.integer('access_list_id').notNull().unsigned();
table.string('address').notNull();
table.string('directive').notNull();
table.json('meta').notNull();
})
.then(function () {
logger.info('[' + migrate_name + '] access_list_client Table created');
return knex.schema.table('access_list', function (access_list) {
access_list.integer('satify_any').notNull().defaultTo(0);
});
})
.then(() => {
logger.info('[' + migrate_name + '] access_list Table altered');
});
};
/**
* Undo Migrate
*
* @param {Object} knex
* @param {Promise} Promise
* @returns {Promise}
*/
exports.down = function (knex/*, Promise*/) {
logger.info('[' + migrate_name + '] Migrating Down...');
return knex.schema.dropTable('access_list_client')
.then(() => {
logger.info('[' + migrate_name + '] access_list_client Table dropped');
});
};

View File

@ -0,0 +1,34 @@
const migrate_name = 'access_list_client_fix';
const logger = require('../logger').migrate;
/**
* Migrate
*
* @see http://knexjs.org/#Schema
*
* @param {Object} knex
* @param {Promise} Promise
* @returns {Promise}
*/
exports.up = function (knex/*, Promise*/) {
logger.info('[' + migrate_name + '] Migrating Up...');
return knex.schema.table('access_list', function (access_list) {
access_list.renameColumn('satify_any', 'satisfy_any');
})
.then(() => {
logger.info('[' + migrate_name + '] access_list Table altered');
});
};
/**
* Undo Migrate
*
* @param {Object} knex
* @param {Promise} Promise
* @returns {Promise}
*/
exports.down = function (knex, Promise) {
logger.warn('[' + migrate_name + '] You can\'t migrate down this one.');
return Promise.resolve(true);
};

View File

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

View File

@ -1,17 +1,19 @@
// Objection Docs:
// http://vincit.github.io/objection.js/
const db = require('../db');
const Model = require('objection').Model;
const User = require('./user');
const AccessListAuth = require('./access_list_auth');
const db = require('../db');
const Model = require('objection').Model;
const User = require('./user');
const AccessListAuth = require('./access_list_auth');
const AccessListClient = require('./access_list_client');
const now = require('./now_helper');
Model.knex(db);
class AccessList extends Model {
$beforeInsert () {
this.created_on = Model.raw('NOW()');
this.modified_on = Model.raw('NOW()');
this.created_on = now();
this.modified_on = now();
// Default for meta
if (typeof this.meta === 'undefined') {
@ -20,7 +22,7 @@ class AccessList extends Model {
}
$beforeUpdate () {
this.modified_on = Model.raw('NOW()');
this.modified_on = now();
}
static get name () {
@ -62,6 +64,17 @@ class AccessList extends Model {
qb.omit(['id', 'created_on', 'modified_on', 'access_list_id', 'meta']);
}
},
clients: {
relation: Model.HasManyRelation,
modelClass: AccessListClient,
join: {
from: 'access_list.id',
to: 'access_list_client.access_list_id'
},
modify: function (qb) {
qb.omit(['id', 'created_on', 'modified_on', 'access_list_id', 'meta']);
}
},
proxy_hosts: {
relation: Model.HasManyRelation,
modelClass: ProxyHost,
@ -76,6 +89,14 @@ class AccessList extends Model {
}
};
}
get satisfy() {
return this.satisfy_any ? 'satisfy any' : 'satisfy all';
}
get passauth() {
return this.pass_auth ? '' : 'proxy_set_header Authorization "";';
}
}
module.exports = AccessList;

View File

@ -3,13 +3,14 @@
const db = require('../db');
const Model = require('objection').Model;
const now = require('./now_helper');
Model.knex(db);
class AccessListAuth extends Model {
$beforeInsert () {
this.created_on = Model.raw('NOW()');
this.modified_on = Model.raw('NOW()');
this.created_on = now();
this.modified_on = now();
// Default for meta
if (typeof this.meta === 'undefined') {
@ -18,7 +19,7 @@ class AccessListAuth extends Model {
}
$beforeUpdate () {
this.modified_on = Model.raw('NOW()');
this.modified_on = now();
}
static get name () {

View File

@ -0,0 +1,59 @@
// Objection Docs:
// http://vincit.github.io/objection.js/
const db = require('../db');
const Model = require('objection').Model;
const now = require('./now_helper');
Model.knex(db);
class AccessListClient extends Model {
$beforeInsert () {
this.created_on = now();
this.modified_on = now();
// Default for meta
if (typeof this.meta === 'undefined') {
this.meta = {};
}
}
$beforeUpdate () {
this.modified_on = now();
}
static get name () {
return 'AccessListClient';
}
static get tableName () {
return 'access_list_client';
}
static get jsonAttributes () {
return ['meta'];
}
static get relationMappings () {
return {
access_list: {
relation: Model.HasOneRelation,
modelClass: require('./access_list'),
join: {
from: 'access_list_client.access_list_id',
to: 'access_list.id'
},
modify: function (qb) {
qb.where('access_list.is_deleted', 0);
qb.omit(['created_on', 'modified_on', 'is_deleted', 'access_list_id']);
}
}
};
}
get rule() {
return `${this.directive} ${this.address}`;
}
}
module.exports = AccessListClient;

View File

@ -4,13 +4,14 @@
const db = require('../db');
const Model = require('objection').Model;
const User = require('./user');
const now = require('./now_helper');
Model.knex(db);
class AuditLog extends Model {
$beforeInsert () {
this.created_on = Model.raw('NOW()');
this.modified_on = Model.raw('NOW()');
this.created_on = now();
this.modified_on = now();
// Default for meta
if (typeof this.meta === 'undefined') {
@ -19,7 +20,7 @@ class AuditLog extends Model {
}
$beforeUpdate () {
this.modified_on = Model.raw('NOW()');
this.modified_on = now();
}
static get name () {

View File

@ -5,6 +5,7 @@ const bcrypt = require('bcrypt');
const db = require('../db');
const Model = require('objection').Model;
const User = require('./user');
const now = require('./now_helper');
Model.knex(db);
@ -24,8 +25,8 @@ function encryptPassword () {
class Auth extends Model {
$beforeInsert (queryContext) {
this.created_on = Model.raw('NOW()');
this.modified_on = Model.raw('NOW()');
this.created_on = now();
this.modified_on = now();
// Default for meta
if (typeof this.meta === 'undefined') {
@ -36,7 +37,7 @@ class Auth extends Model {
}
$beforeUpdate (queryContext) {
this.modified_on = Model.raw('NOW()');
this.modified_on = now();
return encryptPassword.apply(this, queryContext);
}

View File

@ -4,17 +4,18 @@
const db = require('../db');
const Model = require('objection').Model;
const User = require('./user');
const now = require('./now_helper');
Model.knex(db);
class Certificate extends Model {
$beforeInsert () {
this.created_on = Model.raw('NOW()');
this.modified_on = Model.raw('NOW()');
this.created_on = now();
this.modified_on = now();
// Default for expires_on
if (typeof this.expires_on === 'undefined') {
this.expires_on = Model.raw('NOW()');
this.expires_on = now();
}
// Default for domain_names
@ -31,7 +32,7 @@ class Certificate extends Model {
}
$beforeUpdate () {
this.modified_on = Model.raw('NOW()');
this.modified_on = now();
// Sort domain_names
if (typeof this.domain_names !== 'undefined') {

View File

@ -5,13 +5,14 @@ const db = require('../db');
const Model = require('objection').Model;
const User = require('./user');
const Certificate = require('./certificate');
const now = require('./now_helper');
Model.knex(db);
class DeadHost extends Model {
$beforeInsert () {
this.created_on = Model.raw('NOW()');
this.modified_on = Model.raw('NOW()');
this.created_on = now();
this.modified_on = now();
// Default for domain_names
if (typeof this.domain_names === 'undefined') {
@ -27,7 +28,7 @@ class DeadHost extends Model {
}
$beforeUpdate () {
this.modified_on = Model.raw('NOW()');
this.modified_on = now();
// Sort domain_names
if (typeof this.domain_names !== 'undefined') {

View File

@ -0,0 +1,13 @@
const db = require('../db');
const config = require('config');
const Model = require('objection').Model;
Model.knex(db);
module.exports = function () {
if (config.database.knex && config.database.knex.client === 'sqlite3') {
return Model.raw('datetime(\'now\',\'localtime\')');
} else {
return Model.raw('NOW()');
}
};

View File

@ -6,13 +6,14 @@ const Model = require('objection').Model;
const User = require('./user');
const AccessList = require('./access_list');
const Certificate = require('./certificate');
const now = require('./now_helper');
Model.knex(db);
class ProxyHost extends Model {
$beforeInsert () {
this.created_on = Model.raw('NOW()');
this.modified_on = Model.raw('NOW()');
this.created_on = now();
this.modified_on = now();
// Default for domain_names
if (typeof this.domain_names === 'undefined') {
@ -28,7 +29,7 @@ class ProxyHost extends Model {
}
$beforeUpdate () {
this.modified_on = Model.raw('NOW()');
this.modified_on = now();
// Sort domain_names
if (typeof this.domain_names !== 'undefined') {

View File

@ -5,13 +5,14 @@ const db = require('../db');
const Model = require('objection').Model;
const User = require('./user');
const Certificate = require('./certificate');
const now = require('./now_helper');
Model.knex(db);
class RedirectionHost extends Model {
$beforeInsert () {
this.created_on = Model.raw('NOW()');
this.modified_on = Model.raw('NOW()');
this.created_on = now();
this.modified_on = now();
// Default for domain_names
if (typeof this.domain_names === 'undefined') {
@ -27,7 +28,7 @@ class RedirectionHost extends Model {
}
$beforeUpdate () {
this.modified_on = Model.raw('NOW()');
this.modified_on = now();
// Sort domain_names
if (typeof this.domain_names !== 'undefined') {

View File

@ -4,13 +4,14 @@
const db = require('../db');
const Model = require('objection').Model;
const User = require('./user');
const now = require('./now_helper');
Model.knex(db);
class Stream extends Model {
$beforeInsert () {
this.created_on = Model.raw('NOW()');
this.modified_on = Model.raw('NOW()');
this.created_on = now();
this.modified_on = now();
// Default for meta
if (typeof this.meta === 'undefined') {
@ -19,7 +20,7 @@ class Stream extends Model {
}
$beforeUpdate () {
this.modified_on = Model.raw('NOW()');
this.modified_on = now();
}
static get name () {

View File

@ -4,13 +4,14 @@
const db = require('../db');
const Model = require('objection').Model;
const UserPermission = require('./user_permission');
const now = require('./now_helper');
Model.knex(db);
class User extends Model {
$beforeInsert () {
this.created_on = Model.raw('NOW()');
this.modified_on = Model.raw('NOW()');
this.created_on = now();
this.modified_on = now();
// Default for roles
if (typeof this.roles === 'undefined') {
@ -19,7 +20,7 @@ class User extends Model {
}
$beforeUpdate () {
this.modified_on = Model.raw('NOW()');
this.modified_on = now();
}
static get name () {

View File

@ -3,17 +3,18 @@
const db = require('../db');
const Model = require('objection').Model;
const now = require('./now_helper');
Model.knex(db);
class UserPermission extends Model {
$beforeInsert () {
this.created_on = Model.raw('NOW()');
this.modified_on = Model.raw('NOW()');
this.created_on = now();
this.modified_on = now();
}
$beforeUpdate () {
this.modified_on = Model.raw('NOW()');
this.modified_on = now();
}
static get name () {

View File

@ -1,33 +1,35 @@
{
"name": "nginx-proxy-manager",
"version": "2.1.2",
"version": "0.0.0",
"description": "A beautiful interface for creating Nginx endpoints",
"main": "js/index.js",
"dependencies": {
"ajv": "^6.11.0",
"ajv": "^6.12.0",
"batchflow": "^0.4.0",
"bcrypt": "^3.0.8",
"bcrypt": "^5.0.0",
"body-parser": "^1.19.0",
"compression": "^1.7.4",
"config": "^3.2.5",
"config": "^3.3.1",
"diskdb": "^0.1.17",
"express": "^4.17.1",
"express-fileupload": "^1.1.6",
"express-fileupload": "^1.1.9",
"gravatar": "^1.8.0",
"html-entities": "^1.2.1",
"json-schema-ref-parser": "^7.1.3",
"json-schema-ref-parser": "^8.0.0",
"jsonwebtoken": "^8.5.1",
"knex": "^0.20.10",
"liquidjs": "^9.7.1",
"lodash": "^4.17.15",
"knex": "^0.20.13",
"liquidjs": "^9.11.10",
"lodash": "^4.17.19",
"moment": "^2.24.0",
"mysql": "^2.18.1",
"node-rsa": "^1.0.7",
"node-rsa": "^1.0.8",
"nodemon": "^2.0.2",
"objection": "^2.1.3",
"path": "^0.12.7",
"pg": "^7.12.1",
"restler": "^3.4.0",
"signale": "^1.4.0",
"sqlite3": "^4.1.1",
"temp-write": "^4.0.0",
"unix-timestamp": "^0.2.0"
},
@ -40,6 +42,6 @@
"devDependencies": {
"eslint": "^6.8.0",
"eslint-plugin-align-assignments": "^1.1.2",
"prettier": "^1.19.1"
"prettier": "^2.0.4"
}
}

View File

@ -58,6 +58,7 @@ router
.post((req, res, next) => {
apiValidator({$ref: 'endpoints/certificates#/links/1/schema'}, req.body)
.then((payload) => {
req.setTimeout(900000); // 15 minutes timeout
return internalCertificate.create(res.locals.access, payload);
})
.then((result) => {
@ -197,6 +198,7 @@ router
* Renew certificate
*/
.post((req, res, next) => {
req.setTimeout(900000); // 15 minutes timeout
internalCertificate.renew(res.locals.access, {
id: parseInt(req.params.certificate_id, 10)
})

View File

@ -1,168 +1,236 @@
{
"$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"
},
"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"
},
"items": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"username": {
"type": "string",
"minLength": 1
},
"password": {
"type": "string",
"minLength": 1
}
}
}
},
"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"
},
"items": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"username": {
"type": "string",
"minLength": 1
},
"password": {
"type": "string",
"minLength": 0
}
}
}
}
}
},
"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"
}
}
]
"$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

@ -41,6 +41,24 @@
},
"letsencrypt_agree": {
"type": "boolean"
},
"dns_challenge": {
"type": "boolean"
},
"dns_provider": {
"type": "string"
},
"dns_provider_credentials": {
"type": "string"
},
"propagation_seconds": {
"anyOf": [
{
"type": "integer",
"minimum": 0
}
]
}
}
}

View File

@ -25,7 +25,7 @@
"forward_host": {
"type": "string",
"minLength": 1,
"maxLength": 50
"maxLength": 255
},
"forward_port": {
"type": "integer",

View File

@ -5,9 +5,15 @@ const logger = require('./logger').setup;
const userModel = require('./models/user');
const userPermissionModel = require('./models/user_permission');
const authModel = require('./models/auth');
const settingModel = require('./models/setting');
const debug_mode = process.env.NODE_ENV !== 'production' || !!process.env.DEBUG;
module.exports = function () {
/**
* Creates a new JWT RSA Keypair if not alread set on the config
*
* @returns {Promise}
*/
const setupJwt = () => {
return new Promise((resolve, reject) => {
// Now go and check if the jwt gpg keys have been created and if not, create them
if (!config.has('jwt') || !config.has('jwt.key') || !config.has('jwt.pub')) {
@ -27,12 +33,12 @@ module.exports = function () {
}
// Now create the keys and save them in the config.
let key = new NodeRSA({b: 2048});
let key = new NodeRSA({ b: 2048 });
key.generateKeyPair();
config_data.jwt = {
key: key.exportKey('private').toString(),
pub: key.exportKey('public').toString()
pub: key.exportKey('public').toString(),
};
// Write config
@ -47,7 +53,6 @@ module.exports = function () {
process.exit(0);
}
});
} else {
// JWT key pair exists
if (debug_mode) {
@ -56,14 +61,20 @@ module.exports = function () {
resolve();
}
})
.then(() => {
return userModel
.query()
.select(userModel.raw('COUNT(`id`) as `count`'))
.where('is_deleted', 0)
.first();
})
});
};
/**
* Creates a default admin users if one doesn't already exist in the database
*
* @returns {Promise}
*/
const setupDefaultUser = () => {
return userModel
.query()
.select(userModel.raw('COUNT(`id`) as `count`'))
.where('is_deleted', 0)
.first()
.then((row) => {
if (!row.count) {
// Create a new user and set password
@ -75,7 +86,7 @@ module.exports = function () {
name: 'Administrator',
nickname: 'Admin',
avatar: '',
roles: ['admin']
roles: ['admin'],
};
return userModel
@ -88,28 +99,64 @@ module.exports = function () {
user_id: user.id,
type: 'password',
secret: 'changeme',
meta: {}
meta: {},
})
.then(() => {
return userPermissionModel
.query()
.insert({
user_id: user.id,
visibility: 'all',
proxy_hosts: 'manage',
redirection_hosts: 'manage',
dead_hosts: 'manage',
streams: 'manage',
access_lists: 'manage',
certificates: 'manage'
});
return userPermissionModel.query().insert({
user_id: user.id,
visibility: 'all',
proxy_hosts: 'manage',
redirection_hosts: 'manage',
dead_hosts: 'manage',
streams: 'manage',
access_lists: 'manage',
certificates: 'manage',
});
});
})
.then(() => {
logger.info('Initial setup completed');
logger.info('Initial admin setup completed');
});
} else if (debug_mode) {
logger.debug('Admin user setup not required');
}
});
};
/**
* Creates default settings if they don't already exist in the database
*
* @returns {Promise}
*/
const setupDefaultSettings = () => {
return settingModel
.query()
.select(settingModel.raw('COUNT(`id`) as `count`'))
.where({id: 'default-site'})
.first()
.then((row) => {
if (!row.count) {
settingModel
.query()
.insert({
id: 'default-site',
name: 'Default Site',
description: 'What to show when Nginx is hit with an unknown Host',
value: 'congratulations',
meta: {},
})
.then(() => {
logger.info('Default settings added');
});
}
if (debug_mode) {
logger.debug('Default setting setup not required');
}
});
};
module.exports = function () {
return setupJwt()
.then(setupDefaultUser)
.then(setupDefaultSettings);
};

View File

@ -1,5 +1,15 @@
listen 80;
{% if ipv6 -%}
listen [::]:80;
{% else -%}
#listen [::]:80;
{% endif %}
{% if certificate -%}
listen 443 ssl{% if http2_support %} http2{% endif %};
{% if ipv6 -%}
listen [::]:443;
{% else -%}
#listen [::]:443;
{% endif %}
{% endif %}
server_name {{ domain_names | join: " " }};

View File

@ -2,6 +2,10 @@
server {
listen 80;
{% if ipv6 -%}
listen [::]:80;
{% endif %}
server_name {{ domain_names | join: " " }};
access_log /data/logs/letsencrypt-requests.log standard;

View File

@ -21,18 +21,34 @@ server {
{% if use_default_location %}
location / {
{%- if access_list_id > 0 -%}
# Access List
{% if access_list_id > 0 %}
{% if access_list.items.length > 0 %}
# Authorization
auth_basic "Authorization required";
auth_basic_user_file /data/access/{{ access_list_id }};
{%- endif %}
{{ access_list.passauth }}
{% endif %}
# Access Rules
{% for client in access_list.clients %}
{{- client.rule -}};
{% endfor %}deny all;
# Access checks must...
{% if access_list.satisfy %}
{{ access_list.satisfy }};
{% endif %}
{% endif %}
{% include "_forced_ssl.conf" %}
{% include "_hsts.conf" %}
{% if allow_websocket_upgrade == 1 or allow_websocket_upgrade == true %}
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Connection $http_connection;
proxy_http_version 1.1;
{% endif %}

View File

@ -6,6 +6,12 @@
{% if tcp_forwarding == 1 or tcp_forwarding == true -%}
server {
listen {{ incoming_port }};
{% if ipv6 -%}
listen [::]:{{ incoming_port }};
{% else -%}
#listen [::]:{{ incoming_port }};
{% endif %}
proxy_pass {{ forward_ip }}:{{ forwarding_port }};
# Custom
@ -16,6 +22,11 @@ server {
{% if udp_forwarding == 1 or udp_forwarding == true %}
server {
listen {{ incoming_port }} udp;
{% if ipv6 -%}
listen [::]:{{ incoming_port }} udp;
{% else -%}
#listen [::]:{{ incoming_port }} udp;
{% endif %}
proxy_pass {{ forward_ip }}:{{ forwarding_port }};
# Custom

File diff suppressed because it is too large Load Diff

View File

@ -1,17 +0,0 @@
## Advanced Nginx Configuration
If you are a more advanced user, you might be itching for extra Nginx customizability.
NPM has the ability to include different custom configuration snippets in different places.
You can add your custom configuration snippet files at `/data/nginx/custom` as follow:
`/data/nginx/custom/root.conf`: Included at the very end of nginx.conf
`/data/nginx/custom/http.conf`: Included at the end of the main http block
`/data/nginx/custom/server_proxy.conf`: Included at the end of every proxy server block
`/data/nginx/custom/server_redirect.conf`: Included at the end of every redirection server block
`/data/nginx/custom/server_stream.conf`: Included at the end of every stream server block
`/data/nginx/custom/server_stream_tcp.conf`: Included at the end of every TCP stream server block
`/data/nginx/custom/server_stream_udp.conf`: Included at the end of every UDP stream server block
Every file is optional.

View File

@ -1,10 +0,0 @@
{
"database": {
"engine": "mysql",
"host": "db",
"name": "npm",
"user": "npm",
"password": "npm",
"port": 3306
}
}

View File

@ -1,28 +0,0 @@
version: "3"
services:
app:
image: jc21/nginx-proxy-manager:latest
restart: always
ports:
- 80:80
- 81:81
- 443:443
volumes:
- ./config.json:/app/config/production.json
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
depends_on:
- db
environment:
# if you want pretty colors in your docker logs:
- FORCE_COLOR=1
db:
image: jc21/mariadb-aria:latest
restart: always
environment:
MYSQL_ROOT_PASSWORD: "npm"
MYSQL_DATABASE: "npm"
MYSQL_USER: "npm"
MYSQL_PASSWORD: "npm"
volumes:
- ./data/mysql:/var/lib/mysql

View File

@ -16,9 +16,9 @@ ENV S6_FIX_ATTRS_HIDDEN=1
ENV NODE_ENV=production
RUN echo "fs.file-max = 65535" > /etc/sysctl.conf \
&& rm -rf /etc/nginx \
&& apk update \
&& apk add python2 certbot jq \
&& apk add python3 certbot jq \
&& python3 -m ensurepip \
&& rm -rf /var/cache/apk/*
ENV NPM_BUILD_VERSION="${BUILD_VERSION}" NPM_BUILD_COMMIT="${BUILD_COMMIT}" NPM_BUILD_DATE="${BUILD_DATE}"
@ -34,6 +34,7 @@ EXPOSE 443
COPY docker/rootfs /
ADD backend /app
ADD frontend/dist /app/frontend
COPY global /app/global
WORKDIR /app
RUN yarn install

View File

@ -6,9 +6,9 @@ ENV SUPPRESS_NO_CONFIG_WARNING=1
ENV S6_FIX_ATTRS_HIDDEN=1
RUN echo "fs.file-max = 65535" > /etc/sysctl.conf \
&& rm -rf /etc/nginx \
&& apk update \
&& apk add python2 certbot jq \
&& apk add python3 certbot jq \
&& python3 -m ensurepip \
&& rm -rf /var/cache/apk/*
# Task

View File

@ -2,14 +2,14 @@
version: "3"
services:
fullstack:
fullstack-mysql:
image: ${IMAGE}:ci-${BUILD_NUMBER}
environment:
- NODE_ENV=development
- FORCE_COLOR=1
volumes:
- npm_data:/data
- ../.jenkins/config.json:/app/config/production.json
- ../.jenkins/config-mysql.json:/app/config/development.json
expose:
- 81
- 80
@ -17,6 +17,19 @@ services:
depends_on:
- db
fullstack-sqlite:
image: ${IMAGE}:ci-${BUILD_NUMBER}
environment:
- NODE_ENV=development
- FORCE_COLOR=1
volumes:
- npm_data:/data
- ../.jenkins/config-sqlite.json:/app/config/development.json
expose:
- 81
- 80
- 443
db:
image: jc21/mariadb-aria
environment:
@ -27,13 +40,24 @@ services:
volumes:
- db_data:/var/lib/mysql
cypress:
cypress-mysql:
image: ${IMAGE}-cypress:ci-${BUILD_NUMBER}
build:
context: ../
dockerfile: test/cypress/Dockerfile
environment:
CYPRESS_baseUrl: "http://fullstack:81"
CYPRESS_baseUrl: "http://fullstack-mysql:81"
volumes:
- cypress-logs:/results
command: cypress run --browser chrome --config-file=${CYPRESS_CONFIG:-cypress/config/ci.json}
cypress-sqlite:
image: ${IMAGE}-cypress:ci-${BUILD_NUMBER}
build:
context: ../
dockerfile: test/cypress/Dockerfile
environment:
CYPRESS_baseUrl: "http://fullstack-sqlite:81"
volumes:
- cypress-logs:/results
command: cypress run --browser chrome --config-file=${CYPRESS_CONFIG:-cypress/config/ci.json}

View File

@ -11,20 +11,27 @@ services:
- 3080:80
- 3081:81
- 3443:443
networks:
- nginx_proxy_manager
environment:
- NODE_ENV=development
- FORCE_COLOR=1
- DEVELOPMENT=true
#- DISABLE_IPV6=true
volumes:
- npm_data:/data
- le_data:/etc/letsencrypt
- ..:/app
- ../backend:/app
- ../frontend:/app/frontend
- ../global:/app/global
depends_on:
- db
working_dir: /app
db:
image: jc21/mariadb-aria
networks:
- nginx_proxy_manager
environment:
MYSQL_ROOT_PASSWORD: "npm"
MYSQL_DATABASE: "npm"
@ -37,6 +44,8 @@ services:
image: 'swaggerapi/swagger-ui:latest'
ports:
- 3001:80
networks:
- nginx_proxy_manager
environment:
URL: "http://127.0.0.1:3081/api/schema"
PORT: '80'
@ -47,3 +56,6 @@ volumes:
npm_data:
le_data:
db_data:
networks:
nginx_proxy_manager:

View File

@ -0,0 +1,46 @@
#!/bin/bash
# This command reads the `DISABLE_IPV6` env var and will either enable
# or disable ipv6 in all nginx configs based on this setting.
# Lowercase
DISABLE_IPV6=$(echo "${DISABLE_IPV6:-}" | tr '[:upper:]' '[:lower:]')
CYAN='\E[1;36m'
BLUE='\E[1;34m'
YELLOW='\E[1;33m'
RED='\E[1;31m'
RESET='\E[0m'
FOLDER=$1
if [ "$FOLDER" == "" ]; then
echo -e "${RED} $0 requires a absolute folder path as the first argument!${RESET}"
echo -e "${YELLOW} ie: $0 /data/nginx${RESET}"
exit 1
fi
FILES=$(find "$FOLDER" -type f -name "*.conf")
if [ "$DISABLE_IPV6" == "true" ] || [ "$DISABLE_IPV6" == "on" ] || [ "$DISABLE_IPV6" == "1" ] || [ "$DISABLE_IPV6" == "yes" ]; then
# IPV6 is disabled
echo "Disabling IPV6 in hosts"
echo -e "${BLUE} ${CYAN}Disabling IPV6 in hosts: ${YELLOW}${FOLDER}${RESET}"
# Iterate over configs and run the regex
for FILE in $FILES
do
echo -e " ${BLUE} ${YELLOW}${FILE}${RESET}"
sed -E -i 's/^([^#]*)listen \[::\]/\1#listen [::]/g' "$FILE"
done
else
# IPV6 is enabled
echo -e "${BLUE} ${CYAN}Enabling IPV6 in hosts: ${YELLOW}${FOLDER}${RESET}"
# Iterate over configs and run the regex
for FILE in $FILES
do
echo -e " ${BLUE} ${YELLOW}${FILE}${RESET}"
sed -E -i 's/^(\s*)#listen \[::\]/\1listen [::]/g' "$FILE"
done
fi

View File

@ -17,6 +17,9 @@ server {
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_pass http://127.0.0.1:3000/;
proxy_read_timeout 15m;
proxy_send_timeout 15m;
}
location / {

View File

@ -18,6 +18,9 @@ server {
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_pass http://127.0.0.1:3000/;
proxy_read_timeout 15m;
proxy_send_timeout 15m;
}
location / {

View File

@ -26,12 +26,15 @@ http {
tcp_nopush on;
tcp_nodelay on;
client_body_temp_path /tmp/nginx/body 1 2;
keepalive_timeout 65;
keepalive_timeout 90s;
proxy_connect_timeout 90s;
proxy_send_timeout 90s;
proxy_read_timeout 90s;
ssl_prefer_server_ciphers on;
gzip on;
proxy_ignore_client_abort off;
client_max_body_size 2000m;
server_names_hash_bucket_size 64;
server_names_hash_bucket_size 1024;
proxy_http_version 1.1;
proxy_set_header X-Forwarded-Scheme $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
@ -57,6 +60,9 @@ http {
# Real IP Determination
# Docker subnet:
set_real_ip_from 172.0.0.0/8;
# Local subnets:
set_real_ip_from 10.0.0.0/8;
set_real_ip_from 192.0.0.0/8;
# NPM generated CDN ip ranges:
include conf.d/include/ip_ranges.conf;
# always put the following 2 lines after ip subnets:

View File

@ -5,7 +5,7 @@ mkdir -p /data/letsencrypt-acme-challenge
cd /app || echo
if [ "$DEVELOPMENT" == "true" ]; then
cd /app/backend || exit 1
cd /app || exit 1
yarn install
node --max_old_space_size=250 --abort_on_uncaught_exception node_modules/nodemon/bin/nodemon.js
else

View File

@ -42,4 +42,8 @@ then
echo "Complete"
fi
# Handle IPV6 settings
/bin/handle-ipv6-setting /etc/nginx/conf.d
/bin/handle-ipv6-setting /data/nginx
exec nginx

View File

@ -16,5 +16,5 @@ alias h='cd ~;clear;'
echo -e -n '\E[1;34m'
figlet -w 120 "NginxProxyManager"
echo -e "\E[1;36mVersion \E[1;32m${NPM_BUILD_VERSION:-2.0.0-dev}\E[1;36m (${NPM_BUILD_COMMIT:-dev}) ${NPM_BUILD_DATE:-0000-00-00}, Nginx \E[1;32m${NGINX_VERSION:-unknown}\E[1;36m, Alpine \E[1;32m${VERSION_ID:-unknown}\E[1;36m, Kernel \E[1;32m$(uname -r)\E[0m"
echo -e "\E[1;36mVersion \E[1;32m${NPM_BUILD_VERSION:-2.0.0-dev} (${NPM_BUILD_COMMIT:-dev}) ${NPM_BUILD_DATE:-0000-00-00}\E[1;36m, OpenResty \E[1;32m${OPENRESTY_VERSION:-unknown}\E[1;36m, Alpine \E[1;32m${VERSION_ID:-unknown}\E[1;36m, Kernel \E[1;32m$(uname -r)\E[0m"
echo

3
docs/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
.vuepress/dist
node_modules
ts

81
docs/.vuepress/config.js Normal file
View File

@ -0,0 +1,81 @@
module.exports = {
locales: {
"/": {
lang: "en-US",
title: "Nginx Proxy Manager",
description: "Expose your services easily and securely"
}
},
head: [
["link", { rel: "icon", href: "/icon.png" }],
["meta", { name: "description", content: "Docker container and built in Web Application for managing Nginx proxy hosts with a simple, powerful interface, providing free SSL support via Let's Encrypt" }],
["meta", { property: "og:title", content: "Nginx Proxy Manager" }],
["meta", { property: "og:description", content: "Docker container and built in Web Application for managing Nginx proxy hosts with a simple, powerful interface, providing free SSL support via Let's Encrypt"}],
["meta", { property: "og:type", content: "website" }],
["meta", { property: "og:url", content: "https://nginxproxymanager.com/" }],
["meta", { property: "og:image", content: "https://nginxproxymanager.com/icon.png" }],
["meta", { name: "twitter:card", content: "summary"}],
["meta", { name: "twitter:title", content: "Nginx Proxy Manager"}],
["meta", { name: "twitter:description", content: "Docker container and built in Web Application for managing Nginx proxy hosts with a simple, powerful interface, providing free SSL support via Let's Encrypt"}],
["meta", { name: "twitter:image", content: "https://nginxproxymanager.com/icon.png"}],
["meta", { name: "twitter:alt", content: "Nginx Proxy Manager"}],
],
themeConfig: {
logo: "/icon.png",
// the GitHub repo path
repo: "jc21/nginx-proxy-manager",
// the label linking to the repo
repoLabel: "GitHub",
// if your docs are not at the root of the repo:
docsDir: "docs",
// defaults to false, set to true to enable
editLinks: true,
locales: {
"/": {
// text for the language dropdown
selectText: "Languages",
// label for this locale in the language dropdown
label: "English",
// Custom text for edit link. Defaults to "Edit this page"
editLinkText: "Edit this page on GitHub",
// Custom navbar values
nav: [{ text: "Setup", link: "/setup/" }],
// Custom sidebar values
sidebar: [
"/",
["/guide/", "Guide"],
["/screenshots/", "Screenshots"],
["/setup/", "Setup Instructions"],
["/advanced-config/", "Advanced Configuration"],
["/faq/", "Frequently Asked Questions"],
["/third-party/", "Third Party"]
]
}
}
},
plugins: [
[
"@vuepress/google-analytics",
{
ga: "UA-99675467-4"
}
],
[
"sitemap",
{
hostname: "https://nginxproxymanager.com"
}
],
[
'vuepress-plugin-zooming',
{
selector: '.zooming',
delay: 1000,
options: {
bgColor: 'black',
zIndex: 10000,
},
},
],
]
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

View File

@ -0,0 +1,2 @@
User-agent: *
Disallow:

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

View File

@ -0,0 +1,23 @@
.home .hero img
max-width: 500px !important
min-width: 300px
width: 100%
.center
margin 0 auto;
width: 80%
#main-title
display: none
.hero
margin: 150px 25px 70px
@font-face
font-family: 'Nerd Font';
src: url("/nerd-font.woff2") format("woff2");
font-weight: 400;
font-style: normal
code
font-family: 'Nerd Font', source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;

View File

@ -0,0 +1,4 @@
$accentColor = #f15833
$textColor = #663015
$borderColor = #eaecef
$codeBgColor = #282c34

108
docs/README.md Normal file
View File

@ -0,0 +1,108 @@
---
home: true
heroImage: /logo.png
actionText: Get Started →
actionLink: /guide/
footer: MIT Licensed | Copyright © 2016-present jc21.com
---
<div class="features">
<div class="feature">
<h2>Get Connected</h2>
<p>
Expose web services on your network &middot;
Free SSL with Let's Encrypt &middot;
Designed with security in mind &middot;
Perfect for home networks
</p>
</div>
<div class="feature">
<h2>Proxy Hosts</h2>
<p>Expose your private network Web services and get connected anywhere.</p>
</div>
<div class="feature">
<h2>Beautiful UI</h2>
<p>Based on Tabler, the interface is a pleasure to use. Configuring a server has never been so fun.</p>
</div>
<div class="feature">
<h2>Free SSL</h2>
<p>Built in Lets Encrypt support allows you to secure your Web services at no cost to you. The certificates even renew themselves!</p>
</div>
<div class="feature">
<h2>Docker FTW</h2>
<p>Built as a Docker Image, Nginx Proxy Manager only requires a database.</p>
</div>
<div class="feature">
<h2>Multiple Users</h2>
<p>Configure other users to either view or manage their own hosts. Full access permissions are available.</p>
</div>
</div>
### Quick Setup
1. Install Docker and Docker-Compose
- [Docker Install documentation](https://docs.docker.com/install/)
- [Docker-Compose Install documentation](https://docs.docker.com/compose/install/)
2. Create a config file for example
```json
{
"database": {
"engine": "mysql",
"host": "db",
"name": "npm",
"user": "npm",
"password": "npm",
"port": 3306
}
}
```
3. Create a docker-compose.yml file similar to this:
```yml
version: '3'
services:
app:
image: 'jc21/nginx-proxy-manager:latest'
ports:
- '80:80'
- '81:81'
- '443:443'
volumes:
- ./config.json:/app/config/production.json
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
db:
image: 'jc21/mariadb-aria:10.4'
environment:
MYSQL_ROOT_PASSWORD: 'npm'
MYSQL_DATABASE: 'npm'
MYSQL_USER: 'npm'
MYSQL_PASSWORD: 'npm'
volumes:
- ./data/mysql:/var/lib/mysql
```
4. Bring up your stack
```bash
docker-compose up -d
```
5. Log in to the Admin UI
When your docker container is running, connect to it on port `81` for the admin interface.
Sometimes this can take a little bit because of the entropy of keys.
[http://127.0.0.1:81](http://127.0.0.1:81)
Default Admin User:
```
Email: admin@example.com
Password: changeme
```
Immediately after logging in with this default user you will be asked to modify your details and change your password.

View File

@ -0,0 +1,46 @@
# Advanced Configuration
## Disabling IPv6
On some docker hosts IPv6 may not be enabled. In these cases, the following message may be seen in the log:
> Address family not supported by protocol
The easy fix is to add a Docker environment variable to the Nginx Proxy Manager stack:
```yml
environment:
DISABLE_IPV6: 'true'
```
## Custom Nginx Configurations
If you are a more advanced user, you might be itching for extra Nginx customizability.
NPM has the ability to include different custom configuration snippets in different places.
You can add your custom configuration snippet files at `/data/nginx/custom` as follow:
- `/data/nginx/custom/root.conf`: Included at the very end of nginx.conf
- `/data/nginx/custom/http.conf`: Included at the end of the main http block
- `/data/nginx/custom/server_proxy.conf`: Included at the end of every proxy server block
- `/data/nginx/custom/server_redirect.conf`: Included at the end of every redirection server block
- `/data/nginx/custom/server_stream.conf`: Included at the end of every stream server block
- `/data/nginx/custom/server_stream_tcp.conf`: Included at the end of every TCP stream server block
- `/data/nginx/custom/server_stream_udp.conf`: Included at the end of every UDP stream server block
Every file is optional.
## X-FRAME-OPTIONS Header
You can configure the [`X-FRAME-OPTIONS`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options) header
value by specifying it as a Docker environment variable. The default if not specified is `deny`.
```yml
...
environment:
X_FRAME_OPTIONS: "sameorigin"
...
```

16
docs/faq/README.md Normal file
View File

@ -0,0 +1,16 @@
# FAQ
## Do I have to use Docker?
Yes, that's how this project is packaged.
This makes it easier to support the project when I have control over the version of Nginx and NodeJS
being used. In future this could change if the backend was no longer using NodeJS and it's long list
of dependencies.
## Can I run it on a Raspberry Pi?
Yes! The docker image is multi-arch and is built for a variety of architectures. If yours is
[not listed](https://hub.docker.com/r/jc21/nginx-proxy-manager/tags) please open a
[GitHub issue](https://github.com/jc21/nginx-proxy-manager/issues/new?assignees=&labels=enhancement&template=feature_request.md&title=).

1
docs/guide/README.md Symbolic link
View File

@ -0,0 +1 @@
../../README.md

777
docs/package.json Normal file
View File

@ -0,0 +1,777 @@
{
"name": "docs",
"version": "1.0.0",
"description": "",
"main": "index.js",
"dependencies": {
"@vuepress/plugin-google-analytics": "^1.5.3",
"abbrev": "^1.1.1",
"accepts": "^1.3.7",
"acorn": "^7.4.0",
"agentkeepalive": "^4.1.3",
"ajv": "^6.12.3",
"ajv-errors": "^1.0.1",
"ajv-keywords": "^3.5.2",
"algoliasearch": "^4.3.1",
"alphanum-sort": "^1.0.2",
"ansi-colors": "^4.1.1",
"ansi-escapes": "^4.3.1",
"ansi-html": "^0.0.7",
"ansi-regex": "^5.0.0",
"ansi-styles": "^4.2.1",
"anymatch": "^3.1.1",
"aproba": "^2.0.0",
"argparse": "^1.0.10",
"arr-diff": "^4.0.0",
"arr-flatten": "^1.1.0",
"arr-union": "^3.1.0",
"array-flatten": "^3.0.0",
"array-union": "^2.1.0",
"array-uniq": "^2.1.0",
"array-unique": "^0.3.2",
"asn1": "^0.2.4",
"asn1.js": "^5.4.1",
"assert": "^2.0.0",
"assert-plus": "^1.0.0",
"assign-symbols": "^2.0.2",
"async": "^3.2.0",
"async-each": "^1.0.3",
"async-limiter": "^2.0.0",
"asynckit": "^0.4.0",
"atob": "^2.1.2",
"autocomplete.js": "^0.37.1",
"autoprefixer": "^9.8.6",
"aws-sign2": "^0.7.0",
"aws4": "^1.10.0",
"babel-loader": "^8.1.0",
"babel-plugin-dynamic-import-node": "^2.3.3",
"babel-plugin-module-resolver": "^4.0.0",
"balanced-match": "^1.0.0",
"base": "^3.0.0",
"base64-js": "^1.3.1",
"batch": "^0.6.1",
"bcrypt-pbkdf": "^1.0.2",
"big.js": "^5.2.2",
"binary-extensions": "^2.1.0",
"bluebird": "^3.7.2",
"bn.js": "^5.1.2",
"body-parser": "^1.19.0",
"bonjour": "^3.5.0",
"boolbase": "^1.0.0",
"brace-expansion": "^1.1.11",
"braces": "^3.0.2",
"brorand": "^1.1.0",
"browserify-aes": "^1.2.0",
"browserify-cipher": "^1.0.1",
"browserify-des": "^1.0.2",
"browserify-rsa": "^4.0.1",
"browserify-sign": "^4.2.1",
"browserify-zlib": "^0.2.0",
"browserslist": "^4.13.0",
"buffer": "^5.6.0",
"buffer-from": "^1.1.1",
"buffer-indexof": "^1.1.1",
"buffer-json": "^2.0.0",
"buffer-xor": "^2.0.2",
"builtin-status-codes": "^3.0.0",
"bytes": "^3.1.0",
"cac": "^6.6.1",
"cacache": "^15.0.5",
"cache-base": "^4.0.0",
"cache-loader": "^4.1.0",
"call-me-maybe": "^1.0.1",
"caller-callsite": "^4.1.0",
"caller-path": "^3.0.0",
"callsites": "^3.1.0",
"camel-case": "^4.1.1",
"camelcase": "^6.0.0",
"caniuse-api": "^3.0.0",
"caniuse-lite": "^1.0.30001111",
"caseless": "^0.12.0",
"chalk": "^4.1.0",
"chokidar": "^3.4.1",
"chownr": "^2.0.0",
"chrome-trace-event": "^1.0.2",
"ci-info": "^2.0.0",
"cipher-base": "^1.0.4",
"class-utils": "^0.3.6",
"clean-css": "^4.2.3",
"clipboard": "^2.0.6",
"cliui": "^6.0.0",
"coa": "^2.0.2",
"code-point-at": "^1.1.0",
"collection-visit": "^1.0.0",
"color": "^3.1.2",
"color-convert": "^2.0.1",
"color-name": "^1.1.4",
"color-string": "^1.5.3",
"combined-stream": "^1.0.8",
"commander": "^6.0.0",
"commondir": "^1.0.1",
"component-emitter": "^1.3.0",
"compressible": "^2.0.18",
"compression": "^1.7.4",
"concat-map": "^0.0.1",
"concat-stream": "^2.0.0",
"connect-history-api-fallback": "^1.6.0",
"consola": "^2.15.0",
"console-browserify": "^1.2.0",
"consolidate": "^0.15.1",
"constants-browserify": "^1.0.0",
"content-disposition": "^0.5.3",
"content-type": "^1.0.4",
"convert-source-map": "^1.7.0",
"cookie": "^0.4.1",
"cookie-signature": "^1.1.0",
"copy-concurrently": "^1.0.5",
"copy-descriptor": "^0.1.1",
"copy-webpack-plugin": "^6.0.3",
"core-js": "^3.6.5",
"core-util-is": "^1.0.2",
"cosmiconfig": "^7.0.0",
"create-ecdh": "^4.0.4",
"create-hash": "^1.2.0",
"create-hmac": "^1.1.7",
"cross-spawn": "^7.0.3",
"crypto-browserify": "^3.12.0",
"css": "^3.0.0",
"css-color-names": "^1.0.1",
"css-declaration-sorter": "^5.1.2",
"css-loader": "^4.2.0",
"css-parse": "^2.0.0",
"css-select": "^2.1.0",
"css-select-base-adapter": "^0.1.1",
"css-tree": "^1.0.0-alpha.39",
"css-unit-converter": "^1.1.2",
"css-what": "^3.3.0",
"cssesc": "^3.0.0",
"cssnano": "^4.1.10",
"cssnano-preset-default": "^4.0.7",
"cssnano-util-get-arguments": "^4.0.0",
"cssnano-util-get-match": "^4.0.0",
"cssnano-util-raw-cache": "^4.0.1",
"cssnano-util-same-parent": "^4.0.1",
"csso": "^4.0.3",
"cyclist": "^1.0.1",
"dashdash": "^1.14.1",
"de-indent": "^1.0.2",
"debug": "^4.1.1",
"decamelize": "^4.0.0",
"decode-uri-component": "^0.2.0",
"deep-equal": "^2.0.3",
"deepmerge": "^4.2.2",
"default-gateway": "^6.0.1",
"define-properties": "^1.1.3",
"define-property": "^2.0.2",
"del": "^5.1.0",
"delayed-stream": "^1.0.0",
"delegate": "^3.2.0",
"depd": "^2.0.0",
"des.js": "^1.0.1",
"destroy": "^1.0.4",
"detect-node": "^2.0.4",
"diacritics": "^1.3.0",
"diffie-hellman": "^5.0.3",
"dir-glob": "^3.0.1",
"dns-equal": "^1.0.0",
"dns-packet": "^5.2.1",
"dns-txt": "^2.0.2",
"docsearch.js": "^2.6.3",
"dom-converter": "^0.2.0",
"dom-serializer": "^1.0.1",
"dom-walk": "^0.1.2",
"domain-browser": "^4.16.0",
"domelementtype": "^2.0.1",
"domhandler": "^3.0.0",
"domutils": "^2.1.0",
"dot-prop": "^5.2.0",
"duplexify": "^4.1.1",
"ecc-jsbn": "^0.2.0",
"ee-first": "^1.1.1",
"electron-to-chromium": "^1.3.522",
"elliptic": "^6.5.3",
"emoji-regex": "^9.0.0",
"emojis-list": "^3.0.0",
"encodeurl": "^1.0.2",
"end-of-stream": "^1.4.4",
"enhanced-resolve": "^4.3.0",
"entities": "^2.0.3",
"envify": "^4.1.0",
"envinfo": "^7.7.2",
"errno": "^0.1.7",
"error-ex": "^1.3.2",
"es-abstract": "^1.17.6",
"es-to-primitive": "^1.2.1",
"es6-promise": "^4.2.8",
"escape-html": "^1.0.3",
"escape-string-regexp": "^4.0.0",
"eslint-scope": "^5.1.0",
"esprima": "^4.0.1",
"esrecurse": "^4.2.1",
"estraverse": "^5.2.0",
"esutils": "^2.0.3",
"etag": "^1.8.1",
"eventemitter3": "^4.0.4",
"events": "^3.2.0",
"eventsource": "^1.0.7",
"evp_bytestokey": "^1.0.3",
"execa": "^4.0.3",
"expand-brackets": "^4.0.0",
"express": "^4.17.1",
"extend": "^3.0.2",
"extend-shallow": "^3.0.2",
"extglob": "^3.0.0",
"extsprintf": "^1.4.0",
"fast-deep-equal": "^3.1.3",
"fast-glob": "^3.2.4",
"fast-json-stable-stringify": "^2.1.0",
"faye-websocket": "^0.11.3",
"figgy-pudding": "^3.5.2",
"figures": "^3.2.0",
"file-loader": "^6.0.0",
"fill-range": "^7.0.1",
"finalhandler": "^1.1.2",
"find-babel-config": "^1.2.0",
"find-cache-dir": "^3.3.1",
"find-up": "^4.1.0",
"flush-write-stream": "^2.0.0",
"follow-redirects": "^1.12.1",
"for-in": "^1.0.2",
"foreach": "^2.0.5",
"forever-agent": "^0.6.1",
"form-data": "^3.0.0",
"forwarded": "^0.1.2",
"fragment-cache": "^0.2.1",
"fresh": "^0.5.2",
"from2": "^2.3.0",
"fs-extra": "^9.0.1",
"fs-write-stream-atomic": "^1.0.10",
"fs.realpath": "^1.0.0",
"function-bind": "^1.1.1",
"gensync": "^1.0.0-beta.1",
"get-caller-file": "^2.0.5",
"get-stream": "^5.1.0",
"get-value": "^3.0.1",
"getpass": "^0.1.7",
"glob": "^7.1.6",
"glob-parent": "^5.1.1",
"glob-to-regexp": "^0.4.1",
"global": "^4.4.0",
"globals": "^13.1.0",
"globby": "^11.0.1",
"good-listener": "^1.2.2",
"graceful-fs": "^4.2.4",
"gray-matter": "^4.0.2",
"handle-thing": "^2.0.1",
"har-schema": "^2.0.0",
"har-validator": "^5.1.5",
"has": "^1.0.3",
"has-ansi": "^4.0.0",
"has-flag": "^4.0.0",
"has-symbols": "^1.0.1",
"has-value": "^2.0.2",
"has-values": "^2.0.1",
"hash-base": "^3.1.0",
"hash-sum": "^2.0.0",
"hash.js": "^1.1.7",
"he": "^1.2.0",
"hex-color-regex": "^1.1.0",
"hmac-drbg": "^1.0.1",
"hogan.js": "^3.0.2",
"hpack.js": "^2.1.6",
"hsl-regex": "^1.0.0",
"hsla-regex": "^1.0.0",
"html-comment-regex": "^1.1.2",
"html-entities": "^1.3.1",
"html-minifier": "^4.0.0",
"html-tags": "^3.1.0",
"htmlparser2": "^4.1.0",
"http-deceiver": "^1.2.7",
"http-errors": "^1.8.0",
"http-parser-js": "^0.5.2",
"http-proxy": "^1.18.1",
"http-proxy-middleware": "^1.0.5",
"http-signature": "^1.3.4",
"https-browserify": "^1.0.0",
"iconv-lite": "^0.6.2",
"icss-replace-symbols": "^1.1.0",
"icss-utils": "^4.1.1",
"ieee754": "^1.1.13",
"iferr": "^1.0.2",
"ignore": "^5.1.8",
"immediate": "^3.3.0",
"import-cwd": "^3.0.0",
"import-fresh": "^3.2.1",
"import-from": "^3.0.0",
"import-local": "^3.0.2",
"imurmurhash": "^0.1.4",
"indexes-of": "^1.0.1",
"infer-owner": "^1.0.4",
"inflight": "^1.0.6",
"inherits": "^2.0.4",
"internal-ip": "^6.1.0",
"invariant": "^2.2.4",
"invert-kv": "^3.0.1",
"ip": "^1.1.5",
"ip-regex": "^4.1.0",
"ipaddr.js": "^1.9.1",
"is-absolute-url": "^3.0.3",
"is-accessor-descriptor": "^3.0.1",
"is-arguments": "^1.0.4",
"is-arrayish": "^0.3.2",
"is-binary-path": "^2.1.0",
"is-buffer": "^2.0.4",
"is-callable": "^1.2.0",
"is-color-stop": "^1.1.0",
"is-data-descriptor": "^2.0.0",
"is-date-object": "^1.0.2",
"is-descriptor": "^3.0.0",
"is-directory": "^0.3.1",
"is-extendable": "^1.0.1",
"is-extglob": "^2.1.1",
"is-fullwidth-code-point": "^3.0.0",
"is-glob": "^4.0.1",
"is-number": "^7.0.0",
"is-obj": "^2.0.0",
"is-path-cwd": "^2.2.0",
"is-path-in-cwd": "^3.0.0",
"is-path-inside": "^3.0.2",
"is-plain-obj": "^2.1.0",
"is-plain-object": "^4.1.1",
"is-regex": "^1.1.1",
"is-resolvable": "^1.1.0",
"is-stream": "^2.0.0",
"is-svg": "^4.2.1",
"is-symbol": "^1.0.3",
"is-typedarray": "^1.0.0",
"is-windows": "^1.0.2",
"is-wsl": "^2.2.0",
"isarray": "^2.0.5",
"isexe": "^2.0.0",
"isobject": "^4.0.0",
"isstream": "^0.1.2",
"javascript-stringify": "^2.0.1",
"js-levenshtein": "^1.1.6",
"js-tokens": "^6.0.0",
"js-yaml": "^3.14.0",
"jsbn": "^1.1.0",
"jsesc": "^3.0.1",
"json-parse-better-errors": "^1.0.2",
"json-schema": "^0.2.5",
"json-schema-traverse": "^0.4.1",
"json-stringify-safe": "^5.0.1",
"json3": "^3.3.3",
"json5": "^2.1.3",
"jsonfile": "^6.0.1",
"jsprim": "^2.0.0",
"killable": "^1.0.1",
"kind-of": "^6.0.3",
"last-call-webpack-plugin": "^3.0.0",
"lcid": "^3.1.1",
"linkify-it": "^3.0.2",
"load-script": "^1.0.0",
"loader-runner": "^4.0.0",
"loader-utils": "^2.0.0",
"locate-path": "^5.0.0",
"lodash": "^4.17.19",
"lodash._reinterpolate": "^3.0.0",
"lodash.chunk": "^4.2.0",
"lodash.clonedeep": "^4.5.0",
"lodash.debounce": "^4.0.8",
"lodash.kebabcase": "^4.1.1",
"lodash.memoize": "^4.1.2",
"lodash.padstart": "^4.6.1",
"lodash.sortby": "^4.7.0",
"lodash.template": "^4.5.0",
"lodash.templatesettings": "^4.2.0",
"lodash.uniq": "^4.5.0",
"loglevel": "^1.6.8",
"loose-envify": "^1.4.0",
"lower-case": "^2.0.1",
"lru-cache": "^6.0.0",
"make-dir": "^3.1.0",
"mamacro": "^0.0.7",
"map-age-cleaner": "^0.1.3",
"map-cache": "^0.2.2",
"map-visit": "^1.0.0",
"markdown-it": "^11.0.0",
"markdown-it-anchor": "^5.3.0",
"markdown-it-chain": "^1.3.0",
"markdown-it-container": "^3.0.0",
"markdown-it-emoji": "^1.4.0",
"markdown-it-table-of-contents": "^0.4.4",
"md5.js": "^1.3.5",
"mdn-data": "^2.0.11",
"mdurl": "^1.0.1",
"media-typer": "^1.1.0",
"mem": "^6.1.0",
"memory-fs": "^0.5.0",
"merge-descriptors": "^1.0.1",
"merge-source-map": "^1.1.0",
"merge2": "^1.4.1",
"methods": "^1.1.2",
"micromatch": "^4.0.2",
"miller-rabin": "^4.0.1",
"mime": "^2.4.6",
"mime-db": "^1.44.0",
"mime-types": "^2.1.27",
"mimic-fn": "^3.1.0",
"min-document": "^2.19.0",
"mini-css-extract-plugin": "^0.9.0",
"minimalistic-assert": "^1.0.1",
"minimalistic-crypto-utils": "^1.0.1",
"minimatch": "^3.0.4",
"minimist": "^1.2.5",
"mississippi": "^4.0.0",
"mixin-deep": "^2.0.1",
"mkdirp": "^1.0.4",
"move-concurrently": "^1.0.1",
"ms": "^2.1.2",
"multicast-dns": "^7.2.2",
"multicast-dns-service-types": "^1.1.0",
"nanomatch": "^1.2.13",
"negotiator": "^0.6.2",
"neo-async": "^2.6.2",
"nice-try": "^2.0.1",
"no-case": "^3.0.3",
"node-forge": "^0.10.0",
"node-libs-browser": "^2.2.1",
"node-releases": "^1.1.60",
"nopt": "^4.0.3",
"normalize-path": "^3.0.0",
"normalize-range": "^0.1.2",
"normalize-url": "^5.1.0",
"npm-run-path": "^4.0.1",
"nprogress": "^0.2.0",
"nth-check": "^1.0.2",
"num2fraction": "^1.2.2",
"number-is-nan": "^2.0.0",
"oauth-sign": "^0.9.0",
"object-assign": "^4.1.1",
"object-copy": "^1.0.0",
"object-inspect": "^1.8.0",
"object-is": "^1.1.2",
"object-keys": "^1.1.1",
"object-visit": "^1.0.1",
"object.assign": "^4.1.0",
"object.getownpropertydescriptors": "^2.1.0",
"object.pick": "^1.3.0",
"object.values": "^1.1.1",
"obuf": "^1.1.2",
"on-finished": "^2.3.0",
"on-headers": "^1.0.2",
"once": "^1.4.0",
"opencollective-postinstall": "^2.0.3",
"opn": "^6.0.0",
"optimize-css-assets-webpack-plugin": "^5.0.3",
"original": "^1.0.2",
"os-browserify": "^0.3.0",
"os-locale": "^5.0.0",
"p-defer": "^3.0.0",
"p-finally": "^2.0.1",
"p-is-promise": "^3.0.0",
"p-limit": "^3.0.2",
"p-locate": "^4.1.0",
"p-map": "^4.0.0",
"p-retry": "^4.2.0",
"p-try": "^2.2.0",
"pako": "^1.0.11",
"parallel-transform": "^1.2.0",
"param-case": "^3.0.3",
"parse-asn1": "^5.1.5",
"parse-json": "^5.0.1",
"parseurl": "^1.3.3",
"pascalcase": "^1.0.0",
"path-browserify": "^1.0.1",
"path-dirname": "^1.0.2",
"path-exists": "^4.0.0",
"path-is-absolute": "^2.0.0",
"path-is-inside": "^1.0.2",
"path-key": "^3.1.1",
"path-parse": "^1.0.6",
"path-to-regexp": "^6.1.0",
"path-type": "^4.0.0",
"pbkdf2": "^3.1.1",
"performance-now": "^2.1.0",
"pify": "^5.0.0",
"pinkie": "^2.0.4",
"pinkie-promise": "^2.0.1",
"pkg-dir": "^4.2.0",
"pkg-up": "^3.1.0",
"portfinder": "^1.0.28",
"posix-character-classes": "^1.0.0",
"postcss": "^7.0.32",
"postcss-calc": "^7.0.2",
"postcss-colormin": "^4.0.3",
"postcss-convert-values": "^4.0.1",
"postcss-discard-comments": "^4.0.2",
"postcss-discard-duplicates": "^4.0.2",
"postcss-discard-empty": "^4.0.1",
"postcss-discard-overridden": "^4.0.1",
"postcss-load-config": "^2.1.0",
"postcss-loader": "^3.0.0",
"postcss-merge-longhand": "^4.0.11",
"postcss-merge-rules": "^4.0.3",
"postcss-minify-font-values": "^4.0.2",
"postcss-minify-gradients": "^4.0.2",
"postcss-minify-params": "^4.0.2",
"postcss-minify-selectors": "^4.0.2",
"postcss-modules-extract-imports": "^2.0.0",
"postcss-modules-local-by-default": "^3.0.3",
"postcss-modules-scope": "^2.2.0",
"postcss-modules-values": "^3.0.0",
"postcss-normalize-charset": "^4.0.1",
"postcss-normalize-display-values": "^4.0.2",
"postcss-normalize-positions": "^4.0.2",
"postcss-normalize-repeat-style": "^4.0.2",
"postcss-normalize-string": "^4.0.2",
"postcss-normalize-timing-functions": "^4.0.2",
"postcss-normalize-unicode": "^4.0.1",
"postcss-normalize-url": "^4.0.1",
"postcss-normalize-whitespace": "^4.0.2",
"postcss-ordered-values": "^4.1.2",
"postcss-reduce-initial": "^4.0.3",
"postcss-reduce-transforms": "^4.0.2",
"postcss-safe-parser": "^4.0.2",
"postcss-selector-parser": "^6.0.2",
"postcss-svgo": "^4.0.2",
"postcss-unique-selectors": "^4.0.1",
"postcss-value-parser": "^4.1.0",
"prepend-http": "^3.0.1",
"prettier": "^2.0.5",
"pretty-error": "^2.1.1",
"pretty-time": "^1.1.0",
"prismjs": "^1.20.0",
"private": "^0.1.8",
"process": "^0.11.10",
"process-nextick-args": "^2.0.1",
"promise-inflight": "^1.0.1",
"proxy-addr": "^2.0.6",
"prr": "^1.0.1",
"pseudomap": "^1.0.2",
"psl": "^1.8.0",
"public-encrypt": "^4.0.3",
"pump": "^3.0.0",
"pumpify": "^2.0.1",
"punycode": "^2.1.1",
"q": "^1.5.1",
"qs": "^6.9.4",
"query-string": "^6.13.1",
"querystring": "^0.2.0",
"querystring-es3": "^0.2.1",
"querystringify": "^2.1.1",
"randombytes": "^2.1.0",
"randomfill": "^1.0.4",
"range-parser": "^1.2.1",
"raw-body": "^2.4.1",
"readable-stream": "^3.6.0",
"readdirp": "^3.4.0",
"reduce": "^1.0.2",
"regenerate": "^1.4.1",
"regenerate-unicode-properties": "^8.2.0",
"regenerator-runtime": "^0.13.7",
"regenerator-transform": "^0.14.5",
"regex-not": "^1.0.2",
"regexp.prototype.flags": "^1.3.0",
"regexpu-core": "^4.7.0",
"regjsgen": "^0.5.2",
"regjsparser": "^0.6.4",
"relateurl": "^0.2.7",
"remove-trailing-separator": "^1.1.0",
"renderkid": "^2.0.3",
"repeat-element": "^1.1.3",
"repeat-string": "^1.6.1",
"request": "^2.88.2",
"require-directory": "^2.1.1",
"require-main-filename": "^2.0.0",
"requires-port": "^1.0.0",
"reselect": "^4.0.0",
"resolve": "^1.17.0",
"resolve-cwd": "^3.0.0",
"resolve-from": "^5.0.0",
"resolve-url": "^0.2.1",
"ret": "^0.3.1",
"retry": "^0.12.0",
"rgb-regex": "^1.0.1",
"rgba-regex": "^1.0.0",
"rimraf": "^3.0.2",
"ripemd160": "^2.0.2",
"run-queue": "^2.0.1",
"safe-buffer": "^5.2.1",
"safe-regex": "^2.1.1",
"safer-buffer": "^2.1.2",
"sax": "^1.2.4",
"schema-utils": "^2.7.0",
"section-matter": "^1.0.0",
"select": "^1.1.2",
"select-hose": "^2.0.0",
"selfsigned": "^1.10.7",
"semver": "^7.3.2",
"send": "^0.17.1",
"serialize-javascript": "^4.0.0",
"serve-index": "^1.9.1",
"serve-static": "^1.14.1",
"set-blocking": "^2.0.0",
"set-value": "^3.0.2",
"setimmediate": "^1.0.5",
"setprototypeof": "^1.2.0",
"sha.js": "^2.4.11",
"shebang-command": "^2.0.0",
"shebang-regex": "^3.0.0",
"signal-exit": "^3.0.3",
"simple-swizzle": "^0.2.2",
"sitemap": "^6.2.0",
"slash": "^3.0.0",
"smoothscroll-polyfill": "^0.4.4",
"snapdragon": "^0.12.0",
"snapdragon-node": "^3.0.0",
"snapdragon-util": "^5.0.1",
"sockjs": "^0.3.21",
"sockjs-client": "^1.5.0",
"sort-keys": "^4.0.0",
"source-list-map": "^2.0.1",
"source-map": "^0.7.3",
"source-map-resolve": "^0.6.0",
"source-map-support": "^0.5.19",
"source-map-url": "^0.4.0",
"spdy": "^4.0.2",
"spdy-transport": "^3.0.0",
"split-string": "^6.1.0",
"sprintf-js": "^1.1.2",
"sshpk": "^1.16.1",
"ssri": "^8.0.0",
"stable": "^0.1.8",
"stack-utils": "^2.0.2",
"static-extend": "^0.1.2",
"statuses": "^2.0.0",
"std-env": "^2.2.1",
"stream-browserify": "^3.0.0",
"stream-each": "^1.2.3",
"stream-http": "^3.1.1",
"stream-shift": "^1.0.1",
"strict-uri-encode": "^2.0.0",
"string-width": "^4.2.0",
"string.prototype.trimleft": "^2.1.2",
"string.prototype.trimright": "^2.1.2",
"string_decoder": "^1.3.0",
"strip-ansi": "^6.0.0",
"strip-bom-string": "^1.0.0",
"strip-eof": "^2.0.0",
"stylehacks": "^4.0.3",
"stylus": "^0.54.8",
"stylus-loader": "^3.0.2",
"supports-color": "^7.1.0",
"svg-tags": "^1.0.0",
"svgo": "^1.3.2",
"tapable": "^1.1.3",
"terser": "^5.0.0",
"terser-webpack-plugin": "^4.0.0",
"text-table": "^0.2.0",
"through": "^2.3.8",
"through2": "^4.0.2",
"thunky": "^1.1.0",
"timers-browserify": "^2.0.11",
"timsort": "^0.3.0",
"tiny-emitter": "^2.1.0",
"to-arraybuffer": "^1.0.1",
"to-factory": "^1.0.0",
"to-fast-properties": "^3.0.1",
"to-object-path": "^0.3.0",
"to-regex": "^3.0.2",
"to-regex-range": "^5.0.1",
"toidentifier": "^1.0.0",
"toml": "^3.0.0",
"toposort": "^2.0.2",
"tough-cookie": "^4.0.0",
"tr46": "^2.0.2",
"tslib": "^2.0.0",
"tty-browserify": "^0.0.1",
"tunnel-agent": "^0.6.0",
"tweetnacl": "^1.0.3",
"type-fest": "^0.16.0",
"type-is": "^1.6.18",
"typedarray": "^0.0.6",
"uc.micro": "^1.0.6",
"uglify-js": "^3.10.1",
"unicode-canonical-property-names-ecmascript": "^1.0.4",
"unicode-match-property-ecmascript": "^1.0.4",
"unicode-match-property-value-ecmascript": "^1.2.0",
"unicode-property-aliases-ecmascript": "^1.1.0",
"union-value": "^2.0.1",
"uniq": "^1.0.1",
"uniqs": "^2.0.0",
"unique-filename": "^1.1.1",
"unique-slug": "^2.0.2",
"universalify": "^2.0.0",
"unpipe": "^1.0.0",
"unquote": "^1.1.1",
"unset-value": "^1.0.0",
"upath": "^1.2.0",
"upper-case": "^2.0.1",
"uri-js": "^4.2.2",
"urix": "^0.1.0",
"url": "^0.11.0",
"url-loader": "^4.1.0",
"url-parse": "^1.4.7",
"use": "^3.1.1",
"util": "^0.12.3",
"util-deprecate": "^1.0.2",
"util.promisify": "^1.0.1",
"utila": "^0.4.0",
"utils-merge": "^1.0.1",
"uuid": "^8.3.0",
"vary": "^1.1.2",
"vendors": "^1.0.4",
"verror": "^1.10.0",
"vm-browserify": "^1.1.2",
"vue": "^2.6.11",
"vue-hot-reload-api": "^2.3.4",
"vue-loader": "^15.9.3",
"vue-router": "^3.4.0",
"vue-server-renderer": "^2.6.11",
"vue-style-loader": "^4.1.2",
"vue-template-compiler": "^2.6.11",
"vue-template-es2015-compiler": "^1.9.1",
"vuepress": "^1.5.3",
"vuepress-html-webpack-plugin": "^3.2.0",
"vuepress-plugin-container": "^2.1.4",
"vuepress-plugin-sitemap": "^2.3.1",
"vuepress-plugin-smooth-scroll": "^0.0.9",
"vuepress-plugin-zooming": "^1.1.7",
"watchpack": "^1.7.4",
"wbuf": "^1.7.3",
"webidl-conversions": "^6.1.0",
"webpack": "^4.44.1",
"webpack-chain": "^6.5.1",
"webpack-dev-middleware": "^3.7.2",
"webpack-dev-server": "^3.11.0",
"webpack-log": "^3.0.1",
"webpack-merge": "^5.1.1",
"webpack-sources": "^1.4.3",
"webpackbar": "^4.0.0",
"websocket-driver": "^0.7.4",
"websocket-extensions": "^0.1.4",
"whatwg-url": "^8.1.0",
"when": "^3.7.8",
"which": "^2.0.2",
"which-module": "^2.0.0",
"worker-farm": "^1.7.0",
"wrap-ansi": "^7.0.0",
"wrappy": "^1.0.2",
"ws": "^7.3.1",
"xmlbuilder": "^15.1.1",
"xtend": "^4.0.2",
"y18n": "^4.0.0",
"yallist": "^4.0.0",
"yargs": "^15.4.1",
"yargs-parser": "^18.1.3",
"zepto": "^1.2.0"
},
"devDependencies": {},
"scripts": {
"dev": "vuepress dev",
"build": "vuepress build"
},
"author": "",
"license": "ISC"
}

View File

@ -0,0 +1,12 @@
# Screenshots
<img class="no-medium-zoom zooming" src="/screenshots/login.png" alt="Login" title="Login" width="200"/>
<img class="no-medium-zoom zooming" src="/screenshots/dashboard.png" alt="Dashboard" title="Dashboard" width="200"/>
<img class="no-medium-zoom zooming" src="/screenshots/proxy-hosts.png" alt="Proxy Hosts" title="Proxy Hosts" width="200"/>
<img class="no-medium-zoom zooming" src="/screenshots/proxy-hosts-add.png" alt="Add Proxy Host" title="Add Proxy Host" width="200"/>
<img class="no-medium-zoom zooming" src="/screenshots/redirection-hosts.png" alt="Redirection Hosts" title="Redirection Hosts" width="200"/>
<img class="no-medium-zoom zooming" src="/screenshots/dead-hosts.png" alt="404 Hosts" title="404 Hosts" width="200"/>
<img class="no-medium-zoom zooming" src="/screenshots/permissions.png" alt="User Permissions" title="User Permissions" width="200"/>
<img class="no-medium-zoom zooming" src="/screenshots/certificates.png" alt="Certificates" title="Certificates" width="200"/>
<img class="no-medium-zoom zooming" src="/screenshots/audit-log.png" alt="Audit Log" title="Audit Log" width="200"/>
<img class="no-medium-zoom zooming" src="/screenshots/custom-settings.png" alt="Custom Settings" title="Custom Settings" width="200"/>

View File

@ -1,13 +1,4 @@
## Installation and Configuration
If you just want to get up and running in the quickest time possible, grab all the files in
the [doc/example/](https://github.com/jc21/nginx-proxy-manager/tree/master/doc/example)
folder and run:
```bash
docker-compose up -d
```
# Full Setup Instructions
### Configuration File
@ -15,7 +6,7 @@ docker-compose up -d
Don't worry, this is easy to do.
The app requires a configuration file to let it know what database you're using.
The app requires a configuration file to let it know what database you're using. By default, this file is called `config.json`
Here's an example configuration for `mysql` (or mariadb) that is compatible with the docker-compose example below:
@ -32,15 +23,31 @@ Here's an example configuration for `mysql` (or mariadb) that is compatible with
}
```
Alternatively if you would like to use a Sqlite database file:
```json
{
"database": {
"engine": "knex-native",
"knex": {
"client": "sqlite3",
"connection": {
"filename": "/data/database.sqlite"
}
}
}
}
```
Once you've created your configuration file it's easy to mount it in the docker container.
**Note:** After the first run of the application, the config file will be altered to include generated encryption keys unique to your installation. These keys
affect the login and session management of the application. If these keys change for any reason, all users will be logged out.
### Database
### MySQL Database
This app doesn't come with a database, you have to provide one yourself. Currently only `mysql/mariadb` is supported for the minimum versions:
If you opt for the MySQL configuration you will have to provide the database server yourself. You can also use MariaDB. Here are the minimum supported versions:
- MySQL v5.7.8+
- MariaDB v10.2.7+
@ -48,6 +55,12 @@ This app doesn't come with a database, you have to provide one yourself. Current
It's easy to use another docker container for your database also and link it as part of the docker stack, so that's what the following examples
are going to use.
::: warning
When using a `mariadb` database, the NPM configuration file should still use the `mysql` engine!
:::
### Running the App
@ -61,11 +74,14 @@ services:
restart: always
ports:
# Public HTTP Port:
- 80:80
- '80:80'
# Public HTTPS Port:
- 443:443
- '443:443'
# Admin Web Port:
- 81:81
- '81:81'
environment:
# Uncomment this if IPv6 is not enabled on your host
# DISABLE_IPV6: 'true'
volumes:
# Make sure this config.json file exists as per instructions above:
- ./config.json:/app/config/production.json
@ -74,13 +90,13 @@ services:
depends_on:
- db
db:
image: jc21/mariadb-aria:latest
image: jc21/mariadb-aria:10.4
restart: always
environment:
MYSQL_ROOT_PASSWORD: "npm"
MYSQL_DATABASE: "npm"
MYSQL_USER: "npm"
MYSQL_PASSWORD: "npm"
MYSQL_ROOT_PASSWORD: 'npm'
MYSQL_DATABASE: 'npm'
MYSQL_USER: 'npm'
MYSQL_PASSWORD: 'npm'
volumes:
- ./data/mysql:/var/lib/mysql
```
@ -91,6 +107,7 @@ Then:
docker-compose up -d
```
The config file (config.json) must be present in this directory.
### Running on Raspberry PI / ARM devices
@ -102,7 +119,7 @@ The docker images support the following architectures:
The docker images are a manifest of all the architecture docker builds supported, so this means
you don't have to worry about doing anything special and you can follow the common instructions above.
Check out the [dockerhub tags](https://cloud.docker.com/repository/registry-1.docker.io/jc21/nginx-proxy-manager/tags)
Check out the [dockerhub tags](https://hub.docker.com/r/jc21/nginx-proxy-manager/tags)
for a list of supported architectures and if you want one that doesn't exist,
[create a feature request](https://github.com/jc21/nginx-proxy-manager/issues/new?assignees=&labels=enhancement&template=feature_request.md&title=).
@ -114,9 +131,9 @@ on Raspbian.
After the app is running for the first time, the following will happen:
- The database will initialize with table structures
- GPG keys will be generated and saved in the configuration file
- A default admin user will be created
1. The database will initialize with table structures
2. GPG keys will be generated and saved in the configuration file
3. A default admin user will be created
This process can take a couple of minutes depending on your machine.
@ -129,22 +146,3 @@ Password: changeme
```
Immediately after logging in with this default user you will be asked to modify your details and change your password.
### Advanced Options
#### X-FRAME-OPTIONS Header
You can configure the [`X-FRAME-OPTIONS`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options) header
value by specifying it as a Docker environment variable. The default if not specified is `deny`.
```yml
...
environment:
X_FRAME_OPTIONS: "sameorigin"
...
```
```
... -e "X_FRAME_OPTIONS=sameorigin" ...
```

12
docs/third-party/README.md vendored Normal file
View File

@ -0,0 +1,12 @@
# Third Party
As this software gains popularity it's common to see it integrated with other platforms. Please be aware that unless specifically mentioned in the documenation of those
integrations, they are *not supported* by me.
Known integrations:
- [HomeAssistant Hass.io plugin](https://github.com/hassio-addons/addon-nginx-proxy-manager)
- [UnRaid / Synology](https://github.com/jlesage/docker-nginx-proxy-manager)
If you would like your integration of NPM listed, please open a
[Github issue](https://github.com/jc21/nginx-proxy-manager/issues/new?assignees=&labels=enhancement&template=feature_request.md&title=)

10348
docs/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -2,8 +2,8 @@
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/images/favicon/mstile-150x150.png"/>
<TileColor>#f0ad00</TileColor>
<square150x150logo src="/images/favicons/mstile-150x150.png"/>
<TileColor>#333333</TileColor>
</tile>
</msapplication>
</browserconfig>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 958 B

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -9,24 +9,77 @@ Created by potrace 1.11, written by Peter Selinger 2001-2013
</metadata>
<g transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M2384 5110 c-1036 -74 -1922 -761 -2248 -1743 -146 -437 -170 -915
-70 -1367 193 -869 838 -1582 1687 -1864 437 -146 915 -170 1367 -70 632 141
1204 533 1564 1074 222 333 358 697 412 1100 22 167 22 473 0 640 -96 722
-473 1348 -1062 1767 -472 335 -1074 504 -1650 463z m1514 -1050 c173 -18 201
-52 210 -250 8 -190 -30 -414 -110 -655 -112 -338 -282 -619 -509 -842 -131
-130 -233 -203 -384 -278 -211 -105 -410 -162 -663 -188 l-114 -12 -97 -109
c-201 -228 -505 -449 -846 -616 -210 -102 -316 -133 -338 -98 -25 39 106 345
248 578 166 275 296 430 548 657 36 32 47 49 47 72 l0 30 -137 -66 c-229 -109
-615 -273 -644 -273 -15 0 -35 7 -43 16 -24 24 -20 109 8 169 38 82 425 784
473 860 88 136 163 193 292 221 71 15 247 18 324 5 l48 -8 49 61 c305 381 900
682 1434 726 50 4 96 8 101 8 6 1 52 -3 103 -8z m-558 -2245 c-21 -148 -73
-226 -214 -319 -97 -64 -842 -472 -899 -492 -47 -17 -110 -18 -138 -4 -13 7
-19 21 -19 44 0 29 102 278 230 563 36 81 28 76 165 88 273 25 602 139 810
283 l70 48 3 -65 c2 -36 -2 -102 -8 -146z"/>
<path d="M3580 3711 c-72 -23 -112 -76 -112 -151 0 -88 62 -152 150 -153 194
-3 214 278 22 307 -19 3 -46 1 -60 -3z"/>
<path d="M3035 3392 c-68 -33 -128 -93 -158 -161 -31 -69 -29 -178 5 -252 54
-117 159 -184 288 -184 96 1 169 33 234 106 60 66 80 129 74 228 -7 118 -59
201 -160 256 -44 24 -67 30 -138 33 -77 3 -90 1 -145 -26z"/>
<path d="M2348 4766 c-109 -63 -203 -116 -210 -120 -7 -3 -20 -10 -28 -16 -19
-12 -114 -68 -410 -238 -481 -276 -1017 -586 -1087 -628 -39 -23 -73 -40 -76
-38 -3 3 -5 -519 -5 -1160 l0 -1165 292 -169 c293 -170 727 -421 801 -462 22
-12 99 -56 170 -98 167 -96 690 -399 732 -423 l32 -18 207 120 c114 66 226
131 248 143 23 13 214 124 426 246 212 122 401 231 420 242 19 11 127 73 240
138 213 123 225 130 383 220 61 35 95 60 92 68 -3 7 0 10 5 7 11 -7 7 36 -4
54 -4 5 -2 13 4 16 8 5 7 12 -2 23 -10 12 -10 14 0 8 6 -4 12 -2 12 3 0 6 -6
11 -12 11 -10 0 -9 3 1 9 10 6 10 10 1 16 -9 6 -9 9 0 15 9 6 9 12 0 30 -9 17
-9 21 0 15 14 -9 10 27 -5 37 -6 5 -6 8 2 8 11 0 10 30 -2 49 -3 5 -1 13 5 16
8 5 7 11 -1 22 -10 11 -10 16 0 22 10 7 11 13 1 31 -8 15 -8 20 -1 16 6 -4 11
-3 11 3 0 5 -5 13 -11 17 -8 5 -8 9 1 14 9 6 9 12 0 30 -8 15 -8 20 -1 16 15
-10 14 7 -1 22 -9 9 -9 12 0 12 12 0 12 26 -1 46 -3 6 -3 14 0 18 10 10 11
256 1 262 -4 3 -4 13 2 23 6 12 5 22 -3 31 -9 10 -9 11 1 6 6 -4 12 -2 12 3 0
6 -5 11 -10 11 -7 0 -7 6 0 20 6 11 7 20 1 20 -12 0 -9 33 4 43 6 6 5 10 -3
14 -10 4 -10 8 0 21 10 12 10 14 1 8 -7 -4 -13 -2 -13 3 0 6 6 11 13 11 9 0 8
3 -2 9 -11 7 -11 10 0 15 11 5 11 7 1 12 -10 5 -10 9 0 22 10 12 10 14 1 8 -7
-4 -13 -2 -13 3 0 6 6 11 13 11 9 0 8 3 -2 9 -11 7 -11 10 0 15 11 5 11 7 1
12 -10 5 -10 9 0 22 10 12 10 14 1 8 -7 -4 -13 -2 -13 3 0 6 6 11 13 11 9 0 8
3 -2 9 -11 7 -11 10 0 15 11 5 11 7 1 12 -10 5 -10 9 0 22 10 12 10 14 1 8 -7
-4 -13 -3 -13 3 0 5 6 12 13 15 9 5 9 7 0 12 -9 4 -10 11 -3 25 7 13 7 19 1
19 -7 0 -7 8 -1 24 6 16 5 27 -3 35 -8 8 -8 14 -2 19 15 10 17 32 5 46 -5 7
-7 16 -3 20 12 12 10 190 -2 198 -7 5 -7 8 1 8 12 0 12 29 -1 49 -5 10 -4 12
3 7 7 -4 12 -3 12 3 0 5 -5 13 -11 17 -8 5 -8 9 1 14 9 6 9 12 0 30 -8 15 -8
20 -1 16 6 -4 11 -3 11 3 0 5 -5 13 -11 17 -8 5 -8 9 1 14 9 6 9 12 0 30 -8
16 -9 20 -1 15 12 -7 9 34 -3 54 -4 5 -2 13 4 16 8 5 7 12 -2 23 -10 12 -10
14 0 8 6 -4 12 -2 12 3 0 6 -6 11 -12 11 -10 0 -9 3 1 9 10 6 10 10 1 16 -9 6
-9 9 0 15 9 6 9 12 0 30 -9 17 -9 21 0 15 14 -9 10 27 -5 37 -6 5 -6 8 2 8 11
0 10 36 -2 48 -2 3 0 11 6 18 7 8 7 14 -1 19 -5 3 -10 12 -10 18 0 7 4 6 10
-3 8 -12 9 -9 8 12 -2 24 -34 45 -313 206 -458 265 -1024 592 -1070 617 -22
13 -174 100 -338 194 -165 94 -304 171 -310 171 -7 0 -101 -52 -209 -114z
m347 -191 c33 -19 85 -49 116 -66 l56 -32 0 -174 -1 -174 -68 -40 c-80 -47
-184 -105 -214 -120 -18 -9 -43 1 -165 71 -79 46 -146 84 -148 86 -2 2 -4 17
-4 34 0 36 -6 36 -102 11 -204 -54 -409 -148 -565 -259 -164 -116 -329 -287
-433 -447 -105 -163 -197 -382 -232 -554 -16 -78 -20 -102 -30 -191 -7 -50 -6
-247 0 -300 3 -25 7 -69 10 -99 3 -29 8 -57 10 -61 7 -11 -5 -40 -16 -40 -4 0
-35 -16 -68 -35 -64 -38 -77 -42 -78 -22 -1 19 -14 91 -18 105 -25 73 -25 511
0 584 2 7 6 27 8 43 12 77 66 267 97 340 116 269 219 425 399 606 173 172 297
262 506 364 144 71 261 112 412 146 l101 23 -2 50 -1 50 149 88 150 87 35 -20
c20 -10 63 -35 96 -54z m512 -300 c296 -108 565 -305 798 -586 96 -115 237
-374 288 -529 100 -300 129 -643 77 -900 -6 -30 -13 -68 -16 -85 -4 -28 -23
-98 -57 -207 l-15 -48 47 -25 46 -25 0 -175 0 -174 -110 -62 c-60 -35 -127
-74 -148 -88 -45 -29 -21 -38 -253 97 l-91 53 1 174 c1 95 3 175 6 177 3 3 69
41 148 86 141 80 144 81 173 67 16 -9 29 -13 29 -10 0 3 0 7 0 10 0 3 7 22 14
42 21 55 55 211 63 290 9 85 10 330 0 403 -21 171 -95 421 -159 534 -5 11 -22
40 -36 65 -140 253 -346 467 -597 621 -75 46 -204 108 -270 131 -22 7 -43 16
-48 19 -6 5 -12 143 -8 173 2 12 16 9 118 -28z m-231 -776 c645 -372 604 -346
605 -377 0 -15 1 -278 1 -586 l1 -558 -29 -24 c-16 -13 -32 -23 -35 -24 -4 0
-421 -236 -529 -300 -8 -4 -109 -63 -223 -129 l-208 -121 -132 76 c-73 42
-148 85 -167 96 -28 16 -222 127 -473 271 -18 11 -82 48 -142 83 l-111 64 1
589 c0 324 2 591 5 594 3 2 145 84 315 182 171 98 378 217 461 265 83 48 157
90 165 93 11 6 41 24 75 46 1 1 190 -108 420 -240z m-1788 -1533 c71 -41 138
-80 149 -87 19 -11 20 -23 20 -186 l0 -174 -29 -15 c-15 -8 -28 -18 -28 -23 0
-13 167 -174 235 -227 125 -97 270 -180 406 -235 74 -29 194 -67 233 -73 18
-3 39 -8 47 -10 73 -26 352 -39 514 -24 95 8 261 43 354 74 163 55 413 195
535 302 15 12 32 22 38 22 7 0 45 -19 85 -42 l74 -42 -23 -21 c-260 -239 -658
-426 -983 -460 -27 -3 -63 -8 -79 -11 -32 -7 -285 -7 -361 0 -200 18 -437 87
-633 184 -194 96 -315 185 -487 355 l-120 118 -38 -21 -38 -22 -137 78 c-75
42 -144 83 -152 90 -12 10 -15 43 -14 178 0 99 4 170 10 176 8 8 283 168 291
170 1 0 60 -33 131 -74z"/>
<path d="M2475 4451 l-80 -47 -3 -84 c-3 -111 0 -122 46 -147 21 -11 59 -32
83 -46 l44 -26 80 46 c44 25 83 51 87 56 3 6 6 53 5 106 l-2 94 -72 43 c-39
23 -80 44 -90 47 -10 3 -51 -15 -98 -42z"/>
<path d="M3978 1843 l-78 -45 0 -102 0 -102 83 -47 c46 -26 87 -47 92 -47 4 0
44 21 89 47 l83 46 -1 101 -1 101 -80 47 c-44 27 -87 47 -95 47 -8 -1 -50 -22
-92 -46z"/>
<path d="M2307 3015 c-137 -80 -253 -145 -258 -145 -11 0 -10 21 -12 -318 -2
-241 0 -285 13 -296 14 -11 435 -258 487 -285 20 -10 50 4 275 135 139 80 256
151 260 157 3 6 6 142 6 303 l0 293 -132 77 c-72 42 -147 86 -166 96 -19 11
-76 45 -127 74 -50 30 -93 54 -95 54 -1 -1 -115 -66 -251 -145z"/>
<path d="M971 1846 c-40 -24 -76 -48 -80 -54 -5 -8 -6 -160 -2 -194 1 -8 156
-98 169 -98 5 0 46 21 90 47 l81 47 3 70 c6 126 4 131 -60 168 -90 51 -104 58
-116 58 -6 -1 -44 -20 -85 -44z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

@ -1,13 +1,14 @@
{
"name": "",
"short_name": "",
"icons": [
{
"src": "/images/favicon/android-chrome-192x192.png",
"src": "/images/favicons/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/images/favicon/android-chrome-512x512.png",
"src": "/images/favicons/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
@ -15,4 +16,4 @@
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

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