├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── dns_challenge_request.md │ └── feature_request.md └── workflows │ └── docker.yml ├── .gitignore ├── .version ├── Jenkinsfile ├── LICENSE ├── README-en.md ├── README.md ├── backend ├── .eslintrc.json ├── .gitignore ├── .prettierrc ├── app.js ├── config │ ├── README.md │ ├── default.json │ └── sqlite-test-db.json ├── db.js ├── index.js ├── internal │ ├── access-list.js │ ├── audit-log.js │ ├── certificate.js │ ├── dead-host.js │ ├── host.js │ ├── ip_ranges.js │ ├── nginx.js │ ├── proxy-host.js │ ├── redirection-host.js │ ├── report.js │ ├── setting.js │ ├── stream.js │ ├── token.js │ └── user.js ├── knexfile.js ├── lib │ ├── access.js │ ├── access │ │ ├── access_lists-create.json │ │ ├── access_lists-delete.json │ │ ├── access_lists-get.json │ │ ├── access_lists-list.json │ │ ├── access_lists-update.json │ │ ├── auditlog-list.json │ │ ├── certificates-create.json │ │ ├── certificates-delete.json │ │ ├── certificates-get.json │ │ ├── certificates-list.json │ │ ├── certificates-update.json │ │ ├── dead_hosts-create.json │ │ ├── dead_hosts-delete.json │ │ ├── dead_hosts-get.json │ │ ├── dead_hosts-list.json │ │ ├── dead_hosts-update.json │ │ ├── permissions.json │ │ ├── proxy_hosts-create.json │ │ ├── proxy_hosts-delete.json │ │ ├── proxy_hosts-get.json │ │ ├── proxy_hosts-list.json │ │ ├── proxy_hosts-update.json │ │ ├── redirection_hosts-create.json │ │ ├── redirection_hosts-delete.json │ │ ├── redirection_hosts-get.json │ │ ├── redirection_hosts-list.json │ │ ├── redirection_hosts-update.json │ │ ├── reports-hosts.json │ │ ├── roles.json │ │ ├── settings-get.json │ │ ├── settings-list.json │ │ ├── settings-update.json │ │ ├── streams-create.json │ │ ├── streams-delete.json │ │ ├── streams-get.json │ │ ├── streams-list.json │ │ ├── streams-update.json │ │ ├── users-create.json │ │ ├── users-delete.json │ │ ├── users-get.json │ │ ├── users-list.json │ │ ├── users-loginas.json │ │ ├── users-password.json │ │ ├── users-permissions.json │ │ └── users-update.json │ ├── certbot.js │ ├── config.js │ ├── error.js │ ├── express │ │ ├── cors.js │ │ ├── jwt-decode.js │ │ ├── jwt.js │ │ ├── pagination.js │ │ └── user-id-from-me.js │ ├── helpers.js │ ├── migrate_template.js │ ├── utils.js │ └── validator │ │ ├── api.js │ │ └── index.js ├── logger.js ├── migrate.js ├── migrations │ ├── 20180618015850_initial.js │ ├── 20180929054513_websockets.js │ ├── 20181019052346_forward_host.js │ ├── 20181113041458_http2_support.js │ ├── 20181213013211_forward_scheme.js │ ├── 20190104035154_disabled.js │ ├── 20190215115310_customlocations.js │ ├── 20190218060101_hsts.js │ ├── 20190227065017_settings.js │ ├── 20200410143839_access_list_client.js │ ├── 20200410143840_access_list_client_fix.js │ ├── 20201014143841_pass_auth.js │ ├── 20210210154702_redirection_scheme.js │ ├── 20210210154703_redirection_status_code.js │ ├── 20210423103500_stream_domain.js │ ├── 20211108145214_regenerate_default_host.js │ └── 20240427161436_stream_ssl.js ├── models │ ├── access_list.js │ ├── access_list_auth.js │ ├── access_list_client.js │ ├── audit-log.js │ ├── auth.js │ ├── certificate.js │ ├── dead_host.js │ ├── now_helper.js │ ├── proxy_host.js │ ├── redirection_host.js │ ├── setting.js │ ├── stream.js │ ├── token.js │ ├── user.js │ └── user_permission.js ├── nodemon.json ├── package.json ├── routes │ ├── audit-log.js │ ├── main.js │ ├── nginx │ │ ├── access_lists.js │ │ ├── certificates.js │ │ ├── dead_hosts.js │ │ ├── proxy_hosts.js │ │ ├── redirection_hosts.js │ │ └── streams.js │ ├── reports.js │ ├── schema.js │ ├── settings.js │ ├── tokens.js │ └── users.js ├── schema │ ├── common.json │ ├── components │ │ ├── access-list-object.json │ │ ├── audit-log-object.json │ │ ├── certificate-list.json │ │ ├── certificate-object.json │ │ ├── dead-host-list.json │ │ ├── dead-host-object.json │ │ ├── error-object.json │ │ ├── error.json │ │ ├── health-object.json │ │ ├── permission-object.json │ │ ├── proxy-host-list.json │ │ ├── proxy-host-object.json │ │ ├── redirection-host-list.json │ │ ├── redirection-host-object.json │ │ ├── security-schemes.json │ │ ├── setting-list.json │ │ ├── setting-object.json │ │ ├── stream-list.json │ │ ├── stream-object.json │ │ ├── token-object.json │ │ ├── user-list.json │ │ └── user-object.json │ ├── index.js │ ├── paths │ │ ├── audit-log │ │ │ └── get.json │ │ ├── get.json │ │ ├── nginx │ │ │ ├── access-lists │ │ │ │ ├── get.json │ │ │ │ ├── listID │ │ │ │ │ ├── delete.json │ │ │ │ │ ├── get.json │ │ │ │ │ └── put.json │ │ │ │ └── post.json │ │ │ ├── certificates │ │ │ │ ├── certID │ │ │ │ │ ├── delete.json │ │ │ │ │ ├── download │ │ │ │ │ │ └── get.json │ │ │ │ │ ├── get.json │ │ │ │ │ ├── renew │ │ │ │ │ │ └── post.json │ │ │ │ │ └── upload │ │ │ │ │ │ └── post.json │ │ │ │ ├── get.json │ │ │ │ ├── post.json │ │ │ │ ├── test-http │ │ │ │ │ └── get.json │ │ │ │ └── validate │ │ │ │ │ └── post.json │ │ │ ├── dead-hosts │ │ │ │ ├── get.json │ │ │ │ ├── hostID │ │ │ │ │ ├── delete.json │ │ │ │ │ ├── disable │ │ │ │ │ │ └── post.json │ │ │ │ │ ├── enable │ │ │ │ │ │ └── post.json │ │ │ │ │ ├── get.json │ │ │ │ │ └── put.json │ │ │ │ └── post.json │ │ │ ├── proxy-hosts │ │ │ │ ├── get.json │ │ │ │ ├── hostID │ │ │ │ │ ├── delete.json │ │ │ │ │ ├── disable │ │ │ │ │ │ └── post.json │ │ │ │ │ ├── enable │ │ │ │ │ │ └── post.json │ │ │ │ │ ├── get.json │ │ │ │ │ └── put.json │ │ │ │ └── post.json │ │ │ ├── redirection-hosts │ │ │ │ ├── get.json │ │ │ │ ├── hostID │ │ │ │ │ ├── delete.json │ │ │ │ │ ├── disable │ │ │ │ │ │ └── post.json │ │ │ │ │ ├── enable │ │ │ │ │ │ └── post.json │ │ │ │ │ ├── get.json │ │ │ │ │ └── put.json │ │ │ │ └── post.json │ │ │ └── streams │ │ │ │ ├── get.json │ │ │ │ ├── post.json │ │ │ │ └── streamID │ │ │ │ ├── delete.json │ │ │ │ ├── disable │ │ │ │ └── post.json │ │ │ │ ├── enable │ │ │ │ └── post.json │ │ │ │ ├── get.json │ │ │ │ └── put.json │ │ ├── reports │ │ │ └── hosts │ │ │ │ └── get.json │ │ ├── schema │ │ │ └── get.json │ │ ├── settings │ │ │ ├── get.json │ │ │ └── settingID │ │ │ │ ├── get.json │ │ │ │ └── put.json │ │ ├── tokens │ │ │ ├── get.json │ │ │ └── post.json │ │ └── users │ │ │ ├── get.json │ │ │ ├── post.json │ │ │ └── userID │ │ │ ├── auth │ │ │ └── put.json │ │ │ ├── delete.json │ │ │ ├── get.json │ │ │ ├── login │ │ │ └── post.json │ │ │ ├── permissions │ │ │ └── put.json │ │ │ └── put.json │ └── swagger.json ├── scripts │ └── install-certbot-plugins ├── setup.js ├── templates │ ├── _access.conf │ ├── _assets.conf │ ├── _certificates.conf │ ├── _certificates_stream.conf │ ├── _exploits.conf │ ├── _forced_ssl.conf │ ├── _header_comment.conf │ ├── _hsts.conf │ ├── _hsts_map.conf │ ├── _listen.conf │ ├── _location.conf │ ├── dead_host.conf │ ├── default.conf │ ├── ip_ranges.conf │ ├── letsencrypt-request.conf │ ├── proxy_host.conf │ ├── redirection_host.conf │ └── stream.conf ├── validate-schema.js └── yarn.lock ├── docker ├── .dive-ci ├── Dockerfile ├── Dockerfile-zh ├── ci.env ├── ci │ └── postgres │ │ └── authentik.sql.gz ├── dev │ ├── Dockerfile │ ├── dnsrouter-config.json │ ├── letsencrypt.ini │ ├── pdns-db.sql │ ├── pebble-config.json │ └── squid.conf ├── docker-compose.ci.mysql.yml ├── docker-compose.ci.postgres.yml ├── docker-compose.ci.sqlite.yml ├── docker-compose.ci.yml ├── docker-compose.dev.yml ├── rootfs │ ├── etc │ │ ├── letsencrypt.ini │ │ ├── logrotate.d │ │ │ └── nginx-proxy-manager │ │ ├── nginx │ │ │ ├── conf.d │ │ │ │ ├── default.conf │ │ │ │ ├── dev.conf │ │ │ │ ├── include │ │ │ │ │ ├── .gitignore │ │ │ │ │ ├── assets.conf │ │ │ │ │ ├── block-exploits.conf │ │ │ │ │ ├── force-ssl.conf │ │ │ │ │ ├── ip_ranges.conf │ │ │ │ │ ├── letsencrypt-acme-challenge.conf │ │ │ │ │ ├── log.conf │ │ │ │ │ ├── proxy.conf │ │ │ │ │ ├── ssl-cache-stream.conf │ │ │ │ │ ├── ssl-cache.conf │ │ │ │ │ └── ssl-ciphers.conf │ │ │ │ └── production.conf │ │ │ ├── mime.types │ │ │ └── nginx.conf │ │ └── s6-overlay │ │ │ └── s6-rc.d │ │ │ ├── backend │ │ │ ├── dependencies.d │ │ │ │ └── prepare │ │ │ ├── run │ │ │ └── type │ │ │ ├── frontend │ │ │ ├── dependencies.d │ │ │ │ └── prepare │ │ │ ├── run │ │ │ └── type │ │ │ ├── nginx │ │ │ ├── dependencies.d │ │ │ │ └── prepare │ │ │ ├── run │ │ │ └── type │ │ │ ├── prepare │ │ │ ├── 00-all.sh │ │ │ ├── 10-usergroup.sh │ │ │ ├── 20-paths.sh │ │ │ ├── 30-ownership.sh │ │ │ ├── 40-dynamic.sh │ │ │ ├── 50-ipv6.sh │ │ │ ├── 60-secrets.sh │ │ │ ├── 90-banner.sh │ │ │ ├── dependencies.d │ │ │ │ └── base │ │ │ ├── type │ │ │ └── up │ │ │ └── user │ │ │ └── contents.d │ │ │ ├── backend │ │ │ ├── frontend │ │ │ ├── nginx │ │ │ └── prepare │ ├── root │ │ └── .bashrc │ ├── usr │ │ └── bin │ │ │ ├── check-health │ │ │ └── common.sh │ └── var │ │ └── www │ │ └── html │ │ └── index.html └── scripts │ └── install-s6 ├── docs ├── .gitignore ├── .vitepress │ ├── config.mts │ └── theme │ │ ├── custom.css │ │ └── index.ts ├── package.json ├── src │ ├── advanced-config │ │ └── index.md │ ├── faq │ │ └── index.md │ ├── guide │ │ └── index.md │ ├── index.md │ ├── public │ │ ├── github.png │ │ ├── icon.png │ │ ├── logo.svg │ │ ├── robots.txt │ │ └── screenshots │ │ │ ├── access-lists.png │ │ │ ├── audit-log.png │ │ │ ├── certificates.png │ │ │ ├── custom-settings.png │ │ │ ├── dashboard.png │ │ │ ├── dead-hosts.png │ │ │ ├── login.png │ │ │ ├── permissions.png │ │ │ ├── proxy-hosts-add.png │ │ │ ├── proxy-hosts.png │ │ │ └── redirection-hosts.png │ ├── screenshots │ │ └── index.md │ ├── setup │ │ └── index.md │ ├── third-party │ │ └── index.md │ └── upgrading │ │ └── index.md └── yarn.lock ├── frontend ├── .babelrc ├── .gitignore ├── app-images │ ├── default-avatar.jpg │ ├── favicons │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-512x512.png │ │ ├── apple-touch-icon.png │ │ ├── browserconfig.xml │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── favicon.ico │ │ ├── mstile-150x150.png │ │ ├── safari-pinned-tab.svg │ │ └── site.webmanifest │ ├── logo-256.png │ └── logo-text-vertical-grey.png ├── fonts │ ├── feather │ └── source-sans-pro │ │ ├── source-sans-pro-v14-latin-ext_latin-700.woff │ │ ├── source-sans-pro-v14-latin-ext_latin-700.woff2 │ │ ├── source-sans-pro-v14-latin-ext_latin-700italic.woff │ │ ├── source-sans-pro-v14-latin-ext_latin-700italic.woff2 │ │ ├── source-sans-pro-v14-latin-ext_latin-italic.woff │ │ ├── source-sans-pro-v14-latin-ext_latin-italic.woff2 │ │ ├── source-sans-pro-v14-latin-ext_latin-regular.woff │ │ └── source-sans-pro-v14-latin-ext_latin-regular.woff2 ├── html │ ├── index.ejs │ ├── login.ejs │ └── partials │ │ ├── footer.ejs │ │ └── header.ejs ├── images ├── js │ ├── app │ │ ├── api.js │ │ ├── audit-log │ │ │ ├── list │ │ │ │ ├── item.ejs │ │ │ │ ├── item.js │ │ │ │ ├── main.ejs │ │ │ │ └── main.js │ │ │ ├── main.ejs │ │ │ ├── main.js │ │ │ ├── meta.ejs │ │ │ └── meta.js │ │ ├── cache.js │ │ ├── controller.js │ │ ├── dashboard │ │ │ ├── main.ejs │ │ │ └── main.js │ │ ├── empty │ │ │ ├── main.ejs │ │ │ └── main.js │ │ ├── error │ │ │ ├── main.ejs │ │ │ └── main.js │ │ ├── help │ │ │ ├── main.ejs │ │ │ └── main.js │ │ ├── i18n.js │ │ ├── main.js │ │ ├── nginx │ │ │ ├── access │ │ │ │ ├── delete.ejs │ │ │ │ ├── delete.js │ │ │ │ ├── form.ejs │ │ │ │ ├── form.js │ │ │ │ ├── form │ │ │ │ │ ├── client.ejs │ │ │ │ │ ├── client.js │ │ │ │ │ ├── item.ejs │ │ │ │ │ └── item.js │ │ │ │ ├── list │ │ │ │ │ ├── item.ejs │ │ │ │ │ ├── item.js │ │ │ │ │ ├── main.ejs │ │ │ │ │ └── main.js │ │ │ │ ├── main.ejs │ │ │ │ └── main.js │ │ │ ├── certificates-list-item.ejs │ │ │ ├── certificates │ │ │ │ ├── delete.ejs │ │ │ │ ├── delete.js │ │ │ │ ├── form.ejs │ │ │ │ ├── form.js │ │ │ │ ├── list │ │ │ │ │ ├── item.ejs │ │ │ │ │ ├── item.js │ │ │ │ │ ├── main.ejs │ │ │ │ │ └── main.js │ │ │ │ ├── main.ejs │ │ │ │ ├── main.js │ │ │ │ ├── renew.ejs │ │ │ │ ├── renew.js │ │ │ │ ├── test.ejs │ │ │ │ └── test.js │ │ │ ├── dead │ │ │ │ ├── delete.ejs │ │ │ │ ├── delete.js │ │ │ │ ├── form.ejs │ │ │ │ ├── form.js │ │ │ │ ├── list │ │ │ │ │ ├── item.ejs │ │ │ │ │ ├── item.js │ │ │ │ │ ├── main.ejs │ │ │ │ │ └── main.js │ │ │ │ ├── main.ejs │ │ │ │ └── main.js │ │ │ ├── proxy │ │ │ │ ├── access-list-item.ejs │ │ │ │ ├── delete.ejs │ │ │ │ ├── delete.js │ │ │ │ ├── form.ejs │ │ │ │ ├── form.js │ │ │ │ ├── list │ │ │ │ │ ├── item.ejs │ │ │ │ │ ├── item.js │ │ │ │ │ ├── main.ejs │ │ │ │ │ └── main.js │ │ │ │ ├── location-item.ejs │ │ │ │ ├── location.js │ │ │ │ ├── main.ejs │ │ │ │ └── main.js │ │ │ ├── redirection │ │ │ │ ├── delete.ejs │ │ │ │ ├── delete.js │ │ │ │ ├── form.ejs │ │ │ │ ├── form.js │ │ │ │ ├── list │ │ │ │ │ ├── item.ejs │ │ │ │ │ ├── item.js │ │ │ │ │ ├── main.ejs │ │ │ │ │ └── main.js │ │ │ │ ├── main.ejs │ │ │ │ └── main.js │ │ │ └── stream │ │ │ │ ├── delete.ejs │ │ │ │ ├── delete.js │ │ │ │ ├── form.ejs │ │ │ │ ├── form.js │ │ │ │ ├── list │ │ │ │ ├── item.ejs │ │ │ │ ├── item.js │ │ │ │ ├── main.ejs │ │ │ │ └── main.js │ │ │ │ ├── main.ejs │ │ │ │ └── main.js │ │ ├── router.js │ │ ├── settings │ │ │ ├── default-site │ │ │ │ ├── main.ejs │ │ │ │ └── main.js │ │ │ ├── list │ │ │ │ ├── item.ejs │ │ │ │ ├── item.js │ │ │ │ ├── main.ejs │ │ │ │ └── main.js │ │ │ ├── main.ejs │ │ │ └── main.js │ │ ├── tokens.js │ │ ├── ui │ │ │ ├── footer │ │ │ │ ├── main.ejs │ │ │ │ └── main.js │ │ │ ├── header │ │ │ │ ├── main.ejs │ │ │ │ └── main.js │ │ │ ├── main.ejs │ │ │ ├── main.js │ │ │ └── menu │ │ │ │ ├── main.ejs │ │ │ │ └── main.js │ │ ├── user │ │ │ ├── delete.ejs │ │ │ ├── delete.js │ │ │ ├── form.ejs │ │ │ ├── form.js │ │ │ ├── password.ejs │ │ │ ├── password.js │ │ │ ├── permissions.ejs │ │ │ └── permissions.js │ │ └── users │ │ │ ├── list │ │ │ ├── item.ejs │ │ │ ├── item.js │ │ │ ├── main.ejs │ │ │ └── main.js │ │ │ ├── main.ejs │ │ │ └── main.js │ ├── i18n │ │ └── messages.json │ ├── index.js │ ├── lib │ │ ├── helpers.js │ │ └── marionette.js │ ├── login.js │ ├── login │ │ ├── main.js │ │ └── ui │ │ │ ├── login.ejs │ │ │ └── login.js │ └── models │ │ ├── access-list.js │ │ ├── audit-log.js │ │ ├── certificate.js │ │ ├── dead-host.js │ │ ├── proxy-host-location.js │ │ ├── proxy-host.js │ │ ├── redirection-host.js │ │ ├── setting.js │ │ ├── stream.js │ │ └── user.js ├── package.json ├── scss │ ├── custom.scss │ ├── fonts.scss │ ├── selectize.scss │ ├── styles.scss │ └── tabler-extra.scss ├── webpack.config.js └── yarn.lock ├── global ├── README.md └── certbot-dns-plugins.json ├── scripts ├── .common.sh ├── build-zh ├── buildx ├── buildx-zh ├── ci │ ├── frontend-build │ ├── fulltest-cypress │ └── test-and-build ├── cypress-dev ├── destroy-dev ├── docs-build ├── docs-upload ├── start-dev ├── stop-dev └── wait-healthy └── test ├── .eslintrc.json ├── .gitignore ├── .prettierrc ├── README.md ├── cypress ├── Dockerfile ├── config │ └── ci.js ├── e2e │ └── api │ │ ├── Certificates.cy.js │ │ ├── FullCertProvision.cy.js │ │ ├── Health.cy.js │ │ ├── Ldap.cy.js │ │ ├── OAuth.cy.js │ │ ├── ProxyHosts.cy.js │ │ ├── Settings.cy.js │ │ ├── Streams.cy.js │ │ └── Users.cy.js ├── fixtures │ ├── test.example.com-key.pem │ └── test.example.com.pem ├── plugins │ ├── backendApi │ │ ├── client.js │ │ ├── logger.js │ │ └── task.js │ └── index.js └── support │ ├── commands.js │ └── e2e.js ├── jsconfig.json ├── multi-reporter.json ├── package.json └── yarn.lock /.github/ISSUE_TEMPLATE/dns_challenge_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: DNS challenge provider request 3 | about: Suggest a new provider to be available for a certificate DNS challenge 4 | title: '' 5 | labels: dns provider request 6 | assignees: '' 7 | 8 | --- 9 | 10 | **What provider would you like to see added to NPM?** 11 | 12 | 13 | 14 | **Have you checked if a certbot plugin exists?** 15 | 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | 18 | 19 | **Is your feature request related to a problem? Please describe.** 20 | 21 | 22 | 23 | **Describe the solution you'd like** 24 | 25 | 26 | 27 | **Describe alternatives you've considered** 28 | 29 | 30 | 31 | **Additional context** 32 | 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea 3 | ._* 4 | .vscode 5 | certbot-help.txt 6 | test/node_modules 7 | */node_modules 8 | docker/dev/dnsrouter-config.json.tmp 9 | docker/dev/resolv.conf 10 | -------------------------------------------------------------------------------- /.version: -------------------------------------------------------------------------------- 1 | 2.12.3 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /backend/.gitignore: -------------------------------------------------------------------------------- 1 | config/development.json 2 | data/* 3 | yarn-error.log 4 | tmp 5 | certbot.log 6 | node_modules 7 | core.* 8 | 9 | -------------------------------------------------------------------------------- /backend/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 320, 3 | "tabWidth": 4, 4 | "useTabs": true, 5 | "semi": true, 6 | "singleQuote": true, 7 | "bracketSpacing": true, 8 | "jsxBracketSameLine": true, 9 | "trailingComma": "all", 10 | "proseWrap": "always" 11 | } 12 | -------------------------------------------------------------------------------- /backend/config/README.md: -------------------------------------------------------------------------------- 1 | These files are use in development and are not deployed as part of the final product. 2 | -------------------------------------------------------------------------------- /backend/config/default.json: -------------------------------------------------------------------------------- 1 | { 2 | "database": { 3 | "engine": "mysql2", 4 | "host": "db", 5 | "name": "npm", 6 | "user": "npm", 7 | "password": "npm", 8 | "port": 3306 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /backend/config/sqlite-test-db.json: -------------------------------------------------------------------------------- 1 | { 2 | "database": { 3 | "engine": "knex-native", 4 | "knex": { 5 | "client": "sqlite3", 6 | "connection": { 7 | "filename": "/app/config/mydb.sqlite" 8 | }, 9 | "pool": { 10 | "min": 0, 11 | "max": 1, 12 | "createTimeoutMillis": 3000, 13 | "acquireTimeoutMillis": 30000, 14 | "idleTimeoutMillis": 30000, 15 | "reapIntervalMillis": 1000, 16 | "createRetryIntervalMillis": 100, 17 | "propagateCreateError": false 18 | }, 19 | "migrations": { 20 | "tableName": "migrations", 21 | "stub": "src/backend/lib/migrate_template.js", 22 | "directory": "src/backend/migrations" 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /backend/db.js: -------------------------------------------------------------------------------- 1 | const config = require('./lib/config'); 2 | 3 | if (!config.has('database')) { 4 | throw new Error('Database config does not exist! Please read the instructions: https://nginxproxymanager.com/setup/'); 5 | } 6 | 7 | function generateDbConfig() { 8 | const cfg = config.get('database'); 9 | if (cfg.engine === 'knex-native') { 10 | return cfg.knex; 11 | } 12 | return { 13 | client: cfg.engine, 14 | connection: { 15 | host: cfg.host, 16 | user: cfg.user, 17 | password: cfg.password, 18 | database: cfg.name, 19 | port: cfg.port 20 | }, 21 | migrations: { 22 | tableName: 'migrations' 23 | } 24 | }; 25 | } 26 | 27 | module.exports = require('knex')(generateDbConfig()); 28 | -------------------------------------------------------------------------------- /backend/internal/report.js: -------------------------------------------------------------------------------- 1 | const internalProxyHost = require('./proxy-host'); 2 | const internalRedirectionHost = require('./redirection-host'); 3 | const internalDeadHost = require('./dead-host'); 4 | const internalStream = require('./stream'); 5 | 6 | const internalReport = { 7 | 8 | /** 9 | * @param {Access} access 10 | * @return {Promise} 11 | */ 12 | getHostsReport: (access) => { 13 | return access.can('reports:hosts', 1) 14 | .then((access_data) => { 15 | let user_id = access.token.getUserId(1); 16 | 17 | let promises = [ 18 | internalProxyHost.getCount(user_id, access_data.visibility), 19 | internalRedirectionHost.getCount(user_id, access_data.visibility), 20 | internalStream.getCount(user_id, access_data.visibility), 21 | internalDeadHost.getCount(user_id, access_data.visibility) 22 | ]; 23 | 24 | return Promise.all(promises); 25 | }) 26 | .then((counts) => { 27 | return { 28 | proxy: counts.shift(), 29 | redirection: counts.shift(), 30 | stream: counts.shift(), 31 | dead: counts.shift() 32 | }; 33 | }); 34 | 35 | } 36 | }; 37 | 38 | module.exports = internalReport; 39 | -------------------------------------------------------------------------------- /backend/knexfile.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | development: { 3 | client: 'mysql2', 4 | migrations: { 5 | tableName: 'migrations', 6 | stub: 'lib/migrate_template.js', 7 | directory: 'migrations' 8 | } 9 | }, 10 | 11 | production: { 12 | client: 'mysql2', 13 | migrations: { 14 | tableName: 'migrations', 15 | stub: 'lib/migrate_template.js', 16 | directory: 'migrations' 17 | } 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /backend/lib/access/access_lists-create.json: -------------------------------------------------------------------------------- 1 | { 2 | "anyOf": [ 3 | { 4 | "$ref": "roles#/definitions/admin" 5 | }, 6 | { 7 | "type": "object", 8 | "required": ["permission_access_lists", "roles"], 9 | "properties": { 10 | "permission_access_lists": { 11 | "$ref": "perms#/definitions/manage" 12 | }, 13 | "roles": { 14 | "type": "array", 15 | "items": { 16 | "type": "string", 17 | "enum": ["user"] 18 | } 19 | } 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /backend/lib/access/access_lists-delete.json: -------------------------------------------------------------------------------- 1 | { 2 | "anyOf": [ 3 | { 4 | "$ref": "roles#/definitions/admin" 5 | }, 6 | { 7 | "type": "object", 8 | "required": ["permission_access_lists", "roles"], 9 | "properties": { 10 | "permission_access_lists": { 11 | "$ref": "perms#/definitions/manage" 12 | }, 13 | "roles": { 14 | "type": "array", 15 | "items": { 16 | "type": "string", 17 | "enum": ["user"] 18 | } 19 | } 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /backend/lib/access/access_lists-get.json: -------------------------------------------------------------------------------- 1 | { 2 | "anyOf": [ 3 | { 4 | "$ref": "roles#/definitions/admin" 5 | }, 6 | { 7 | "type": "object", 8 | "required": ["permission_access_lists", "roles"], 9 | "properties": { 10 | "permission_access_lists": { 11 | "$ref": "perms#/definitions/view" 12 | }, 13 | "roles": { 14 | "type": "array", 15 | "items": { 16 | "type": "string", 17 | "enum": ["user"] 18 | } 19 | } 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /backend/lib/access/access_lists-list.json: -------------------------------------------------------------------------------- 1 | { 2 | "anyOf": [ 3 | { 4 | "$ref": "roles#/definitions/admin" 5 | }, 6 | { 7 | "type": "object", 8 | "required": ["permission_access_lists", "roles"], 9 | "properties": { 10 | "permission_access_lists": { 11 | "$ref": "perms#/definitions/view" 12 | }, 13 | "roles": { 14 | "type": "array", 15 | "items": { 16 | "type": "string", 17 | "enum": ["user"] 18 | } 19 | } 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /backend/lib/access/access_lists-update.json: -------------------------------------------------------------------------------- 1 | { 2 | "anyOf": [ 3 | { 4 | "$ref": "roles#/definitions/admin" 5 | }, 6 | { 7 | "type": "object", 8 | "required": ["permission_access_lists", "roles"], 9 | "properties": { 10 | "permission_access_lists": { 11 | "$ref": "perms#/definitions/manage" 12 | }, 13 | "roles": { 14 | "type": "array", 15 | "items": { 16 | "type": "string", 17 | "enum": ["user"] 18 | } 19 | } 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /backend/lib/access/auditlog-list.json: -------------------------------------------------------------------------------- 1 | { 2 | "anyOf": [ 3 | { 4 | "$ref": "roles#/definitions/admin" 5 | } 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /backend/lib/access/certificates-create.json: -------------------------------------------------------------------------------- 1 | { 2 | "anyOf": [ 3 | { 4 | "$ref": "roles#/definitions/admin" 5 | }, 6 | { 7 | "type": "object", 8 | "required": ["permission_certificates", "roles"], 9 | "properties": { 10 | "permission_certificates": { 11 | "$ref": "perms#/definitions/manage" 12 | }, 13 | "roles": { 14 | "type": "array", 15 | "items": { 16 | "type": "string", 17 | "enum": ["user"] 18 | } 19 | } 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /backend/lib/access/certificates-delete.json: -------------------------------------------------------------------------------- 1 | { 2 | "anyOf": [ 3 | { 4 | "$ref": "roles#/definitions/admin" 5 | }, 6 | { 7 | "type": "object", 8 | "required": ["permission_certificates", "roles"], 9 | "properties": { 10 | "permission_certificates": { 11 | "$ref": "perms#/definitions/manage" 12 | }, 13 | "roles": { 14 | "type": "array", 15 | "items": { 16 | "type": "string", 17 | "enum": ["user"] 18 | } 19 | } 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /backend/lib/access/certificates-get.json: -------------------------------------------------------------------------------- 1 | { 2 | "anyOf": [ 3 | { 4 | "$ref": "roles#/definitions/admin" 5 | }, 6 | { 7 | "type": "object", 8 | "required": ["permission_certificates", "roles"], 9 | "properties": { 10 | "permission_certificates": { 11 | "$ref": "perms#/definitions/view" 12 | }, 13 | "roles": { 14 | "type": "array", 15 | "items": { 16 | "type": "string", 17 | "enum": ["user"] 18 | } 19 | } 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /backend/lib/access/certificates-list.json: -------------------------------------------------------------------------------- 1 | { 2 | "anyOf": [ 3 | { 4 | "$ref": "roles#/definitions/admin" 5 | }, 6 | { 7 | "type": "object", 8 | "required": ["permission_certificates", "roles"], 9 | "properties": { 10 | "permission_certificates": { 11 | "$ref": "perms#/definitions/view" 12 | }, 13 | "roles": { 14 | "type": "array", 15 | "items": { 16 | "type": "string", 17 | "enum": ["user"] 18 | } 19 | } 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /backend/lib/access/certificates-update.json: -------------------------------------------------------------------------------- 1 | { 2 | "anyOf": [ 3 | { 4 | "$ref": "roles#/definitions/admin" 5 | }, 6 | { 7 | "type": "object", 8 | "required": ["permission_certificates", "roles"], 9 | "properties": { 10 | "permission_certificates": { 11 | "$ref": "perms#/definitions/manage" 12 | }, 13 | "roles": { 14 | "type": "array", 15 | "items": { 16 | "type": "string", 17 | "enum": ["user"] 18 | } 19 | } 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /backend/lib/access/dead_hosts-create.json: -------------------------------------------------------------------------------- 1 | { 2 | "anyOf": [ 3 | { 4 | "$ref": "roles#/definitions/admin" 5 | }, 6 | { 7 | "type": "object", 8 | "required": ["permission_dead_hosts", "roles"], 9 | "properties": { 10 | "permission_dead_hosts": { 11 | "$ref": "perms#/definitions/manage" 12 | }, 13 | "roles": { 14 | "type": "array", 15 | "items": { 16 | "type": "string", 17 | "enum": ["user"] 18 | } 19 | } 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /backend/lib/access/dead_hosts-delete.json: -------------------------------------------------------------------------------- 1 | { 2 | "anyOf": [ 3 | { 4 | "$ref": "roles#/definitions/admin" 5 | }, 6 | { 7 | "type": "object", 8 | "required": ["permission_dead_hosts", "roles"], 9 | "properties": { 10 | "permission_dead_hosts": { 11 | "$ref": "perms#/definitions/manage" 12 | }, 13 | "roles": { 14 | "type": "array", 15 | "items": { 16 | "type": "string", 17 | "enum": ["user"] 18 | } 19 | } 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /backend/lib/access/dead_hosts-get.json: -------------------------------------------------------------------------------- 1 | { 2 | "anyOf": [ 3 | { 4 | "$ref": "roles#/definitions/admin" 5 | }, 6 | { 7 | "type": "object", 8 | "required": ["permission_dead_hosts", "roles"], 9 | "properties": { 10 | "permission_dead_hosts": { 11 | "$ref": "perms#/definitions/view" 12 | }, 13 | "roles": { 14 | "type": "array", 15 | "items": { 16 | "type": "string", 17 | "enum": ["user"] 18 | } 19 | } 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /backend/lib/access/dead_hosts-list.json: -------------------------------------------------------------------------------- 1 | { 2 | "anyOf": [ 3 | { 4 | "$ref": "roles#/definitions/admin" 5 | }, 6 | { 7 | "type": "object", 8 | "required": ["permission_dead_hosts", "roles"], 9 | "properties": { 10 | "permission_dead_hosts": { 11 | "$ref": "perms#/definitions/view" 12 | }, 13 | "roles": { 14 | "type": "array", 15 | "items": { 16 | "type": "string", 17 | "enum": ["user"] 18 | } 19 | } 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /backend/lib/access/dead_hosts-update.json: -------------------------------------------------------------------------------- 1 | { 2 | "anyOf": [ 3 | { 4 | "$ref": "roles#/definitions/admin" 5 | }, 6 | { 7 | "type": "object", 8 | "required": ["permission_dead_hosts", "roles"], 9 | "properties": { 10 | "permission_dead_hosts": { 11 | "$ref": "perms#/definitions/manage" 12 | }, 13 | "roles": { 14 | "type": "array", 15 | "items": { 16 | "type": "string", 17 | "enum": ["user"] 18 | } 19 | } 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /backend/lib/access/permissions.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "perms", 3 | "definitions": { 4 | "view": { 5 | "type": "string", 6 | "pattern": "^(view|manage)$" 7 | }, 8 | "manage": { 9 | "type": "string", 10 | "pattern": "^(manage)$" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /backend/lib/access/proxy_hosts-create.json: -------------------------------------------------------------------------------- 1 | { 2 | "anyOf": [ 3 | { 4 | "$ref": "roles#/definitions/admin" 5 | }, 6 | { 7 | "type": "object", 8 | "required": ["permission_proxy_hosts", "roles"], 9 | "properties": { 10 | "permission_proxy_hosts": { 11 | "$ref": "perms#/definitions/manage" 12 | }, 13 | "roles": { 14 | "type": "array", 15 | "items": { 16 | "type": "string", 17 | "enum": ["user"] 18 | } 19 | } 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /backend/lib/access/proxy_hosts-delete.json: -------------------------------------------------------------------------------- 1 | { 2 | "anyOf": [ 3 | { 4 | "$ref": "roles#/definitions/admin" 5 | }, 6 | { 7 | "type": "object", 8 | "required": ["permission_proxy_hosts", "roles"], 9 | "properties": { 10 | "permission_proxy_hosts": { 11 | "$ref": "perms#/definitions/manage" 12 | }, 13 | "roles": { 14 | "type": "array", 15 | "items": { 16 | "type": "string", 17 | "enum": ["user"] 18 | } 19 | } 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /backend/lib/access/proxy_hosts-get.json: -------------------------------------------------------------------------------- 1 | { 2 | "anyOf": [ 3 | { 4 | "$ref": "roles#/definitions/admin" 5 | }, 6 | { 7 | "type": "object", 8 | "required": ["permission_proxy_hosts", "roles"], 9 | "properties": { 10 | "permission_proxy_hosts": { 11 | "$ref": "perms#/definitions/view" 12 | }, 13 | "roles": { 14 | "type": "array", 15 | "items": { 16 | "type": "string", 17 | "enum": ["user"] 18 | } 19 | } 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /backend/lib/access/proxy_hosts-list.json: -------------------------------------------------------------------------------- 1 | { 2 | "anyOf": [ 3 | { 4 | "$ref": "roles#/definitions/admin" 5 | }, 6 | { 7 | "type": "object", 8 | "required": ["permission_proxy_hosts", "roles"], 9 | "properties": { 10 | "permission_proxy_hosts": { 11 | "$ref": "perms#/definitions/view" 12 | }, 13 | "roles": { 14 | "type": "array", 15 | "items": { 16 | "type": "string", 17 | "enum": ["user"] 18 | } 19 | } 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /backend/lib/access/proxy_hosts-update.json: -------------------------------------------------------------------------------- 1 | { 2 | "anyOf": [ 3 | { 4 | "$ref": "roles#/definitions/admin" 5 | }, 6 | { 7 | "type": "object", 8 | "required": ["permission_proxy_hosts", "roles"], 9 | "properties": { 10 | "permission_proxy_hosts": { 11 | "$ref": "perms#/definitions/manage" 12 | }, 13 | "roles": { 14 | "type": "array", 15 | "items": { 16 | "type": "string", 17 | "enum": ["user"] 18 | } 19 | } 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /backend/lib/access/redirection_hosts-create.json: -------------------------------------------------------------------------------- 1 | { 2 | "anyOf": [ 3 | { 4 | "$ref": "roles#/definitions/admin" 5 | }, 6 | { 7 | "type": "object", 8 | "required": ["permission_redirection_hosts", "roles"], 9 | "properties": { 10 | "permission_redirection_hosts": { 11 | "$ref": "perms#/definitions/manage" 12 | }, 13 | "roles": { 14 | "type": "array", 15 | "items": { 16 | "type": "string", 17 | "enum": ["user"] 18 | } 19 | } 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /backend/lib/access/redirection_hosts-delete.json: -------------------------------------------------------------------------------- 1 | { 2 | "anyOf": [ 3 | { 4 | "$ref": "roles#/definitions/admin" 5 | }, 6 | { 7 | "type": "object", 8 | "required": ["permission_redirection_hosts", "roles"], 9 | "properties": { 10 | "permission_redirection_hosts": { 11 | "$ref": "perms#/definitions/manage" 12 | }, 13 | "roles": { 14 | "type": "array", 15 | "items": { 16 | "type": "string", 17 | "enum": ["user"] 18 | } 19 | } 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /backend/lib/access/redirection_hosts-get.json: -------------------------------------------------------------------------------- 1 | { 2 | "anyOf": [ 3 | { 4 | "$ref": "roles#/definitions/admin" 5 | }, 6 | { 7 | "type": "object", 8 | "required": ["permission_redirection_hosts", "roles"], 9 | "properties": { 10 | "permission_redirection_hosts": { 11 | "$ref": "perms#/definitions/view" 12 | }, 13 | "roles": { 14 | "type": "array", 15 | "items": { 16 | "type": "string", 17 | "enum": ["user"] 18 | } 19 | } 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /backend/lib/access/redirection_hosts-list.json: -------------------------------------------------------------------------------- 1 | { 2 | "anyOf": [ 3 | { 4 | "$ref": "roles#/definitions/admin" 5 | }, 6 | { 7 | "type": "object", 8 | "required": ["permission_redirection_hosts", "roles"], 9 | "properties": { 10 | "permission_redirection_hosts": { 11 | "$ref": "perms#/definitions/view" 12 | }, 13 | "roles": { 14 | "type": "array", 15 | "items": { 16 | "type": "string", 17 | "enum": ["user"] 18 | } 19 | } 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /backend/lib/access/redirection_hosts-update.json: -------------------------------------------------------------------------------- 1 | { 2 | "anyOf": [ 3 | { 4 | "$ref": "roles#/definitions/admin" 5 | }, 6 | { 7 | "type": "object", 8 | "required": ["permission_redirection_hosts", "roles"], 9 | "properties": { 10 | "permission_redirection_hosts": { 11 | "$ref": "perms#/definitions/manage" 12 | }, 13 | "roles": { 14 | "type": "array", 15 | "items": { 16 | "type": "string", 17 | "enum": ["user"] 18 | } 19 | } 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /backend/lib/access/reports-hosts.json: -------------------------------------------------------------------------------- 1 | { 2 | "anyOf": [ 3 | { 4 | "$ref": "roles#/definitions/user" 5 | } 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /backend/lib/access/roles.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "roles", 3 | "definitions": { 4 | "admin": { 5 | "type": "object", 6 | "required": ["scope", "roles"], 7 | "properties": { 8 | "scope": { 9 | "type": "array", 10 | "contains": { 11 | "type": "string", 12 | "pattern": "^user$" 13 | } 14 | }, 15 | "roles": { 16 | "type": "array", 17 | "contains": { 18 | "type": "string", 19 | "pattern": "^admin$" 20 | } 21 | } 22 | } 23 | }, 24 | "user": { 25 | "type": "object", 26 | "required": ["scope"], 27 | "properties": { 28 | "scope": { 29 | "type": "array", 30 | "contains": { 31 | "type": "string", 32 | "pattern": "^user$" 33 | } 34 | } 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /backend/lib/access/settings-get.json: -------------------------------------------------------------------------------- 1 | { 2 | "anyOf": [ 3 | { 4 | "$ref": "roles#/definitions/admin" 5 | } 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /backend/lib/access/settings-list.json: -------------------------------------------------------------------------------- 1 | { 2 | "anyOf": [ 3 | { 4 | "$ref": "roles#/definitions/admin" 5 | } 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /backend/lib/access/settings-update.json: -------------------------------------------------------------------------------- 1 | { 2 | "anyOf": [ 3 | { 4 | "$ref": "roles#/definitions/admin" 5 | } 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /backend/lib/access/streams-create.json: -------------------------------------------------------------------------------- 1 | { 2 | "anyOf": [ 3 | { 4 | "$ref": "roles#/definitions/admin" 5 | }, 6 | { 7 | "type": "object", 8 | "required": ["permission_streams", "roles"], 9 | "properties": { 10 | "permission_streams": { 11 | "$ref": "perms#/definitions/manage" 12 | }, 13 | "roles": { 14 | "type": "array", 15 | "items": { 16 | "type": "string", 17 | "enum": ["user"] 18 | } 19 | } 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /backend/lib/access/streams-delete.json: -------------------------------------------------------------------------------- 1 | { 2 | "anyOf": [ 3 | { 4 | "$ref": "roles#/definitions/admin" 5 | }, 6 | { 7 | "type": "object", 8 | "required": ["permission_streams", "roles"], 9 | "properties": { 10 | "permission_streams": { 11 | "$ref": "perms#/definitions/manage" 12 | }, 13 | "roles": { 14 | "type": "array", 15 | "items": { 16 | "type": "string", 17 | "enum": ["user"] 18 | } 19 | } 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /backend/lib/access/streams-get.json: -------------------------------------------------------------------------------- 1 | { 2 | "anyOf": [ 3 | { 4 | "$ref": "roles#/definitions/admin" 5 | }, 6 | { 7 | "type": "object", 8 | "required": ["permission_streams", "roles"], 9 | "properties": { 10 | "permission_streams": { 11 | "$ref": "perms#/definitions/view" 12 | }, 13 | "roles": { 14 | "type": "array", 15 | "items": { 16 | "type": "string", 17 | "enum": ["user"] 18 | } 19 | } 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /backend/lib/access/streams-list.json: -------------------------------------------------------------------------------- 1 | { 2 | "anyOf": [ 3 | { 4 | "$ref": "roles#/definitions/admin" 5 | }, 6 | { 7 | "type": "object", 8 | "required": ["permission_streams", "roles"], 9 | "properties": { 10 | "permission_streams": { 11 | "$ref": "perms#/definitions/view" 12 | }, 13 | "roles": { 14 | "type": "array", 15 | "items": { 16 | "type": "string", 17 | "enum": ["user"] 18 | } 19 | } 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /backend/lib/access/streams-update.json: -------------------------------------------------------------------------------- 1 | { 2 | "anyOf": [ 3 | { 4 | "$ref": "roles#/definitions/admin" 5 | }, 6 | { 7 | "type": "object", 8 | "required": ["permission_streams", "roles"], 9 | "properties": { 10 | "permission_streams": { 11 | "$ref": "perms#/definitions/manage" 12 | }, 13 | "roles": { 14 | "type": "array", 15 | "items": { 16 | "type": "string", 17 | "enum": ["user"] 18 | } 19 | } 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /backend/lib/access/users-create.json: -------------------------------------------------------------------------------- 1 | { 2 | "anyOf": [ 3 | { 4 | "$ref": "roles#/definitions/admin" 5 | } 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /backend/lib/access/users-delete.json: -------------------------------------------------------------------------------- 1 | { 2 | "anyOf": [ 3 | { 4 | "$ref": "roles#/definitions/admin" 5 | } 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /backend/lib/access/users-get.json: -------------------------------------------------------------------------------- 1 | { 2 | "anyOf": [ 3 | { 4 | "$ref": "roles#/definitions/admin" 5 | }, 6 | { 7 | "type": "object", 8 | "required": ["data", "scope"], 9 | "properties": { 10 | "data": { 11 | "$ref": "objects#/properties/users" 12 | }, 13 | "scope": { 14 | "type": "array", 15 | "contains": { 16 | "type": "string", 17 | "pattern": "^user$" 18 | } 19 | } 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /backend/lib/access/users-list.json: -------------------------------------------------------------------------------- 1 | { 2 | "anyOf": [ 3 | { 4 | "$ref": "roles#/definitions/admin" 5 | } 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /backend/lib/access/users-loginas.json: -------------------------------------------------------------------------------- 1 | { 2 | "anyOf": [ 3 | { 4 | "$ref": "roles#/definitions/admin" 5 | } 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /backend/lib/access/users-password.json: -------------------------------------------------------------------------------- 1 | { 2 | "anyOf": [ 3 | { 4 | "$ref": "roles#/definitions/admin" 5 | }, 6 | { 7 | "type": "object", 8 | "required": ["data", "scope"], 9 | "properties": { 10 | "data": { 11 | "$ref": "objects#/properties/users" 12 | }, 13 | "scope": { 14 | "type": "array", 15 | "contains": { 16 | "type": "string", 17 | "pattern": "^user$" 18 | } 19 | } 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /backend/lib/access/users-permissions.json: -------------------------------------------------------------------------------- 1 | { 2 | "anyOf": [ 3 | { 4 | "$ref": "roles#/definitions/admin" 5 | } 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /backend/lib/access/users-update.json: -------------------------------------------------------------------------------- 1 | { 2 | "anyOf": [ 3 | { 4 | "$ref": "roles#/definitions/admin" 5 | }, 6 | { 7 | "type": "object", 8 | "required": ["data", "scope"], 9 | "properties": { 10 | "data": { 11 | "$ref": "objects#/properties/users" 12 | }, 13 | "scope": { 14 | "type": "array", 15 | "contains": { 16 | "type": "string", 17 | "pattern": "^user$" 18 | } 19 | } 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /backend/lib/express/cors.js: -------------------------------------------------------------------------------- 1 | module.exports = function (req, res, next) { 2 | if (req.headers.origin) { 3 | res.set({ 4 | 'Access-Control-Allow-Origin': req.headers.origin, 5 | 'Access-Control-Allow-Credentials': true, 6 | 'Access-Control-Allow-Methods': 'OPTIONS, GET, POST', 7 | 'Access-Control-Allow-Headers': 'Content-Type, Cache-Control, Pragma, Expires, Authorization, X-Dataset-Total, X-Dataset-Offset, X-Dataset-Limit', 8 | 'Access-Control-Max-Age': 5 * 60, 9 | 'Access-Control-Expose-Headers': 'X-Dataset-Total, X-Dataset-Offset, X-Dataset-Limit' 10 | }); 11 | next(); 12 | } else { 13 | // No origin 14 | next(); 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /backend/lib/express/jwt-decode.js: -------------------------------------------------------------------------------- 1 | const Access = require('../access'); 2 | 3 | module.exports = () => { 4 | return function (req, res, next) { 5 | res.locals.access = null; 6 | let access = new Access(res.locals.token || null); 7 | access.load() 8 | .then(() => { 9 | res.locals.access = access; 10 | next(); 11 | }) 12 | .catch(next); 13 | }; 14 | }; 15 | 16 | -------------------------------------------------------------------------------- /backend/lib/express/jwt.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | return function (req, res, next) { 3 | if (req.headers.authorization) { 4 | let parts = req.headers.authorization.split(' '); 5 | 6 | if (parts && parts[0] === 'Bearer' && parts[1]) { 7 | res.locals.token = parts[1]; 8 | } 9 | } 10 | 11 | next(); 12 | }; 13 | }; 14 | -------------------------------------------------------------------------------- /backend/lib/express/user-id-from-me.js: -------------------------------------------------------------------------------- 1 | module.exports = (req, res, next) => { 2 | if (req.params.user_id === 'me' && res.locals.access) { 3 | req.params.user_id = res.locals.access.token.get('attrs').id; 4 | } else { 5 | req.params.user_id = parseInt(req.params.user_id, 10); 6 | } 7 | 8 | next(); 9 | }; 10 | -------------------------------------------------------------------------------- /backend/lib/validator/api.js: -------------------------------------------------------------------------------- 1 | const Ajv = require('ajv/dist/2020'); 2 | const error = require('../error'); 3 | 4 | const ajv = new Ajv({ 5 | verbose: true, 6 | allErrors: true, 7 | allowUnionTypes: true, 8 | strict: false, 9 | coerceTypes: true, 10 | }); 11 | 12 | /** 13 | * @param {Object} schema 14 | * @param {Object} payload 15 | * @returns {Promise} 16 | */ 17 | function apiValidator (schema, payload/*, description*/) { 18 | return new Promise(function Promise_apiValidator (resolve, reject) { 19 | if (schema === null) { 20 | reject(new error.ValidationError('Schema is undefined')); 21 | return; 22 | } 23 | 24 | if (typeof payload === 'undefined') { 25 | reject(new error.ValidationError('Payload is undefined')); 26 | return; 27 | } 28 | 29 | const validate = ajv.compile(schema); 30 | const valid = validate(payload); 31 | 32 | if (valid && !validate.errors) { 33 | resolve(payload); 34 | } else { 35 | let message = ajv.errorsText(validate.errors); 36 | let err = new error.ValidationError(message); 37 | err.debug = [validate.errors, payload]; 38 | reject(err); 39 | } 40 | }); 41 | } 42 | 43 | module.exports = apiValidator; 44 | -------------------------------------------------------------------------------- /backend/logger.js: -------------------------------------------------------------------------------- 1 | const {Signale} = require('signale'); 2 | 3 | module.exports = { 4 | global: new Signale({scope: 'Global '}), 5 | migrate: new Signale({scope: 'Migrate '}), 6 | express: new Signale({scope: 'Express '}), 7 | access: new Signale({scope: 'Access '}), 8 | nginx: new Signale({scope: 'Nginx '}), 9 | ssl: new Signale({scope: 'SSL '}), 10 | certbot: new Signale({scope: 'Certbot '}), 11 | import: new Signale({scope: 'Importer '}), 12 | setup: new Signale({scope: 'Setup '}), 13 | ip_ranges: new Signale({scope: 'IP Ranges'}) 14 | }; 15 | -------------------------------------------------------------------------------- /backend/migrate.js: -------------------------------------------------------------------------------- 1 | const db = require('./db'); 2 | const logger = require('./logger').migrate; 3 | 4 | module.exports = { 5 | latest: function () { 6 | return db.migrate.currentVersion() 7 | .then((version) => { 8 | logger.info('Current database version:', version); 9 | return db.migrate.latest({ 10 | tableName: 'migrations', 11 | directory: 'migrations' 12 | }); 13 | }); 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /backend/migrations/20180929054513_websockets.js: -------------------------------------------------------------------------------- 1 | const migrate_name = 'websockets'; 2 | const logger = require('../logger').migrate; 3 | 4 | /** 5 | * Migrate 6 | * 7 | * @see http://knexjs.org/#Schema 8 | * 9 | * @param {Object} knex 10 | * @param {Promise} Promise 11 | * @returns {Promise} 12 | */ 13 | exports.up = function (knex/*, Promise*/) { 14 | logger.info('[' + migrate_name + '] Migrating Up...'); 15 | 16 | return knex.schema.table('proxy_host', function (proxy_host) { 17 | proxy_host.integer('allow_websocket_upgrade').notNull().unsigned().defaultTo(0); 18 | }) 19 | .then(() => { 20 | logger.info('[' + migrate_name + '] proxy_host Table altered'); 21 | }); 22 | 23 | }; 24 | 25 | /** 26 | * Undo Migrate 27 | * 28 | * @param {Object} knex 29 | * @param {Promise} Promise 30 | * @returns {Promise} 31 | */ 32 | exports.down = function (knex, Promise) { 33 | logger.warn('[' + migrate_name + '] You can\'t migrate down this one.'); 34 | return Promise.resolve(true); 35 | }; -------------------------------------------------------------------------------- /backend/migrations/20181019052346_forward_host.js: -------------------------------------------------------------------------------- 1 | const migrate_name = 'forward_host'; 2 | const logger = require('../logger').migrate; 3 | 4 | /** 5 | * Migrate 6 | * 7 | * @see http://knexjs.org/#Schema 8 | * 9 | * @param {Object} knex 10 | * @param {Promise} Promise 11 | * @returns {Promise} 12 | */ 13 | exports.up = function (knex/*, Promise*/) { 14 | logger.info('[' + migrate_name + '] Migrating Up...'); 15 | 16 | return knex.schema.table('proxy_host', function (proxy_host) { 17 | proxy_host.renameColumn('forward_ip', 'forward_host'); 18 | }) 19 | .then(() => { 20 | logger.info('[' + migrate_name + '] proxy_host Table altered'); 21 | }); 22 | }; 23 | 24 | /** 25 | * Undo Migrate 26 | * 27 | * @param {Object} knex 28 | * @param {Promise} Promise 29 | * @returns {Promise} 30 | */ 31 | exports.down = function (knex, Promise) { 32 | logger.warn('[' + migrate_name + '] You can\'t migrate down this one.'); 33 | return Promise.resolve(true); 34 | }; -------------------------------------------------------------------------------- /backend/migrations/20181213013211_forward_scheme.js: -------------------------------------------------------------------------------- 1 | const migrate_name = 'forward_scheme'; 2 | const logger = require('../logger').migrate; 3 | 4 | /** 5 | * Migrate 6 | * 7 | * @see http://knexjs.org/#Schema 8 | * 9 | * @param {Object} knex 10 | * @param {Promise} Promise 11 | * @returns {Promise} 12 | */ 13 | exports.up = function (knex/*, Promise*/) { 14 | logger.info('[' + migrate_name + '] Migrating Up...'); 15 | 16 | return knex.schema.table('proxy_host', function (proxy_host) { 17 | proxy_host.string('forward_scheme').notNull().defaultTo('http'); 18 | }) 19 | .then(() => { 20 | logger.info('[' + migrate_name + '] proxy_host Table altered'); 21 | }); 22 | }; 23 | 24 | /** 25 | * Undo Migrate 26 | * 27 | * @param {Object} knex 28 | * @param {Promise} Promise 29 | * @returns {Promise} 30 | */ 31 | exports.down = function (knex, Promise) { 32 | logger.warn('[' + migrate_name + '] You can\'t migrate down this one.'); 33 | return Promise.resolve(true); 34 | }; 35 | -------------------------------------------------------------------------------- /backend/migrations/20190215115310_customlocations.js: -------------------------------------------------------------------------------- 1 | const migrate_name = 'custom_locations'; 2 | const logger = require('../logger').migrate; 3 | 4 | /** 5 | * Migrate 6 | * Extends proxy_host table with locations field 7 | * 8 | * @see http://knexjs.org/#Schema 9 | * 10 | * @param {Object} knex 11 | * @param {Promise} Promise 12 | * @returns {Promise} 13 | */ 14 | exports.up = function (knex/*, Promise*/) { 15 | logger.info('[' + migrate_name + '] Migrating Up...'); 16 | 17 | return knex.schema.table('proxy_host', function (proxy_host) { 18 | proxy_host.json('locations'); 19 | }) 20 | .then(() => { 21 | logger.info('[' + migrate_name + '] proxy_host Table altered'); 22 | }); 23 | }; 24 | 25 | /** 26 | * Undo Migrate 27 | * 28 | * @param {Object} knex 29 | * @param {Promise} Promise 30 | * @returns {Promise} 31 | */ 32 | exports.down = function (knex, Promise) { 33 | logger.warn('[' + migrate_name + '] You can\'t migrate down this one.'); 34 | return Promise.resolve(true); 35 | }; 36 | -------------------------------------------------------------------------------- /backend/migrations/20190227065017_settings.js: -------------------------------------------------------------------------------- 1 | const migrate_name = 'settings'; 2 | const logger = require('../logger').migrate; 3 | 4 | /** 5 | * Migrate 6 | * 7 | * @see http://knexjs.org/#Schema 8 | * 9 | * @param {Object} knex 10 | * @param {Promise} Promise 11 | * @returns {Promise} 12 | */ 13 | exports.up = function (knex/*, Promise*/) { 14 | logger.info('[' + migrate_name + '] Migrating Up...'); 15 | 16 | return knex.schema.createTable('setting', (table) => { 17 | table.string('id').notNull().primary(); 18 | table.string('name', 100).notNull(); 19 | table.string('description', 255).notNull(); 20 | table.string('value', 255).notNull(); 21 | table.json('meta').notNull(); 22 | }) 23 | .then(() => { 24 | logger.info('[' + migrate_name + '] setting Table created'); 25 | }); 26 | }; 27 | 28 | /** 29 | * Undo Migrate 30 | * 31 | * @param {Object} knex 32 | * @param {Promise} Promise 33 | * @returns {Promise} 34 | */ 35 | exports.down = function (knex, Promise) { 36 | logger.warn('[' + migrate_name + '] You can\'t migrate down the initial data.'); 37 | return Promise.resolve(true); 38 | }; 39 | -------------------------------------------------------------------------------- /backend/migrations/20200410143840_access_list_client_fix.js: -------------------------------------------------------------------------------- 1 | const migrate_name = 'access_list_client_fix'; 2 | const logger = require('../logger').migrate; 3 | 4 | /** 5 | * Migrate 6 | * 7 | * @see http://knexjs.org/#Schema 8 | * 9 | * @param {Object} knex 10 | * @param {Promise} Promise 11 | * @returns {Promise} 12 | */ 13 | exports.up = function (knex/*, Promise*/) { 14 | logger.info('[' + migrate_name + '] Migrating Up...'); 15 | 16 | return knex.schema.table('access_list', function (access_list) { 17 | access_list.renameColumn('satify_any', 'satisfy_any'); 18 | }) 19 | .then(() => { 20 | logger.info('[' + migrate_name + '] access_list Table altered'); 21 | }); 22 | }; 23 | 24 | /** 25 | * Undo Migrate 26 | * 27 | * @param {Object} knex 28 | * @param {Promise} Promise 29 | * @returns {Promise} 30 | */ 31 | exports.down = function (knex, Promise) { 32 | logger.warn('[' + migrate_name + '] You can\'t migrate down this one.'); 33 | return Promise.resolve(true); 34 | }; 35 | -------------------------------------------------------------------------------- /backend/migrations/20201014143841_pass_auth.js: -------------------------------------------------------------------------------- 1 | const migrate_name = 'pass_auth'; 2 | const logger = require('../logger').migrate; 3 | 4 | /** 5 | * Migrate 6 | * 7 | * @see http://knexjs.org/#Schema 8 | * 9 | * @param {Object} knex 10 | * @param {Promise} Promise 11 | * @returns {Promise} 12 | */ 13 | exports.up = function (knex/*, Promise*/) { 14 | 15 | logger.info('[' + migrate_name + '] Migrating Up...'); 16 | 17 | return knex.schema.table('access_list', function (access_list) { 18 | access_list.integer('pass_auth').notNull().defaultTo(1); 19 | }) 20 | .then(() => { 21 | logger.info('[' + migrate_name + '] access_list Table altered'); 22 | }); 23 | }; 24 | 25 | /** 26 | * Undo Migrate 27 | * 28 | * @param {Object} knex 29 | * @param {Promise} Promise 30 | * @returns {Promise} 31 | */ 32 | exports.down = function (knex/*, Promise*/) { 33 | logger.info('[' + migrate_name + '] Migrating Down...'); 34 | 35 | return knex.schema.table('access_list', function (access_list) { 36 | access_list.dropColumn('pass_auth'); 37 | }) 38 | .then(() => { 39 | logger.info('[' + migrate_name + '] access_list pass_auth Column dropped'); 40 | }); 41 | }; 42 | -------------------------------------------------------------------------------- /backend/migrations/20210210154702_redirection_scheme.js: -------------------------------------------------------------------------------- 1 | const migrate_name = 'redirection_scheme'; 2 | const logger = require('../logger').migrate; 3 | 4 | /** 5 | * Migrate 6 | * 7 | * @see http://knexjs.org/#Schema 8 | * 9 | * @param {Object} knex 10 | * @param {Promise} Promise 11 | * @returns {Promise} 12 | */ 13 | exports.up = function (knex/*, Promise*/) { 14 | 15 | logger.info('[' + migrate_name + '] Migrating Up...'); 16 | 17 | return knex.schema.table('redirection_host', (table) => { 18 | table.string('forward_scheme').notNull().defaultTo('$scheme'); 19 | }) 20 | .then(function () { 21 | logger.info('[' + migrate_name + '] redirection_host Table altered'); 22 | }); 23 | }; 24 | 25 | /** 26 | * Undo Migrate 27 | * 28 | * @param {Object} knex 29 | * @param {Promise} Promise 30 | * @returns {Promise} 31 | */ 32 | exports.down = function (knex/*, Promise*/) { 33 | logger.info('[' + migrate_name + '] Migrating Down...'); 34 | 35 | return knex.schema.table('redirection_host', (table) => { 36 | table.dropColumn('forward_scheme'); 37 | }) 38 | .then(function () { 39 | logger.info('[' + migrate_name + '] redirection_host Table altered'); 40 | }); 41 | }; 42 | -------------------------------------------------------------------------------- /backend/migrations/20210210154703_redirection_status_code.js: -------------------------------------------------------------------------------- 1 | const migrate_name = 'redirection_status_code'; 2 | const logger = require('../logger').migrate; 3 | 4 | /** 5 | * Migrate 6 | * 7 | * @see http://knexjs.org/#Schema 8 | * 9 | * @param {Object} knex 10 | * @param {Promise} Promise 11 | * @returns {Promise} 12 | */ 13 | exports.up = function (knex/*, Promise*/) { 14 | 15 | logger.info('[' + migrate_name + '] Migrating Up...'); 16 | 17 | return knex.schema.table('redirection_host', (table) => { 18 | table.integer('forward_http_code').notNull().unsigned().defaultTo(302); 19 | }) 20 | .then(function () { 21 | logger.info('[' + migrate_name + '] redirection_host Table altered'); 22 | }); 23 | }; 24 | 25 | /** 26 | * Undo Migrate 27 | * 28 | * @param {Object} knex 29 | * @param {Promise} Promise 30 | * @returns {Promise} 31 | */ 32 | exports.down = function (knex/*, Promise*/) { 33 | logger.info('[' + migrate_name + '] Migrating Down...'); 34 | 35 | return knex.schema.table('redirection_host', (table) => { 36 | table.dropColumn('forward_http_code'); 37 | }) 38 | .then(function () { 39 | logger.info('[' + migrate_name + '] redirection_host Table altered'); 40 | }); 41 | }; 42 | -------------------------------------------------------------------------------- /backend/migrations/20210423103500_stream_domain.js: -------------------------------------------------------------------------------- 1 | const migrate_name = 'stream_domain'; 2 | const logger = require('../logger').migrate; 3 | 4 | /** 5 | * Migrate 6 | * 7 | * @see http://knexjs.org/#Schema 8 | * 9 | * @param {Object} knex 10 | * @param {Promise} Promise 11 | * @returns {Promise} 12 | */ 13 | exports.up = function (knex/*, Promise*/) { 14 | logger.info('[' + migrate_name + '] Migrating Up...'); 15 | 16 | return knex.schema.table('stream', (table) => { 17 | table.renameColumn('forward_ip', 'forwarding_host'); 18 | }) 19 | .then(function () { 20 | logger.info('[' + migrate_name + '] stream Table altered'); 21 | }); 22 | }; 23 | 24 | /** 25 | * Undo Migrate 26 | * 27 | * @param {Object} knex 28 | * @param {Promise} Promise 29 | * @returns {Promise} 30 | */ 31 | exports.down = function (knex/*, Promise*/) { 32 | logger.info('[' + migrate_name + '] Migrating Down...'); 33 | 34 | return knex.schema.table('stream', (table) => { 35 | table.renameColumn('forwarding_host', 'forward_ip'); 36 | }) 37 | .then(function () { 38 | logger.info('[' + migrate_name + '] stream Table altered'); 39 | }); 40 | }; 41 | -------------------------------------------------------------------------------- /backend/migrations/20240427161436_stream_ssl.js: -------------------------------------------------------------------------------- 1 | const migrate_name = 'stream_ssl'; 2 | const logger = require('../logger').migrate; 3 | 4 | /** 5 | * Migrate 6 | * 7 | * @see http://knexjs.org/#Schema 8 | * 9 | * @param {Object} knex 10 | * @returns {Promise} 11 | */ 12 | exports.up = function (knex) { 13 | logger.info('[' + migrate_name + '] Migrating Up...'); 14 | 15 | return knex.schema.table('stream', (table) => { 16 | table.integer('certificate_id').notNull().unsigned().defaultTo(0); 17 | }) 18 | .then(function () { 19 | logger.info('[' + migrate_name + '] stream Table altered'); 20 | }); 21 | }; 22 | 23 | /** 24 | * Undo Migrate 25 | * 26 | * @param {Object} knex 27 | * @returns {Promise} 28 | */ 29 | exports.down = function (knex) { 30 | logger.info('[' + migrate_name + '] Migrating Down...'); 31 | 32 | return knex.schema.table('stream', (table) => { 33 | table.dropColumn('certificate_id'); 34 | }) 35 | .then(function () { 36 | logger.info('[' + migrate_name + '] stream Table altered'); 37 | }); 38 | }; 39 | -------------------------------------------------------------------------------- /backend/models/audit-log.js: -------------------------------------------------------------------------------- 1 | // Objection Docs: 2 | // http://vincit.github.io/objection.js/ 3 | 4 | const db = require('../db'); 5 | const Model = require('objection').Model; 6 | const User = require('./user'); 7 | const now = require('./now_helper'); 8 | 9 | Model.knex(db); 10 | 11 | class AuditLog extends Model { 12 | $beforeInsert () { 13 | this.created_on = now(); 14 | this.modified_on = now(); 15 | 16 | // Default for meta 17 | if (typeof this.meta === 'undefined') { 18 | this.meta = {}; 19 | } 20 | } 21 | 22 | $beforeUpdate () { 23 | this.modified_on = now(); 24 | } 25 | 26 | static get name () { 27 | return 'AuditLog'; 28 | } 29 | 30 | static get tableName () { 31 | return 'audit_log'; 32 | } 33 | 34 | static get jsonAttributes () { 35 | return ['meta']; 36 | } 37 | 38 | static get relationMappings () { 39 | return { 40 | user: { 41 | relation: Model.HasOneRelation, 42 | modelClass: User, 43 | join: { 44 | from: 'audit_log.user_id', 45 | to: 'user.id' 46 | } 47 | } 48 | }; 49 | } 50 | } 51 | 52 | module.exports = AuditLog; 53 | -------------------------------------------------------------------------------- /backend/models/now_helper.js: -------------------------------------------------------------------------------- 1 | const db = require('../db'); 2 | const config = require('../lib/config'); 3 | const Model = require('objection').Model; 4 | 5 | Model.knex(db); 6 | 7 | module.exports = function () { 8 | if (config.isSqlite()) { 9 | // eslint-disable-next-line 10 | return Model.raw("datetime('now','localtime')"); 11 | } 12 | return Model.raw('NOW()'); 13 | }; 14 | -------------------------------------------------------------------------------- /backend/models/setting.js: -------------------------------------------------------------------------------- 1 | // Objection Docs: 2 | // http://vincit.github.io/objection.js/ 3 | 4 | const db = require('../db'); 5 | const Model = require('objection').Model; 6 | 7 | Model.knex(db); 8 | 9 | class Setting extends Model { 10 | $beforeInsert () { 11 | // Default for meta 12 | if (typeof this.meta === 'undefined') { 13 | this.meta = {}; 14 | } 15 | } 16 | 17 | static get name () { 18 | return 'Setting'; 19 | } 20 | 21 | static get tableName () { 22 | return 'setting'; 23 | } 24 | 25 | static get jsonAttributes () { 26 | return ['meta']; 27 | } 28 | } 29 | 30 | module.exports = Setting; 31 | -------------------------------------------------------------------------------- /backend/models/user_permission.js: -------------------------------------------------------------------------------- 1 | // Objection Docs: 2 | // http://vincit.github.io/objection.js/ 3 | 4 | const db = require('../db'); 5 | const Model = require('objection').Model; 6 | const now = require('./now_helper'); 7 | 8 | Model.knex(db); 9 | 10 | class UserPermission extends Model { 11 | $beforeInsert () { 12 | this.created_on = now(); 13 | this.modified_on = now(); 14 | } 15 | 16 | $beforeUpdate () { 17 | this.modified_on = now(); 18 | } 19 | 20 | static get name () { 21 | return 'UserPermission'; 22 | } 23 | 24 | static get tableName () { 25 | return 'user_permission'; 26 | } 27 | } 28 | 29 | module.exports = UserPermission; 30 | -------------------------------------------------------------------------------- /backend/nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "verbose": false, 3 | "ignore": [ 4 | "data" 5 | ], 6 | "ext": "js json ejs" 7 | } 8 | -------------------------------------------------------------------------------- /backend/routes/reports.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const jwtdecode = require('../lib/express/jwt-decode'); 3 | const internalReport = require('../internal/report'); 4 | 5 | let router = express.Router({ 6 | caseSensitive: true, 7 | strict: true, 8 | mergeParams: true 9 | }); 10 | 11 | router 12 | .route('/hosts') 13 | .options((_, res) => { 14 | res.sendStatus(204); 15 | }) 16 | 17 | /** 18 | * GET /reports/hosts 19 | */ 20 | .get(jwtdecode(), (_, res, next) => { 21 | internalReport.getHostsReport(res.locals.access) 22 | .then((data) => { 23 | res.status(200) 24 | .send(data); 25 | }) 26 | .catch(next); 27 | }); 28 | 29 | module.exports = router; 30 | -------------------------------------------------------------------------------- /backend/routes/schema.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const schema = require('../schema'); 3 | const PACKAGE = require('../package.json'); 4 | 5 | const router = express.Router({ 6 | caseSensitive: true, 7 | strict: true, 8 | mergeParams: true 9 | }); 10 | 11 | router 12 | .route('/') 13 | .options((_, res) => { 14 | res.sendStatus(204); 15 | }) 16 | 17 | /** 18 | * GET /schema 19 | */ 20 | .get(async (req, res) => { 21 | let swaggerJSON = await schema.getCompiledSchema(); 22 | 23 | let proto = req.protocol; 24 | if (typeof req.headers['x-forwarded-proto'] !== 'undefined' && req.headers['x-forwarded-proto']) { 25 | proto = req.headers['x-forwarded-proto']; 26 | } 27 | 28 | let origin = proto + '://' + req.hostname; 29 | if (typeof req.headers.origin !== 'undefined' && req.headers.origin) { 30 | origin = req.headers.origin; 31 | } 32 | 33 | swaggerJSON.info.version = PACKAGE.version; 34 | swaggerJSON.servers[0].url = origin + '/api'; 35 | res.status(200).send(swaggerJSON); 36 | }); 37 | 38 | module.exports = router; 39 | -------------------------------------------------------------------------------- /backend/schema/components/audit-log-object.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "description": "Audit Log object", 4 | "required": ["id", "created_on", "modified_on", "user_id", "object_type", "object_id", "action", "meta"], 5 | "additionalProperties": false, 6 | "properties": { 7 | "id": { 8 | "$ref": "../common.json#/properties/id" 9 | }, 10 | "created_on": { 11 | "$ref": "../common.json#/properties/created_on" 12 | }, 13 | "modified_on": { 14 | "$ref": "../common.json#/properties/modified_on" 15 | }, 16 | "user_id": { 17 | "$ref": "../common.json#/properties/user_id" 18 | }, 19 | "object_type": { 20 | "type": "string" 21 | }, 22 | "object_id": { 23 | "$ref": "../common.json#/properties/id" 24 | }, 25 | "action": { 26 | "type": "string" 27 | }, 28 | "meta": { 29 | "type": "object" 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /backend/schema/components/certificate-list.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "array", 3 | "description": "Certificates list", 4 | "items": { 5 | "$ref": "./certificate-object.json" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /backend/schema/components/dead-host-list.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "array", 3 | "description": "404 Hosts list", 4 | "items": { 5 | "$ref": "./dead-host-object.json" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /backend/schema/components/error-object.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "description": "Error object", 4 | "additionalProperties": false, 5 | "required": ["code", "message"], 6 | "properties": { 7 | "code": { 8 | "type": "integer" 9 | }, 10 | "message": { 11 | "type": "string" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /backend/schema/components/error.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "description": "Error", 4 | "properties": { 5 | "error": { 6 | "$ref": "./error-object.json" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /backend/schema/components/health-object.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "description": "Health object", 4 | "additionalProperties": false, 5 | "required": ["status", "version"], 6 | "properties": { 7 | "status": { 8 | "type": "string", 9 | "description": "Healthy", 10 | "example": "OK" 11 | }, 12 | "version": { 13 | "type": "object", 14 | "description": "The version object", 15 | "example": { 16 | "major": 2, 17 | "minor": 0, 18 | "revision": 0 19 | }, 20 | "additionalProperties": false, 21 | "required": ["major", "minor", "revision"], 22 | "properties": { 23 | "major": { 24 | "type": "integer", 25 | "minimum": 0 26 | }, 27 | "minor": { 28 | "type": "integer", 29 | "minimum": 0 30 | }, 31 | "revision": { 32 | "type": "integer", 33 | "minimum": 0 34 | } 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /backend/schema/components/permission-object.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "minProperties": 1, 4 | "properties": { 5 | "visibility": { 6 | "type": "string", 7 | "description": "Visibility Type", 8 | "enum": ["all", "user"] 9 | }, 10 | "access_lists": { 11 | "type": "string", 12 | "description": "Access Lists Permissions", 13 | "enum": ["hidden", "view", "manage"] 14 | }, 15 | "dead_hosts": { 16 | "type": "string", 17 | "description": "404 Hosts Permissions", 18 | "enum": ["hidden", "view", "manage"] 19 | }, 20 | "proxy_hosts": { 21 | "type": "string", 22 | "description": "Proxy Hosts Permissions", 23 | "enum": ["hidden", "view", "manage"] 24 | }, 25 | "redirection_hosts": { 26 | "type": "string", 27 | "description": "Redirection Permissions", 28 | "enum": ["hidden", "view", "manage"] 29 | }, 30 | "streams": { 31 | "type": "string", 32 | "description": "Streams Permissions", 33 | "enum": ["hidden", "view", "manage"] 34 | }, 35 | "certificates": { 36 | "type": "string", 37 | "description": "Certificates Permissions", 38 | "enum": ["hidden", "view", "manage"] 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /backend/schema/components/proxy-host-list.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "array", 3 | "description": "Proxy Hosts list", 4 | "items": { 5 | "$ref": "./proxy-host-object.json" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /backend/schema/components/redirection-host-list.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "array", 3 | "description": "Redirection Hosts list", 4 | "items": { 5 | "$ref": "./redirection-host-object.json" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /backend/schema/components/security-schemes.json: -------------------------------------------------------------------------------- 1 | { 2 | "BearerAuth": { 3 | "type": "http", 4 | "scheme": "bearer" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /backend/schema/components/setting-list.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "array", 3 | "description": "Setting list", 4 | "items": { 5 | "$ref": "./setting-object.json" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /backend/schema/components/stream-list.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "array", 3 | "description": "Proxy Hosts list", 4 | "items": { 5 | "$ref": "./proxy-host-object.json" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /backend/schema/components/token-object.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "description": "Token object", 4 | "required": ["expires", "token"], 5 | "additionalProperties": false, 6 | "properties": { 7 | "expires": { 8 | "description": "Token Expiry ISO Time String", 9 | "example": "2025-02-04T20:40:46.340Z", 10 | "type": "string" 11 | }, 12 | "token": { 13 | "description": "JWT Token", 14 | "example": "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.ey...xaHKYr3Kk6MvkUjcC4", 15 | "type": "string" 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /backend/schema/components/user-list.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "array", 3 | "description": "User list", 4 | "items": { 5 | "$ref": "./user-object.json" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /backend/schema/paths/get.json: -------------------------------------------------------------------------------- 1 | { 2 | "operationId": "health", 3 | "summary": "Returns the API health status", 4 | "tags": ["Public"], 5 | "responses": { 6 | "200": { 7 | "description": "200 response", 8 | "content": { 9 | "application/json": { 10 | "examples": { 11 | "default": { 12 | "value": { 13 | "status": "OK", 14 | "version": { 15 | "major": 2, 16 | "minor": 1, 17 | "revision": 0 18 | } 19 | } 20 | } 21 | }, 22 | "schema": { 23 | "$ref": "../components/health-object.json" 24 | } 25 | } 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /backend/schema/paths/nginx/access-lists/get.json: -------------------------------------------------------------------------------- 1 | { 2 | "operationId": "getAccessLists", 3 | "summary": "Get all access lists", 4 | "tags": ["Access Lists"], 5 | "security": [ 6 | { 7 | "BearerAuth": ["access_lists"] 8 | } 9 | ], 10 | "parameters": [ 11 | { 12 | "in": "query", 13 | "name": "expand", 14 | "description": "Expansions", 15 | "schema": { 16 | "type": "string", 17 | "enum": ["owner", "items", "clients", "proxy_hosts"] 18 | } 19 | } 20 | ], 21 | "responses": { 22 | "200": { 23 | "description": "200 response", 24 | "content": { 25 | "application/json": { 26 | "examples": { 27 | "default": { 28 | "value": [ 29 | { 30 | "id": 1, 31 | "created_on": "2024-10-08T22:15:40.000Z", 32 | "modified_on": "2024-10-08T22:15:40.000Z", 33 | "owner_user_id": 1, 34 | "name": "test1234", 35 | "meta": {}, 36 | "satisfy_any": true, 37 | "pass_auth": false, 38 | "proxy_host_count": 0 39 | } 40 | ] 41 | } 42 | }, 43 | "schema": { 44 | "$ref": "../../../components/access-list-object.json" 45 | } 46 | } 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /backend/schema/paths/nginx/access-lists/listID/delete.json: -------------------------------------------------------------------------------- 1 | { 2 | "operationId": "deleteAccessList", 3 | "summary": "Delete a Access List", 4 | "tags": ["Access Lists"], 5 | "security": [ 6 | { 7 | "BearerAuth": ["access_lists"] 8 | } 9 | ], 10 | "parameters": [ 11 | { 12 | "in": "path", 13 | "name": "listID", 14 | "schema": { 15 | "type": "integer", 16 | "minimum": 1 17 | }, 18 | "required": true, 19 | "example": 2 20 | } 21 | ], 22 | "responses": { 23 | "200": { 24 | "description": "200 response", 25 | "content": { 26 | "application/json": { 27 | "examples": { 28 | "default": { 29 | "value": true 30 | } 31 | }, 32 | "schema": { 33 | "type": "boolean" 34 | } 35 | } 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /backend/schema/paths/nginx/access-lists/listID/get.json: -------------------------------------------------------------------------------- 1 | { 2 | "operationId": "getAccessList", 3 | "summary": "Get a access List", 4 | "tags": ["Access Lists"], 5 | "security": [ 6 | { 7 | "BearerAuth": ["access_lists"] 8 | } 9 | ], 10 | "parameters": [ 11 | { 12 | "in": "path", 13 | "name": "listID", 14 | "schema": { 15 | "type": "integer", 16 | "minimum": 1 17 | }, 18 | "required": true, 19 | "example": 1 20 | } 21 | ], 22 | "responses": { 23 | "200": { 24 | "description": "200 response", 25 | "content": { 26 | "application/json": { 27 | "examples": { 28 | "default": { 29 | "value": { 30 | "id": 1, 31 | "created_on": "2020-01-30T09:36:08.000Z", 32 | "modified_on": "2020-01-30T09:41:04.000Z", 33 | "is_disabled": false, 34 | "email": "jc@jc21.com", 35 | "name": "Jamie Curnow", 36 | "nickname": "James", 37 | "avatar": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm", 38 | "roles": ["admin"] 39 | } 40 | } 41 | }, 42 | "schema": { 43 | "$ref": "../../../../components/access-list-object.json" 44 | } 45 | } 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /backend/schema/paths/nginx/certificates/certID/delete.json: -------------------------------------------------------------------------------- 1 | { 2 | "operationId": "deleteCertificate", 3 | "summary": "Delete a Certificate", 4 | "tags": ["Certificates"], 5 | "security": [ 6 | { 7 | "BearerAuth": ["certificates"] 8 | } 9 | ], 10 | "parameters": [ 11 | { 12 | "in": "path", 13 | "name": "certID", 14 | "schema": { 15 | "type": "integer", 16 | "minimum": 1 17 | }, 18 | "required": true, 19 | "example": 2 20 | } 21 | ], 22 | "responses": { 23 | "200": { 24 | "description": "200 response", 25 | "content": { 26 | "application/json": { 27 | "examples": { 28 | "default": { 29 | "value": true 30 | } 31 | }, 32 | "schema": { 33 | "type": "boolean" 34 | } 35 | } 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /backend/schema/paths/nginx/certificates/certID/download/get.json: -------------------------------------------------------------------------------- 1 | { 2 | "operationId": "downloadCertificate", 3 | "summary": "Downloads a Certificate", 4 | "tags": ["Certificates"], 5 | "security": [ 6 | { 7 | "BearerAuth": ["certificates"] 8 | } 9 | ], 10 | "parameters": [ 11 | { 12 | "in": "path", 13 | "name": "certID", 14 | "schema": { 15 | "type": "integer", 16 | "minimum": 1 17 | }, 18 | "required": true, 19 | "example": 1 20 | } 21 | ], 22 | "responses": { 23 | "200": { 24 | "description": "200 response", 25 | "content": { 26 | "application/zip": { 27 | "schema": { 28 | "type": "string", 29 | "format": "binary" 30 | } 31 | } 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /backend/schema/paths/nginx/certificates/test-http/get.json: -------------------------------------------------------------------------------- 1 | { 2 | "operationId": "testHttpReach", 3 | "summary": "Test HTTP Reachability", 4 | "tags": ["Certificates"], 5 | "security": [ 6 | { 7 | "BearerAuth": ["certificates"] 8 | } 9 | ], 10 | "parameters": [ 11 | { 12 | "in": "query", 13 | "name": "domains", 14 | "description": "Expansions", 15 | "required": true, 16 | "schema": { 17 | "type": "string", 18 | "example": "[\"test.example.ord\",\"test.example.com\",\"nonexistent.example.com\"]" 19 | } 20 | } 21 | ], 22 | "responses": { 23 | "200": { 24 | "description": "200 response", 25 | "content": { 26 | "application/json": { 27 | "examples": { 28 | "default": { 29 | "value": { 30 | "test.example.org": "ok", 31 | "test.example.com": "other:Invalid domain or IP", 32 | "nonexistent.example.com": "404" 33 | } 34 | } 35 | } 36 | } 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /backend/schema/paths/nginx/dead-hosts/hostID/delete.json: -------------------------------------------------------------------------------- 1 | { 2 | "operationId": "deleteDeadHost", 3 | "summary": "Delete a 404 Host", 4 | "tags": ["404 Hosts"], 5 | "security": [ 6 | { 7 | "BearerAuth": ["dead_hosts"] 8 | } 9 | ], 10 | "parameters": [ 11 | { 12 | "in": "path", 13 | "name": "hostID", 14 | "schema": { 15 | "type": "integer", 16 | "minimum": 1 17 | }, 18 | "required": true, 19 | "example": 2 20 | } 21 | ], 22 | "responses": { 23 | "200": { 24 | "description": "200 response", 25 | "content": { 26 | "application/json": { 27 | "examples": { 28 | "default": { 29 | "value": true 30 | } 31 | }, 32 | "schema": { 33 | "type": "boolean" 34 | } 35 | } 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /backend/schema/paths/nginx/dead-hosts/hostID/disable/post.json: -------------------------------------------------------------------------------- 1 | { 2 | "operationId": "disableDeadHost", 3 | "summary": "Disable a 404 Host", 4 | "tags": ["404 Hosts"], 5 | "security": [ 6 | { 7 | "BearerAuth": ["dead_hosts"] 8 | } 9 | ], 10 | "parameters": [ 11 | { 12 | "in": "path", 13 | "name": "hostID", 14 | "schema": { 15 | "type": "integer", 16 | "minimum": 1 17 | }, 18 | "required": true, 19 | "example": 2 20 | } 21 | ], 22 | "responses": { 23 | "200": { 24 | "description": "200 response", 25 | "content": { 26 | "application/json": { 27 | "examples": { 28 | "default": { 29 | "value": true 30 | } 31 | }, 32 | "schema": { 33 | "type": "boolean" 34 | } 35 | } 36 | } 37 | }, 38 | "400": { 39 | "description": "400 response", 40 | "content": { 41 | "application/json": { 42 | "examples": { 43 | "default": { 44 | "value": { 45 | "error": { 46 | "code": 400, 47 | "message": "Host is already disabled" 48 | } 49 | } 50 | } 51 | }, 52 | "schema": { 53 | "$ref": "../../../../../components/error.json" 54 | } 55 | } 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /backend/schema/paths/nginx/dead-hosts/hostID/enable/post.json: -------------------------------------------------------------------------------- 1 | { 2 | "operationId": "enableDeadHost", 3 | "summary": "Enable a 404 Host", 4 | "tags": ["404 Hosts"], 5 | "security": [ 6 | { 7 | "BearerAuth": ["dead_hosts"] 8 | } 9 | ], 10 | "parameters": [ 11 | { 12 | "in": "path", 13 | "name": "hostID", 14 | "schema": { 15 | "type": "integer", 16 | "minimum": 1 17 | }, 18 | "required": true, 19 | "example": 2 20 | } 21 | ], 22 | "responses": { 23 | "200": { 24 | "description": "200 response", 25 | "content": { 26 | "application/json": { 27 | "examples": { 28 | "default": { 29 | "value": true 30 | } 31 | }, 32 | "schema": { 33 | "type": "boolean" 34 | } 35 | } 36 | } 37 | }, 38 | "400": { 39 | "description": "400 response", 40 | "content": { 41 | "application/json": { 42 | "examples": { 43 | "default": { 44 | "value": { 45 | "error": { 46 | "code": 400, 47 | "message": "Host is already enabled" 48 | } 49 | } 50 | } 51 | }, 52 | "schema": { 53 | "$ref": "../../../../../components/error.json" 54 | } 55 | } 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /backend/schema/paths/nginx/proxy-hosts/hostID/delete.json: -------------------------------------------------------------------------------- 1 | { 2 | "operationId": "deleteProxyHost", 3 | "summary": "Delete a Proxy Host", 4 | "tags": ["Proxy Hosts"], 5 | "security": [ 6 | { 7 | "BearerAuth": ["proxy_hosts"] 8 | } 9 | ], 10 | "parameters": [ 11 | { 12 | "in": "path", 13 | "name": "hostID", 14 | "schema": { 15 | "type": "integer", 16 | "minimum": 1 17 | }, 18 | "required": true, 19 | "example": 2 20 | } 21 | ], 22 | "responses": { 23 | "200": { 24 | "description": "200 response", 25 | "content": { 26 | "application/json": { 27 | "examples": { 28 | "default": { 29 | "value": true 30 | } 31 | }, 32 | "schema": { 33 | "type": "boolean" 34 | } 35 | } 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /backend/schema/paths/nginx/redirection-hosts/hostID/delete.json: -------------------------------------------------------------------------------- 1 | { 2 | "operationId": "deleteRedirectionHost", 3 | "summary": "Delete a Redirection Host", 4 | "tags": ["Redirection Hosts"], 5 | "security": [ 6 | { 7 | "BearerAuth": ["redirection_hosts"] 8 | } 9 | ], 10 | "parameters": [ 11 | { 12 | "in": "path", 13 | "name": "hostID", 14 | "schema": { 15 | "type": "integer", 16 | "minimum": 1 17 | }, 18 | "required": true, 19 | "example": 2 20 | } 21 | ], 22 | "responses": { 23 | "200": { 24 | "description": "200 response", 25 | "content": { 26 | "application/json": { 27 | "examples": { 28 | "default": { 29 | "value": true 30 | } 31 | }, 32 | "schema": { 33 | "type": "boolean" 34 | } 35 | } 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /backend/schema/paths/nginx/streams/streamID/delete.json: -------------------------------------------------------------------------------- 1 | { 2 | "operationId": "deleteStream", 3 | "summary": "Delete a Stream", 4 | "tags": ["Streams"], 5 | "security": [ 6 | { 7 | "BearerAuth": ["streams"] 8 | } 9 | ], 10 | "parameters": [ 11 | { 12 | "in": "path", 13 | "name": "streamID", 14 | "schema": { 15 | "type": "integer", 16 | "minimum": 1 17 | }, 18 | "required": true, 19 | "example": 2 20 | } 21 | ], 22 | "responses": { 23 | "200": { 24 | "description": "200 response", 25 | "content": { 26 | "application/json": { 27 | "examples": { 28 | "default": { 29 | "value": true 30 | } 31 | }, 32 | "schema": { 33 | "type": "boolean" 34 | } 35 | } 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /backend/schema/paths/nginx/streams/streamID/disable/post.json: -------------------------------------------------------------------------------- 1 | { 2 | "operationId": "disableStream", 3 | "summary": "Disable a Stream", 4 | "tags": ["Streams"], 5 | "security": [ 6 | { 7 | "BearerAuth": ["streams"] 8 | } 9 | ], 10 | "parameters": [ 11 | { 12 | "in": "path", 13 | "name": "streamID", 14 | "schema": { 15 | "type": "integer", 16 | "minimum": 1 17 | }, 18 | "required": true, 19 | "example": 2 20 | } 21 | ], 22 | "responses": { 23 | "200": { 24 | "description": "200 response", 25 | "content": { 26 | "application/json": { 27 | "examples": { 28 | "default": { 29 | "value": true 30 | } 31 | }, 32 | "schema": { 33 | "type": "boolean" 34 | } 35 | } 36 | } 37 | }, 38 | "400": { 39 | "description": "400 response", 40 | "content": { 41 | "application/json": { 42 | "examples": { 43 | "default": { 44 | "value": { 45 | "error": { 46 | "code": 400, 47 | "message": "Host is already disabled" 48 | } 49 | } 50 | } 51 | }, 52 | "schema": { 53 | "$ref": "../../../../../components/error.json" 54 | } 55 | } 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /backend/schema/paths/nginx/streams/streamID/enable/post.json: -------------------------------------------------------------------------------- 1 | { 2 | "operationId": "enableStream", 3 | "summary": "Enable a Stream", 4 | "tags": ["Streams"], 5 | "security": [ 6 | { 7 | "BearerAuth": ["streams"] 8 | } 9 | ], 10 | "parameters": [ 11 | { 12 | "in": "path", 13 | "name": "streamID", 14 | "schema": { 15 | "type": "integer", 16 | "minimum": 1 17 | }, 18 | "required": true, 19 | "example": 2 20 | } 21 | ], 22 | "responses": { 23 | "200": { 24 | "description": "200 response", 25 | "content": { 26 | "application/json": { 27 | "examples": { 28 | "default": { 29 | "value": true 30 | } 31 | }, 32 | "schema": { 33 | "type": "boolean" 34 | } 35 | } 36 | } 37 | }, 38 | "400": { 39 | "description": "400 response", 40 | "content": { 41 | "application/json": { 42 | "examples": { 43 | "default": { 44 | "value": { 45 | "error": { 46 | "code": 400, 47 | "message": "Host is already enabled" 48 | } 49 | } 50 | } 51 | }, 52 | "schema": { 53 | "$ref": "../../../../../components/error.json" 54 | } 55 | } 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /backend/schema/paths/reports/hosts/get.json: -------------------------------------------------------------------------------- 1 | { 2 | "operationId": "reportsHosts", 3 | "summary": "Report on Host Statistics", 4 | "tags": ["Reports"], 5 | "security": [ 6 | { 7 | "BearerAuth": ["reports"] 8 | } 9 | ], 10 | "responses": { 11 | "200": { 12 | "description": "200 response", 13 | "content": { 14 | "application/json": { 15 | "examples": { 16 | "default": { 17 | "value": { 18 | "proxy": 20, 19 | "redirection": 1, 20 | "stream": 0, 21 | "dead": 1 22 | } 23 | } 24 | }, 25 | "schema": { 26 | "type": "object", 27 | "properties": { 28 | "proxy": { 29 | "type": "integer", 30 | "description": "Proxy Hosts Count" 31 | }, 32 | "redirection": { 33 | "type": "integer", 34 | "description": "Redirection Hosts Count" 35 | }, 36 | "stream": { 37 | "type": "integer", 38 | "description": "Streams Count" 39 | }, 40 | "dead": { 41 | "type": "integer", 42 | "description": "404 Hosts Count" 43 | } 44 | } 45 | } 46 | } 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /backend/schema/paths/schema/get.json: -------------------------------------------------------------------------------- 1 | { 2 | "operationId": "schema", 3 | "summary": "Returns this swagger API schema", 4 | "tags": ["Public"], 5 | "responses": { 6 | "200": { 7 | "description": "200 response" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /backend/schema/paths/settings/get.json: -------------------------------------------------------------------------------- 1 | { 2 | "operationId": "getSettings", 3 | "summary": "Get all settings", 4 | "tags": ["Settings"], 5 | "security": [ 6 | { 7 | "BearerAuth": ["settings"] 8 | } 9 | ], 10 | "responses": { 11 | "200": { 12 | "description": "200 response", 13 | "content": { 14 | "application/json": { 15 | "examples": { 16 | "default": { 17 | "value": [ 18 | { 19 | "id": "default-site", 20 | "name": "Default Site", 21 | "description": "What to show when Nginx is hit with an unknown Host", 22 | "value": "congratulations", 23 | "meta": {} 24 | } 25 | ] 26 | } 27 | }, 28 | "schema": { 29 | "$ref": "../../components/setting-list.json" 30 | } 31 | } 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /backend/schema/paths/settings/settingID/get.json: -------------------------------------------------------------------------------- 1 | { 2 | "operationId": "getSetting", 3 | "summary": "Get a setting", 4 | "tags": ["Settings"], 5 | "security": [ 6 | { 7 | "BearerAuth": ["settings"] 8 | } 9 | ], 10 | "parameters": [ 11 | { 12 | "in": "path", 13 | "name": "settingID", 14 | "schema": { 15 | "type": "string", 16 | "minLength": 1 17 | }, 18 | "required": true, 19 | "description": "Setting ID", 20 | "example": "default-site" 21 | } 22 | ], 23 | "responses": { 24 | "200": { 25 | "description": "200 response", 26 | "content": { 27 | "application/json": { 28 | "examples": { 29 | "default": { 30 | "value": { 31 | "id": "default-site", 32 | "name": "Default Site", 33 | "description": "What to show when Nginx is hit with an unknown Host", 34 | "value": "congratulations", 35 | "meta": {} 36 | } 37 | } 38 | }, 39 | "schema": { 40 | "$ref": "../../../components/setting-object.json" 41 | } 42 | } 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /backend/schema/paths/tokens/get.json: -------------------------------------------------------------------------------- 1 | { 2 | "operationId": "refreshToken", 3 | "summary": "Refresh your access token", 4 | "tags": ["Tokens"], 5 | "security": [ 6 | { 7 | "BearerAuth": ["tokens"] 8 | } 9 | ], 10 | "responses": { 11 | "200": { 12 | "description": "200 response", 13 | "content": { 14 | "application/json": { 15 | "examples": { 16 | "default": { 17 | "value": { 18 | "expires": "2025-02-04T20:40:46.340Z", 19 | "token": "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.ey...xaHKYr3Kk6MvkUjcC4" 20 | } 21 | } 22 | }, 23 | "schema": { 24 | "$ref": "../../components/token-object.json" 25 | } 26 | } 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /backend/schema/paths/users/userID/delete.json: -------------------------------------------------------------------------------- 1 | { 2 | "operationId": "deleteUser", 3 | "summary": "Delete a User", 4 | "tags": ["Users"], 5 | "security": [ 6 | { 7 | "BearerAuth": ["users"] 8 | } 9 | ], 10 | "parameters": [ 11 | { 12 | "in": "path", 13 | "name": "userID", 14 | "schema": { 15 | "type": "integer", 16 | "minimum": 1 17 | }, 18 | "required": true, 19 | "description": "User ID", 20 | "example": 2 21 | } 22 | ], 23 | "responses": { 24 | "200": { 25 | "description": "200 response", 26 | "content": { 27 | "application/json": { 28 | "examples": { 29 | "default": { 30 | "value": true 31 | } 32 | }, 33 | "schema": { 34 | "type": "boolean" 35 | } 36 | } 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /backend/schema/paths/users/userID/permissions/put.json: -------------------------------------------------------------------------------- 1 | { 2 | "operationId": "updateUserPermissions", 3 | "summary": "Update a User's Permissions", 4 | "tags": ["Users"], 5 | "security": [ 6 | { 7 | "BearerAuth": ["users"] 8 | } 9 | ], 10 | "parameters": [ 11 | { 12 | "in": "path", 13 | "name": "userID", 14 | "schema": { 15 | "type": "integer", 16 | "minimum": 1 17 | }, 18 | "required": true, 19 | "description": "User ID", 20 | "example": 2 21 | } 22 | ], 23 | "requestBody": { 24 | "description": "Permissions Payload", 25 | "required": true, 26 | "content": { 27 | "application/json": { 28 | "schema": { 29 | "$ref": "../../../../components/permission-object.json" 30 | } 31 | } 32 | } 33 | }, 34 | "responses": { 35 | "200": { 36 | "description": "200 response", 37 | "content": { 38 | "application/json": { 39 | "examples": { 40 | "default": { 41 | "value": true 42 | } 43 | }, 44 | "schema": { 45 | "type": "boolean" 46 | } 47 | } 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /backend/templates/_access.conf: -------------------------------------------------------------------------------- 1 | {% if access_list_id > 0 %} 2 | {% if access_list.items.length > 0 %} 3 | # Authorization 4 | auth_basic "Authorization required"; 5 | auth_basic_user_file /data/access/{{ access_list_id }}; 6 | 7 | {% if access_list.pass_auth == 0 or access_list.pass_auth == true %} 8 | proxy_set_header Authorization ""; 9 | {% endif %} 10 | 11 | {% endif %} 12 | 13 | # Access Rules: {{ access_list.clients | size }} total 14 | {% for client in access_list.clients %} 15 | {{client | nginxAccessRule}} 16 | {% endfor %} 17 | deny all; 18 | 19 | # Access checks must... 20 | {% if access_list.satisfy_any == 1 or access_list.satisfy_any == true %} 21 | satisfy any; 22 | {% else %} 23 | satisfy all; 24 | {% endif %} 25 | {% endif %} 26 | -------------------------------------------------------------------------------- /backend/templates/_assets.conf: -------------------------------------------------------------------------------- 1 | {% if caching_enabled == 1 or caching_enabled == true -%} 2 | # Asset Caching 3 | include conf.d/include/assets.conf; 4 | {% endif %} -------------------------------------------------------------------------------- /backend/templates/_certificates.conf: -------------------------------------------------------------------------------- 1 | {% if certificate and certificate_id > 0 -%} 2 | {% if certificate.provider == "letsencrypt" %} 3 | # Let's Encrypt SSL 4 | include conf.d/include/letsencrypt-acme-challenge.conf; 5 | include conf.d/include/ssl-cache.conf; 6 | include conf.d/include/ssl-ciphers.conf; 7 | ssl_certificate /etc/letsencrypt/live/npm-{{ certificate_id }}/fullchain.pem; 8 | ssl_certificate_key /etc/letsencrypt/live/npm-{{ certificate_id }}/privkey.pem; 9 | {% else %} 10 | # Custom SSL 11 | ssl_certificate /data/custom_ssl/npm-{{ certificate_id }}/fullchain.pem; 12 | ssl_certificate_key /data/custom_ssl/npm-{{ certificate_id }}/privkey.pem; 13 | {% endif %} 14 | {% endif %} 15 | 16 | -------------------------------------------------------------------------------- /backend/templates/_certificates_stream.conf: -------------------------------------------------------------------------------- 1 | {% if certificate and certificate_id > 0 %} 2 | {% if certificate.provider == "letsencrypt" %} 3 | # Let's Encrypt SSL 4 | include conf.d/include/ssl-cache-stream.conf; 5 | include conf.d/include/ssl-ciphers.conf; 6 | ssl_certificate /etc/letsencrypt/live/npm-{{ certificate_id }}/fullchain.pem; 7 | ssl_certificate_key /etc/letsencrypt/live/npm-{{ certificate_id }}/privkey.pem; 8 | {%- else %} 9 | # Custom SSL 10 | ssl_certificate /data/custom_ssl/npm-{{ certificate_id }}/fullchain.pem; 11 | ssl_certificate_key /data/custom_ssl/npm-{{ certificate_id }}/privkey.pem; 12 | {%- endif -%} 13 | {%- endif -%} 14 | -------------------------------------------------------------------------------- /backend/templates/_exploits.conf: -------------------------------------------------------------------------------- 1 | {% if block_exploits == 1 or block_exploits == true %} 2 | # Block Exploits 3 | include conf.d/include/block-exploits.conf; 4 | {% endif %} -------------------------------------------------------------------------------- /backend/templates/_forced_ssl.conf: -------------------------------------------------------------------------------- 1 | {% if certificate and certificate_id > 0 -%} 2 | {% if ssl_forced == 1 or ssl_forced == true %} 3 | # Force SSL 4 | include conf.d/include/force-ssl.conf; 5 | {% endif %} 6 | {% endif %} -------------------------------------------------------------------------------- /backend/templates/_header_comment.conf: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------ 2 | # {{ domain_names | join: ", " }} 3 | # ------------------------------------------------------------ -------------------------------------------------------------------------------- /backend/templates/_hsts.conf: -------------------------------------------------------------------------------- 1 | {% if certificate and certificate_id > 0 -%} 2 | {% if ssl_forced == 1 or ssl_forced == true %} 3 | {% if hsts_enabled == 1 or hsts_enabled == true %} 4 | # HSTS (ngx_http_headers_module is required) (63072000 seconds = 2 years) 5 | add_header Strict-Transport-Security $hsts_header always; 6 | {% endif %} 7 | {% endif %} 8 | {% endif %} 9 | -------------------------------------------------------------------------------- /backend/templates/_hsts_map.conf: -------------------------------------------------------------------------------- 1 | map $scheme $hsts_header { 2 | https "max-age=63072000;{% if hsts_subdomains == 1 or hsts_subdomains == true -%} includeSubDomains;{% endif %} preload"; 3 | } -------------------------------------------------------------------------------- /backend/templates/_listen.conf: -------------------------------------------------------------------------------- 1 | listen 80; 2 | {% if ipv6 -%} 3 | listen [::]:80; 4 | {% else -%} 5 | #listen [::]:80; 6 | {% endif %} 7 | {% if certificate -%} 8 | listen 443 ssl; 9 | {% if ipv6 -%} 10 | listen [::]:443 ssl; 11 | {% else -%} 12 | #listen [::]:443; 13 | {% endif %} 14 | {% endif %} 15 | server_name {{ domain_names | join: " " }}; 16 | {% if http2_support == 1 or http2_support == true %} 17 | http2 on; 18 | {% else -%} 19 | http2 off; 20 | {% endif %} -------------------------------------------------------------------------------- /backend/templates/_location.conf: -------------------------------------------------------------------------------- 1 | location {{ path }} { 2 | {{ advanced_config }} 3 | 4 | proxy_set_header Host $host; 5 | proxy_set_header X-Forwarded-Scheme $scheme; 6 | proxy_set_header X-Forwarded-Proto $scheme; 7 | proxy_set_header X-Forwarded-For $remote_addr; 8 | proxy_set_header X-Real-IP $remote_addr; 9 | 10 | proxy_pass {{ forward_scheme }}://{{ forward_host }}:{{ forward_port }}{{ forward_path }}; 11 | 12 | {% include "_access.conf" %} 13 | {% include "_assets.conf" %} 14 | {% include "_exploits.conf" %} 15 | {% include "_forced_ssl.conf" %} 16 | {% include "_hsts.conf" %} 17 | 18 | {% if allow_websocket_upgrade == 1 or allow_websocket_upgrade == true %} 19 | proxy_set_header Upgrade $http_upgrade; 20 | proxy_set_header Connection $http_connection; 21 | proxy_http_version 1.1; 22 | {% endif %} 23 | } 24 | 25 | -------------------------------------------------------------------------------- /backend/templates/dead_host.conf: -------------------------------------------------------------------------------- 1 | {% include "_header_comment.conf" %} 2 | 3 | {% if enabled %} 4 | 5 | {% include "_hsts_map.conf" %} 6 | 7 | server { 8 | {% include "_listen.conf" %} 9 | {% include "_certificates.conf" %} 10 | {% include "_hsts.conf" %} 11 | {% include "_forced_ssl.conf" %} 12 | 13 | access_log /data/logs/dead-host-{{ id }}_access.log standard; 14 | error_log /data/logs/dead-host-{{ id }}_error.log warn; 15 | 16 | {{ advanced_config }} 17 | 18 | {% if use_default_location %} 19 | location / { 20 | {% include "_hsts.conf" %} 21 | return 404; 22 | } 23 | {% endif %} 24 | 25 | # Custom 26 | include /data/nginx/custom/server_dead[.]conf; 27 | } 28 | {% endif %} 29 | -------------------------------------------------------------------------------- /backend/templates/default.conf: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------ 2 | # Default Site 3 | # ------------------------------------------------------------ 4 | {% if value == "congratulations" %} 5 | # Skipping output, congratulations page configration is baked in. 6 | {%- else %} 7 | server { 8 | listen 80 default; 9 | {% if ipv6 -%} 10 | listen [::]:80 default; 11 | {% else -%} 12 | #listen [::]:80 default; 13 | {% endif %} 14 | server_name default-host.localhost; 15 | access_log /data/logs/default-host_access.log combined; 16 | error_log /data/logs/default-host_error.log warn; 17 | {% include "_exploits.conf" %} 18 | 19 | include conf.d/include/letsencrypt-acme-challenge.conf; 20 | 21 | {%- if value == "404" %} 22 | location / { 23 | return 404; 24 | } 25 | {% endif %} 26 | 27 | {%- if value == "444" %} 28 | location / { 29 | return 444; 30 | } 31 | {% endif %} 32 | 33 | {%- if value == "redirect" %} 34 | location / { 35 | return 301 {{ meta.redirect }}; 36 | } 37 | {%- endif %} 38 | 39 | {%- if value == "html" %} 40 | root /data/nginx/default_www; 41 | location / { 42 | try_files $uri /index.html; 43 | } 44 | {%- endif %} 45 | } 46 | {% endif %} 47 | -------------------------------------------------------------------------------- /backend/templates/ip_ranges.conf: -------------------------------------------------------------------------------- 1 | {% for range in ip_ranges %} 2 | set_real_ip_from {{ range }}; 3 | {% endfor %} -------------------------------------------------------------------------------- /backend/templates/letsencrypt-request.conf: -------------------------------------------------------------------------------- 1 | {% include "_header_comment.conf" %} 2 | 3 | server { 4 | listen 80; 5 | {% if ipv6 -%} 6 | listen [::]:80; 7 | {% endif %} 8 | 9 | server_name {{ domain_names | join: " " }}; 10 | 11 | access_log /data/logs/letsencrypt-requests_access.log standard; 12 | error_log /data/logs/letsencrypt-requests_error.log warn; 13 | 14 | include conf.d/include/letsencrypt-acme-challenge.conf; 15 | 16 | location / { 17 | return 404; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /backend/templates/redirection_host.conf: -------------------------------------------------------------------------------- 1 | {% include "_header_comment.conf" %} 2 | 3 | {% if enabled %} 4 | 5 | {% include "_hsts_map.conf" %} 6 | 7 | server { 8 | {% include "_listen.conf" %} 9 | {% include "_certificates.conf" %} 10 | {% include "_assets.conf" %} 11 | {% include "_exploits.conf" %} 12 | {% include "_hsts.conf" %} 13 | {% include "_forced_ssl.conf" %} 14 | 15 | access_log /data/logs/redirection-host-{{ id }}_access.log standard; 16 | error_log /data/logs/redirection-host-{{ id }}_error.log warn; 17 | 18 | {{ advanced_config }} 19 | 20 | {% if use_default_location %} 21 | location / { 22 | {% include "_hsts.conf" %} 23 | 24 | {% if preserve_path == 1 or preserve_path == true %} 25 | return {{ forward_http_code }} {{ forward_scheme }}://{{ forward_domain_name }}$request_uri; 26 | {% else %} 27 | return {{ forward_http_code }} {{ forward_scheme }}://{{ forward_domain_name }}; 28 | {% endif %} 29 | } 30 | {% endif %} 31 | 32 | # Custom 33 | include /data/nginx/custom/server_redirect[.]conf; 34 | } 35 | {% endif %} 36 | -------------------------------------------------------------------------------- /backend/templates/stream.conf: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------ 2 | # {{ incoming_port }} TCP: {{ tcp_forwarding }} UDP: {{ udp_forwarding }} 3 | # ------------------------------------------------------------ 4 | 5 | {% if enabled %} 6 | {% if tcp_forwarding == 1 or tcp_forwarding == true -%} 7 | server { 8 | listen {{ incoming_port }} {%- if certificate %} ssl {%- endif %}; 9 | {% unless ipv6 -%} # {%- endunless -%} listen [::]:{{ incoming_port }} {%- if certificate %} ssl {%- endif %}; 10 | 11 | {%- include "_certificates_stream.conf" %} 12 | 13 | proxy_pass {{ forwarding_host }}:{{ forwarding_port }}; 14 | 15 | # Custom 16 | include /data/nginx/custom/server_stream[.]conf; 17 | include /data/nginx/custom/server_stream_tcp[.]conf; 18 | } 19 | {% endif %} 20 | 21 | {% if udp_forwarding == 1 or udp_forwarding == true -%} 22 | server { 23 | listen {{ incoming_port }} udp; 24 | {% unless ipv6 -%} # {%- endunless -%} listen [::]:{{ incoming_port }} udp; 25 | 26 | proxy_pass {{ forwarding_host }}:{{ forwarding_port }}; 27 | 28 | # Custom 29 | include /data/nginx/custom/server_stream[.]conf; 30 | include /data/nginx/custom/server_stream_udp[.]conf; 31 | } 32 | {% endif %} 33 | {% endif %} -------------------------------------------------------------------------------- /backend/validate-schema.js: -------------------------------------------------------------------------------- 1 | const SwaggerParser = require('@apidevtools/swagger-parser'); 2 | const chalk = require('chalk'); 3 | const schema = require('./schema'); 4 | const log = console.log; 5 | 6 | schema.getCompiledSchema().then(async (swaggerJSON) => { 7 | try { 8 | const api = await SwaggerParser.validate(swaggerJSON); 9 | console.log('API name: %s, Version: %s', api.info.title, api.info.version); 10 | log(chalk.green('❯ Schema is valid')); 11 | } catch (e) { 12 | console.error(e); 13 | log(chalk.red('❯', e.message), '\n'); 14 | process.exit(1); 15 | } 16 | }); 17 | -------------------------------------------------------------------------------- /docker/.dive-ci: -------------------------------------------------------------------------------- 1 | rules: 2 | # If the efficiency is measured below X%, mark as failed. 3 | # Expressed as a ratio between 0-1. 4 | lowestEfficiency: 0.99 5 | 6 | # If the amount of wasted space is at least X or larger than X, mark as failed. 7 | # Expressed in B, KB, MB, and GB. 8 | highestWastedBytes: 15MB 9 | 10 | # If the amount of wasted space makes up for X% or more of the image, mark as failed. 11 | # Note: the base image layer is NOT included in the total image size. 12 | # Expressed as a ratio between 0-1; fails if the threshold is met or crossed. 13 | highestUserWastedPercent: 0.02 14 | 15 | -------------------------------------------------------------------------------- /docker/Dockerfile-zh: -------------------------------------------------------------------------------- 1 | FROM jc21/nginx-proxy-manager:2.12.3 2 | 3 | ENV NPM_LANGUAGE="zh" 4 | 5 | EXPOSE 80 81 443 6 | 7 | RUN rm -rf /app/frontend /var/www/html/index.html 8 | COPY frontend/dist /app/frontend 9 | COPY docker/rootfs/var/www/html/index.html /var/www/html/index.html 10 | 11 | WORKDIR /app 12 | 13 | VOLUME [ "/data", "/etc/letsencrypt" ] 14 | ENTRYPOINT [ "/init" ] 15 | 16 | LABEL org.label-schema.schema-version="1.0" \ 17 | org.label-schema.license="MIT" \ 18 | org.label-schema.name="nginx-proxy-manager-zh" \ 19 | org.label-schema.description="Docker container for managing Nginx proxy hosts with a simple, powerful interface " \ 20 | org.label-schema.url="https://github.com/xiaoxinpro/nginx-proxy-manager-zh" \ 21 | org.label-schema.vcs-url="https://github.com/xiaoxinpro/nginx-proxy-manager-zh.git" \ 22 | org.label-schema.cmd="docker run --rm -ti chishin/nginx-proxy-manager-zh:latest" 23 | -------------------------------------------------------------------------------- /docker/ci.env: -------------------------------------------------------------------------------- 1 | AUTHENTIK_SECRET_KEY=gl8woZe8L6IIX8SC0c5Ocsj0xPkX5uJo5DVZCFl+L/QGbzuplfutYuua2ODNLEiDD3aFd9H2ylJmrke0 2 | AUTHENTIK_REDIS__HOST=authentik-redis 3 | AUTHENTIK_POSTGRESQL__HOST=db-postgres 4 | AUTHENTIK_POSTGRESQL__USER=authentik 5 | AUTHENTIK_POSTGRESQL__NAME=authentik 6 | AUTHENTIK_POSTGRESQL__PASSWORD=07EKS5NLI6Tpv68tbdvrxfvj 7 | AUTHENTIK_BOOTSTRAP_PASSWORD=admin 8 | AUTHENTIK_BOOTSTRAP_EMAIL=admin@example.com 9 | -------------------------------------------------------------------------------- /docker/ci/postgres/authentik.sql.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoxinpro/nginx-proxy-manager-zh/fcf8fce1e026a0e003ac2fe9120108484ca860ca/docker/ci/postgres/authentik.sql.gz -------------------------------------------------------------------------------- /docker/dev/dnsrouter-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "log": { 3 | "format": "nice", 4 | "level": "debug" 5 | }, 6 | "servers": [ 7 | { 8 | "host": "0.0.0.0", 9 | "port": 53, 10 | "upstreams": [ 11 | { 12 | "regex": "website[0-9]+.example\\.com", 13 | "upstream": "127.0.0.11" 14 | }, 15 | { 16 | "regex": ".*\\.example\\.com", 17 | "upstream": "1.1.1.1" 18 | }, 19 | { 20 | "regex": "local", 21 | "nxdomain": true 22 | } 23 | ], 24 | "internal": null, 25 | "default_upstream": "127.0.0.11" 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /docker/dev/letsencrypt.ini: -------------------------------------------------------------------------------- 1 | text = True 2 | non-interactive = True 3 | webroot-path = /data/letsencrypt-acme-challenge 4 | key-type = ecdsa 5 | elliptic-curve = secp384r1 6 | preferred-chain = ISRG Root X1 7 | server = 8 | -------------------------------------------------------------------------------- /docker/dev/pebble-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "pebble": { 3 | "listenAddress": "0.0.0.0:443", 4 | "managementListenAddress": "0.0.0.0:15000", 5 | "certificate": "test/certs/localhost/cert.pem", 6 | "privateKey": "test/certs/localhost/key.pem", 7 | "httpPort": 80, 8 | "tlsPort": 443, 9 | "ocspResponderURL": "", 10 | "externalAccountBindingRequired": false 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /docker/docker-compose.ci.mysql.yml: -------------------------------------------------------------------------------- 1 | # WARNING: This is a CI docker-compose file used for building and testing of the entire app, it should not be used for production. 2 | services: 3 | 4 | fullstack: 5 | environment: 6 | DB_MYSQL_HOST: 'db-mysql' 7 | DB_MYSQL_PORT: '3306' 8 | DB_MYSQL_USER: 'npm' 9 | DB_MYSQL_PASSWORD: 'npmpass' 10 | DB_MYSQL_NAME: 'npm' 11 | depends_on: 12 | - db-mysql 13 | 14 | db-mysql: 15 | image: jc21/mariadb-aria 16 | environment: 17 | MYSQL_ROOT_PASSWORD: 'npm' 18 | MYSQL_DATABASE: 'npm' 19 | MYSQL_USER: 'npm' 20 | MYSQL_PASSWORD: 'npmpass' 21 | volumes: 22 | - mysql_vol:/var/lib/mysql 23 | networks: 24 | - fulltest 25 | 26 | volumes: 27 | mysql_vol: 28 | -------------------------------------------------------------------------------- /docker/docker-compose.ci.sqlite.yml: -------------------------------------------------------------------------------- 1 | # WARNING: This is a CI docker-compose file used for building and testing of the entire app, it should not be used for production. 2 | services: 3 | 4 | fullstack: 5 | environment: 6 | DB_SQLITE_FILE: '/data/mydb.sqlite' 7 | PUID: 1000 8 | PGID: 1000 9 | DISABLE_IPV6: 'true' 10 | -------------------------------------------------------------------------------- /docker/rootfs/etc/letsencrypt.ini: -------------------------------------------------------------------------------- 1 | text = True 2 | non-interactive = True 3 | webroot-path = /data/letsencrypt-acme-challenge 4 | key-type = ecdsa 5 | elliptic-curve = secp384r1 6 | preferred-chain = ISRG Root X1 7 | -------------------------------------------------------------------------------- /docker/rootfs/etc/logrotate.d/nginx-proxy-manager: -------------------------------------------------------------------------------- 1 | /data/logs/*_access.log /data/logs/*/access.log { 2 | su npm npm 3 | create 0644 4 | weekly 5 | rotate 4 6 | missingok 7 | notifempty 8 | compress 9 | sharedscripts 10 | postrotate 11 | kill -USR1 `cat /run/nginx/nginx.pid 2>/dev/null` 2>/dev/null || true 12 | endscript 13 | } 14 | 15 | /data/logs/*_error.log /data/logs/*/error.log { 16 | su npm npm 17 | create 0644 18 | weekly 19 | rotate 10 20 | missingok 21 | notifempty 22 | compress 23 | sharedscripts 24 | postrotate 25 | kill -USR1 `cat /run/nginx/nginx.pid 2>/dev/null` 2>/dev/null || true 26 | endscript 27 | } 28 | -------------------------------------------------------------------------------- /docker/rootfs/etc/nginx/conf.d/default.conf: -------------------------------------------------------------------------------- 1 | # "You are not configured" page, which is the default if another default doesn't exist 2 | server { 3 | listen 80; 4 | listen [::]:80; 5 | 6 | set $forward_scheme "http"; 7 | set $server "127.0.0.1"; 8 | set $port "80"; 9 | 10 | server_name localhost-nginx-proxy-manager; 11 | access_log /data/logs/fallback_access.log standard; 12 | error_log /data/logs/fallback_error.log warn; 13 | include conf.d/include/assets.conf; 14 | include conf.d/include/block-exploits.conf; 15 | include conf.d/include/letsencrypt-acme-challenge.conf; 16 | 17 | location / { 18 | index index.html; 19 | root /var/www/html; 20 | } 21 | } 22 | 23 | # First 443 Host, which is the default if another default doesn't exist 24 | server { 25 | listen 443 ssl; 26 | listen [::]:443 ssl; 27 | 28 | set $forward_scheme "https"; 29 | set $server "127.0.0.1"; 30 | set $port "443"; 31 | 32 | server_name localhost; 33 | access_log /data/logs/fallback_access.log standard; 34 | error_log /dev/null crit; 35 | include conf.d/include/ssl-ciphers.conf; 36 | ssl_reject_handshake on; 37 | 38 | return 444; 39 | } 40 | -------------------------------------------------------------------------------- /docker/rootfs/etc/nginx/conf.d/dev.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 81 default; 3 | listen [::]:81 default; 4 | 5 | server_name nginxproxymanager-dev; 6 | root /app/frontend/dist; 7 | access_log /dev/null; 8 | 9 | location /api { 10 | return 302 /api/; 11 | } 12 | 13 | location /api/ { 14 | add_header X-Served-By $host; 15 | proxy_set_header Host $host; 16 | proxy_set_header X-Forwarded-Scheme $scheme; 17 | proxy_set_header X-Forwarded-Proto $scheme; 18 | proxy_set_header X-Forwarded-For $remote_addr; 19 | proxy_pass http://127.0.0.1:3000/; 20 | 21 | proxy_read_timeout 15m; 22 | proxy_send_timeout 15m; 23 | } 24 | 25 | location / { 26 | index index.html; 27 | try_files $uri $uri.html $uri/ /index.html; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /docker/rootfs/etc/nginx/conf.d/include/.gitignore: -------------------------------------------------------------------------------- 1 | resolvers.conf 2 | -------------------------------------------------------------------------------- /docker/rootfs/etc/nginx/conf.d/include/assets.conf: -------------------------------------------------------------------------------- 1 | location ~* ^.*\.(css|js|jpe?g|gif|png|webp|woff|woff2|eot|ttf|svg|ico|css\.map|js\.map)$ { 2 | if_modified_since off; 3 | 4 | # use the public cache 5 | proxy_cache public-cache; 6 | proxy_cache_key $host$request_uri; 7 | 8 | # ignore these headers for media 9 | proxy_ignore_headers Set-Cookie Cache-Control Expires X-Accel-Expires; 10 | 11 | # cache 200s and also 404s (not ideal but there are a few 404 images for some reason) 12 | proxy_cache_valid any 30m; 13 | proxy_cache_valid 404 1m; 14 | 15 | # strip this header to avoid If-Modified-Since requests 16 | proxy_hide_header Last-Modified; 17 | proxy_hide_header Cache-Control; 18 | proxy_hide_header Vary; 19 | 20 | proxy_cache_bypass 0; 21 | proxy_no_cache 0; 22 | 23 | proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504 http_404; 24 | proxy_connect_timeout 5s; 25 | proxy_read_timeout 45s; 26 | 27 | expires @30m; 28 | access_log off; 29 | 30 | include conf.d/include/proxy.conf; 31 | } 32 | -------------------------------------------------------------------------------- /docker/rootfs/etc/nginx/conf.d/include/force-ssl.conf: -------------------------------------------------------------------------------- 1 | set $test ""; 2 | if ($scheme = "http") { 3 | set $test "H"; 4 | } 5 | if ($request_uri = /.well-known/acme-challenge/test-challenge) { 6 | set $test "${test}T"; 7 | } 8 | if ($test = H) { 9 | return 301 https://$host$request_uri; 10 | } 11 | -------------------------------------------------------------------------------- /docker/rootfs/etc/nginx/conf.d/include/ip_ranges.conf: -------------------------------------------------------------------------------- 1 | # This should be left blank is it is populated programatically 2 | # by the application backend. 3 | -------------------------------------------------------------------------------- /docker/rootfs/etc/nginx/conf.d/include/log.conf: -------------------------------------------------------------------------------- 1 | log_format proxy '[$time_local] $upstream_cache_status $upstream_status $status - $request_method $scheme $host "$request_uri" [Client $remote_addr] [Length $body_bytes_sent] [Gzip $gzip_ratio] [Sent-to $server] "$http_user_agent" "$http_referer"'; 2 | log_format standard '[$time_local] $status - $request_method $scheme $host "$request_uri" [Client $remote_addr] [Length $body_bytes_sent] [Gzip $gzip_ratio] "$http_user_agent" "$http_referer"'; 3 | 4 | access_log /data/logs/fallback_access.log proxy; 5 | -------------------------------------------------------------------------------- /docker/rootfs/etc/nginx/conf.d/include/proxy.conf: -------------------------------------------------------------------------------- 1 | add_header X-Served-By $host; 2 | proxy_set_header Host $host; 3 | proxy_set_header X-Forwarded-Scheme $scheme; 4 | proxy_set_header X-Forwarded-Proto $scheme; 5 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 6 | proxy_set_header X-Real-IP $remote_addr; 7 | proxy_pass $forward_scheme://$server:$port$request_uri; 8 | 9 | -------------------------------------------------------------------------------- /docker/rootfs/etc/nginx/conf.d/include/ssl-cache-stream.conf: -------------------------------------------------------------------------------- 1 | ssl_session_timeout 5m; 2 | ssl_session_cache shared:SSL_stream:50m; 3 | -------------------------------------------------------------------------------- /docker/rootfs/etc/nginx/conf.d/include/ssl-cache.conf: -------------------------------------------------------------------------------- 1 | ssl_session_timeout 5m; 2 | ssl_session_cache shared:SSL:50m; 3 | -------------------------------------------------------------------------------- /docker/rootfs/etc/nginx/conf.d/include/ssl-ciphers.conf: -------------------------------------------------------------------------------- 1 | # intermediate configuration. tweak to your needs. 2 | ssl_protocols TLSv1.2 TLSv1.3; 3 | ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384'; 4 | ssl_prefer_server_ciphers off; 5 | -------------------------------------------------------------------------------- /docker/rootfs/etc/nginx/conf.d/production.conf: -------------------------------------------------------------------------------- 1 | # Admin Interface 2 | server { 3 | listen 81 default; 4 | listen [::]:81 default; 5 | 6 | server_name nginxproxymanager; 7 | root /app/frontend; 8 | access_log /dev/null; 9 | 10 | location /api { 11 | return 302 /api/; 12 | } 13 | 14 | location /api/ { 15 | add_header X-Served-By $host; 16 | proxy_set_header Host $host; 17 | proxy_set_header X-Forwarded-Scheme $scheme; 18 | proxy_set_header X-Forwarded-Proto $scheme; 19 | proxy_set_header X-Forwarded-For $remote_addr; 20 | proxy_pass http://127.0.0.1:3000/; 21 | 22 | proxy_read_timeout 15m; 23 | proxy_send_timeout 15m; 24 | } 25 | 26 | location / { 27 | index index.html; 28 | if ($request_uri ~ ^/(.*)\.html$) { 29 | return 302 /$1; 30 | } 31 | try_files $uri $uri.html $uri/ /index.html; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /docker/rootfs/etc/s6-overlay/s6-rc.d/backend/dependencies.d/prepare: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoxinpro/nginx-proxy-manager-zh/fcf8fce1e026a0e003ac2fe9120108484ca860ca/docker/rootfs/etc/s6-overlay/s6-rc.d/backend/dependencies.d/prepare -------------------------------------------------------------------------------- /docker/rootfs/etc/s6-overlay/s6-rc.d/backend/run: -------------------------------------------------------------------------------- 1 | #!/command/with-contenv bash 2 | # shellcheck shell=bash 3 | 4 | set -e 5 | 6 | . /usr/bin/common.sh 7 | 8 | cd /app || exit 1 9 | 10 | log_info 'Starting backend ...' 11 | 12 | if [ "${DEVELOPMENT:-}" = 'true' ]; then 13 | s6-setuidgid "$PUID:$PGID" yarn install 14 | exec s6-setuidgid "$PUID:$PGID" bash -c "export HOME=$NPMHOME;node --max_old_space_size=250 --abort_on_uncaught_exception node_modules/nodemon/bin/nodemon.js" 15 | else 16 | while : 17 | do 18 | s6-setuidgid "$PUID:$PGID" bash -c "export HOME=$NPMHOME;node --abort_on_uncaught_exception --max_old_space_size=250 index.js" 19 | sleep 1 20 | done 21 | fi 22 | -------------------------------------------------------------------------------- /docker/rootfs/etc/s6-overlay/s6-rc.d/backend/type: -------------------------------------------------------------------------------- 1 | longrun 2 | -------------------------------------------------------------------------------- /docker/rootfs/etc/s6-overlay/s6-rc.d/frontend/dependencies.d/prepare: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoxinpro/nginx-proxy-manager-zh/fcf8fce1e026a0e003ac2fe9120108484ca860ca/docker/rootfs/etc/s6-overlay/s6-rc.d/frontend/dependencies.d/prepare -------------------------------------------------------------------------------- /docker/rootfs/etc/s6-overlay/s6-rc.d/frontend/run: -------------------------------------------------------------------------------- 1 | #!/command/with-contenv bash 2 | # shellcheck shell=bash 3 | 4 | set -e 5 | 6 | # This service is DEVELOPMENT only. 7 | 8 | if [ "$DEVELOPMENT" = 'true' ]; then 9 | . /usr/bin/common.sh 10 | cd /app/frontend || exit 1 11 | HOME=$NPMHOME 12 | export HOME 13 | mkdir -p /app/frontend/dist 14 | chown -R "$PUID:$PGID" /app/frontend/dist 15 | 16 | log_info 'Starting frontend ...' 17 | s6-setuidgid "$PUID:$PGID" yarn install 18 | exec s6-setuidgid "$PUID:$PGID" yarn watch 19 | else 20 | exit 0 21 | fi 22 | -------------------------------------------------------------------------------- /docker/rootfs/etc/s6-overlay/s6-rc.d/frontend/type: -------------------------------------------------------------------------------- 1 | longrun 2 | -------------------------------------------------------------------------------- /docker/rootfs/etc/s6-overlay/s6-rc.d/nginx/dependencies.d/prepare: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoxinpro/nginx-proxy-manager-zh/fcf8fce1e026a0e003ac2fe9120108484ca860ca/docker/rootfs/etc/s6-overlay/s6-rc.d/nginx/dependencies.d/prepare -------------------------------------------------------------------------------- /docker/rootfs/etc/s6-overlay/s6-rc.d/nginx/run: -------------------------------------------------------------------------------- 1 | #!/command/with-contenv bash 2 | # shellcheck shell=bash 3 | 4 | set -e 5 | 6 | . /usr/bin/common.sh 7 | 8 | log_info 'Starting nginx ...' 9 | exec s6-setuidgid "$PUID:$PGID" nginx 10 | -------------------------------------------------------------------------------- /docker/rootfs/etc/s6-overlay/s6-rc.d/nginx/type: -------------------------------------------------------------------------------- 1 | longrun 2 | -------------------------------------------------------------------------------- /docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/00-all.sh: -------------------------------------------------------------------------------- 1 | #!/command/with-contenv bash 2 | # shellcheck shell=bash 3 | 4 | set -e 5 | 6 | . /usr/bin/common.sh 7 | 8 | if [ "$(id -u)" != "0" ]; then 9 | log_fatal "This docker container must be run as root, do not specify a user.\nYou can specify PUID and PGID env vars to run processes as that user and group after initialization." 10 | fi 11 | 12 | if [ "$DEBUG" = "true" ]; then 13 | set -x 14 | fi 15 | 16 | . /etc/s6-overlay/s6-rc.d/prepare/10-usergroup.sh 17 | . /etc/s6-overlay/s6-rc.d/prepare/20-paths.sh 18 | . /etc/s6-overlay/s6-rc.d/prepare/30-ownership.sh 19 | . /etc/s6-overlay/s6-rc.d/prepare/40-dynamic.sh 20 | . /etc/s6-overlay/s6-rc.d/prepare/50-ipv6.sh 21 | . /etc/s6-overlay/s6-rc.d/prepare/60-secrets.sh 22 | . /etc/s6-overlay/s6-rc.d/prepare/90-banner.sh 23 | -------------------------------------------------------------------------------- /docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/10-usergroup.sh: -------------------------------------------------------------------------------- 1 | #!/command/with-contenv bash 2 | # shellcheck shell=bash 3 | 4 | set -e 5 | 6 | log_info "Configuring $NPMUSER user ..." 7 | 8 | if id -u "$NPMUSER" 2>/dev/null; then 9 | # user already exists 10 | usermod -u "$PUID" "$NPMUSER" 11 | else 12 | # Add user 13 | useradd -o -u "$PUID" -U -d "$NPMHOME" -s /bin/false "$NPMUSER" 14 | fi 15 | 16 | log_info "Configuring $NPMGROUP group ..." 17 | if [ "$(get_group_id "$NPMGROUP")" = '' ]; then 18 | # Add group. This will not set the id properly if it's already taken 19 | groupadd -f -g "$PGID" "$NPMGROUP" 20 | else 21 | groupmod -o -g "$PGID" "$NPMGROUP" 22 | fi 23 | 24 | # Set the group ID and check it 25 | groupmod -o -g "$PGID" "$NPMGROUP" 26 | if [ "$(get_group_id "$NPMGROUP")" != "$PGID" ]; then 27 | echo "ERROR: Unable to set group id properly" 28 | exit 1 29 | fi 30 | 31 | # Set the group against the user and check it 32 | usermod -G "$PGID" "$NPMGROUP" 33 | if [ "$(id -g "$NPMUSER")" != "$PGID" ] ; then 34 | echo "ERROR: Unable to set group against the user properly" 35 | exit 1 36 | fi 37 | 38 | # Home for user 39 | mkdir -p "$NPMHOME" 40 | chown -R "$PUID:$PGID" "$NPMHOME" 41 | -------------------------------------------------------------------------------- /docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/20-paths.sh: -------------------------------------------------------------------------------- 1 | #!/command/with-contenv bash 2 | # shellcheck shell=bash 3 | 4 | set -e 5 | 6 | log_info 'Checking paths ...' 7 | 8 | # Ensure /data is mounted 9 | if [ ! -d '/data' ]; then 10 | log_fatal '/data is not mounted! Check your docker configuration.' 11 | fi 12 | # Ensure /etc/letsencrypt is mounted 13 | if [ ! -d '/etc/letsencrypt' ]; then 14 | log_fatal '/etc/letsencrypt is not mounted! Check your docker configuration.' 15 | fi 16 | 17 | # Create required folders 18 | mkdir -p \ 19 | /data/nginx \ 20 | /data/custom_ssl \ 21 | /data/logs \ 22 | /data/access \ 23 | /data/nginx/default_host \ 24 | /data/nginx/default_www \ 25 | /data/nginx/proxy_host \ 26 | /data/nginx/redirection_host \ 27 | /data/nginx/stream \ 28 | /data/nginx/dead_host \ 29 | /data/nginx/temp \ 30 | /data/letsencrypt-acme-challenge \ 31 | /run/nginx \ 32 | /tmp/nginx/body \ 33 | /var/log/nginx \ 34 | /var/lib/nginx/cache/public \ 35 | /var/lib/nginx/cache/private \ 36 | /var/cache/nginx/proxy_temp 37 | 38 | touch /var/log/nginx/error.log || true 39 | chmod 777 /var/log/nginx/error.log || true 40 | chmod -R 777 /var/cache/nginx || true 41 | chmod 644 /etc/logrotate.d/nginx-proxy-manager 42 | -------------------------------------------------------------------------------- /docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/30-ownership.sh: -------------------------------------------------------------------------------- 1 | #!/command/with-contenv bash 2 | # shellcheck shell=bash 3 | 4 | set -e 5 | 6 | log_info 'Setting ownership ...' 7 | 8 | # root 9 | chown root /tmp/nginx 10 | 11 | # npm user and group 12 | chown -R "$PUID:$PGID" /data 13 | chown -R "$PUID:$PGID" /etc/letsencrypt 14 | chown -R "$PUID:$PGID" /run/nginx 15 | chown -R "$PUID:$PGID" /tmp/nginx 16 | chown -R "$PUID:$PGID" /var/cache/nginx 17 | chown -R "$PUID:$PGID" /var/lib/logrotate 18 | chown -R "$PUID:$PGID" /var/lib/nginx 19 | chown -R "$PUID:$PGID" /var/log/nginx 20 | 21 | # Don't chown entire /etc/nginx folder as this causes crashes on some systems 22 | chown -R "$PUID:$PGID" /etc/nginx/nginx 23 | chown -R "$PUID:$PGID" /etc/nginx/nginx.conf 24 | chown -R "$PUID:$PGID" /etc/nginx/conf.d 25 | 26 | # Prevents errors when installing python certbot plugins when non-root 27 | chown "$PUID:$PGID" /opt/certbot /opt/certbot/bin 28 | find /opt/certbot/lib/python*/site-packages -not -user "$PUID" -execdir chown "$PUID:$PGID" {} \+ 29 | -------------------------------------------------------------------------------- /docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/40-dynamic.sh: -------------------------------------------------------------------------------- 1 | #!/command/with-contenv bash 2 | # shellcheck shell=bash 3 | 4 | set -e 5 | 6 | log_info 'Dynamic resolvers ...' 7 | 8 | DISABLE_IPV6=$(echo "${DISABLE_IPV6:-}" | tr '[:upper:]' '[:lower:]') 9 | 10 | # Dynamically generate resolvers file, if resolver is IPv6, enclose in `[]` 11 | # thanks @tfmm 12 | if [ "$DISABLE_IPV6" == "true" ] || [ "$DISABLE_IPV6" == "on" ] || [ "$DISABLE_IPV6" == "1" ] || [ "$DISABLE_IPV6" == "yes" ]; 13 | then 14 | echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" { sub(/%.*$/,"",$2); print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf) ipv6=off valid=10s;" > /etc/nginx/conf.d/include/resolvers.conf 15 | else 16 | echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" { sub(/%.*$/,"",$2); print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf) valid=10s;" > /etc/nginx/conf.d/include/resolvers.conf 17 | fi 18 | -------------------------------------------------------------------------------- /docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/50-ipv6.sh: -------------------------------------------------------------------------------- 1 | #!/command/with-contenv bash 2 | # shellcheck shell=bash 3 | 4 | # This command reads the `DISABLE_IPV6` env var and will either enable 5 | # or disable ipv6 in all nginx configs based on this setting. 6 | 7 | set -e 8 | 9 | log_info 'IPv6 ...' 10 | 11 | # Lowercase 12 | DISABLE_IPV6=$(echo "${DISABLE_IPV6:-}" | tr '[:upper:]' '[:lower:]') 13 | 14 | process_folder () { 15 | FILES=$(find "$1" -type f -name "*.conf") 16 | SED_REGEX= 17 | 18 | if [ "$DISABLE_IPV6" == "true" ] || [ "$DISABLE_IPV6" == "on" ] || [ "$DISABLE_IPV6" == "1" ] || [ "$DISABLE_IPV6" == "yes" ]; then 19 | # IPV6 is disabled 20 | echo "Disabling IPV6 in hosts in: $1" 21 | SED_REGEX='s/^([^#]*)listen \[::\]/\1#listen [::]/g' 22 | else 23 | # IPV6 is enabled 24 | echo "Enabling IPV6 in hosts in: $1" 25 | SED_REGEX='s/^(\s*)#listen \[::\]/\1listen [::]/g' 26 | fi 27 | 28 | for FILE in $FILES 29 | do 30 | echo "- ${FILE}" 31 | echo "$(sed -E "$SED_REGEX" "$FILE")" > $FILE 32 | done 33 | 34 | # ensure the files are still owned by the npm user 35 | chown -R "$PUID:$PGID" "$1" 36 | } 37 | 38 | process_folder /etc/nginx/conf.d 39 | process_folder /data/nginx 40 | -------------------------------------------------------------------------------- /docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/90-banner.sh: -------------------------------------------------------------------------------- 1 | #!/command/with-contenv bash 2 | # shellcheck shell=bash 3 | 4 | set -e 5 | set +x 6 | 7 | echo " 8 | ------------------------------------- 9 | _ _ ____ __ __ 10 | | \ | | _ \| \/ | 11 | | \| | |_) | |\/| | 12 | | |\ | __/| | | | 13 | |_| \_|_| |_| |_| 14 | ------------------------------------- 15 | User: $NPMUSER PUID:$PUID ID:$(id -u "$NPMUSER") GROUP:$(id -g "$NPMUSER") 16 | Group: $NPMGROUP PGID:$PGID ID:$(get_group_id "$NPMGROUP") 17 | ------------------------------------- 18 | " 19 | -------------------------------------------------------------------------------- /docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/dependencies.d/base: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoxinpro/nginx-proxy-manager-zh/fcf8fce1e026a0e003ac2fe9120108484ca860ca/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/dependencies.d/base -------------------------------------------------------------------------------- /docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/type: -------------------------------------------------------------------------------- 1 | oneshot 2 | -------------------------------------------------------------------------------- /docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/up: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | /etc/s6-overlay/s6-rc.d/prepare/00-all.sh 3 | -------------------------------------------------------------------------------- /docker/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/backend: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoxinpro/nginx-proxy-manager-zh/fcf8fce1e026a0e003ac2fe9120108484ca860ca/docker/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/backend -------------------------------------------------------------------------------- /docker/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/frontend: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoxinpro/nginx-proxy-manager-zh/fcf8fce1e026a0e003ac2fe9120108484ca860ca/docker/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/frontend -------------------------------------------------------------------------------- /docker/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/nginx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoxinpro/nginx-proxy-manager-zh/fcf8fce1e026a0e003ac2fe9120108484ca860ca/docker/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/nginx -------------------------------------------------------------------------------- /docker/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/prepare: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoxinpro/nginx-proxy-manager-zh/fcf8fce1e026a0e003ac2fe9120108484ca860ca/docker/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/prepare -------------------------------------------------------------------------------- /docker/rootfs/root/.bashrc: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ -t 1 ]; then 4 | export PS1="\e[1;34m[\e[1;33m\u@\e[1;32mdocker-\h\e[1;37m:\w\[\e[1;34m]\e[1;36m\\$ \e[0m" 5 | fi 6 | 7 | # Aliases 8 | alias l='ls -lAsh --color' 9 | alias ls='ls -C1 --color' 10 | alias cp='cp -ip' 11 | alias rm='rm -i' 12 | alias mv='mv -i' 13 | alias h='cd ~;clear;' 14 | 15 | . /etc/os-release 16 | 17 | echo -e -n '\E[1;34m' 18 | figlet -w 120 "NginxProxyManager" 19 | 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, ${ID:-debian} \E[1;32m${VERSION:-unknown}\E[1;36m, Certbot \E[1;32m$(certbot --version)\E[0m" 20 | echo -e -n '\E[1;34m' 21 | cat /built-for-arch 22 | echo -e '\E[0m' 23 | -------------------------------------------------------------------------------- /docker/rootfs/usr/bin/check-health: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | OK=$(curl --silent http://127.0.0.1:81/api/ | jq --raw-output '.status') 4 | 5 | if [ "$OK" == "OK" ]; then 6 | echo "OK" 7 | exit 0 8 | else 9 | echo "NOT OK" 10 | exit 1 11 | fi 12 | -------------------------------------------------------------------------------- /docker/rootfs/var/www/html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 |您已成功启动 Nginx 代理管理器。
18 |如果您看到此站点,则说明您正在尝试访问尚未设置的主机。
19 |登录管理面板开始使用。
20 |Powered by Nginx Proxy Manager
22 |