From 6205434140755718b21c508ec3973e6af11c027d Mon Sep 17 00:00:00 2001 From: Jamie Curnow Date: Mon, 14 Jun 2021 19:29:35 +1000 Subject: [PATCH] Version 3 starter --- .gitignore | 19 +- .version | 2 +- DEV-README.md | 27 + Jenkinsfile | 168 +- README.md | 255 +- backend/.editorconfig | 8 + backend/.eslintrc.json | 73 - backend/.gitignore | 8 - backend/.golangci.yml | 92 + backend/.nancy-ignore | 22 + backend/.vscode/settings.json | 8 - backend/Taskfile.yml | 56 + backend/app.js | 90 - backend/cmd/server/main.go | 45 + backend/config/README.md | 2 - backend/config/default.json | 10 - backend/config/sqlite-test-db.json | 26 - backend/db.js | 33 - backend/doc/api.swagger.json | 1384 +- .../components/CertificateAuthorityList.json | 45 + .../CertificateAuthorityObject.json | 36 + backend/doc/components/CertificateList.json | 45 + backend/doc/components/CertificateObject.json | 82 + backend/doc/components/ConfigObject.json | 4 + backend/doc/components/DNSProviderList.json | 45 + backend/doc/components/DNSProviderObject.json | 45 + .../doc/components/DeletedItemResponse.json | 17 + backend/doc/components/ErrorObject.json | 20 + backend/doc/components/FilterObject.json | 28 + backend/doc/components/HealthObject.json | 41 + backend/doc/components/HostList.json | 45 + backend/doc/components/HostObject.json | 55 + backend/doc/components/SettingList.json | 45 + backend/doc/components/SettingObject.json | 45 + backend/doc/components/SortObject.json | 20 + backend/doc/components/StreamList.json | 45 + backend/doc/components/StreamObject.json | 55 + backend/doc/components/TokenObject.json | 22 + backend/doc/components/UserList.json | 45 + backend/doc/components/UserObject.json | 75 + backend/doc/main.go | 9 + .../certificates-authorities/caID/delete.json | 39 + .../certificates-authorities/caID/get.json | 52 + .../certificates-authorities/caID/put.json | 61 + .../paths/certificates-authorities/get.json | 88 + .../paths/certificates-authorities/post.json | 48 + .../certificates/certificateID/delete.json | 60 + .../paths/certificates/certificateID/get.json | 60 + .../paths/certificates/certificateID/put.json | 69 + backend/doc/paths/certificates/get.json | 89 + backend/doc/paths/certificates/post.json | 56 + backend/doc/paths/config/get.json | 36 + backend/doc/paths/dns-providers/get.json | 87 + backend/doc/paths/dns-providers/post.json | 54 + .../dns-providers/providerID/delete.json | 60 + .../paths/dns-providers/providerID/get.json | 58 + .../paths/dns-providers/providerID/put.json | 69 + backend/doc/paths/get.json | 47 + backend/doc/paths/hosts/get.json | 75 + backend/doc/paths/hosts/hostID/delete.json | 60 + backend/doc/paths/hosts/hostID/get.json | 46 + backend/doc/paths/hosts/hostID/put.json | 55 + backend/doc/paths/hosts/post.json | 42 + backend/doc/paths/schema/get.json | 9 + backend/doc/paths/settings/get.json | 84 + backend/doc/paths/settings/name/get.json | 55 + backend/doc/paths/settings/name/put.json | 64 + backend/doc/paths/settings/post.json | 51 + backend/doc/paths/streams/get.json | 75 + backend/doc/paths/streams/post.json | 42 + .../doc/paths/streams/streamID/delete.json | 60 + backend/doc/paths/streams/streamID/get.json | 46 + backend/doc/paths/streams/streamID/put.json | 55 + backend/doc/paths/tokens/get.json | 37 + backend/doc/paths/tokens/post.json | 79 + backend/doc/paths/users/get.json | 121 + backend/doc/paths/users/post.json | 88 + backend/doc/paths/users/userID/auth/post.json | 63 + backend/doc/paths/users/userID/delete.json | 60 + backend/doc/paths/users/userID/get.json | 66 + backend/doc/paths/users/userID/put.json | 119 + backend/go.mod | 20 + backend/go.sum | 265 + backend/index.js | 134 - backend/internal/access-list.js | 534 - backend/internal/api/context/context.go | 25 + backend/internal/api/filters/helpers.go | 208 + backend/internal/api/handler/auth.go | 54 + .../api/handler/certificate_authorities.go | 126 + backend/internal/api/handler/certificates.go | 145 + backend/internal/api/handler/config.go | 15 + backend/internal/api/handler/dns_providers.go | 129 + backend/internal/api/handler/health.go | 31 + backend/internal/api/handler/helpers.go | 151 + backend/internal/api/handler/hosts.go | 135 + backend/internal/api/handler/not_allowed.go | 14 + backend/internal/api/handler/not_found.go | 65 + backend/internal/api/handler/schema.go | 99 + backend/internal/api/handler/settings.go | 98 + backend/internal/api/handler/streams.go | 129 + backend/internal/api/handler/tokens.go | 77 + backend/internal/api/handler/users.go | 206 + backend/internal/api/http/requests.go | 46 + backend/internal/api/http/responses.go | 90 + .../internal/api/middleware/access_control.go | 13 + backend/internal/api/middleware/auth.go | 64 + .../internal/api/middleware/body_context.go | 26 + backend/internal/api/middleware/cors.go | 88 + .../internal/api/middleware/enforce_setup.go | 28 + backend/internal/api/middleware/filters.go | 114 + .../internal/api/middleware/pretty_print.go | 23 + backend/internal/api/middleware/schema.go | 55 + backend/internal/api/router.go | 171 + backend/internal/api/router_test.go | 44 + backend/internal/api/schema/certificates.go | 191 + backend/internal/api/schema/common.go | 61 + .../schema/create_certificate_authority.go | 21 + .../api/schema/create_dns_provider.go | 25 + backend/internal/api/schema/create_host.go | 75 + backend/internal/api/schema/create_setting.go | 21 + backend/internal/api/schema/create_stream.go | 27 + backend/internal/api/schema/create_user.go | 42 + backend/internal/api/schema/get_token.go | 28 + backend/internal/api/schema/set_auth.go | 21 + .../schema/update_certificate_authority.go | 17 + .../api/schema/update_dns_provider.go | 19 + backend/internal/api/schema/update_host.go | 22 + backend/internal/api/schema/update_setting.go | 16 + backend/internal/api/schema/update_stream.go | 22 + backend/internal/api/schema/update_user.go | 22 + backend/internal/api/server.go | 19 + backend/internal/audit-log.js | 78 - backend/internal/cache/cache.go | 51 + backend/internal/certificate.js | 1073 -- backend/internal/config/config.go | 78 + backend/internal/config/keys.go | 112 + backend/internal/config/vars.go | 34 + backend/internal/database/helpers.go | 46 + backend/internal/database/setup.go | 38 + backend/internal/database/sqlite.go | 73 + backend/internal/dead-host.js | 461 - backend/internal/entity/auth/methods.go | 82 + backend/internal/entity/auth/model.go | 98 + .../internal/entity/certificate/filters.go | 25 + .../internal/entity/certificate/methods.go | 173 + backend/internal/entity/certificate/model.go | 178 + .../internal/entity/certificate/structs.go | 15 + .../entity/certificateauthority/filters.go | 25 + .../entity/certificateauthority/methods.go | 125 + .../entity/certificateauthority/model.go | 67 + .../entity/certificateauthority/structs.go | 15 + .../internal/entity/dnsprovider/filters.go | 25 + .../internal/entity/dnsprovider/methods.go | 131 + backend/internal/entity/dnsprovider/model.go | 73 + .../internal/entity/dnsprovider/structs.go | 15 + backend/internal/entity/filters.go | 158 + backend/internal/entity/filters_schema.go | 223 + backend/internal/entity/host/filters.go | 25 + backend/internal/entity/host/methods.go | 171 + backend/internal/entity/host/model.go | 94 + backend/internal/entity/host/structs.go | 15 + backend/internal/entity/lists_query.go | 80 + backend/internal/entity/setting/apply.go | 15 + backend/internal/entity/setting/filters.go | 25 + backend/internal/entity/setting/methods.go | 124 + backend/internal/entity/setting/model.go | 69 + backend/internal/entity/setting/structs.go | 15 + backend/internal/entity/stream/filters.go | 25 + backend/internal/entity/stream/methods.go | 135 + backend/internal/entity/stream/model.go | 75 + backend/internal/entity/stream/structs.go | 15 + backend/internal/entity/user/filters.go | 25 + backend/internal/entity/user/methods.go | 181 + backend/internal/entity/user/model.go | 97 + backend/internal/entity/user/structs.go | 15 + backend/internal/errors/errors.go | 10 + backend/internal/host.js | 235 - backend/internal/ip_ranges.js | 147 - backend/internal/jwt/jwt.go | 60 + backend/internal/jwt/keys.go | 86 + backend/internal/logger/config.go | 40 + backend/internal/logger/logger.go | 242 + backend/internal/logger/logger_test.go | 168 + backend/internal/model/filter.go | 8 + backend/internal/model/pageinfo.go | 22 + backend/internal/nginx.js | 435 - backend/internal/proxy-host.js | 466 - backend/internal/redirection-host.js | 461 - backend/internal/report.js | 38 - backend/internal/setting.js | 133 - backend/internal/state/state.go | 31 + backend/internal/stream.js | 348 - backend/internal/token.js | 162 - backend/internal/types/db_date.go | 39 + backend/internal/types/jsonb.go | 58 + backend/internal/types/nullable_db_date.go | 54 + backend/internal/types/roles.go | 36 + backend/internal/user.js | 518 - backend/internal/util/maps.go | 9 + backend/internal/util/maps_test.go | 45 + backend/internal/util/slices.go | 35 + backend/internal/util/slices_test.go | 92 + backend/internal/validator/hosts.go | 23 + backend/internal/worker/certificate.go | 61 + backend/knexfile.js | 19 - backend/lib/access.js | 314 - backend/lib/access/access_lists-create.json | 23 - backend/lib/access/access_lists-delete.json | 23 - backend/lib/access/access_lists-get.json | 23 - backend/lib/access/access_lists-list.json | 23 - backend/lib/access/access_lists-update.json | 23 - backend/lib/access/auditlog-list.json | 7 - backend/lib/access/certificates-create.json | 23 - backend/lib/access/certificates-delete.json | 23 - backend/lib/access/certificates-get.json | 23 - backend/lib/access/certificates-list.json | 23 - backend/lib/access/certificates-update.json | 23 - backend/lib/access/dead_hosts-create.json | 23 - backend/lib/access/dead_hosts-delete.json | 23 - backend/lib/access/dead_hosts-get.json | 23 - backend/lib/access/dead_hosts-list.json | 23 - backend/lib/access/dead_hosts-update.json | 23 - backend/lib/access/permissions.json | 14 - backend/lib/access/proxy_hosts-create.json | 23 - backend/lib/access/proxy_hosts-delete.json | 23 - backend/lib/access/proxy_hosts-get.json | 23 - backend/lib/access/proxy_hosts-list.json | 23 - backend/lib/access/proxy_hosts-update.json | 23 - .../lib/access/redirection_hosts-create.json | 23 - .../lib/access/redirection_hosts-delete.json | 23 - backend/lib/access/redirection_hosts-get.json | 23 - .../lib/access/redirection_hosts-list.json | 23 - .../lib/access/redirection_hosts-update.json | 23 - backend/lib/access/reports-hosts.json | 7 - backend/lib/access/roles.json | 39 - backend/lib/access/settings-get.json | 7 - backend/lib/access/settings-list.json | 7 - backend/lib/access/settings-update.json | 7 - backend/lib/access/streams-create.json | 23 - backend/lib/access/streams-delete.json | 23 - backend/lib/access/streams-get.json | 23 - backend/lib/access/streams-list.json | 23 - backend/lib/access/streams-update.json | 23 - backend/lib/access/users-create.json | 7 - backend/lib/access/users-delete.json | 7 - backend/lib/access/users-get.json | 23 - backend/lib/access/users-list.json | 7 - backend/lib/access/users-loginas.json | 7 - backend/lib/access/users-password.json | 23 - backend/lib/access/users-permissions.json | 7 - backend/lib/access/users-update.json | 23 - backend/lib/error.js | 90 - backend/lib/express/cors.js | 40 - backend/lib/express/jwt-decode.js | 15 - backend/lib/express/jwt.js | 13 - backend/lib/express/pagination.js | 55 - backend/lib/express/user-id-from-me.js | 9 - backend/lib/helpers.js | 32 - backend/lib/migrate_template.js | 55 - backend/lib/utils.js | 20 - backend/lib/validator/api.js | 45 - backend/lib/validator/index.js | 49 - backend/logger.js | 13 - backend/migrate.js | 15 - backend/migrations/20180618015850_initial.js | 205 - .../migrations/20180929054513_websockets.js | 35 - .../migrations/20181019052346_forward_host.js | 34 - .../20181113041458_http2_support.js | 49 - .../20181213013211_forward_scheme.js | 34 - backend/migrations/20190104035154_disabled.js | 55 - .../20190215115310_customlocations.js | 35 - backend/migrations/20190218060101_hsts.js | 51 - backend/migrations/20190227065017_settings.js | 38 - .../20200410143839_access_list_client.js | 53 - .../20200410143840_access_list_client_fix.js | 34 - .../20201013035318_initial_schema.sql | 172 + .../20201013035839_initial_data.sql | 38 + .../migrations/20201014143841_pass_auth.js | 41 - .../20210210154702_redirection_scheme.js | 41 - .../20210210154703_redirection_status_code.js | 41 - backend/models/access_list.js | 102 - backend/models/access_list_auth.js | 55 - backend/models/access_list_client.js | 59 - backend/models/audit-log.js | 55 - backend/models/auth.js | 86 - backend/models/certificate.js | 73 - backend/models/dead_host.js | 81 - backend/models/now_helper.js | 13 - backend/models/proxy_host.js | 94 - backend/models/redirection_host.js | 81 - backend/models/setting.js | 30 - backend/models/stream.js | 56 - backend/models/token.js | 147 - backend/models/user.js | 56 - backend/models/user_permission.js | 29 - backend/nodemon.json | 7 - backend/package.json | 47 - backend/routes/api/audit-log.js | 52 - backend/routes/api/main.js | 51 - backend/routes/api/nginx/access_lists.js | 148 - backend/routes/api/nginx/certificates.js | 245 - backend/routes/api/nginx/dead_hosts.js | 196 - backend/routes/api/nginx/proxy_hosts.js | 196 - backend/routes/api/nginx/redirection_hosts.js | 196 - backend/routes/api/nginx/streams.js | 196 - backend/routes/api/reports.js | 29 - backend/routes/api/schema.js | 36 - backend/routes/api/settings.js | 96 - backend/routes/api/tokens.js | 54 - backend/routes/api/users.js | 239 - backend/schema/definitions.json | 240 - backend/schema/endpoints/access-lists.json | 236 - backend/schema/endpoints/certificates.json | 162 - backend/schema/endpoints/dead-hosts.json | 240 - backend/schema/endpoints/proxy-hosts.json | 387 - .../schema/endpoints/redirection-hosts.json | 305 - backend/schema/endpoints/settings.json | 99 - backend/schema/endpoints/streams.json | 223 - backend/schema/endpoints/tokens.json | 100 - backend/schema/endpoints/users.json | 287 - backend/schema/examples.json | 23 - backend/schema/index.json | 42 - backend/scripts/lint.sh | 21 + backend/scripts/test.sh | 5 + backend/setup.js | 209 - backend/templates/_assets.conf | 4 - backend/templates/_certificates.conf | 14 - backend/templates/_exploits.conf | 4 - backend/templates/_forced_ssl.conf | 6 - backend/templates/_header_comment.conf | 3 - backend/templates/_hsts.conf | 8 - backend/templates/_listen.conf | 15 - backend/templates/_location.conf | 45 - backend/templates/dead_host.conf | 22 - backend/templates/default.conf | 37 - backend/templates/ip_ranges.conf | 3 - backend/templates/letsencrypt-request.conf | 18 - backend/templates/proxy_host.conf | 70 - backend/templates/redirection_host.conf | 31 - backend/templates/stream.conf | 37 - backend/yarn.lock | 3757 ------ docker/Dockerfile | 115 +- docker/dev/Dockerfile | 38 +- docker/docker-compose.ci.yml | 64 +- docker/docker-compose.dev.yml | 54 +- docker/rootfs/bin/check-health | 11 - docker/rootfs/bin/handle-ipv6-setting | 46 - docker/rootfs/etc/cont-init.d/.gitignore | 3 - .../etc/cont-init.d/01_s6-secret-init.sh | 29 - docker/rootfs/etc/cont-init.d/10-nginx | 44 + docker/rootfs/etc/cont-init.d/20-adduser | 33 + docker/rootfs/etc/cont-init.d/30-dbmate | 16 + docker/rootfs/etc/letsencrypt.ini | 4 - docker/rootfs/etc/nginx/conf.d/default.conf | 23 +- docker/rootfs/etc/nginx/conf.d/dev.conf | 22 +- .../etc/nginx/conf.d/include/.gitignore | 1 - .../etc/nginx/conf.d/include/assets.conf | 42 +- .../nginx/conf.d/include/block-exploits.conf | 60 +- .../etc/nginx/conf.d/include/force-ssl.conf | 2 +- .../etc/nginx/conf.d/include/ip_ranges.conf | 2 - .../include/letsencrypt-acme-challenge.conf | 29 - .../etc/nginx/conf.d/include/proxy.conf | 2 - .../etc/nginx/conf.d/include/resolvers.conf | 1 + .../rootfs/etc/nginx/conf.d/production.conf | 23 +- docker/rootfs/etc/nginx/nginx.conf | 49 +- docker/rootfs/etc/services.d/backend/finish | 5 + docker/rootfs/etc/services.d/backend/run | 23 + docker/rootfs/etc/services.d/frontend/finish | 1 - docker/rootfs/etc/services.d/frontend/run | 8 +- docker/rootfs/etc/services.d/manager/finish | 3 - docker/rootfs/etc/services.d/manager/run | 18 - docker/rootfs/etc/services.d/nginx/finish | 7 +- docker/rootfs/etc/services.d/nginx/run | 48 +- docker/rootfs/root/.bashrc | 6 +- docker/rootfs/root/.config/litecli/config | 13 + .../var/www/html/fonts/Aldrich-Regular.ttf | Bin 0 -> 56548 bytes .../var/www/html/fonts/Poppins-Bold.ttf | Bin 0 -> 141260 bytes .../var/www/html/fonts/Poppins-Regular.ttf | Bin 0 -> 145312 bytes docker/rootfs/var/www/html/images/bg.jpg | Bin 0 -> 839380 bytes docker/rootfs/var/www/html/images/logo.png | Bin 0 -> 2837 bytes docker/rootfs/var/www/html/index.html | 48 +- docker/rootfs/var/www/html/main.css | 399 + docs/.gitignore | 2 + docs/.vuepress/config.js | 2 +- docs/README.md | 36 +- docs/setup/README.md | 161 +- frontend/.babelrc | 17 - frontend/.env.development | 2 + frontend/.eslintrc | 119 + frontend/.gitignore | 8 +- {backend => frontend}/.prettierrc | 6 +- frontend/README.md | 44 + frontend/fonts/feather | 1 - ...urce-sans-pro-v14-latin-ext_latin-700.woff | Bin 31740 -> 0 bytes ...rce-sans-pro-v14-latin-ext_latin-700.woff2 | Bin 25348 -> 0 bytes ...ans-pro-v14-latin-ext_latin-700italic.woff | Bin 28540 -> 0 bytes ...ns-pro-v14-latin-ext_latin-700italic.woff2 | Bin 22240 -> 0 bytes ...e-sans-pro-v14-latin-ext_latin-italic.woff | Bin 28744 -> 0 bytes ...-sans-pro-v14-latin-ext_latin-italic.woff2 | Bin 22436 -> 0 bytes ...-sans-pro-v14-latin-ext_latin-regular.woff | Bin 32128 -> 0 bytes ...sans-pro-v14-latin-ext_latin-regular.woff2 | Bin 25656 -> 0 bytes frontend/globalSetup.js | 4 + frontend/html/index.ejs | 9 - frontend/html/login.ejs | 9 - frontend/html/partials/footer.ejs | 2 - frontend/html/partials/header.ejs | 33 - frontend/images | 1 - frontend/jest.eslint.js | 6 + frontend/js/app/api.js | 694 - frontend/js/app/audit-log/list/item.ejs | 80 - frontend/js/app/audit-log/list/item.js | 32 - frontend/js/app/audit-log/list/main.ejs | 9 - frontend/js/app/audit-log/list/main.js | 27 - frontend/js/app/audit-log/main.ejs | 15 - frontend/js/app/audit-log/main.js | 53 - frontend/js/app/audit-log/meta.ejs | 27 - frontend/js/app/audit-log/meta.js | 7 - frontend/js/app/cache.js | 10 - frontend/js/app/controller.js | 434 - frontend/js/app/dashboard/main.ejs | 67 - frontend/js/app/dashboard/main.js | 92 - frontend/js/app/empty/main.ejs | 11 - frontend/js/app/empty/main.js | 33 - frontend/js/app/error/main.ejs | 7 - frontend/js/app/error/main.js | 27 - frontend/js/app/help/main.ejs | 12 - frontend/js/app/help/main.js | 16 - frontend/js/app/i18n.js | 23 - frontend/js/app/main.js | 155 - frontend/js/app/nginx/access/delete.ejs | 23 - frontend/js/app/nginx/access/delete.js | 32 - frontend/js/app/nginx/access/form.ejs | 108 - frontend/js/app/nginx/access/form.js | 153 - frontend/js/app/nginx/access/form/client.ejs | 13 - frontend/js/app/nginx/access/form/client.js | 7 - frontend/js/app/nginx/access/form/item.ejs | 10 - frontend/js/app/nginx/access/form/item.js | 7 - frontend/js/app/nginx/access/list/item.ejs | 42 - frontend/js/app/nginx/access/list/item.js | 33 - frontend/js/app/nginx/access/list/main.ejs | 14 - frontend/js/app/nginx/access/list/main.js | 32 - frontend/js/app/nginx/access/main.ejs | 20 - frontend/js/app/nginx/access/main.js | 81 - .../js/app/nginx/certificates-list-item.ejs | 18 - frontend/js/app/nginx/certificates/delete.ejs | 19 - frontend/js/app/nginx/certificates/delete.js | 34 - frontend/js/app/nginx/certificates/form.ejs | 177 - frontend/js/app/nginx/certificates/form.js | 267 - .../js/app/nginx/certificates/list/item.ejs | 50 - .../js/app/nginx/certificates/list/item.js | 46 - .../js/app/nginx/certificates/list/main.ejs | 12 - .../js/app/nginx/certificates/list/main.js | 32 - frontend/js/app/nginx/certificates/main.ejs | 28 - frontend/js/app/nginx/certificates/main.js | 82 - frontend/js/app/nginx/certificates/renew.ejs | 14 - frontend/js/app/nginx/certificates/renew.js | 31 - frontend/js/app/nginx/dead/delete.ejs | 23 - frontend/js/app/nginx/dead/delete.js | 32 - frontend/js/app/nginx/dead/form.ejs | 206 - frontend/js/app/nginx/dead/form.js | 286 - frontend/js/app/nginx/dead/list/item.ejs | 54 - frontend/js/app/nginx/dead/list/item.js | 61 - frontend/js/app/nginx/dead/list/main.ejs | 12 - frontend/js/app/nginx/dead/list/main.js | 32 - frontend/js/app/nginx/dead/main.ejs | 20 - frontend/js/app/nginx/dead/main.js | 81 - .../js/app/nginx/proxy/access-list-item.ejs | 13 - frontend/js/app/nginx/proxy/delete.ejs | 23 - frontend/js/app/nginx/proxy/delete.js | 32 - frontend/js/app/nginx/proxy/form.ejs | 280 - frontend/js/app/nginx/proxy/form.js | 369 - frontend/js/app/nginx/proxy/list/item.ejs | 60 - frontend/js/app/nginx/proxy/list/item.js | 61 - frontend/js/app/nginx/proxy/list/main.ejs | 14 - frontend/js/app/nginx/proxy/list/main.js | 32 - frontend/js/app/nginx/proxy/location-item.ejs | 64 - frontend/js/app/nginx/proxy/location.js | 54 - frontend/js/app/nginx/proxy/main.ejs | 20 - frontend/js/app/nginx/proxy/main.js | 81 - frontend/js/app/nginx/redirection/delete.ejs | 23 - frontend/js/app/nginx/redirection/delete.js | 32 - frontend/js/app/nginx/redirection/form.ejs | 253 - frontend/js/app/nginx/redirection/form.js | 288 - .../js/app/nginx/redirection/list/item.ejs | 63 - .../js/app/nginx/redirection/list/item.js | 61 - .../js/app/nginx/redirection/list/main.ejs | 15 - .../js/app/nginx/redirection/list/main.js | 32 - frontend/js/app/nginx/redirection/main.ejs | 20 - frontend/js/app/nginx/redirection/main.js | 81 - frontend/js/app/nginx/stream/delete.ejs | 19 - frontend/js/app/nginx/stream/delete.js | 32 - frontend/js/app/nginx/stream/form.ejs | 55 - frontend/js/app/nginx/stream/form.js | 91 - frontend/js/app/nginx/stream/list/item.ejs | 53 - frontend/js/app/nginx/stream/list/item.js | 54 - frontend/js/app/nginx/stream/list/main.ejs | 13 - frontend/js/app/nginx/stream/list/main.js | 32 - frontend/js/app/nginx/stream/main.ejs | 20 - frontend/js/app/nginx/stream/main.js | 81 - frontend/js/app/router.js | 19 - .../js/app/settings/default-site/main.ejs | 53 - frontend/js/app/settings/default-site/main.js | 69 - frontend/js/app/settings/list/item.ejs | 21 - frontend/js/app/settings/list/item.js | 23 - frontend/js/app/settings/list/main.ejs | 8 - frontend/js/app/settings/list/main.js | 27 - frontend/js/app/settings/main.ejs | 14 - frontend/js/app/settings/main.js | 48 - frontend/js/app/tokens.js | 126 - frontend/js/app/ui/footer/main.ejs | 16 - frontend/js/app/ui/footer/main.js | 14 - frontend/js/app/ui/header/main.ejs | 31 - frontend/js/app/ui/header/main.js | 67 - frontend/js/app/ui/main.ejs | 19 - frontend/js/app/ui/main.js | 98 - frontend/js/app/ui/menu/main.ejs | 52 - frontend/js/app/ui/menu/main.js | 39 - frontend/js/app/user/delete.ejs | 19 - frontend/js/app/user/delete.js | 34 - frontend/js/app/user/form.ejs | 58 - frontend/js/app/user/form.js | 108 - frontend/js/app/user/password.ejs | 31 - frontend/js/app/user/password.js | 69 - frontend/js/app/user/permissions.ejs | 68 - frontend/js/app/user/permissions.js | 95 - frontend/js/app/users/list/item.ejs | 45 - frontend/js/app/users/list/item.js | 68 - frontend/js/app/users/list/main.ejs | 10 - frontend/js/app/users/list/main.js | 27 - frontend/js/app/users/main.ejs | 18 - frontend/js/app/users/main.js | 55 - frontend/js/i18n/messages.json | 273 - frontend/js/index.js | 112 - frontend/js/lib/helpers.js | 26 - frontend/js/lib/marionette.js | 15 - frontend/js/login.js | 5 - frontend/js/login/main.js | 14 - frontend/js/login/ui/login.ejs | 37 - frontend/js/login/ui/login.js | 42 - frontend/js/models/access-list.js | 25 - frontend/js/models/audit-log.js | 18 - frontend/js/models/certificate.js | 38 - frontend/js/models/dead-host.js | 32 - frontend/js/models/proxy-host-location.js | 35 - frontend/js/models/proxy-host.js | 40 - frontend/js/models/redirection-host.js | 37 - frontend/js/models/setting.js | 22 - frontend/js/models/stream.js | 29 - frontend/js/models/user.js | 54 - frontend/package.json | 135 +- .../images}/default-avatar.jpg | Bin .../favicon}/android-chrome-192x192.png | Bin .../favicon}/android-chrome-512x512.png | Bin .../images/favicon}/apple-touch-icon.png | Bin .../images/favicon}/browserconfig.xml | 0 .../images/favicon}/favicon-16x16.png | Bin .../images/favicon}/favicon-32x32.png | Bin .../images/favicon}/favicon.ico | Bin .../images/favicon}/mstile-150x150.png | Bin .../images/favicon}/safari-pinned-tab.svg | 0 .../images/favicon}/site.webmanifest | 0 .../images}/logo-256.png | Bin .../images/logo-bold-horizontal-grey.svg | 1 + .../images/logo-text-horizontal-grey.png | Bin 0 -> 22203 bytes .../images}/logo-text-vertical-grey.png | Bin frontend/public/index.html | 59 + frontend/scss/custom.scss | 42 - frontend/scss/fonts.scss | 39 - frontend/scss/selectize.scss | 196 - frontend/scss/styles.scss | 17 - frontend/scss/tabler-extra.scss | 170 - frontend/src/App.test.tsx | 11 + frontend/src/App.tsx | 16 + frontend/src/api/npm/base.ts | 89 + frontend/src/api/npm/createUser.ts | 32 + frontend/src/api/npm/getToken.ts | 24 + frontend/src/api/npm/getUser.ts | 12 + frontend/src/api/npm/index.ts | 6 + frontend/src/api/npm/refreshToken.ts | 14 + frontend/src/api/npm/requestHealth.ts | 15 + frontend/src/api/npm/responseTypes.ts | 33 + frontend/src/components/Footer.tsx | 56 + frontend/src/components/Loading.tsx | 18 + frontend/src/components/Router.tsx | 57 + frontend/src/components/SinglePage.tsx | 32 + frontend/src/components/SiteWrapper.tsx | 193 + frontend/src/components/Unhealthy.tsx | 45 + frontend/src/components/index.ts | 6 + frontend/src/components/page/Footer.tsx | 56 + frontend/src/components/page/Header.tsx | 130 + frontend/src/components/page/index.ts | 2 + frontend/src/context/AuthContext.tsx | 76 + frontend/src/context/HealthContext.tsx | 82 + frontend/src/context/UserContext.tsx | 54 + frontend/src/context/index.ts | 3 + .../src/fonts/feather/feather-webfont.eot | Bin 0 -> 64468 bytes .../src/fonts/feather/feather-webfont.svg | 1038 ++ .../src/fonts/feather/feather-webfont.ttf | Bin 0 -> 66816 bytes .../src/fonts/feather/feather-webfont.woff | Bin 0 -> 28984 bytes .../fonts/tabler-webfont/tabler-webfont.eot | Bin 0 -> 86436 bytes .../fonts/tabler-webfont/tabler-webfont.svg | 1044 ++ .../fonts/tabler-webfont/tabler-webfont.ttf | Bin 0 -> 86244 bytes .../fonts/tabler-webfont/tabler-webfont.woff | Bin 0 -> 41456 bytes .../fonts/tabler-webfont/tabler-webfont.woff2 | Bin 0 -> 33880 bytes frontend/src/img/logo-text-vertical-grey.png | Bin 0 -> 14281 bytes frontend/src/index.scss | 62 + frontend/src/index.tsx | 10 + frontend/src/modules/AuthStore.ts | 84 + frontend/src/pages/Dashboard/index.tsx | 24 + frontend/src/pages/Login/index.tsx | 94 + frontend/src/pages/Setup/index.tsx | 143 + frontend/src/react-app-env.d.ts | 1 + frontend/src/tabler.css | 2 + frontend/tsconfig.json | 27 + frontend/webpack.config.js | 144 - frontend/yarn.lock | 11125 ++++++++++++---- global/certbot-dns-plugins.js | 415 - scripts/build-cleanup | 19 + scripts/buildx | 10 +- scripts/ci/frontend-build | 34 + scripts/docs-build | 2 +- scripts/frontend-build | 17 - scripts/frontend-lint | 17 + scripts/install-s6 | 34 - scripts/sqlite | 20 + scripts/start-dev | 5 +- scripts/test-dev | 2 +- test/.dockerignore | 1 - test/.gitignore | 6 +- test/cypress/Dockerfile | 7 +- .../integration/api/Certificates.spec.js | 119 + test/cypress/integration/api/Health.spec.js | 12 +- test/cypress/integration/api/Settings.spec.js | 118 + .../integration/api/SetupPhase.spec.js | 29 + .../integration/api/SwaggerSchema.spec.js | 9 + test/cypress/integration/api/Users.spec.js | 156 +- test/cypress/plugins/backendApi/client.js | 40 +- test/cypress/plugins/backendApi/task.js | 2 +- test/cypress/plugins/index.js | 2 +- test/cypress/support/commands.js | 62 +- test/package.json | 2 +- test/yarn.lock | 408 +- 642 files changed, 25817 insertions(+), 32319 deletions(-) create mode 100644 DEV-README.md create mode 100644 backend/.editorconfig delete mode 100644 backend/.eslintrc.json delete mode 100644 backend/.gitignore create mode 100644 backend/.golangci.yml create mode 100644 backend/.nancy-ignore delete mode 100644 backend/.vscode/settings.json create mode 100644 backend/Taskfile.yml delete mode 100644 backend/app.js create mode 100644 backend/cmd/server/main.go delete mode 100644 backend/config/README.md delete mode 100644 backend/config/default.json delete mode 100644 backend/config/sqlite-test-db.json delete mode 100644 backend/db.js create mode 100644 backend/doc/components/CertificateAuthorityList.json create mode 100644 backend/doc/components/CertificateAuthorityObject.json create mode 100644 backend/doc/components/CertificateList.json create mode 100644 backend/doc/components/CertificateObject.json create mode 100644 backend/doc/components/ConfigObject.json create mode 100644 backend/doc/components/DNSProviderList.json create mode 100644 backend/doc/components/DNSProviderObject.json create mode 100644 backend/doc/components/DeletedItemResponse.json create mode 100644 backend/doc/components/ErrorObject.json create mode 100644 backend/doc/components/FilterObject.json create mode 100644 backend/doc/components/HealthObject.json create mode 100644 backend/doc/components/HostList.json create mode 100644 backend/doc/components/HostObject.json create mode 100644 backend/doc/components/SettingList.json create mode 100644 backend/doc/components/SettingObject.json create mode 100644 backend/doc/components/SortObject.json create mode 100644 backend/doc/components/StreamList.json create mode 100644 backend/doc/components/StreamObject.json create mode 100644 backend/doc/components/TokenObject.json create mode 100644 backend/doc/components/UserList.json create mode 100644 backend/doc/components/UserObject.json create mode 100644 backend/doc/main.go create mode 100644 backend/doc/paths/certificates-authorities/caID/delete.json create mode 100644 backend/doc/paths/certificates-authorities/caID/get.json create mode 100644 backend/doc/paths/certificates-authorities/caID/put.json create mode 100644 backend/doc/paths/certificates-authorities/get.json create mode 100644 backend/doc/paths/certificates-authorities/post.json create mode 100644 backend/doc/paths/certificates/certificateID/delete.json create mode 100644 backend/doc/paths/certificates/certificateID/get.json create mode 100644 backend/doc/paths/certificates/certificateID/put.json create mode 100644 backend/doc/paths/certificates/get.json create mode 100644 backend/doc/paths/certificates/post.json create mode 100644 backend/doc/paths/config/get.json create mode 100644 backend/doc/paths/dns-providers/get.json create mode 100644 backend/doc/paths/dns-providers/post.json create mode 100644 backend/doc/paths/dns-providers/providerID/delete.json create mode 100644 backend/doc/paths/dns-providers/providerID/get.json create mode 100644 backend/doc/paths/dns-providers/providerID/put.json create mode 100644 backend/doc/paths/get.json create mode 100644 backend/doc/paths/hosts/get.json create mode 100644 backend/doc/paths/hosts/hostID/delete.json create mode 100644 backend/doc/paths/hosts/hostID/get.json create mode 100644 backend/doc/paths/hosts/hostID/put.json create mode 100644 backend/doc/paths/hosts/post.json create mode 100644 backend/doc/paths/schema/get.json create mode 100644 backend/doc/paths/settings/get.json create mode 100644 backend/doc/paths/settings/name/get.json create mode 100644 backend/doc/paths/settings/name/put.json create mode 100644 backend/doc/paths/settings/post.json create mode 100644 backend/doc/paths/streams/get.json create mode 100644 backend/doc/paths/streams/post.json create mode 100644 backend/doc/paths/streams/streamID/delete.json create mode 100644 backend/doc/paths/streams/streamID/get.json create mode 100644 backend/doc/paths/streams/streamID/put.json create mode 100644 backend/doc/paths/tokens/get.json create mode 100644 backend/doc/paths/tokens/post.json create mode 100644 backend/doc/paths/users/get.json create mode 100644 backend/doc/paths/users/post.json create mode 100644 backend/doc/paths/users/userID/auth/post.json create mode 100644 backend/doc/paths/users/userID/delete.json create mode 100644 backend/doc/paths/users/userID/get.json create mode 100644 backend/doc/paths/users/userID/put.json create mode 100644 backend/go.mod create mode 100644 backend/go.sum delete mode 100644 backend/index.js delete mode 100644 backend/internal/access-list.js create mode 100644 backend/internal/api/context/context.go create mode 100644 backend/internal/api/filters/helpers.go create mode 100644 backend/internal/api/handler/auth.go create mode 100644 backend/internal/api/handler/certificate_authorities.go create mode 100644 backend/internal/api/handler/certificates.go create mode 100644 backend/internal/api/handler/config.go create mode 100644 backend/internal/api/handler/dns_providers.go create mode 100644 backend/internal/api/handler/health.go create mode 100644 backend/internal/api/handler/helpers.go create mode 100644 backend/internal/api/handler/hosts.go create mode 100644 backend/internal/api/handler/not_allowed.go create mode 100644 backend/internal/api/handler/not_found.go create mode 100644 backend/internal/api/handler/schema.go create mode 100644 backend/internal/api/handler/settings.go create mode 100644 backend/internal/api/handler/streams.go create mode 100644 backend/internal/api/handler/tokens.go create mode 100644 backend/internal/api/handler/users.go create mode 100644 backend/internal/api/http/requests.go create mode 100644 backend/internal/api/http/responses.go create mode 100644 backend/internal/api/middleware/access_control.go create mode 100644 backend/internal/api/middleware/auth.go create mode 100644 backend/internal/api/middleware/body_context.go create mode 100644 backend/internal/api/middleware/cors.go create mode 100644 backend/internal/api/middleware/enforce_setup.go create mode 100644 backend/internal/api/middleware/filters.go create mode 100644 backend/internal/api/middleware/pretty_print.go create mode 100644 backend/internal/api/middleware/schema.go create mode 100644 backend/internal/api/router.go create mode 100644 backend/internal/api/router_test.go create mode 100644 backend/internal/api/schema/certificates.go create mode 100644 backend/internal/api/schema/common.go create mode 100644 backend/internal/api/schema/create_certificate_authority.go create mode 100644 backend/internal/api/schema/create_dns_provider.go create mode 100644 backend/internal/api/schema/create_host.go create mode 100644 backend/internal/api/schema/create_setting.go create mode 100644 backend/internal/api/schema/create_stream.go create mode 100644 backend/internal/api/schema/create_user.go create mode 100644 backend/internal/api/schema/get_token.go create mode 100644 backend/internal/api/schema/set_auth.go create mode 100644 backend/internal/api/schema/update_certificate_authority.go create mode 100644 backend/internal/api/schema/update_dns_provider.go create mode 100644 backend/internal/api/schema/update_host.go create mode 100644 backend/internal/api/schema/update_setting.go create mode 100644 backend/internal/api/schema/update_stream.go create mode 100644 backend/internal/api/schema/update_user.go create mode 100644 backend/internal/api/server.go delete mode 100644 backend/internal/audit-log.js create mode 100644 backend/internal/cache/cache.go delete mode 100644 backend/internal/certificate.js create mode 100644 backend/internal/config/config.go create mode 100644 backend/internal/config/keys.go create mode 100644 backend/internal/config/vars.go create mode 100644 backend/internal/database/helpers.go create mode 100644 backend/internal/database/setup.go create mode 100644 backend/internal/database/sqlite.go delete mode 100644 backend/internal/dead-host.js create mode 100644 backend/internal/entity/auth/methods.go create mode 100644 backend/internal/entity/auth/model.go create mode 100644 backend/internal/entity/certificate/filters.go create mode 100644 backend/internal/entity/certificate/methods.go create mode 100644 backend/internal/entity/certificate/model.go create mode 100644 backend/internal/entity/certificate/structs.go create mode 100644 backend/internal/entity/certificateauthority/filters.go create mode 100644 backend/internal/entity/certificateauthority/methods.go create mode 100644 backend/internal/entity/certificateauthority/model.go create mode 100644 backend/internal/entity/certificateauthority/structs.go create mode 100644 backend/internal/entity/dnsprovider/filters.go create mode 100644 backend/internal/entity/dnsprovider/methods.go create mode 100644 backend/internal/entity/dnsprovider/model.go create mode 100644 backend/internal/entity/dnsprovider/structs.go create mode 100644 backend/internal/entity/filters.go create mode 100644 backend/internal/entity/filters_schema.go create mode 100644 backend/internal/entity/host/filters.go create mode 100644 backend/internal/entity/host/methods.go create mode 100644 backend/internal/entity/host/model.go create mode 100644 backend/internal/entity/host/structs.go create mode 100644 backend/internal/entity/lists_query.go create mode 100644 backend/internal/entity/setting/apply.go create mode 100644 backend/internal/entity/setting/filters.go create mode 100644 backend/internal/entity/setting/methods.go create mode 100644 backend/internal/entity/setting/model.go create mode 100644 backend/internal/entity/setting/structs.go create mode 100644 backend/internal/entity/stream/filters.go create mode 100644 backend/internal/entity/stream/methods.go create mode 100644 backend/internal/entity/stream/model.go create mode 100644 backend/internal/entity/stream/structs.go create mode 100644 backend/internal/entity/user/filters.go create mode 100644 backend/internal/entity/user/methods.go create mode 100644 backend/internal/entity/user/model.go create mode 100644 backend/internal/entity/user/structs.go create mode 100644 backend/internal/errors/errors.go delete mode 100644 backend/internal/host.js delete mode 100644 backend/internal/ip_ranges.js create mode 100644 backend/internal/jwt/jwt.go create mode 100644 backend/internal/jwt/keys.go create mode 100644 backend/internal/logger/config.go create mode 100644 backend/internal/logger/logger.go create mode 100644 backend/internal/logger/logger_test.go create mode 100644 backend/internal/model/filter.go create mode 100644 backend/internal/model/pageinfo.go delete mode 100644 backend/internal/nginx.js delete mode 100644 backend/internal/proxy-host.js delete mode 100644 backend/internal/redirection-host.js delete mode 100644 backend/internal/report.js delete mode 100644 backend/internal/setting.js create mode 100644 backend/internal/state/state.go delete mode 100644 backend/internal/stream.js delete mode 100644 backend/internal/token.js create mode 100644 backend/internal/types/db_date.go create mode 100644 backend/internal/types/jsonb.go create mode 100644 backend/internal/types/nullable_db_date.go create mode 100644 backend/internal/types/roles.go delete mode 100644 backend/internal/user.js create mode 100644 backend/internal/util/maps.go create mode 100644 backend/internal/util/maps_test.go create mode 100644 backend/internal/util/slices.go create mode 100644 backend/internal/util/slices_test.go create mode 100644 backend/internal/validator/hosts.go create mode 100644 backend/internal/worker/certificate.go delete mode 100644 backend/knexfile.js delete mode 100644 backend/lib/access.js delete mode 100644 backend/lib/access/access_lists-create.json delete mode 100644 backend/lib/access/access_lists-delete.json delete mode 100644 backend/lib/access/access_lists-get.json delete mode 100644 backend/lib/access/access_lists-list.json delete mode 100644 backend/lib/access/access_lists-update.json delete mode 100644 backend/lib/access/auditlog-list.json delete mode 100644 backend/lib/access/certificates-create.json delete mode 100644 backend/lib/access/certificates-delete.json delete mode 100644 backend/lib/access/certificates-get.json delete mode 100644 backend/lib/access/certificates-list.json delete mode 100644 backend/lib/access/certificates-update.json delete mode 100644 backend/lib/access/dead_hosts-create.json delete mode 100644 backend/lib/access/dead_hosts-delete.json delete mode 100644 backend/lib/access/dead_hosts-get.json delete mode 100644 backend/lib/access/dead_hosts-list.json delete mode 100644 backend/lib/access/dead_hosts-update.json delete mode 100644 backend/lib/access/permissions.json delete mode 100644 backend/lib/access/proxy_hosts-create.json delete mode 100644 backend/lib/access/proxy_hosts-delete.json delete mode 100644 backend/lib/access/proxy_hosts-get.json delete mode 100644 backend/lib/access/proxy_hosts-list.json delete mode 100644 backend/lib/access/proxy_hosts-update.json delete mode 100644 backend/lib/access/redirection_hosts-create.json delete mode 100644 backend/lib/access/redirection_hosts-delete.json delete mode 100644 backend/lib/access/redirection_hosts-get.json delete mode 100644 backend/lib/access/redirection_hosts-list.json delete mode 100644 backend/lib/access/redirection_hosts-update.json delete mode 100644 backend/lib/access/reports-hosts.json delete mode 100644 backend/lib/access/roles.json delete mode 100644 backend/lib/access/settings-get.json delete mode 100644 backend/lib/access/settings-list.json delete mode 100644 backend/lib/access/settings-update.json delete mode 100644 backend/lib/access/streams-create.json delete mode 100644 backend/lib/access/streams-delete.json delete mode 100644 backend/lib/access/streams-get.json delete mode 100644 backend/lib/access/streams-list.json delete mode 100644 backend/lib/access/streams-update.json delete mode 100644 backend/lib/access/users-create.json delete mode 100644 backend/lib/access/users-delete.json delete mode 100644 backend/lib/access/users-get.json delete mode 100644 backend/lib/access/users-list.json delete mode 100644 backend/lib/access/users-loginas.json delete mode 100644 backend/lib/access/users-password.json delete mode 100644 backend/lib/access/users-permissions.json delete mode 100644 backend/lib/access/users-update.json delete mode 100644 backend/lib/error.js delete mode 100644 backend/lib/express/cors.js delete mode 100644 backend/lib/express/jwt-decode.js delete mode 100644 backend/lib/express/jwt.js delete mode 100644 backend/lib/express/pagination.js delete mode 100644 backend/lib/express/user-id-from-me.js delete mode 100644 backend/lib/helpers.js delete mode 100644 backend/lib/migrate_template.js delete mode 100644 backend/lib/utils.js delete mode 100644 backend/lib/validator/api.js delete mode 100644 backend/lib/validator/index.js delete mode 100644 backend/logger.js delete mode 100644 backend/migrate.js delete mode 100644 backend/migrations/20180618015850_initial.js delete mode 100644 backend/migrations/20180929054513_websockets.js delete mode 100644 backend/migrations/20181019052346_forward_host.js delete mode 100644 backend/migrations/20181113041458_http2_support.js delete mode 100644 backend/migrations/20181213013211_forward_scheme.js delete mode 100644 backend/migrations/20190104035154_disabled.js delete mode 100644 backend/migrations/20190215115310_customlocations.js delete mode 100644 backend/migrations/20190218060101_hsts.js delete mode 100644 backend/migrations/20190227065017_settings.js delete mode 100644 backend/migrations/20200410143839_access_list_client.js delete mode 100644 backend/migrations/20200410143840_access_list_client_fix.js create mode 100644 backend/migrations/20201013035318_initial_schema.sql create mode 100644 backend/migrations/20201013035839_initial_data.sql delete mode 100644 backend/migrations/20201014143841_pass_auth.js delete mode 100644 backend/migrations/20210210154702_redirection_scheme.js delete mode 100644 backend/migrations/20210210154703_redirection_status_code.js delete mode 100644 backend/models/access_list.js delete mode 100644 backend/models/access_list_auth.js delete mode 100644 backend/models/access_list_client.js delete mode 100644 backend/models/audit-log.js delete mode 100644 backend/models/auth.js delete mode 100644 backend/models/certificate.js delete mode 100644 backend/models/dead_host.js delete mode 100644 backend/models/now_helper.js delete mode 100644 backend/models/proxy_host.js delete mode 100644 backend/models/redirection_host.js delete mode 100644 backend/models/setting.js delete mode 100644 backend/models/stream.js delete mode 100644 backend/models/token.js delete mode 100644 backend/models/user.js delete mode 100644 backend/models/user_permission.js delete mode 100644 backend/nodemon.json delete mode 100644 backend/package.json delete mode 100644 backend/routes/api/audit-log.js delete mode 100644 backend/routes/api/main.js delete mode 100644 backend/routes/api/nginx/access_lists.js delete mode 100644 backend/routes/api/nginx/certificates.js delete mode 100644 backend/routes/api/nginx/dead_hosts.js delete mode 100644 backend/routes/api/nginx/proxy_hosts.js delete mode 100644 backend/routes/api/nginx/redirection_hosts.js delete mode 100644 backend/routes/api/nginx/streams.js delete mode 100644 backend/routes/api/reports.js delete mode 100644 backend/routes/api/schema.js delete mode 100644 backend/routes/api/settings.js delete mode 100644 backend/routes/api/tokens.js delete mode 100644 backend/routes/api/users.js delete mode 100644 backend/schema/definitions.json delete mode 100644 backend/schema/endpoints/access-lists.json delete mode 100644 backend/schema/endpoints/certificates.json delete mode 100644 backend/schema/endpoints/dead-hosts.json delete mode 100644 backend/schema/endpoints/proxy-hosts.json delete mode 100644 backend/schema/endpoints/redirection-hosts.json delete mode 100644 backend/schema/endpoints/settings.json delete mode 100644 backend/schema/endpoints/streams.json delete mode 100644 backend/schema/endpoints/tokens.json delete mode 100644 backend/schema/endpoints/users.json delete mode 100644 backend/schema/examples.json delete mode 100644 backend/schema/index.json create mode 100755 backend/scripts/lint.sh create mode 100755 backend/scripts/test.sh delete mode 100644 backend/setup.js delete mode 100644 backend/templates/_assets.conf delete mode 100644 backend/templates/_certificates.conf delete mode 100644 backend/templates/_exploits.conf delete mode 100644 backend/templates/_forced_ssl.conf delete mode 100644 backend/templates/_header_comment.conf delete mode 100644 backend/templates/_hsts.conf delete mode 100644 backend/templates/_listen.conf delete mode 100644 backend/templates/_location.conf delete mode 100644 backend/templates/dead_host.conf delete mode 100644 backend/templates/default.conf delete mode 100644 backend/templates/ip_ranges.conf delete mode 100644 backend/templates/letsencrypt-request.conf delete mode 100644 backend/templates/proxy_host.conf delete mode 100644 backend/templates/redirection_host.conf delete mode 100644 backend/templates/stream.conf delete mode 100644 backend/yarn.lock delete mode 100755 docker/rootfs/bin/check-health delete mode 100755 docker/rootfs/bin/handle-ipv6-setting delete mode 100644 docker/rootfs/etc/cont-init.d/.gitignore delete mode 100644 docker/rootfs/etc/cont-init.d/01_s6-secret-init.sh create mode 100755 docker/rootfs/etc/cont-init.d/10-nginx create mode 100755 docker/rootfs/etc/cont-init.d/20-adduser create mode 100755 docker/rootfs/etc/cont-init.d/30-dbmate delete mode 100644 docker/rootfs/etc/letsencrypt.ini delete mode 100644 docker/rootfs/etc/nginx/conf.d/include/.gitignore delete mode 100644 docker/rootfs/etc/nginx/conf.d/include/ip_ranges.conf delete mode 100644 docker/rootfs/etc/nginx/conf.d/include/letsencrypt-acme-challenge.conf create mode 100644 docker/rootfs/etc/nginx/conf.d/include/resolvers.conf create mode 100755 docker/rootfs/etc/services.d/backend/finish create mode 100755 docker/rootfs/etc/services.d/backend/run delete mode 100755 docker/rootfs/etc/services.d/manager/finish delete mode 100755 docker/rootfs/etc/services.d/manager/run mode change 120000 => 100755 docker/rootfs/etc/services.d/nginx/finish create mode 100644 docker/rootfs/root/.config/litecli/config create mode 100644 docker/rootfs/var/www/html/fonts/Aldrich-Regular.ttf create mode 100644 docker/rootfs/var/www/html/fonts/Poppins-Bold.ttf create mode 100644 docker/rootfs/var/www/html/fonts/Poppins-Regular.ttf create mode 100644 docker/rootfs/var/www/html/images/bg.jpg create mode 100644 docker/rootfs/var/www/html/images/logo.png create mode 100644 docker/rootfs/var/www/html/main.css delete mode 100644 frontend/.babelrc create mode 100644 frontend/.env.development create mode 100644 frontend/.eslintrc rename {backend => frontend}/.prettierrc (70%) create mode 100644 frontend/README.md delete mode 120000 frontend/fonts/feather delete mode 100644 frontend/fonts/source-sans-pro/source-sans-pro-v14-latin-ext_latin-700.woff delete mode 100644 frontend/fonts/source-sans-pro/source-sans-pro-v14-latin-ext_latin-700.woff2 delete mode 100644 frontend/fonts/source-sans-pro/source-sans-pro-v14-latin-ext_latin-700italic.woff delete mode 100644 frontend/fonts/source-sans-pro/source-sans-pro-v14-latin-ext_latin-700italic.woff2 delete mode 100644 frontend/fonts/source-sans-pro/source-sans-pro-v14-latin-ext_latin-italic.woff delete mode 100644 frontend/fonts/source-sans-pro/source-sans-pro-v14-latin-ext_latin-italic.woff2 delete mode 100644 frontend/fonts/source-sans-pro/source-sans-pro-v14-latin-ext_latin-regular.woff delete mode 100644 frontend/fonts/source-sans-pro/source-sans-pro-v14-latin-ext_latin-regular.woff2 create mode 100644 frontend/globalSetup.js delete mode 100644 frontend/html/index.ejs delete mode 100644 frontend/html/login.ejs delete mode 100644 frontend/html/partials/footer.ejs delete mode 100644 frontend/html/partials/header.ejs delete mode 120000 frontend/images create mode 100644 frontend/jest.eslint.js delete mode 100644 frontend/js/app/api.js delete mode 100644 frontend/js/app/audit-log/list/item.ejs delete mode 100644 frontend/js/app/audit-log/list/item.js delete mode 100644 frontend/js/app/audit-log/list/main.ejs delete mode 100644 frontend/js/app/audit-log/list/main.js delete mode 100644 frontend/js/app/audit-log/main.ejs delete mode 100644 frontend/js/app/audit-log/main.js delete mode 100644 frontend/js/app/audit-log/meta.ejs delete mode 100644 frontend/js/app/audit-log/meta.js delete mode 100644 frontend/js/app/cache.js delete mode 100644 frontend/js/app/controller.js delete mode 100644 frontend/js/app/dashboard/main.ejs delete mode 100644 frontend/js/app/dashboard/main.js delete mode 100644 frontend/js/app/empty/main.ejs delete mode 100644 frontend/js/app/empty/main.js delete mode 100644 frontend/js/app/error/main.ejs delete mode 100644 frontend/js/app/error/main.js delete mode 100644 frontend/js/app/help/main.ejs delete mode 100644 frontend/js/app/help/main.js delete mode 100644 frontend/js/app/i18n.js delete mode 100644 frontend/js/app/main.js delete mode 100644 frontend/js/app/nginx/access/delete.ejs delete mode 100644 frontend/js/app/nginx/access/delete.js delete mode 100644 frontend/js/app/nginx/access/form.ejs delete mode 100644 frontend/js/app/nginx/access/form.js delete mode 100644 frontend/js/app/nginx/access/form/client.ejs delete mode 100644 frontend/js/app/nginx/access/form/client.js delete mode 100644 frontend/js/app/nginx/access/form/item.ejs delete mode 100644 frontend/js/app/nginx/access/form/item.js delete mode 100644 frontend/js/app/nginx/access/list/item.ejs delete mode 100644 frontend/js/app/nginx/access/list/item.js delete mode 100644 frontend/js/app/nginx/access/list/main.ejs delete mode 100644 frontend/js/app/nginx/access/list/main.js delete mode 100644 frontend/js/app/nginx/access/main.ejs delete mode 100644 frontend/js/app/nginx/access/main.js delete mode 100644 frontend/js/app/nginx/certificates-list-item.ejs delete mode 100644 frontend/js/app/nginx/certificates/delete.ejs delete mode 100644 frontend/js/app/nginx/certificates/delete.js delete mode 100644 frontend/js/app/nginx/certificates/form.ejs delete mode 100644 frontend/js/app/nginx/certificates/form.js delete mode 100644 frontend/js/app/nginx/certificates/list/item.ejs delete mode 100644 frontend/js/app/nginx/certificates/list/item.js delete mode 100644 frontend/js/app/nginx/certificates/list/main.ejs delete mode 100644 frontend/js/app/nginx/certificates/list/main.js delete mode 100644 frontend/js/app/nginx/certificates/main.ejs delete mode 100644 frontend/js/app/nginx/certificates/main.js delete mode 100644 frontend/js/app/nginx/certificates/renew.ejs delete mode 100644 frontend/js/app/nginx/certificates/renew.js delete mode 100644 frontend/js/app/nginx/dead/delete.ejs delete mode 100644 frontend/js/app/nginx/dead/delete.js delete mode 100644 frontend/js/app/nginx/dead/form.ejs delete mode 100644 frontend/js/app/nginx/dead/form.js delete mode 100644 frontend/js/app/nginx/dead/list/item.ejs delete mode 100644 frontend/js/app/nginx/dead/list/item.js delete mode 100644 frontend/js/app/nginx/dead/list/main.ejs delete mode 100644 frontend/js/app/nginx/dead/list/main.js delete mode 100644 frontend/js/app/nginx/dead/main.ejs delete mode 100644 frontend/js/app/nginx/dead/main.js delete mode 100644 frontend/js/app/nginx/proxy/access-list-item.ejs delete mode 100644 frontend/js/app/nginx/proxy/delete.ejs delete mode 100644 frontend/js/app/nginx/proxy/delete.js delete mode 100644 frontend/js/app/nginx/proxy/form.ejs delete mode 100644 frontend/js/app/nginx/proxy/form.js delete mode 100644 frontend/js/app/nginx/proxy/list/item.ejs delete mode 100644 frontend/js/app/nginx/proxy/list/item.js delete mode 100644 frontend/js/app/nginx/proxy/list/main.ejs delete mode 100644 frontend/js/app/nginx/proxy/list/main.js delete mode 100644 frontend/js/app/nginx/proxy/location-item.ejs delete mode 100644 frontend/js/app/nginx/proxy/location.js delete mode 100644 frontend/js/app/nginx/proxy/main.ejs delete mode 100644 frontend/js/app/nginx/proxy/main.js delete mode 100644 frontend/js/app/nginx/redirection/delete.ejs delete mode 100644 frontend/js/app/nginx/redirection/delete.js delete mode 100644 frontend/js/app/nginx/redirection/form.ejs delete mode 100644 frontend/js/app/nginx/redirection/form.js delete mode 100644 frontend/js/app/nginx/redirection/list/item.ejs delete mode 100644 frontend/js/app/nginx/redirection/list/item.js delete mode 100644 frontend/js/app/nginx/redirection/list/main.ejs delete mode 100644 frontend/js/app/nginx/redirection/list/main.js delete mode 100644 frontend/js/app/nginx/redirection/main.ejs delete mode 100644 frontend/js/app/nginx/redirection/main.js delete mode 100644 frontend/js/app/nginx/stream/delete.ejs delete mode 100644 frontend/js/app/nginx/stream/delete.js delete mode 100644 frontend/js/app/nginx/stream/form.ejs delete mode 100644 frontend/js/app/nginx/stream/form.js delete mode 100644 frontend/js/app/nginx/stream/list/item.ejs delete mode 100644 frontend/js/app/nginx/stream/list/item.js delete mode 100644 frontend/js/app/nginx/stream/list/main.ejs delete mode 100644 frontend/js/app/nginx/stream/list/main.js delete mode 100644 frontend/js/app/nginx/stream/main.ejs delete mode 100644 frontend/js/app/nginx/stream/main.js delete mode 100644 frontend/js/app/router.js delete mode 100644 frontend/js/app/settings/default-site/main.ejs delete mode 100644 frontend/js/app/settings/default-site/main.js delete mode 100644 frontend/js/app/settings/list/item.ejs delete mode 100644 frontend/js/app/settings/list/item.js delete mode 100644 frontend/js/app/settings/list/main.ejs delete mode 100644 frontend/js/app/settings/list/main.js delete mode 100644 frontend/js/app/settings/main.ejs delete mode 100644 frontend/js/app/settings/main.js delete mode 100644 frontend/js/app/tokens.js delete mode 100644 frontend/js/app/ui/footer/main.ejs delete mode 100644 frontend/js/app/ui/footer/main.js delete mode 100644 frontend/js/app/ui/header/main.ejs delete mode 100644 frontend/js/app/ui/header/main.js delete mode 100644 frontend/js/app/ui/main.ejs delete mode 100644 frontend/js/app/ui/main.js delete mode 100644 frontend/js/app/ui/menu/main.ejs delete mode 100644 frontend/js/app/ui/menu/main.js delete mode 100644 frontend/js/app/user/delete.ejs delete mode 100644 frontend/js/app/user/delete.js delete mode 100644 frontend/js/app/user/form.ejs delete mode 100644 frontend/js/app/user/form.js delete mode 100644 frontend/js/app/user/password.ejs delete mode 100644 frontend/js/app/user/password.js delete mode 100644 frontend/js/app/user/permissions.ejs delete mode 100644 frontend/js/app/user/permissions.js delete mode 100644 frontend/js/app/users/list/item.ejs delete mode 100644 frontend/js/app/users/list/item.js delete mode 100644 frontend/js/app/users/list/main.ejs delete mode 100644 frontend/js/app/users/list/main.js delete mode 100644 frontend/js/app/users/main.ejs delete mode 100644 frontend/js/app/users/main.js delete mode 100644 frontend/js/i18n/messages.json delete mode 100644 frontend/js/index.js delete mode 100644 frontend/js/lib/helpers.js delete mode 100644 frontend/js/lib/marionette.js delete mode 100644 frontend/js/login.js delete mode 100644 frontend/js/login/main.js delete mode 100644 frontend/js/login/ui/login.ejs delete mode 100644 frontend/js/login/ui/login.js delete mode 100644 frontend/js/models/access-list.js delete mode 100644 frontend/js/models/audit-log.js delete mode 100644 frontend/js/models/certificate.js delete mode 100644 frontend/js/models/dead-host.js delete mode 100644 frontend/js/models/proxy-host-location.js delete mode 100644 frontend/js/models/proxy-host.js delete mode 100644 frontend/js/models/redirection-host.js delete mode 100644 frontend/js/models/setting.js delete mode 100644 frontend/js/models/stream.js delete mode 100644 frontend/js/models/user.js rename frontend/{app-images => public/images}/default-avatar.jpg (100%) rename frontend/{app-images/favicons => public/images/favicon}/android-chrome-192x192.png (100%) rename frontend/{app-images/favicons => public/images/favicon}/android-chrome-512x512.png (100%) rename frontend/{app-images/favicons => public/images/favicon}/apple-touch-icon.png (100%) rename frontend/{app-images/favicons => public/images/favicon}/browserconfig.xml (100%) rename frontend/{app-images/favicons => public/images/favicon}/favicon-16x16.png (100%) rename frontend/{app-images/favicons => public/images/favicon}/favicon-32x32.png (100%) rename frontend/{app-images/favicons => public/images/favicon}/favicon.ico (100%) rename frontend/{app-images/favicons => public/images/favicon}/mstile-150x150.png (100%) rename frontend/{app-images/favicons => public/images/favicon}/safari-pinned-tab.svg (100%) rename frontend/{app-images/favicons => public/images/favicon}/site.webmanifest (100%) rename frontend/{app-images => public/images}/logo-256.png (100%) create mode 100644 frontend/public/images/logo-bold-horizontal-grey.svg create mode 100644 frontend/public/images/logo-text-horizontal-grey.png rename frontend/{app-images => public/images}/logo-text-vertical-grey.png (100%) create mode 100644 frontend/public/index.html delete mode 100644 frontend/scss/custom.scss delete mode 100644 frontend/scss/fonts.scss delete mode 100644 frontend/scss/selectize.scss delete mode 100644 frontend/scss/styles.scss delete mode 100644 frontend/scss/tabler-extra.scss create mode 100644 frontend/src/App.test.tsx create mode 100644 frontend/src/App.tsx create mode 100644 frontend/src/api/npm/base.ts create mode 100644 frontend/src/api/npm/createUser.ts create mode 100644 frontend/src/api/npm/getToken.ts create mode 100644 frontend/src/api/npm/getUser.ts create mode 100644 frontend/src/api/npm/index.ts create mode 100644 frontend/src/api/npm/refreshToken.ts create mode 100644 frontend/src/api/npm/requestHealth.ts create mode 100644 frontend/src/api/npm/responseTypes.ts create mode 100644 frontend/src/components/Footer.tsx create mode 100644 frontend/src/components/Loading.tsx create mode 100644 frontend/src/components/Router.tsx create mode 100644 frontend/src/components/SinglePage.tsx create mode 100644 frontend/src/components/SiteWrapper.tsx create mode 100644 frontend/src/components/Unhealthy.tsx create mode 100644 frontend/src/components/index.ts create mode 100644 frontend/src/components/page/Footer.tsx create mode 100644 frontend/src/components/page/Header.tsx create mode 100644 frontend/src/components/page/index.ts create mode 100644 frontend/src/context/AuthContext.tsx create mode 100644 frontend/src/context/HealthContext.tsx create mode 100644 frontend/src/context/UserContext.tsx create mode 100644 frontend/src/context/index.ts create mode 100755 frontend/src/fonts/feather/feather-webfont.eot create mode 100755 frontend/src/fonts/feather/feather-webfont.svg create mode 100755 frontend/src/fonts/feather/feather-webfont.ttf create mode 100755 frontend/src/fonts/feather/feather-webfont.woff create mode 100755 frontend/src/fonts/tabler-webfont/tabler-webfont.eot create mode 100755 frontend/src/fonts/tabler-webfont/tabler-webfont.svg create mode 100755 frontend/src/fonts/tabler-webfont/tabler-webfont.ttf create mode 100755 frontend/src/fonts/tabler-webfont/tabler-webfont.woff create mode 100755 frontend/src/fonts/tabler-webfont/tabler-webfont.woff2 create mode 100644 frontend/src/img/logo-text-vertical-grey.png create mode 100644 frontend/src/index.scss create mode 100644 frontend/src/index.tsx create mode 100644 frontend/src/modules/AuthStore.ts create mode 100644 frontend/src/pages/Dashboard/index.tsx create mode 100644 frontend/src/pages/Login/index.tsx create mode 100644 frontend/src/pages/Setup/index.tsx create mode 100644 frontend/src/react-app-env.d.ts create mode 100755 frontend/src/tabler.css create mode 100644 frontend/tsconfig.json delete mode 100644 frontend/webpack.config.js delete mode 100644 global/certbot-dns-plugins.js create mode 100755 scripts/build-cleanup create mode 100755 scripts/ci/frontend-build delete mode 100755 scripts/frontend-build create mode 100755 scripts/frontend-lint delete mode 100755 scripts/install-s6 create mode 100755 scripts/sqlite delete mode 100644 test/.dockerignore create mode 100644 test/cypress/integration/api/Certificates.spec.js create mode 100644 test/cypress/integration/api/Settings.spec.js create mode 100644 test/cypress/integration/api/SetupPhase.spec.js create mode 100644 test/cypress/integration/api/SwaggerSchema.spec.js diff --git a/.gitignore b/.gitignore index 08462849..fc0c073a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,20 @@ -.DS_Store .idea +.env +.DS_Store ._* +*.code-workspace +vendor +dist +backend/config.json +backend/internal/api/handler/assets +test/node_modules +*/node_modules +docs/.vuepress/dist +frontend/build +frontend/yarn-error.log +frontend/yarn.lock +frontend/.npmrc +test/cypress/fixtures/example.json .vscode -certbot-help.txt +docker-build +data \ No newline at end of file diff --git a/.version b/.version index 4db4b035..5efd7ac5 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -2.9.3 +3.0.0a diff --git a/DEV-README.md b/DEV-README.md new file mode 100644 index 00000000..05b8c63b --- /dev/null +++ b/DEV-README.md @@ -0,0 +1,27 @@ +# Nginx Proxy Manager 3 + +WIP + + +## Usage + +environment variables + + +## Building + +### Backend API Server + +```bash +go build -ldflags="-X main.commit=$(git log -n 1 --format=%h)" -o bin/server ./cmd/server/main.go +``` + + +## Development + +```bash +git clone nginxproxymanager +cd nginxproxymanager +./scripts/start-dev +curl http://127.0.0.1:3000/api/ +``` diff --git a/Jenkinsfile b/Jenkinsfile index 3161a254..cfe6c212 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -8,14 +8,17 @@ pipeline { ansiColor('xterm') } environment { - IMAGE = "nginx-proxy-manager" + IMAGE = 'nginx-proxy-manager' BUILD_VERSION = getVersion() - MAJOR_VERSION = "2" + BUILD_COMMIT = getCommit() + MAJOR_VERSION = '3' BRANCH_LOWER = "${BRANCH_NAME.toLowerCase().replaceAll('/', '-')}" COMPOSE_PROJECT_NAME = "npm_${BRANCH_LOWER}_${BUILD_NUMBER}" COMPOSE_FILE = 'docker/docker-compose.ci.yml' COMPOSE_INTERACTIVE_NO_CLI = 1 BUILDX_NAME = "${COMPOSE_PROJECT_NAME}" + DOCS_BUCKET = 'jc21-npm-site-next' // TODO: change to prod when official + DOCS_CDN = 'E2Z0128EHS0Q23' // TODO: same } stages { stage('Environment') { @@ -45,10 +48,9 @@ pipeline { } stage('Versions') { steps { + // Is this frontend version stuff still applicable? sh 'cat frontend/package.json | jq --arg BUILD_VERSION "${BUILD_VERSION}" \'.version = $BUILD_VERSION\' | sponge frontend/package.json' sh 'echo -e "\\E[1;36mFrontend Version is:\\E[1;33m $(cat frontend/package.json | jq -r .version)\\E[0m"' - sh 'cat backend/package.json | jq --arg BUILD_VERSION "${BUILD_VERSION}" \'.version = $BUILD_VERSION\' | sponge backend/package.json' - sh 'echo -e "\\E[1;36mBackend Version is:\\E[1;33m $(cat backend/package.json | jq -r .version)\\E[0m"' sh 'sed -i -E "s/(version-)[0-9]+\\.[0-9]+\\.[0-9]+(-green)/\\1${BUILD_VERSION}\\2/" README.md' } } @@ -56,78 +58,57 @@ pipeline { } stage('Frontend') { steps { - sh './scripts/frontend-build' + sh './scripts/ci/frontend-build' + } + post { + always { + junit 'frontend/eslint.xml' + junit 'frontend/junit.xml' + } } } stage('Backend') { steps { - echo 'Checking Syntax ...' - // See: https://github.com/yarnpkg/yarn/issues/3254 - sh '''docker run --rm \\ - -v "$(pwd)/backend:/app" \\ - -v "$(pwd)/global:/app/global" \\ - -w /app \\ - node:latest \\ - sh -c "yarn install && yarn eslint . && rm -rf node_modules" - ''' - - echo 'Docker Build ...' - sh '''docker build --pull --no-cache --squash --compress \\ - -t "${IMAGE}:ci-${BUILD_NUMBER}" \\ - -f docker/Dockerfile \\ - --build-arg TARGETPLATFORM=linux/amd64 \\ - --build-arg BUILDPLATFORM=linux/amd64 \\ - --build-arg BUILD_VERSION="${BUILD_VERSION}" \\ - --build-arg BUILD_COMMIT="${BUILD_COMMIT}" \\ - --build-arg BUILD_DATE="$(date '+%Y-%m-%d %T %Z')" \\ - . - ''' - } - } - stage('Integration Tests Sqlite') { - steps { - // Bring up a stack - sh 'docker-compose up -d fullstack-sqlite' - sh './scripts/wait-healthy $(docker-compose ps -q fullstack-sqlite) 120' - - // Run tests - sh 'rm -rf test/results' - sh 'docker-compose up cypress-sqlite' - // Get results - sh 'docker cp -L "$(docker-compose ps -q cypress-sqlite):/test/results" test/' - } - post { - always { - // Dumps to analyze later - sh 'mkdir -p debug' - sh 'docker-compose logs fullstack-sqlite | gzip > debug/docker_fullstack_sqlite.log.gz' - sh 'docker-compose logs db | gzip > debug/docker_db.log.gz' - // Cypress videos and screenshot artifacts - dir(path: 'test/results') { - archiveArtifacts allowEmptyArchive: true, artifacts: '**/*', excludes: '**/*.xml' - } - junit 'test/results/junit/*' + withCredentials([usernamePassword(credentialsId: 'oss-index-token', passwordVariable: 'NANCY_TOKEN', usernameVariable: 'NANCY_USER')]) { + sh '''docker build --pull --no-cache --squash --compress \\ + -t ${IMAGE}:ci-${BUILD_NUMBER} \\ + -f docker/Dockerfile \\ + --build-arg TARGETPLATFORM=linux/amd64 \\ + --build-arg BUILDPLATFORM=linux/amd64 \\ + --build-arg BUILD_DATE="$(date '+%Y-%m-%d %T %Z')" \\ + --build-arg BUILD_VERSION="${BUILD_VERSION}" \\ + --build-arg BUILD_COMMIT="${BUILD_COMMIT}" \\ + --build-arg SENTRY_DSN="${SENTRY_DSN:-}" \\ + --build-arg GOPROXY="${GOPROXY:-}" \\ + --build-arg GOPRIVATE="${GOPRIVATE:-}" \\ + --build-arg NANCY_USER="${NANCY_USER}" \\ + --build-arg NANCY_TOKEN="${NANCY_TOKEN}" \\ + . + ''' } } } - stage('Integration Tests Mysql') { + stage('Test') { + when { + not { + equals expected: 'UNSTABLE', actual: currentBuild.result + } + } steps { // Bring up a stack - sh 'docker-compose up -d fullstack-mysql' - sh './scripts/wait-healthy $(docker-compose ps -q fullstack-mysql) 120' - + sh 'docker-compose up -d fullstack' + sh './scripts/wait-healthy $(docker-compose ps -q fullstack) 120' // Run tests sh 'rm -rf test/results' - sh 'docker-compose up cypress-mysql' + sh 'docker-compose up cypress' // Get results - sh 'docker cp -L "$(docker-compose ps -q cypress-mysql):/test/results" test/' + sh 'docker cp -L "$(docker-compose ps -q cypress):/test/results" test/' } post { always { // Dumps to analyze later sh 'mkdir -p debug' - sh 'docker-compose logs fullstack-mysql | gzip > debug/docker_fullstack_mysql.log.gz' - sh 'docker-compose logs db | gzip > debug/docker_db.log.gz' + sh 'docker-compose logs fullstack | gzip > debug/docker_fullstack.log.gz' // Cypress videos and screenshot artifacts dir(path: 'test/results') { archiveArtifacts allowEmptyArchive: true, artifacts: '**/*', excludes: '**/*.xml' @@ -148,6 +129,11 @@ pipeline { sh 'yarn build' } + // API Docs: + sh 'docker-compose exec -T fullstack curl -s --output /temp-docs/api-schema.json "http://fullstack:81/api/schema"' + sh 'mkdir -p "docs/.vuepress/dist/api"' + sh 'mv docs/api-schema.json docs/.vuepress/dist/api/' + dir(path: 'docs/.vuepress/dist') { sh 'tar -czf ../../docs.tgz *' } @@ -155,21 +141,29 @@ pipeline { archiveArtifacts(artifacts: 'docs/docs.tgz', allowEmptyArchive: false) } } + /* stage('MultiArch Build') { when { - not { - equals expected: 'UNSTABLE', actual: currentBuild.result + allOf { + branch 'master' + not { + equals expected: 'UNSTABLE', actual: currentBuild.result + } } } steps { - withCredentials([usernamePassword(credentialsId: 'jc21-dockerhub', passwordVariable: 'dpass', usernameVariable: 'duser')]) { - // Docker Login - sh "docker login -u '${duser}' -p '${dpass}'" - // Buildx with push from cache - sh "./scripts/buildx --push ${BUILDX_PUSH_TAGS}" + withCredentials([string(credentialsId: 'npm-sentry-dsn', variable: 'SENTRY_DSN')]) { + withCredentials([usernamePassword(credentialsId: 'jc21-dockerhub', passwordVariable: 'dpass', usernameVariable: 'duser')]) { + sh "docker login -u '${duser}' -p '${dpass}'" + // Buildx to local files + // sh './scripts/buildx -o type=local,dest=docker-build' + // Buildx to with push + sh "./scripts/buildx --push ${BUILDX_PUSH_TAGS}" + } } } } + */ stage('Docs Deploy') { when { allOf { @@ -183,7 +177,7 @@ pipeline { withCredentials([[$class: 'AmazonWebServicesCredentialsBinding', accessKeyVariable: 'AWS_ACCESS_KEY_ID', credentialsId: 'npm-s3-docs', secretKeyVariable: 'AWS_SECRET_ACCESS_KEY']]) { sh """docker run --rm \\ --name \${COMPOSE_PROJECT_NAME}-docs-upload \\ - -e S3_BUCKET=jc21-npm-site \\ + -e S3_BUCKET=$DOCS_BUCKET \\ -e AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID \\ -e AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY \\ -v \$(pwd):/app \\ @@ -197,7 +191,7 @@ pipeline { -e AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID \\ -e AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY \\ jc21/ci-tools \\ - aws cloudfront create-invalidation --distribution-id EN1G6DEWZUTDT --paths '/*' + aws cloudfront create-invalidation --distribution-id $DOCS_CDN --paths '/*' """ } } @@ -213,7 +207,40 @@ pipeline { } steps { script { - def comment = pullRequest.comment("This is an automated message from CI:\n\nDocker Image for build ${BUILD_NUMBER} is available on [DockerHub](https://cloud.docker.com/repository/docker/jc21/${IMAGE}) as `jc21/${IMAGE}:github-${BRANCH_LOWER}`\n\n**Note:** ensure you backup your NPM instance before testing this PR image! Especially if this PR contains database changes.") + def comment = pullRequest.comment("This is an automated message from CI:\n\nDocker Image for build ${BUILD_NUMBER} is available on [DockerHub](https://cloud.docker.com/repository/docker/jc21/${IMAGE}) as `jc21/${IMAGE}:git-3-${BRANCH_LOWER}`\n\n**Note:** ensure you backup your NPM instance before testing this PR image! Especially if this PR contains database changes.") + } + } + } + stage('Artifacts') { + when { + allOf { + branch 'master' + not { + equals expected: 'UNSTABLE', actual: currentBuild.result + } + } + } + steps { + sh 'mkdir -p artifacts' + // Docs + dir(path: 'docs/.vuepress/dist') { + sh 'zip -qr ../../../artifacts/docs.zip *' + } + // Multiarch builds + /* + dir(path: 'docker-build/linux_amd64/app') { + sh 'zip -qr ../../../artifacts/linux_amd64.zip *' + } + dir(path: 'docker-build/linux_arm64/app') { + sh 'zip -qr ../../../artifacts/linux_arm64.zip *' + } + dir(path: 'docker-build/linux_arm_v7/app') { + sh 'zip -qr ../../../artifacts/linux_arm_v7.zip *' + } + **/ + // Archive them + dir(path: 'artifacts') { + archiveArtifacts artifacts: '**/*' } } } @@ -221,8 +248,9 @@ pipeline { post { always { sh 'docker-compose down --rmi all --remove-orphans --volumes -t 30' + sh './scripts/build-cleanup' sh 'echo Reverting ownership' - sh 'docker run --rm -v $(pwd):/data jc21/ci-tools chown -R $(id -u):$(id -g) /data' + sh 'docker run --rm -v $(pwd):/data node:latest chown -R "$(id -u):$(id -g)" /data' } success { juxtapose event: 'success' diff --git a/README.md b/README.md index d50e5e7d..f00e86b1 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@



- + @@ -14,16 +14,12 @@ Gitter - - Reddit - -

This project comes as a pre-built docker image that enables you to easily forward to your websites running at home or otherwise, including free SSL, without having to know too much about Nginx or Letsencrypt. -- [Quick Setup](#quick-setup) +- [Quick Setup](https://nginxproxymanager.com#quick-setup) - [Full Setup](https://nginxproxymanager.com/setup/) - [Screenshots](https://nginxproxymanager.com/screenshots/) @@ -56,67 +52,6 @@ I won't go in to too much detail here but here are the basics for someone new to 3. Configure your domain name details to point to your home, either with a static ip or a service like DuckDNS or [Amazon Route53](https://github.com/jc21/route53-ddns) 4. Use the Nginx Proxy Manager as your gateway to forward to your other web based services -## Quick Setup - -1. Install Docker and Docker-Compose - -- [Docker Install documentation](https://docs.docker.com/install/) -- [Docker-Compose Install documentation](https://docs.docker.com/compose/install/) - -2. Create a docker-compose.yml file similar to this: - -```yml -version: '3' -services: - app: - image: 'jc21/nginx-proxy-manager:latest' - restart: unless-stopped - ports: - - '80:80' - - '81:81' - - '443:443' - environment: - DB_MYSQL_HOST: "db" - DB_MYSQL_PORT: 3306 - DB_MYSQL_USER: "npm" - DB_MYSQL_PASSWORD: "npm" - DB_MYSQL_NAME: "npm" - volumes: - - ./data:/data - - ./letsencrypt:/etc/letsencrypt - db: - image: 'jc21/mariadb-aria:latest' - restart: unless-stopped - environment: - MYSQL_ROOT_PASSWORD: 'npm' - MYSQL_DATABASE: 'npm' - MYSQL_USER: 'npm' - MYSQL_PASSWORD: 'npm' - volumes: - - ./data/mysql:/var/lib/mysql -``` - -3. Bring up your stack - -```bash -docker-compose up -d -``` - -4. Log in to the Admin UI - -When your docker container is running, connect to it on port `81` for the admin interface. -Sometimes this can take a little bit because of the entropy of keys. - -[http://127.0.0.1:81](http://127.0.0.1:81) - -Default Admin User: -``` -Email: admin@example.com -Password: changeme -``` - -Immediately after logging in with this default user you will be asked to modify your details and change your password. - ## Contributors @@ -128,43 +63,43 @@ Special thanks to the following contributors: - +
Sebastian Valle
- +
Kyle Klaus
- +
ƬHE ЯAW
- +
Spencer
- +
Xantios Krugor
- +
David Panesso
- +
IronTooch
@@ -172,43 +107,43 @@ Special thanks to the following contributors: - +
Damiano
- +
Russ
- +
Marcelo Castagna
- +
Steven Harris
- +
Jocelyn Le Sage
- +
Carl Mercier
- +
Paul Mansfield
@@ -216,43 +151,43 @@ Special thanks to the following contributors: - +
OhHeyAlan
- +
Carl Sutton
- +
Gergő Törcsvári
- +
vrenjith
- +
David Rivera
- +
Jaap-Jan de Wit
- +
James Morgan
@@ -260,160 +195,22 @@ Special thanks to the following contributors: - +
chaptergy
- +
Philip Mooney
- +
WaterCalm
- - - -
lebrou34 -
- - - - -
Mário Franco -
- - - - -
Kyle Harding -
- - - - -
Alex Graber -
- - - - - - -
MooBaloo -
- - - - -
Shuro -
- - - - -
Loris Bergeron -
- - - - -
hepelayo -
- - - - -
Jonas Leder -
- - - - -
Bastian Stegmann -
- - - - -
Stealthii -
- - - - - - -
THEGamingninja -
- - - - -
Italo Borssatto -
- - - - -
Gurjinder Singh -
- - - - -
David Dosoudil -
- - - - -
ijaron -
- - - - -
Niels Bouma -
- - - - -
Orko Garai -
- - - - - - -
Filippo Baruffaldi -
- - - - -
Bikramjeet Singh -
- - - - -
Razvan Stoica -
- - - - -
RBXII3 -
- diff --git a/backend/.editorconfig b/backend/.editorconfig new file mode 100644 index 00000000..8b96428f --- /dev/null +++ b/backend/.editorconfig @@ -0,0 +1,8 @@ +root = true + +[*] +indent_style = tab +indent_size = 4 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = false diff --git a/backend/.eslintrc.json b/backend/.eslintrc.json deleted file mode 100644 index 6d6172a4..00000000 --- a/backend/.eslintrc.json +++ /dev/null @@ -1,73 +0,0 @@ -{ - "env": { - "node": true, - "es6": true - }, - "extends": [ - "eslint:recommended" - ], - "globals": { - "Atomics": "readonly", - "SharedArrayBuffer": "readonly" - }, - "parserOptions": { - "ecmaVersion": 2018, - "sourceType": "module" - }, - "plugins": [ - "align-assignments" - ], - "rules": { - "arrow-parens": [ - "error", - "always" - ], - "indent": [ - "error", - "tab" - ], - "linebreak-style": [ - "error", - "unix" - ], - "quotes": [ - "error", - "single" - ], - "semi": [ - "error", - "always" - ], - "key-spacing": [ - "error", - { - "align": "value" - } - ], - "comma-spacing": [ - "error", - { - "before": false, - "after": true - } - ], - "func-call-spacing": [ - "error", - "never" - ], - "keyword-spacing": [ - "error", - { - "before": true - } - ], - "no-irregular-whitespace": "error", - "no-unused-expressions": 0, - "align-assignments/align-assignments": [ - 2, - { - "requiresOnly": false - } - ] - } -} \ No newline at end of file diff --git a/backend/.gitignore b/backend/.gitignore deleted file mode 100644 index 149080b9..00000000 --- a/backend/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -config/development.json -data/* -yarn-error.log -tmp -certbot.log -node_modules -core.* - diff --git a/backend/.golangci.yml b/backend/.golangci.yml new file mode 100644 index 00000000..f61dc69d --- /dev/null +++ b/backend/.golangci.yml @@ -0,0 +1,92 @@ +linters: + enable: + # Prevents against memory leaks in production caused by not closing file handle + - bodyclose + # Detects unused declarations in a go package + - deadcode + # Detects cloned code. DRY is good programming practice. Can cause issues with testing code where + # simplicity is preferred over duplication. Disabled for test code. + #- dupl + # Detects unchecked errors in go programs. These unchecked errors can be critical bugs in some cases. + - errcheck + # Simplifies go code. + - gosimple + # Reports suspicious constructs, maintained by goteam. e.g. Printf unused params not caught + # at compile time. + - govet + # Detect security issues with gocode. Use of secrets in code or obsolete security algorithms. + # It's imaged heuristic methods are used in finding problems. If issues with rules are found + # particular rules can be disabled as required. + # Could possibility cause issues with testing. Disabled for test code. + - gosec + # Detect repeated strings that could be replaced by a constant + - goconst + # Misc linters missing from other projects. Grouped into 3 categories diagnostics, style + # and performance + - gocritic + # Limits code cyclomatic complexity + - gocyclo + # Detects if code needs to be gofmt'd + - gofmt + # Detects unused go package imports + - goimports + # Detcts style mistakes not correctness. Golint is meant to carry out the + # stylistic conventions put forth in Effective Go and CodeReviewComments. + # golint has false positives and false negatives and can be tweaked. + - golint + # Detects ineffectual assignments in code + - ineffassign + # Detect commonly misspelled english words in comments + - misspell + # Detect naked returns on non-trivial functions, and conform with Go CodeReviewComments + - nakedret + # Detect slice allocations that can be preallocated + - prealloc + # Misc collection of static analysis tools + - staticcheck + # Detects unused struct fields + - structcheck + # Parses and typechecks the code like the go compiler + - typecheck + # Detects unused constants, variables, functions and types + - unused + # Detects unused global variables and constants + - varcheck + # Remove unnecessary type conversions + - unconvert + # Remove unnecessary(unused) function parameters + - unparam +linters-settings: + goconst: + # minimal length of string constant + # default: 3 + min-len: 2 + # minimum number of occurrences of string constant + # default: 3 + min-occurences: 2 + misspell: + locale: UK + ignore-words: + - color +issues: + # Maximum count of issues with the same text. Set to 0 to disable. Default is 3. + # We have chosen an arbitrary value that works based on practical usage. + max-same: 20 + # See cmdline flag documentation for more info about default excludes --exclude-use-default + # Nothing is excluded by default + exclude-use-default: false + # Excluding configuration per-path, per-linter, per-text and per-source + exclude-rules: + # Exclude some linters from running on tests files. # TODO: Add examples why this is good + + - path: _test\.go + linters: + # Tests should be simple? Add example why this is good? + - gocyclo + # Error checking adds verbosity and complexity for minimal value + - errcheck + # Table test encourage duplication in defining the table tests. + - dupl + # Hard coded example tokens, SQL injection and other bad practices may + # want to be tested + - gosec diff --git a/backend/.nancy-ignore b/backend/.nancy-ignore new file mode 100644 index 00000000..5736e87a --- /dev/null +++ b/backend/.nancy-ignore @@ -0,0 +1,22 @@ +# If you need to ignore any of nancy's warnings add them +# here with a reference to the package/version that +# triggers them and rational for ignoring it. + +# pkg:golang/github.com/coreos/etcd@3.3.10 +# etcd before versions 3.3.23 and 3.4.10 does not perform any password length validation +CVE-2020-15115 + +# pkg:golang/github.com/coreos/etcd@3.3.10 +# In ectd before versions 3.4.10 and 3.3.23, gateway TLS authentication is only applied to endpoints detected in DNS SRV records +CVE-2020-15136 + +# pkg:golang/github.com/coreos/etcd@3.3.10 +# In etcd before versions 3.3.23 and 3.4.10, the etcd gateway is a simple TCP proxy to allow for basic service discovery and access +CVE-2020-15114 + +# pkg:golang/github.com/gorilla/websocket@1.4.0 +# Integer Overflow or Wraparound +CWE-190 + +# jwt-go before 4.0.0-preview1 allows attackers to bypass intended access restrict... +CVE-2020-26160 diff --git a/backend/.vscode/settings.json b/backend/.vscode/settings.json deleted file mode 100644 index 4e540ab3..00000000 --- a/backend/.vscode/settings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "editor.insertSpaces": false, - "editor.formatOnSave": true, - "files.trimTrailingWhitespace": true, - "editor.codeActionsOnSave": { - "source.fixAll.eslint": true - } -} \ No newline at end of file diff --git a/backend/Taskfile.yml b/backend/Taskfile.yml new file mode 100644 index 00000000..dd49f17c --- /dev/null +++ b/backend/Taskfile.yml @@ -0,0 +1,56 @@ +version: '2' + +tasks: + default: + cmds: + - task: run + + run: + desc: Build and run + sources: + - internal/**/*.go + - cmd/**/*.go + cmds: + - task: build + - cmd: echo -e "==> Running..." + silent: true + - cmd: ../dist/bin/server + ignore_error: true + silent: true + env: + LOG_LEVEL: debug + + build: + desc: Build the server + cmds: + - cmd: echo -e "==> Building..." + silent: true + - cmd: rm -f dist/bin/* + silent: true + - cmd: go build -ldflags="-X main.commit={{.GIT_COMMIT}} -X main.version={{.VERSION}}" -o ../dist/bin/server ./cmd/server/main.go + silent: true + - task: lint + vars: + GIT_COMMIT: + sh: git log -n 1 --format=%h + VERSION: + sh: cat ../.version + env: + GO111MODULE: on + CGO_ENABLED: 1 + + lint: + desc: Linting + cmds: + - cmd: echo -e "==> Linting..." + silent: true + - cmd: bash scripts/lint.sh + silent: true + + test: + desc: Testing + cmds: + - cmd: echo -e "==> Testing..." + silent: true + - cmd: bash scripts/test.sh + silent: true diff --git a/backend/app.js b/backend/app.js deleted file mode 100644 index 33ffacc5..00000000 --- a/backend/app.js +++ /dev/null @@ -1,90 +0,0 @@ -const express = require('express'); -const bodyParser = require('body-parser'); -const fileUpload = require('express-fileupload'); -const compression = require('compression'); -const log = require('./logger').express; - -/** - * App - */ -const app = express(); -app.use(fileUpload()); -app.use(bodyParser.json()); -app.use(bodyParser.urlencoded({extended: true})); - -// Gzip -app.use(compression()); - -/** - * General Logging, BEFORE routes - */ - -app.disable('x-powered-by'); -app.enable('trust proxy', ['loopback', 'linklocal', 'uniquelocal']); -app.enable('strict routing'); - -// pretty print JSON when not live -if (process.env.NODE_ENV !== 'production') { - app.set('json spaces', 2); -} - -// CORS for everything -app.use(require('./lib/express/cors')); - -// General security/cache related headers + server header -app.use(function (req, res, next) { - let x_frame_options = 'DENY'; - - if (typeof process.env.X_FRAME_OPTIONS !== 'undefined' && process.env.X_FRAME_OPTIONS) { - x_frame_options = process.env.X_FRAME_OPTIONS; - } - - res.set({ - 'Strict-Transport-Security': 'includeSubDomains; max-age=631138519; preload', - 'X-XSS-Protection': '1; mode=block', - 'X-Content-Type-Options': 'nosniff', - 'X-Frame-Options': x_frame_options, - 'Cache-Control': 'no-cache, no-store, max-age=0, must-revalidate', - Pragma: 'no-cache', - Expires: 0 - }); - next(); -}); - -app.use(require('./lib/express/jwt')()); -app.use('/', require('./routes/api/main')); - -// production error handler -// no stacktraces leaked to user -// eslint-disable-next-line -app.use(function (err, req, res, next) { - - let payload = { - error: { - code: err.status, - message: err.public ? err.message : 'Internal Error' - } - }; - - if (process.env.NODE_ENV === 'development' || (req.baseUrl + req.path).includes('nginx/certificates')) { - payload.debug = { - stack: typeof err.stack !== 'undefined' && err.stack ? err.stack.split('\n') : null, - previous: err.previous - }; - } - - // Not every error is worth logging - but this is good for now until it gets annoying. - if (typeof err.stack !== 'undefined' && err.stack) { - if (process.env.NODE_ENV === 'development') { - log.debug(err.stack); - } else if (typeof err.public == 'undefined' || !err.public) { - log.warn(err.message); - } - } - - res - .status(err.status || 500) - .send(payload); -}); - -module.exports = app; diff --git a/backend/cmd/server/main.go b/backend/cmd/server/main.go new file mode 100644 index 00000000..0875c68f --- /dev/null +++ b/backend/cmd/server/main.go @@ -0,0 +1,45 @@ +package main + +import ( + "os" + "os/signal" + "syscall" + + "npm/internal/api" + "npm/internal/config" + "npm/internal/database" + "npm/internal/entity/setting" + "npm/internal/logger" + "npm/internal/state" + "npm/internal/worker" +) + +var commit string +var version string +var sentryDSN string + +func main() { + config.Init(&version, &commit, &sentryDSN) + appstate := state.NewState() + + setting.ApplySettings() + database.CheckSetup() + + go worker.StartCertificateWorker(appstate) + + api.StartServer() + irqchan := make(chan os.Signal, 1) + signal.Notify(irqchan, syscall.SIGINT, syscall.SIGTERM) + + for irq := range irqchan { + if irq == syscall.SIGINT || irq == syscall.SIGTERM { + logger.Info("Got ", irq, " shutting server down ...") + // Close db + err := database.GetInstance().Close() + if err != nil { + logger.Error("DatabaseCloseError", err) + } + break + } + } +} diff --git a/backend/config/README.md b/backend/config/README.md deleted file mode 100644 index 26268a11..00000000 --- a/backend/config/README.md +++ /dev/null @@ -1,2 +0,0 @@ -These files are use in development and are not deployed as part of the final product. - \ No newline at end of file diff --git a/backend/config/default.json b/backend/config/default.json deleted file mode 100644 index 64ab577c..00000000 --- a/backend/config/default.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "database": { - "engine": "mysql", - "host": "db", - "name": "npm", - "user": "npm", - "password": "npm", - "port": 3306 - } -} diff --git a/backend/config/sqlite-test-db.json b/backend/config/sqlite-test-db.json deleted file mode 100644 index ad548865..00000000 --- a/backend/config/sqlite-test-db.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "database": { - "engine": "knex-native", - "knex": { - "client": "sqlite3", - "connection": { - "filename": "/app/config/mydb.sqlite" - }, - "pool": { - "min": 0, - "max": 1, - "createTimeoutMillis": 3000, - "acquireTimeoutMillis": 30000, - "idleTimeoutMillis": 30000, - "reapIntervalMillis": 1000, - "createRetryIntervalMillis": 100, - "propagateCreateError": false - }, - "migrations": { - "tableName": "migrations", - "stub": "src/backend/lib/migrate_template.js", - "directory": "src/backend/migrations" - } - } - } -} diff --git a/backend/db.js b/backend/db.js deleted file mode 100644 index ce5338f0..00000000 --- a/backend/db.js +++ /dev/null @@ -1,33 +0,0 @@ -const config = require('config'); - -if (!config.has('database')) { - throw new Error('Database config does not exist! Please read the instructions: https://github.com/jc21/nginx-proxy-manager/blob/master/doc/INSTALL.md'); -} - -function generateDbConfig() { - if (config.database.engine === 'knex-native') { - return config.database.knex; - } else - return { - client: config.database.engine, - connection: { - host: config.database.host, - user: config.database.user, - password: config.database.password, - database: config.database.name, - port: config.database.port - }, - migrations: { - tableName: 'migrations' - } - }; -} - - -let data = generateDbConfig(); - -if (typeof config.database.version !== 'undefined') { - data.version = config.database.version; -} - -module.exports = require('knex')(data); diff --git a/backend/doc/api.swagger.json b/backend/doc/api.swagger.json index 06c02564..26bb1577 100644 --- a/backend/doc/api.swagger.json +++ b/backend/doc/api.swagger.json @@ -2,1253 +2,233 @@ "openapi": "3.0.0", "info": { "title": "Nginx Proxy Manager API", - "version": "2.x.x" + "version": "{{VERSION}}" }, - "servers": [ - { - "url": "http://127.0.0.1:81/api" - } - ], "paths": { "/": { "get": { - "operationId": "health", - "summary": "Returns the API health status", - "responses": { - "200": { - "description": "200 response", - "content": { - "application/json": { - "examples": { - "default": { - "value": { - "status": "OK", - "version": { - "major": 2, - "minor": 1, - "revision": 0 - } - } - } - }, - "schema": { - "$ref": "#/components/schemas/HealthObject" - } - } - } - } - } + "$ref": "file://./paths/get.json" + } + }, + "/certificates": { + "get": { + "$ref": "file://./paths/certificates/get.json" + }, + "post": { + "$ref": "file://./paths/certificates/post.json" + } + }, + "/certificates/{certificateID}": { + "get": { + "$ref": "file://./paths/certificates/certificateID/get.json" + }, + "put": { + "$ref": "file://./paths/certificates/certificateID/put.json" + }, + "delete": { + "$ref": "file://./paths/certificates/certificateID/delete.json" + } + }, + "/certificates-authorities": { + "get": { + "$ref": "file://./paths/certificates-authorities/get.json" + }, + "post": { + "$ref": "file://./paths/certificates-authorities/post.json" + } + }, + "/certificates-authorities/{caID}": { + "get": { + "$ref": "file://./paths/certificates-authorities/caID/get.json" + }, + "put": { + "$ref": "file://./paths/certificates-authorities/caID/put.json" + }, + "delete": { + "$ref": "file://./paths/certificates-authorities/caID/delete.json" + } + }, + "/config": { + "get": { + "$ref": "file://./paths/config/get.json" + } + }, + "/dns-providers": { + "get": { + "$ref": "file://./paths/dns-providers/get.json" + }, + "post": { + "$ref": "file://./paths/dns-providers/post.json" + } + }, + "/dns-providers/{providerID}": { + "get": { + "$ref": "file://./paths/dns-providers/providerID/get.json" + }, + "put": { + "$ref": "file://./paths/dns-providers/providerID/put.json" + }, + "delete": { + "$ref": "file://./paths/dns-providers/providerID/delete.json" + } + }, + "/hosts": { + "get": { + "$ref": "file://./paths/hosts/get.json" + }, + "post": { + "$ref": "file://./paths/hosts/post.json" + } + }, + "/hosts/{hostID}": { + "get": { + "$ref": "file://./paths/hosts/hostID/get.json" + }, + "put": { + "$ref": "file://./paths/hosts/hostID/put.json" + }, + "delete": { + "$ref": "file://./paths/hosts/hostID/delete.json" } }, "/schema": { "get": { - "operationId": "schema", - "responses": { - "200": { - "description": "200 response" - } - }, - "summary": "Returns this swagger API schema" - } - }, - "/tokens": { - "get": { - "operationId": "refreshToken", - "summary": "Refresh your access token", - "tags": [ - "Tokens" - ], - "security": [ - { - "BearerAuth": [ - "tokens" - ] - } - ], - "responses": { - "200": { - "description": "200 response", - "content": { - "application/json": { - "examples": { - "default": { - "value": { - "expires": 1566540510, - "token": "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.ey...xaHKYr3Kk6MvkUjcC4" - } - } - }, - "schema": { - "$ref": "#/components/schemas/TokenObject" - } - } - } - } - } - }, - "post": { - "operationId": "requestToken", - "parameters": [ - { - "description": "Credentials Payload", - "in": "body", - "name": "credentials", - "required": true, - "schema": { - "additionalProperties": false, - "properties": { - "identity": { - "minLength": 1, - "type": "string" - }, - "scope": { - "minLength": 1, - "type": "string", - "enum": [ - "user" - ] - }, - "secret": { - "minLength": 1, - "type": "string" - } - }, - "required": [ - "identity", - "secret" - ], - "type": "object" - } - } - ], - "responses": { - "200": { - "content": { - "application/json": { - "examples": { - "default": { - "value": { - "result": { - "expires": 1566540510, - "token": "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.ey...xaHKYr3Kk6MvkUjcC4" - } - } - } - }, - "schema": { - "$ref": "#/components/schemas/TokenObject" - } - } - }, - "description": "200 response" - } - }, - "summary": "Request a new access token from credentials", - "tags": [ - "Tokens" - ] + "$ref": "file://./paths/schema/get.json" } }, "/settings": { "get": { - "operationId": "getSettings", - "summary": "Get all settings", - "tags": [ - "Settings" - ], - "security": [ - { - "BearerAuth": [ - "settings" - ] - } - ], - "responses": { - "200": { - "description": "200 response", - "content": { - "application/json": { - "examples": { - "default": { - "value": [ - { - "id": "default-site", - "name": "Default Site", - "description": "What to show when Nginx is hit with an unknown Host", - "value": "congratulations", - "meta": {} - } - ] - } - }, - "schema": { - "$ref": "#/components/schemas/SettingsList" - } - } - } - } - } + "$ref": "file://./paths/settings/get.json" + }, + "post": { + "$ref": "file://./paths/settings/post.json" } }, - "/settings/{settingID}": { + "/settings/{name}": { "get": { - "operationId": "getSetting", - "summary": "Get a setting", - "tags": [ - "Settings" - ], - "security": [ - { - "BearerAuth": [ - "settings" - ] - } - ], - "parameters": [ - { - "in": "path", - "name": "settingID", - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "description": "Setting ID", - "example": "default-site" - } - ], - "responses": { - "200": { - "description": "200 response", - "content": { - "application/json": { - "examples": { - "default": { - "value": { - "id": "default-site", - "name": "Default Site", - "description": "What to show when Nginx is hit with an unknown Host", - "value": "congratulations", - "meta": {} - } - } - }, - "schema": { - "$ref": "#/components/schemas/SettingObject" - } - } - } - } - } + "$ref": "file://./paths/settings/name/get.json" }, "put": { - "operationId": "updateSetting", - "summary": "Update a setting", - "tags": [ - "Settings" - ], - "security": [ - { - "BearerAuth": [ - "settings" - ] - } - ], - "parameters": [ - { - "in": "path", - "name": "settingID", - "schema": { - "type": "string", - "minLength": 1 - }, - "required": true, - "description": "Setting ID", - "example": "default-site" - }, - { - "in": "body", - "name": "setting", - "description": "Setting Payload", - "required": true, - "schema": { - "$ref": "#/components/schemas/SettingObject" - } - } - ], - "responses": { - "200": { - "description": "200 response", - "content": { - "application/json": { - "examples": { - "default": { - "value": { - "id": "default-site", - "name": "Default Site", - "description": "What to show when Nginx is hit with an unknown Host", - "value": "congratulations", - "meta": {} - } - } - }, - "schema": { - "$ref": "#/components/schemas/SettingObject" - } - } - } - } - } + "$ref": "file://./paths/settings/name/put.json" + } + }, + "/streams": { + "get": { + "$ref": "file://./paths/streams/get.json" + }, + "post": { + "$ref": "file://./paths/streams/post.json" + } + }, + "/streams/{streamID}": { + "get": { + "$ref": "file://./paths/streams/streamID/get.json" + }, + "put": { + "$ref": "file://./paths/streams/streamID/put.json" + }, + "delete": { + "$ref": "file://./paths/streams/streamID/delete.json" + } + }, + "/tokens": { + "get": { + "$ref": "file://./paths/tokens/get.json" + }, + "post": { + "$ref": "file://./paths/tokens/post.json" } }, "/users": { "get": { - "operationId": "getUsers", - "summary": "Get all users", - "tags": [ - "Users" - ], - "security": [ - { - "BearerAuth": [ - "users" - ] - } - ], - "parameters": [ - { - "in": "query", - "name": "expand", - "description": "Expansions", - "schema": { - "type": "string", - "enum": [ - "permissions" - ] - } - } - ], - "responses": { - "200": { - "description": "200 response", - "content": { - "application/json": { - "examples": { - "default": { - "value": [ - { - "id": 1, - "created_on": "2020-01-30T09:36:08.000Z", - "modified_on": "2020-01-30T09:41:04.000Z", - "is_disabled": 0, - "email": "jc@jc21.com", - "name": "Jamie Curnow", - "nickname": "James", - "avatar": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm", - "roles": [ - "admin" - ] - } - ] - }, - "withPermissions": { - "value": [ - { - "id": 1, - "created_on": "2020-01-30T09:36:08.000Z", - "modified_on": "2020-01-30T09:41:04.000Z", - "is_disabled": 0, - "email": "jc@jc21.com", - "name": "Jamie Curnow", - "nickname": "James", - "avatar": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm", - "roles": [ - "admin" - ], - "permissions": { - "visibility": "all", - "proxy_hosts": "manage", - "redirection_hosts": "manage", - "dead_hosts": "manage", - "streams": "manage", - "access_lists": "manage", - "certificates": "manage" - } - } - ] - } - }, - "schema": { - "$ref": "#/components/schemas/UsersList" - } - } - } - } - } + "$ref": "file://./paths/users/get.json" }, "post": { - "operationId": "createUser", - "summary": "Create a User", - "tags": [ - "Users" - ], - "security": [ - { - "BearerAuth": [ - "users" - ] - } - ], - "parameters": [ - { - "in": "body", - "name": "user", - "description": "User Payload", - "required": true, - "schema": { - "$ref": "#/components/schemas/UserObject" - } - } - ], - "responses": { - "201": { - "description": "201 response", - "content": { - "application/json": { - "examples": { - "default": { - "value": { - "id": 2, - "created_on": "2020-01-30T09:36:08.000Z", - "modified_on": "2020-01-30T09:41:04.000Z", - "is_disabled": 0, - "email": "jc@jc21.com", - "name": "Jamie Curnow", - "nickname": "James", - "avatar": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm", - "roles": [ - "admin" - ], - "permissions": { - "visibility": "all", - "proxy_hosts": "manage", - "redirection_hosts": "manage", - "dead_hosts": "manage", - "streams": "manage", - "access_lists": "manage", - "certificates": "manage" - } - } - } - }, - "schema": { - "$ref": "#/components/schemas/UserObject" - } - } - } - } - } + "$ref": "file://./paths/users/post.json" } }, "/users/{userID}": { "get": { - "operationId": "getUser", - "summary": "Get a user", - "tags": [ - "Users" - ], - "security": [ - { - "BearerAuth": [ - "users" - ] - } - ], - "parameters": [ - { - "in": "path", - "name": "userID", - "schema": { - "oneOf": [ - { - "type": "string", - "pattern": "^me$" - }, - { - "type": "integer", - "minimum": 1 - } - ] - }, - "required": true, - "description": "User ID or 'me' for yourself", - "example": 1 - } - ], - "responses": { - "200": { - "description": "200 response", - "content": { - "application/json": { - "examples": { - "default": { - "value": { - "id": 1, - "created_on": "2020-01-30T09:36:08.000Z", - "modified_on": "2020-01-30T09:41:04.000Z", - "is_disabled": 0, - "email": "jc@jc21.com", - "name": "Jamie Curnow", - "nickname": "James", - "avatar": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm", - "roles": [ - "admin" - ] - } - } - }, - "schema": { - "$ref": "#/components/schemas/UserObject" - } - } - } - } - } + "$ref": "file://./paths/users/userID/get.json" }, "put": { - "operationId": "updateUser", - "summary": "Update a User", - "tags": [ - "Users" - ], - "security": [ - { - "BearerAuth": [ - "users" - ] - } - ], - "parameters": [ - { - "in": "path", - "name": "userID", - "schema": { - "oneOf": [ - { - "type": "string", - "pattern": "^me$" - }, - { - "type": "integer", - "minimum": 1 - } - ] - }, - "required": true, - "description": "User ID or 'me' for yourself", - "example": 2 - }, - { - "in": "body", - "name": "user", - "description": "User Payload", - "required": true, - "schema": { - "$ref": "#/components/schemas/UserObject" - } - } - ], - "responses": { - "200": { - "description": "200 response", - "content": { - "application/json": { - "examples": { - "default": { - "value": { - "id": 2, - "created_on": "2020-01-30T09:36:08.000Z", - "modified_on": "2020-01-30T09:41:04.000Z", - "is_disabled": 0, - "email": "jc@jc21.com", - "name": "Jamie Curnow", - "nickname": "James", - "avatar": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm", - "roles": [ - "admin" - ] - } - } - }, - "schema": { - "$ref": "#/components/schemas/UserObject" - } - } - } - } - } + "$ref": "file://./paths/users/userID/put.json" }, "delete": { - "operationId": "deleteUser", - "summary": "Delete a User", - "tags": [ - "Users" - ], - "security": [ - { - "BearerAuth": [ - "users" - ] - } - ], - "parameters": [ - { - "in": "path", - "name": "userID", - "schema": { - "type": "integer", - "minimum": 1 - }, - "required": true, - "description": "User ID", - "example": 2 - } - ], - "responses": { - "200": { - "description": "200 response", - "content": { - "application/json": { - "examples": { - "default": { - "value": true - } - }, - "schema": { - "type": "boolean" - } - } - } - } - } + "$ref": "file://./paths/users/userID/delete.json" } }, "/users/{userID}/auth": { - "put": { - "operationId": "updateUserAuth", - "summary": "Update a User's Authentication", - "tags": [ - "Users" - ], - "security": [ - { - "BearerAuth": [ - "users" - ] - } - ], - "parameters": [ - { - "in": "path", - "name": "userID", - "schema": { - "oneOf": [ - { - "type": "string", - "pattern": "^me$" - }, - { - "type": "integer", - "minimum": 1 - } - ] - }, - "required": true, - "description": "User ID or 'me' for yourself", - "example": 2 - }, - { - "in": "body", - "name": "user", - "description": "User Payload", - "required": true, - "schema": { - "$ref": "#/components/schemas/AuthObject" - } - } - ], - "responses": { - "200": { - "description": "200 response", - "content": { - "application/json": { - "examples": { - "default": { - "value": true - } - }, - "schema": { - "type": "boolean" - } - } - } - } - } - } - }, - "/users/{userID}/permissions": { - "put": { - "operationId": "updateUserPermissions", - "summary": "Update a User's Permissions", - "tags": [ - "Users" - ], - "security": [ - { - "BearerAuth": [ - "users" - ] - } - ], - "parameters": [ - { - "in": "path", - "name": "userID", - "schema": { - "type": "integer", - "minimum": 1 - }, - "required": true, - "description": "User ID", - "example": 2 - }, - { - "in": "body", - "name": "user", - "description": "Permissions Payload", - "required": true, - "schema": { - "$ref": "#/components/schemas/PermissionsObject" - } - } - ], - "responses": { - "200": { - "description": "200 response", - "content": { - "application/json": { - "examples": { - "default": { - "value": true - } - }, - "schema": { - "type": "boolean" - } - } - } - } - } - } - }, - "/users/{userID}/login": { - "put": { - "operationId": "loginAsUser", - "summary": "Login as this user", - "tags": [ - "Users" - ], - "security": [ - { - "BearerAuth": [ - "users" - ] - } - ], - "parameters": [ - { - "in": "path", - "name": "userID", - "schema": { - "type": "integer", - "minimum": 1 - }, - "required": true, - "description": "User ID", - "example": 2 - } - ], - "responses": { - "200": { - "description": "200 response", - "content": { - "application/json": { - "examples": { - "default": { - "value": { - "token": "eyJhbGciOiJSUzI1NiIsInR...16OjT8B3NLyXg", - "expires": "2020-01-31T10:56:23.239Z", - "user": { - "id": 1, - "created_on": "2020-01-30T10:43:44.000Z", - "modified_on": "2020-01-30T10:43:44.000Z", - "is_disabled": 0, - "email": "jc@jc21.com", - "name": "Jamie Curnow", - "nickname": "James", - "avatar": "//www.gravatar.com/avatar/3c8d73f45fd8763f827b964c76e6032a?default=mm", - "roles": [ - "admin" - ] - } - } - } - }, - "schema": { - "type": "object", - "description": "Login object", - "required": [ - "expires", - "token", - "user" - ], - "additionalProperties": false, - "properties": { - "expires": { - "description": "Token Expiry Unix Time", - "example": 1566540249, - "minimum": 1, - "type": "number" - }, - "token": { - "description": "JWT Token", - "example": "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.ey...xaHKYr3Kk6MvkUjcC4", - "type": "string" - }, - "user": { - "$ref": "#/components/schemas/UserObject" - } - } - } - } - } - } - } - } - }, - "/reports/hosts": { - "get": { - "operationId": "reportsHosts", - "summary": "Report on Host Statistics", - "tags": [ - "Reports" - ], - "security": [ - { - "BearerAuth": [ - "reports" - ] - } - ], - "responses": { - "200": { - "description": "200 response", - "content": { - "application/json": { - "examples": { - "default": { - "value": { - "proxy": 20, - "redirection": 1, - "stream": 0, - "dead": 1 - } - } - }, - "schema": { - "$ref": "#/components/schemas/HostReportObject" - } - } - } - } - } - } - }, - "/audit-log": { - "get": { - "operationId": "getAuditLog", - "summary": "Get Audit Log", - "tags": [ - "Audit Log" - ], - "security": [ - { - "BearerAuth": [ - "audit-log" - ] - } - ], - "responses": { - "200": { - "description": "200 response", - "content": { - "application/json": { - "examples": { - "default": { - "value": { - "proxy": 20, - "redirection": 1, - "stream": 0, - "dead": 1 - } - } - }, - "schema": { - "$ref": "#/components/schemas/HostReportObject" - } - } - } - } - } + "post": { + "$ref": "file://./paths/users/userID/auth/post.json" } } }, "components": { - "securitySchemes": { - "BearerAuth": { - "type": "http", - "scheme": "bearer" - } - }, "schemas": { - "HealthObject": { - "type": "object", - "description": "Health object", - "additionalProperties": false, - "required": [ - "status", - "version" - ], - "properties": { - "status": { - "type": "string", - "description": "Healthy", - "example": "OK" - }, - "version": { - "type": "object", - "description": "The version object", - "example": { - "major": 2, - "minor": 0, - "revision": 0 - }, - "additionalProperties": false, - "required": [ - "major", - "minor", - "revision" - ], - "properties": { - "major": { - "type": "integer", - "minimum": 0 - }, - "minor": { - "type": "integer", - "minimum": 0 - }, - "revision": { - "type": "integer", - "minimum": 0 - } - } - } - } + "CertificateAuthorityList": { + "$ref": "file://./components/CertificateAuthorityList.json" }, - "TokenObject": { - "type": "object", - "description": "Token object", - "required": [ - "expires", - "token" - ], - "additionalProperties": false, - "properties": { - "expires": { - "description": "Token Expiry Unix Time", - "example": 1566540249, - "minimum": 1, - "type": "number" - }, - "token": { - "description": "JWT Token", - "example": "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.ey...xaHKYr3Kk6MvkUjcC4", - "type": "string" - } - } + "CertificateAuthorityObject": { + "$ref": "file://./components/CertificateAuthorityObject.json" + }, + "CertificateList": { + "$ref": "file://./components/CertificateList.json" + }, + "CertificateObject": { + "$ref": "file://./components/CertificateObject.json" + }, + "ConfigObject": { + "$ref": "file://./components/ConfigObject.json" + }, + "DeletedItemResponse": { + "$ref": "file://./components/DeletedItemResponse.json" + }, + "DNSProviderList": { + "$ref": "file://./components/DNSProviderList.json" + }, + "DNSProviderObject": { + "$ref": "file://./components/DNSProviderObject.json" + }, + "ErrorObject": { + "$ref": "file://./components/ErrorObject.json" + }, + "FilterObject": { + "$ref": "file://./components/FilterObject.json" + }, + "HealthObject": { + "$ref": "file://./components/HealthObject.json" + }, + "HostList": { + "$ref": "file://./components/HostList.json" + }, + "HostObject": { + "$ref": "file://./components/HostObject.json" + }, + "SettingList": { + "$ref": "file://./components/SettingList.json" }, "SettingObject": { - "type": "object", - "description": "Setting object", - "required": [ - "id", - "name", - "description", - "value", - "meta" - ], - "additionalProperties": false, - "properties": { - "id": { - "type": "string", - "description": "Setting ID", - "minLength": 1, - "example": "default-site" - }, - "name": { - "type": "string", - "description": "Setting Display Name", - "minLength": 1, - "example": "Default Site" - }, - "description": { - "type": "string", - "description": "Meaningful description", - "minLength": 1, - "example": "What to show when Nginx is hit with an unknown Host" - }, - "value": { - "description": "Value in almost any form", - "example": "congratulations", - "oneOf": [ - { - "type": "string", - "minLength": 1 - }, - { - "type": "integer" - }, - { - "type": "object" - }, - { - "type": "number" - }, - { - "type": "array" - } - ] - }, - "meta": { - "description": "Extra metadata", - "example": {}, - "type": "object" - } - } + "$ref": "file://./components/SettingObject.json" }, - "SettingsList": { - "type": "array", - "description": "Setting list", - "items": { - "$ref": "#/components/schemas/SettingObject" - } + "SortObject": { + "$ref": "file://./components/SortObject.json" + }, + "StreamList": { + "$ref": "file://./components/StreamList.json" + }, + "StreamObject": { + "$ref": "file://./components/StreamObject.json" + }, + "TokenObject": { + "$ref": "file://./components/TokenObject.json" + }, + "UserList": { + "$ref": "file://./components/UserList.json" }, "UserObject": { - "type": "object", - "description": "User object", - "required": [ - "id", - "created_on", - "modified_on", - "is_disabled", - "email", - "name", - "nickname", - "avatar", - "roles" - ], - "additionalProperties": false, - "properties": { - "id": { - "type": "integer", - "description": "User ID", - "minimum": 1, - "example": 1 - }, - "created_on": { - "type": "string", - "description": "Created Date", - "example": "2020-01-30T09:36:08.000Z" - }, - "modified_on": { - "type": "string", - "description": "Modified Date", - "example": "2020-01-30T09:41:04.000Z" - }, - "is_disabled": { - "type": "integer", - "minimum": 0, - "maximum": 1, - "description": "Is user Disabled (0 = false, 1 = true)", - "example": 0 - }, - "email": { - "type": "string", - "description": "Email", - "minLength": 3, - "example": "jc@jc21.com" - }, - "name": { - "type": "string", - "description": "Name", - "minLength": 1, - "example": "Jamie Curnow" - }, - "nickname": { - "type": "string", - "description": "Nickname", - "example": "James" - }, - "avatar": { - "type": "string", - "description": "Gravatar URL based on email, without scheme", - "example": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm" - }, - "roles": { - "description": "Roles applied", - "example": [ - "admin" - ], - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "UsersList": { - "type": "array", - "description": "User list", - "items": { - "$ref": "#/components/schemas/UserObject" - } - }, - "AuthObject": { - "type": "object", - "description": "Authentication Object", - "required": [ - "type", - "secret" - ], - "properties": { - "type": { - "type": "string", - "pattern": "^password$", - "example": "password" - }, - "current": { - "type": "string", - "minLength": 1, - "maxLength": 64, - "example": "changeme" - }, - "secret": { - "type": "string", - "minLength": 8, - "maxLength": 64, - "example": "mySuperN3wP@ssword!" - } - } - }, - "PermissionsObject": { - "type": "object", - "properties": { - "visibility": { - "type": "string", - "description": "Visibility Type", - "enum": [ - "all", - "user" - ] - }, - "access_lists": { - "type": "string", - "description": "Access Lists Permissions", - "enum": [ - "hidden", - "view", - "manage" - ] - }, - "dead_hosts": { - "type": "string", - "description": "404 Hosts Permissions", - "enum": [ - "hidden", - "view", - "manage" - ] - }, - "proxy_hosts": { - "type": "string", - "description": "Proxy Hosts Permissions", - "enum": [ - "hidden", - "view", - "manage" - ] - }, - "redirection_hosts": { - "type": "string", - "description": "Redirection Permissions", - "enum": [ - "hidden", - "view", - "manage" - ] - }, - "streams": { - "type": "string", - "description": "Streams Permissions", - "enum": [ - "hidden", - "view", - "manage" - ] - }, - "certificates": { - "type": "string", - "description": "Certificates Permissions", - "enum": [ - "hidden", - "view", - "manage" - ] - } - } - }, - "HostReportObject": { - "type": "object", - "properties": { - "proxy": { - "type": "integer", - "description": "Proxy Hosts Count" - }, - "redirection": { - "type": "integer", - "description": "Redirection Hosts Count" - }, - "stream": { - "type": "integer", - "description": "Streams Count" - }, - "dead": { - "type": "integer", - "description": "404 Hosts Count" - } - } + "$ref": "file://./components/UserObject.json" } } } -} \ No newline at end of file +} diff --git a/backend/doc/components/CertificateAuthorityList.json b/backend/doc/components/CertificateAuthorityList.json new file mode 100644 index 00000000..73c6db58 --- /dev/null +++ b/backend/doc/components/CertificateAuthorityList.json @@ -0,0 +1,45 @@ +{ + "type": "object", + "description": "CertificateAuthorityList", + "additionalProperties": false, + "required": [ + "total", + "offset", + "limit", + "sort" + ], + "properties": { + "total": { + "type": "integer", + "description": "Total number of rows" + }, + "offset": { + "type": "integer", + "description": "Pagination Offset" + }, + "limit": { + "type": "integer", + "description": "Pagination Limit" + }, + "sort": { + "type": "array", + "description": "Sorting", + "items": { + "$ref": "#/components/schemas/SortObject" + } + }, + "filter": { + "type": "array", + "description": "Filters", + "items": { + "$ref": "#/components/schemas/FilterObject" + } + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CertificateAuthorityObject" + } + } + } +} \ No newline at end of file diff --git a/backend/doc/components/CertificateAuthorityObject.json b/backend/doc/components/CertificateAuthorityObject.json new file mode 100644 index 00000000..62289cce --- /dev/null +++ b/backend/doc/components/CertificateAuthorityObject.json @@ -0,0 +1,36 @@ +{ + "type": "object", + "description": "CertificateAuthorityObject", + "additionalProperties": false, + "required": [ + "id", + "created_on", + "modified_on", + "name", + "acme2_url" + ], + "properties": { + "id": { + "type": "integer", + "minimum": 1 + }, + "created_on": { + "type": "integer", + "minimum": 1 + }, + "modified_on": { + "type": "integer", + "minimum": 1 + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "acme2_url": { + "type": "string", + "minLength": 8, + "maxLength": 255 + } + } +} \ No newline at end of file diff --git a/backend/doc/components/CertificateList.json b/backend/doc/components/CertificateList.json new file mode 100644 index 00000000..54b62d69 --- /dev/null +++ b/backend/doc/components/CertificateList.json @@ -0,0 +1,45 @@ +{ + "type": "object", + "description": "CertificateList", + "additionalProperties": false, + "required": [ + "total", + "offset", + "limit", + "sort" + ], + "properties": { + "total": { + "type": "integer", + "description": "Total number of rows" + }, + "offset": { + "type": "integer", + "description": "Pagination Offset" + }, + "limit": { + "type": "integer", + "description": "Pagination Limit" + }, + "sort": { + "type": "array", + "description": "Sorting", + "items": { + "$ref": "#/components/schemas/SortObject" + } + }, + "filter": { + "type": "array", + "description": "Filters", + "items": { + "$ref": "#/components/schemas/FilterObject" + } + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CertificateObject" + } + } + } +} \ No newline at end of file diff --git a/backend/doc/components/CertificateObject.json b/backend/doc/components/CertificateObject.json new file mode 100644 index 00000000..abf36513 --- /dev/null +++ b/backend/doc/components/CertificateObject.json @@ -0,0 +1,82 @@ +{ + "type": "object", + "description": "CertificateObject", + "additionalProperties": false, + "required": [ + "id", + "created_on", + "modified_on", + "expires_on", + "type", + "user_id", + "certificate_authority_id", + "dns_provider_id", + "name", + "status", + "domain_names" + ], + "properties": { + "id": { + "type": "integer", + "minimum": 1 + }, + "created_on": { + "type": "integer", + "minimum": 1 + }, + "modified_on": { + "type": "integer", + "minimum": 1 + }, + "expires_on": { + "type": "integer", + "minimum": 1, + "nullable": true + }, + "type": { + "type": "string", + "enum": [ + "custom", + "http", + "dns" + ] + }, + "user_id": { + "type": "integer", + "minimum": 1 + }, + "certificate_authority_id": { + "type": "integer", + "minimum": 0 + }, + "dns_provider_id": { + "type": "integer", + "minimum": 0 + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "domain_names": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "minLength": 4 + } + }, + "status": { + "type": "string", + "enum": [ + "ready", + "requesting", + "failed", + "provided" + ] + }, + "error_message": { + "type": "string" + } + } +} \ No newline at end of file diff --git a/backend/doc/components/ConfigObject.json b/backend/doc/components/ConfigObject.json new file mode 100644 index 00000000..644e4a4c --- /dev/null +++ b/backend/doc/components/ConfigObject.json @@ -0,0 +1,4 @@ +{ + "type": "object", + "description": "ConfigObject" +} \ No newline at end of file diff --git a/backend/doc/components/DNSProviderList.json b/backend/doc/components/DNSProviderList.json new file mode 100644 index 00000000..2186fbb3 --- /dev/null +++ b/backend/doc/components/DNSProviderList.json @@ -0,0 +1,45 @@ +{ + "type": "object", + "description": "DNSProviderList", + "additionalProperties": false, + "required": [ + "total", + "offset", + "limit", + "sort" + ], + "properties": { + "total": { + "type": "integer", + "description": "Total number of rows" + }, + "offset": { + "type": "integer", + "description": "Pagination Offset" + }, + "limit": { + "type": "integer", + "description": "Pagination Limit" + }, + "sort": { + "type": "array", + "description": "Sorting", + "items": { + "$ref": "#/components/schemas/SortObject" + } + }, + "filter": { + "type": "array", + "description": "Filters", + "items": { + "$ref": "#/components/schemas/FilterObject" + } + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DNSProviderObject" + } + } + } +} \ No newline at end of file diff --git a/backend/doc/components/DNSProviderObject.json b/backend/doc/components/DNSProviderObject.json new file mode 100644 index 00000000..ddad518c --- /dev/null +++ b/backend/doc/components/DNSProviderObject.json @@ -0,0 +1,45 @@ +{ + "type": "object", + "description": "DNSProviderObject", + "additionalProperties": false, + "required": [ + "id", + "created_on", + "modified_on", + "user_id", + "provider_key", + "name", + "meta" + ], + "properties": { + "id": { + "type": "integer", + "minimum": 1 + }, + "created_on": { + "type": "integer", + "minimum": 1 + }, + "modified_on": { + "type": "integer", + "minimum": 1 + }, + "user_id": { + "type": "integer", + "minimum": 1 + }, + "provider_key": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "meta": { + "type": "object" + } + } +} \ No newline at end of file diff --git a/backend/doc/components/DeletedItemResponse.json b/backend/doc/components/DeletedItemResponse.json new file mode 100644 index 00000000..34478bc5 --- /dev/null +++ b/backend/doc/components/DeletedItemResponse.json @@ -0,0 +1,17 @@ +{ + "type": "object", + "description": "DeletedItemResponse", + "additionalProperties": false, + "required": [ + "result" + ], + "properties": { + "result": { + "type": "boolean", + "nullable": true + }, + "error": { + "$ref": "#/components/schemas/ErrorObject" + } + } +} diff --git a/backend/doc/components/ErrorObject.json b/backend/doc/components/ErrorObject.json new file mode 100644 index 00000000..b6cfb338 --- /dev/null +++ b/backend/doc/components/ErrorObject.json @@ -0,0 +1,20 @@ +{ + "type": "object", + "description": "ErrorObject", + "additionalProperties": false, + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "type": "integer", + "description": "Error code", + "minimum": 0 + }, + "message": { + "type": "string", + "description": "Error message" + } + } +} \ No newline at end of file diff --git a/backend/doc/components/FilterObject.json b/backend/doc/components/FilterObject.json new file mode 100644 index 00000000..a7ef4828 --- /dev/null +++ b/backend/doc/components/FilterObject.json @@ -0,0 +1,28 @@ +{ + "type": "object", + "description": "FilterObject", + "additionalProperties": false, + "required": [ + "field", + "modifier", + "value" + ], + "properties": { + "field": { + "type": "string", + "description": "Field to filter with" + }, + "modifier": { + "type": "string", + "description": "Filter modifier", + "pattern": "^(equals|not|min|max|greater|lesser|contains|starts|ends|in|notin)$" + }, + "value": { + "type": "array", + "description": "Values used for filtering", + "items": { + "type": "string" + } + } + } +} diff --git a/backend/doc/components/HealthObject.json b/backend/doc/components/HealthObject.json new file mode 100644 index 00000000..c75274a7 --- /dev/null +++ b/backend/doc/components/HealthObject.json @@ -0,0 +1,41 @@ +{ + "type": "object", + "description": "HealthObject", + "additionalProperties": false, + "required": [ + "version", + "commit", + "healthy", + "setup", + "error_reporting" + ], + "properties": { + "version": { + "type": "string", + "description": "Version", + "example": "3.0.0", + "minLength": 1 + }, + "commit": { + "type": "string", + "description": "Commit hash", + "example": "946b88f", + "minLength": 7 + }, + "healthy": { + "type": "boolean", + "description": "Healthy?", + "example": true + }, + "setup": { + "type": "boolean", + "description": "Is the application set up?", + "example": true + }, + "error_reporting": { + "type": "boolean", + "description": "Will the application send any error reporting?", + "example": true + } + } +} \ No newline at end of file diff --git a/backend/doc/components/HostList.json b/backend/doc/components/HostList.json new file mode 100644 index 00000000..fc40b33b --- /dev/null +++ b/backend/doc/components/HostList.json @@ -0,0 +1,45 @@ +{ + "type": "object", + "description": "HostList", + "additionalProperties": false, + "required": [ + "total", + "offset", + "limit", + "sort" + ], + "properties": { + "total": { + "type": "integer", + "description": "Total number of rows" + }, + "offset": { + "type": "integer", + "description": "Pagination Offset" + }, + "limit": { + "type": "integer", + "description": "Pagination Limit" + }, + "sort": { + "type": "array", + "description": "Sorting", + "items": { + "$ref": "#/components/schemas/SortObject" + } + }, + "filter": { + "type": "array", + "description": "Filters", + "items": { + "$ref": "#/components/schemas/FilterObject" + } + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/HostObject" + } + } + } +} \ No newline at end of file diff --git a/backend/doc/components/HostObject.json b/backend/doc/components/HostObject.json new file mode 100644 index 00000000..6e78a40b --- /dev/null +++ b/backend/doc/components/HostObject.json @@ -0,0 +1,55 @@ +{ + "type": "object", + "description": "HostObject", + "additionalProperties": false, + "required": [ + "id", + "created_on", + "modified_on", + "expires_on", + "user_id", + "provider", + "name", + "domain_names" + ], + "properties": { + "id": { + "type": "integer", + "minimum": 1 + }, + "created_on": { + "type": "integer", + "minimum": 1 + }, + "modified_on": { + "type": "integer", + "minimum": 1 + }, + "expires_on": { + "type": "integer", + "minimum": 1 + }, + "user_id": { + "type": "integer", + "minimum": 1 + }, + "provider": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "domain_names": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "minLength": 4 + } + } + } +} \ No newline at end of file diff --git a/backend/doc/components/SettingList.json b/backend/doc/components/SettingList.json new file mode 100644 index 00000000..bea224b2 --- /dev/null +++ b/backend/doc/components/SettingList.json @@ -0,0 +1,45 @@ +{ + "type": "object", + "description": "SettingList", + "additionalProperties": false, + "required": [ + "total", + "offset", + "limit", + "sort" + ], + "properties": { + "total": { + "type": "integer", + "description": "Total number of rows" + }, + "offset": { + "type": "integer", + "description": "Pagination Offset" + }, + "limit": { + "type": "integer", + "description": "Pagination Limit" + }, + "sort": { + "type": "array", + "description": "Sorting", + "items": { + "$ref": "#/components/schemas/SortObject" + } + }, + "filter": { + "type": "array", + "description": "Filters", + "items": { + "$ref": "#/components/schemas/FilterObject" + } + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SettingObject" + } + } + } +} \ No newline at end of file diff --git a/backend/doc/components/SettingObject.json b/backend/doc/components/SettingObject.json new file mode 100644 index 00000000..a7c6e055 --- /dev/null +++ b/backend/doc/components/SettingObject.json @@ -0,0 +1,45 @@ +{ + "type": "object", + "description": "SettingObject", + "additionalProperties": false, + "required": [ + "id", + "name", + "value" + ], + "properties": { + "id": { + "type": "integer", + "minimum": 1 + }, + "created_on": { + "type": "integer", + "minimum": 1 + }, + "modified_on": { + "type": "integer", + "minimum": 1 + }, + "name": { + "type": "string", + "minLength": 2, + "maxLength": 100 + }, + "value": { + "oneOf": [ + { + "type": "array" + }, + { + "type": "boolean" + }, + { + "type": "object" + }, + { + "type": "integer" + } + ] + } + } +} \ No newline at end of file diff --git a/backend/doc/components/SortObject.json b/backend/doc/components/SortObject.json new file mode 100644 index 00000000..b0e0ddc6 --- /dev/null +++ b/backend/doc/components/SortObject.json @@ -0,0 +1,20 @@ +{ + "type": "object", + "description": "SortObject", + "additionalProperties": false, + "required": [ + "field", + "direction" + ], + "properties": { + "field": { + "type": "string", + "description": "Field for sorting on" + }, + "direction": { + "type": "string", + "description": "Sort order", + "pattern": "^(ASC|DESC)$" + } + } +} diff --git a/backend/doc/components/StreamList.json b/backend/doc/components/StreamList.json new file mode 100644 index 00000000..3f7be7f4 --- /dev/null +++ b/backend/doc/components/StreamList.json @@ -0,0 +1,45 @@ +{ + "type": "object", + "description": "StreamList", + "additionalProperties": false, + "required": [ + "total", + "offset", + "limit", + "sort" + ], + "properties": { + "total": { + "type": "integer", + "description": "Total number of rows" + }, + "offset": { + "type": "integer", + "description": "Pagination Offset" + }, + "limit": { + "type": "integer", + "description": "Pagination Limit" + }, + "sort": { + "type": "array", + "description": "Sorting", + "items": { + "$ref": "#/components/schemas/SortObject" + } + }, + "filter": { + "type": "array", + "description": "Filters", + "items": { + "$ref": "#/components/schemas/FilterObject" + } + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/StreamObject" + } + } + } +} \ No newline at end of file diff --git a/backend/doc/components/StreamObject.json b/backend/doc/components/StreamObject.json new file mode 100644 index 00000000..de48bed0 --- /dev/null +++ b/backend/doc/components/StreamObject.json @@ -0,0 +1,55 @@ +{ + "type": "object", + "description": "StreamObject", + "additionalProperties": false, + "required": [ + "id", + "created_on", + "modified_on", + "expires_on", + "user_id", + "provider", + "name", + "domain_names" + ], + "properties": { + "id": { + "type": "integer", + "minimum": 1 + }, + "created_on": { + "type": "integer", + "minimum": 1 + }, + "modified_on": { + "type": "integer", + "minimum": 1 + }, + "expires_on": { + "type": "integer", + "minimum": 1 + }, + "user_id": { + "type": "integer", + "minimum": 1 + }, + "provider": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "domain_names": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "minLength": 4 + } + } + } +} \ No newline at end of file diff --git a/backend/doc/components/TokenObject.json b/backend/doc/components/TokenObject.json new file mode 100644 index 00000000..3d2d9573 --- /dev/null +++ b/backend/doc/components/TokenObject.json @@ -0,0 +1,22 @@ +{ + "type": "object", + "description": "TokenObject", + "additionalProperties": false, + "required": [ + "expires", + "token" + ], + "properties": { + "expires": { + "type": "number", + "description": "Token Expiry Unix Time", + "example": 1566540249, + "minimum": 1 + }, + "token": { + "type": "string", + "description": "JWT Token", + "example": "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.ey...xaHKYr3Kk6MvkUjcC4" + } + } +} \ No newline at end of file diff --git a/backend/doc/components/UserList.json b/backend/doc/components/UserList.json new file mode 100644 index 00000000..f4a624f4 --- /dev/null +++ b/backend/doc/components/UserList.json @@ -0,0 +1,45 @@ +{ + "type": "object", + "description": "UserList", + "additionalProperties": false, + "required": [ + "total", + "offset", + "limit", + "sort" + ], + "properties": { + "total": { + "type": "integer", + "description": "Total number of rows" + }, + "offset": { + "type": "integer", + "description": "Pagination Offset" + }, + "limit": { + "type": "integer", + "description": "Pagination Limit" + }, + "sort": { + "type": "array", + "description": "Sorting", + "items": { + "$ref": "#/components/schemas/SortObject" + } + }, + "filter": { + "type": "array", + "description": "Filters", + "items": { + "$ref": "#/components/schemas/FilterObject" + } + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/UserObject" + } + } + } +} \ No newline at end of file diff --git a/backend/doc/components/UserObject.json b/backend/doc/components/UserObject.json new file mode 100644 index 00000000..7cc4aed8 --- /dev/null +++ b/backend/doc/components/UserObject.json @@ -0,0 +1,75 @@ +{ + "type": "object", + "description": "UserObject", + "additionalProperties": false, + "required": [ + "id", + "name", + "nickname", + "email", + "created_on", + "modified_on", + "roles", + "is_disabled" + ], + "properties": { + "id": { + "type": "integer", + "minimum": 1 + }, + "name": { + "type": "string", + "minLength": 2, + "maxLength": 100 + }, + "nickname": { + "type": "string", + "minLength": 2, + "maxLength": 100 + }, + "email": { + "type": "string", + "minLength": 5, + "maxLength": 150 + }, + "created_on": { + "type": "integer", + "minimum": 1 + }, + "modified_on": { + "type": "integer", + "minimum": 1 + }, + "roles": { + "type": "array", + "items": { + "type": "string", + "minLength": 2 + } + }, + "gravatar_url": { + "type": "string" + }, + "is_disabled": { + "type": "boolean" + }, + "is_deleted": { + "type": "boolean" + }, + "auth": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "id": { + "type": "integer" + }, + "type": { + "type": "string", + "pattern": "^password$" + } + } + } + } +} \ No newline at end of file diff --git a/backend/doc/main.go b/backend/doc/main.go new file mode 100644 index 00000000..95756af7 --- /dev/null +++ b/backend/doc/main.go @@ -0,0 +1,9 @@ +package doc + +import "embed" + +// SwaggerFiles contain all the files used for swagger schema generation +//go:embed api.swagger.json +//go:embed components +//go:embed paths +var SwaggerFiles embed.FS diff --git a/backend/doc/paths/certificates-authorities/caID/delete.json b/backend/doc/paths/certificates-authorities/caID/delete.json new file mode 100644 index 00000000..3ae3bea8 --- /dev/null +++ b/backend/doc/paths/certificates-authorities/caID/delete.json @@ -0,0 +1,39 @@ +{ + "operationId": "deleteCertificateAuthority", + "summary": "Delete a Certificate Authority", + "tags": [ + "Certificate Authorities" + ], + "parameters": [ + { + "in": "path", + "name": "caID", + "schema": { + "type": "integer", + "minimum": 1 + }, + "required": true, + "description": "Numeric ID of the Certificate Authority", + "example": 1 + } + ], + "responses": { + "200": { + "description": "200 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeletedItemResponse" + }, + "examples": { + "default": { + "value": { + "result": true + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/backend/doc/paths/certificates-authorities/caID/get.json b/backend/doc/paths/certificates-authorities/caID/get.json new file mode 100644 index 00000000..33f35416 --- /dev/null +++ b/backend/doc/paths/certificates-authorities/caID/get.json @@ -0,0 +1,52 @@ +{ + "operationId": "getCertificateAuthority", + "summary": "Get a Certificate Authority object by ID", + "tags": [ + "Certificate Authorities" + ], + "parameters": [ + { + "in": "path", + "name": "caID", + "schema": { + "type": "integer", + "minimum": 1 + }, + "required": true, + "description": "ID of the Certificate Authority", + "example": 1 + } + ], + "responses": { + "200": { + "description": "200 response", + "content": { + "application/json": { + "schema": { + "required": [ + "result" + ], + "properties": { + "result": { + "$ref": "#/components/schemas/CertificateAuthorityObject" + } + } + }, + "examples": { + "default": { + "value": { + "result": { + "id": 1, + "created_on": 1602588511, + "modified_on": 1602588511, + "name": "Let's Encrypt", + "acme2_url": "https://acme-v02.api.letsencrypt.org/directory" + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/backend/doc/paths/certificates-authorities/caID/put.json b/backend/doc/paths/certificates-authorities/caID/put.json new file mode 100644 index 00000000..b1e99602 --- /dev/null +++ b/backend/doc/paths/certificates-authorities/caID/put.json @@ -0,0 +1,61 @@ +{ + "operationId": "updateCertificateAuthority", + "summary": "Update an existing Certificate Authority", + "tags": [ + "Certificate Authorities" + ], + "parameters": [ + { + "in": "path", + "name": "caID", + "schema": { + "type": "integer", + "minimum": 1 + }, + "required": true, + "description": "ID of the Certificate Authority", + "example": 1 + } + ], + "requestBody": { + "description": "Certificate Authority details to update", + "required": true, + "content": { + "application/json": { + "schema": "{{schema.UpdateCertificateAuthority}}" + } + } + }, + "responses": { + "200": { + "description": "200 response", + "content": { + "application/json": { + "schema": { + "required": [ + "result" + ], + "properties": { + "result": { + "$ref": "#/components/schemas/CertificateAuthorityObject" + } + } + }, + "examples": { + "default": { + "value": { + "result": { + "id": 1, + "created_on": 1602588511, + "modified_on": 1602588511, + "name": "Let's Encrypt", + "acme2_url": "https://acme-v02.api.letsencrypt.org/directory" + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/backend/doc/paths/certificates-authorities/get.json b/backend/doc/paths/certificates-authorities/get.json new file mode 100644 index 00000000..14e54070 --- /dev/null +++ b/backend/doc/paths/certificates-authorities/get.json @@ -0,0 +1,88 @@ +{ + "operationId": "getCertificateAuthorities", + "summary": "Get a list of Certificate Authorities", + "tags": [ + "Certificate Authorities" + ], + "parameters": [ + { + "in": "query", + "name": "offset", + "schema": { + "type": "number" + }, + "description": "The pagination row offset, default 0", + "example": 0 + }, + { + "in": "query", + "name": "limit", + "schema": { + "type": "number" + }, + "description": "The pagination row limit, default 10", + "example": 10 + }, + { + "in": "query", + "name": "sort", + "schema": { + "type": "string" + }, + "description": "The sorting of the list", + "example": "id,name.asc,value.desc" + } + ], + "responses": { + "200": { + "description": "200 response", + "content": { + "application/json": { + "schema": { + "required": [ + "result" + ], + "properties": { + "result": { + "$ref": "#/components/schemas/CertificateAuthorityList" + } + } + }, + "examples": { + "default": { + "value": { + "result": { + "total": 2, + "offset": 0, + "limit": 10, + "sort": [ + { + "field": "name", + "direction": "ASC" + } + ], + "items": [ + { + "id": 1, + "created_on": 1602588511, + "modified_on": 1602588511, + "name": "Let's Encrypt", + "acme2_url": "https://acme-v02.api.letsencrypt.org/directory" + }, + { + "id": 2, + "created_on": 1602588511, + "modified_on": 1602588511, + "name": "Let's Encrypt (Staging)", + "acme2_url": "https://acme-staging-v02.api.letsencrypt.org/directory" + } + ] + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/backend/doc/paths/certificates-authorities/post.json b/backend/doc/paths/certificates-authorities/post.json new file mode 100644 index 00000000..8cd359db --- /dev/null +++ b/backend/doc/paths/certificates-authorities/post.json @@ -0,0 +1,48 @@ +{ + "operationId": "createCertificateAuthority", + "summary": "Create a new Certificate Authority", + "tags": [ + "Certificate Authorities" + ], + "requestBody": { + "description": "Certificate Authority to Create", + "required": true, + "content": { + "application/json": { + "schema": "{{schema.CreateCertificateAuthority}}" + } + } + }, + "responses": { + "201": { + "description": "201 response", + "content": { + "application/json": { + "schema": { + "required": [ + "result" + ], + "properties": { + "result": { + "$ref": "#/components/schemas/CertificateAuthorityObject" + } + } + }, + "examples": { + "default": { + "value": { + "result": { + "id": 3, + "created_on": 1602588900, + "modified_on": 1602588900, + "name": "Boulder", + "acme2_url": "https://boulder.local/directory" + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/backend/doc/paths/certificates/certificateID/delete.json b/backend/doc/paths/certificates/certificateID/delete.json new file mode 100644 index 00000000..98acfaf7 --- /dev/null +++ b/backend/doc/paths/certificates/certificateID/delete.json @@ -0,0 +1,60 @@ +{ + "operationId": "deleteCertificate", + "summary": "Delete a Certificate", + "tags": [ + "Certificates" + ], + "parameters": [ + { + "in": "path", + "name": "certificateID", + "schema": { + "type": "integer", + "minimum": 1 + }, + "required": true, + "description": "Numeric ID of the certificate", + "example": 1 + } + ], + "responses": { + "200": { + "description": "200 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeletedItemResponse" + }, + "examples": { + "default": { + "value": { + "result": true + } + } + } + } + } + }, + "400": { + "description": "400 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeletedItemResponse" + }, + "examples": { + "default": { + "value": { + "result": null, + "error": { + "code": 400, + "message": "You cannot delete a certificate that is in use!" + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/backend/doc/paths/certificates/certificateID/get.json b/backend/doc/paths/certificates/certificateID/get.json new file mode 100644 index 00000000..99613b67 --- /dev/null +++ b/backend/doc/paths/certificates/certificateID/get.json @@ -0,0 +1,60 @@ +{ + "operationId": "getCertificate", + "summary": "Get a certificate object by ID", + "tags": [ + "Certificates" + ], + "parameters": [ + { + "in": "path", + "name": "certificateID", + "schema": { + "type": "integer", + "minimum": 1 + }, + "required": true, + "description": "ID of the certificate", + "example": 1 + } + ], + "responses": { + "200": { + "description": "200 response", + "content": { + "application/json": { + "schema": { + "required": [ + "result" + ], + "properties": { + "result": { + "$ref": "#/components/schemas/CertificateObject" + } + } + }, + "examples": { + "default": { + "value": { + "result": { + "id": 1, + "created_on": 1604536109, + "modified_on": 1604536109, + "expires_on": null, + "type": "dns", + "user_id": 1, + "certificate_authority_id": 2, + "dns_provider_id": 1, + "name": "test1.jc21.com.au", + "domain_names": [ + "test1.jc21.com.au" + ], + "status": "ready" + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/backend/doc/paths/certificates/certificateID/put.json b/backend/doc/paths/certificates/certificateID/put.json new file mode 100644 index 00000000..514790eb --- /dev/null +++ b/backend/doc/paths/certificates/certificateID/put.json @@ -0,0 +1,69 @@ +{ + "operationId": "updateCertificate", + "summary": "Update an existing Certificate", + "tags": [ + "Certificates" + ], + "parameters": [ + { + "in": "path", + "name": "certificateID", + "schema": { + "type": "integer", + "minimum": 1 + }, + "required": true, + "description": "ID of the certificate", + "example": 1 + } + ], + "requestBody": { + "description": "Certificate details to update", + "required": true, + "content": { + "application/json": { + "schema": "{{schema.UpdateCertificate}}" + } + } + }, + "responses": { + "200": { + "description": "200 response", + "content": { + "application/json": { + "schema": { + "required": [ + "result" + ], + "properties": { + "result": { + "$ref": "#/components/schemas/CertificateObject" + } + } + }, + "examples": { + "default": { + "value": { + "result": { + "id": 1, + "created_on": 1604536109, + "modified_on": 1604536109, + "expires_on": null, + "type": "dns", + "user_id": 1, + "certificate_authority_id": 2, + "dns_provider_id": 1, + "name": "test1.jc21.com.au", + "domain_names": [ + "test1.jc21.com.au" + ], + "status": "ready" + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/backend/doc/paths/certificates/get.json b/backend/doc/paths/certificates/get.json new file mode 100644 index 00000000..1a31135c --- /dev/null +++ b/backend/doc/paths/certificates/get.json @@ -0,0 +1,89 @@ +{ + "operationId": "getCertificates", + "summary": "Get a list of certificates", + "tags": [ + "Certificates" + ], + "parameters": [ + { + "in": "query", + "name": "offset", + "schema": { + "type": "number" + }, + "description": "The pagination row offset, default 0", + "example": 0 + }, + { + "in": "query", + "name": "limit", + "schema": { + "type": "number" + }, + "description": "The pagination row limit, default 10", + "example": 10 + }, + { + "in": "query", + "name": "sort", + "schema": { + "type": "string" + }, + "description": "The sorting of the list", + "example": "id,name.asc,value.desc" + } + ], + "responses": { + "200": { + "description": "200 response", + "content": { + "application/json": { + "schema": { + "required": [ + "result" + ], + "properties": { + "result": { + "$ref": "#/components/schemas/CertificateList" + } + } + }, + "examples": { + "default": { + "value": { + "result": { + "total": 1, + "offset": 0, + "limit": 10, + "sort": [ + { + "field": "name", + "direction": "ASC" + } + ], + "items": [ + { + "id": 1, + "created_on": 1604536109, + "modified_on": 1604536109, + "expires_on": null, + "type": "dns", + "user_id": 1, + "certificate_authority_id": 2, + "dns_provider_id": 1, + "name": "test1.jc21.com.au", + "domain_names": [ + "test1.jc21.com.au" + ], + "status": "ready" + } + ] + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/backend/doc/paths/certificates/post.json b/backend/doc/paths/certificates/post.json new file mode 100644 index 00000000..b4566e6c --- /dev/null +++ b/backend/doc/paths/certificates/post.json @@ -0,0 +1,56 @@ +{ + "operationId": "createCertificate", + "summary": "Create a new Certificate", + "tags": [ + "Certificates" + ], + "requestBody": { + "description": "Certificate to create", + "required": true, + "content": { + "application/json": { + "schema": "{{schema.CreateCertificate}}" + } + } + }, + "responses": { + "201": { + "description": "201 response", + "content": { + "application/json": { + "schema": { + "required": [ + "result" + ], + "properties": { + "result": { + "$ref": "#/components/schemas/CertificateObject" + } + } + }, + "examples": { + "default": { + "value": { + "result": { + "id": 1, + "created_on": 1604536109, + "modified_on": 1604536109, + "expires_on": null, + "type": "dns", + "user_id": 1, + "certificate_authority_id": 2, + "dns_provider_id": 1, + "name": "test1.jc21.com.au", + "domain_names": [ + "test1.jc21.com.au" + ], + "status": "ready" + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/backend/doc/paths/config/get.json b/backend/doc/paths/config/get.json new file mode 100644 index 00000000..ea1f0701 --- /dev/null +++ b/backend/doc/paths/config/get.json @@ -0,0 +1,36 @@ +{ + "operationId": "config", + "summary": "Returns the API Service configuration", + "responses": { + "200": { + "description": "200 response", + "content": { + "application/json": { + "schema": { + "required": [ + "result" + ], + "properties": { + "result": { + "$ref": "#/components/schemas/ConfigObject" + } + } + }, + "examples": { + "default": { + "value": { + "result": { + "data": "/data", + "log": { + "level": "debug", + "format": "nice" + } + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/backend/doc/paths/dns-providers/get.json b/backend/doc/paths/dns-providers/get.json new file mode 100644 index 00000000..19a298bf --- /dev/null +++ b/backend/doc/paths/dns-providers/get.json @@ -0,0 +1,87 @@ +{ + "operationId": "getDNSProviders", + "summary": "Get a list of DNS Providers", + "tags": [ + "DNS Providers" + ], + "parameters": [ + { + "in": "query", + "name": "offset", + "schema": { + "type": "number" + }, + "description": "The pagination row offset, default 0", + "example": 0 + }, + { + "in": "query", + "name": "limit", + "schema": { + "type": "number" + }, + "description": "The pagination row limit, default 10", + "example": 10 + }, + { + "in": "query", + "name": "sort", + "schema": { + "type": "string" + }, + "description": "The sorting of the list", + "example": "id,name.asc,value.desc" + } + ], + "responses": { + "200": { + "description": "200 response", + "content": { + "application/json": { + "schema": { + "required": [ + "result" + ], + "properties": { + "result": { + "$ref": "#/components/schemas/DNSProviderList" + } + } + }, + "examples": { + "default": { + "value": { + "result": { + "total": 1, + "offset": 0, + "limit": 10, + "sort": [ + { + "field": "name", + "direction": "ASC" + } + ], + "items": [ + { + "id": 1, + "created_on": 1602593653, + "modified_on": 1602593653, + "user_id": 1, + "provider_key": "route53", + "name": "Route53", + "meta": { + "access_key": "abc123", + "access_secret": "def098", + "zone_id": "ABC123" + } + } + ] + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/backend/doc/paths/dns-providers/post.json b/backend/doc/paths/dns-providers/post.json new file mode 100644 index 00000000..35bc2bc3 --- /dev/null +++ b/backend/doc/paths/dns-providers/post.json @@ -0,0 +1,54 @@ +{ + "operationId": "createDNSProvider", + "summary": "Create a new DNS Provider", + "tags": [ + "DNS Providers" + ], + "requestBody": { + "description": "DNS Provider to Create", + "required": true, + "content": { + "application/json": { + "schema": "{{schema.CreateDNSProvider}}" + } + } + }, + "responses": { + "201": { + "description": "201 response", + "content": { + "application/json": { + "schema": { + "required": [ + "result" + ], + "properties": { + "result": { + "$ref": "#/components/schemas/DNSProviderObject" + } + } + }, + "examples": { + "default": { + "value": { + "result": { + "id": 1, + "created_on": 1602593653, + "modified_on": 1602593653, + "user_id": 1, + "provider_key": "route53", + "name": "Route53", + "meta": { + "access_key": "abc123", + "access_secret": "def098", + "zone_id": "ABC123" + } + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/backend/doc/paths/dns-providers/providerID/delete.json b/backend/doc/paths/dns-providers/providerID/delete.json new file mode 100644 index 00000000..32b77b0d --- /dev/null +++ b/backend/doc/paths/dns-providers/providerID/delete.json @@ -0,0 +1,60 @@ +{ + "operationId": "deleteDNSProvider", + "summary": "Delete a DNS Provider", + "tags": [ + "DNS Providers" + ], + "parameters": [ + { + "in": "path", + "name": "providerID", + "schema": { + "type": "integer", + "minimum": 1 + }, + "required": true, + "description": "Numeric ID of the DNS Provider", + "example": 1 + } + ], + "responses": { + "200": { + "description": "200 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeletedItemResponse" + }, + "examples": { + "default": { + "value": { + "result": true + } + } + } + } + } + }, + "400": { + "description": "400 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeletedItemResponse" + }, + "examples": { + "default": { + "value": { + "result": null, + "error": { + "code": 400, + "message": "You cannot delete a DNS Provider that is in use!" + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/backend/doc/paths/dns-providers/providerID/get.json b/backend/doc/paths/dns-providers/providerID/get.json new file mode 100644 index 00000000..316e8d2e --- /dev/null +++ b/backend/doc/paths/dns-providers/providerID/get.json @@ -0,0 +1,58 @@ +{ + "operationId": "getDNSProvider", + "summary": "Get a DNS Provider object by ID", + "tags": [ + "DNS Providers" + ], + "parameters": [ + { + "in": "path", + "name": "providerID", + "schema": { + "type": "integer", + "minimum": 1 + }, + "required": true, + "description": "ID of the DNS Provider", + "example": 1 + } + ], + "responses": { + "200": { + "description": "200 response", + "content": { + "application/json": { + "schema": { + "required": [ + "result" + ], + "properties": { + "result": { + "$ref": "#/components/schemas/DNSProviderObject" + } + } + }, + "examples": { + "default": { + "value": { + "result": { + "id": 1, + "created_on": 1602593653, + "modified_on": 1602593653, + "user_id": 1, + "provider_key": "route53", + "name": "Route53", + "meta": { + "access_key": "abc123", + "access_secret": "def098", + "zone_id": "ABC123" + } + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/backend/doc/paths/dns-providers/providerID/put.json b/backend/doc/paths/dns-providers/providerID/put.json new file mode 100644 index 00000000..9b1d69fa --- /dev/null +++ b/backend/doc/paths/dns-providers/providerID/put.json @@ -0,0 +1,69 @@ +{ + "operationId": "updateDNSProvider", + "summary": "Update an existing DNS Provider", + "tags": [ + "DNS Providers" + ], + "parameters": [ + { + "in": "path", + "name": "providerID", + "schema": { + "type": "integer", + "minimum": 1 + }, + "required": true, + "description": "ID of the DNS Provider", + "example": 1 + } + ], + "requestBody": { + "description": "DNS Provider details to update", + "required": true, + "content": { + "application/json": { + "schema": "{{schema.UpdateDNSProvider}}" + } + } + }, + "responses": { + "200": { + "description": "200 response", + "content": { + "application/json": { + "schema": { + "required": [ + "result" + ], + "properties": { + "result": { + "$ref": "#/components/schemas/DNSProviderObject" + } + } + }, + "examples": { + "default": { + "value": { + "result": { + "result": { + "id": 1, + "created_on": 1602593653, + "modified_on": 1602593653, + "user_id": 1, + "provider_key": "route53", + "name": "Route53", + "meta": { + "access_key": "abc123", + "access_secret": "def098", + "zone_id": "ABC123" + } + } + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/backend/doc/paths/get.json b/backend/doc/paths/get.json new file mode 100644 index 00000000..0f9506f1 --- /dev/null +++ b/backend/doc/paths/get.json @@ -0,0 +1,47 @@ +{ + "operationId": "health", + "summary": "Returns the API health status", + "responses": { + "200": { + "description": "200 response", + "content": { + "application/json": { + "schema": { + "required": [ + "result" + ], + "properties": { + "result": { + "$ref": "#/components/schemas/HealthObject" + } + } + }, + "examples": { + "default": { + "value": { + "result": { + "version": "3.0.0", + "commit": "9f119b6", + "healthy": true, + "setup": true, + "error_reporting": true + } + } + }, + "unhealthy": { + "value": { + "result": { + "version": "3.0.0", + "commit": "9f119b6", + "healthy": false, + "setup": true, + "error_reporting": true + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/backend/doc/paths/hosts/get.json b/backend/doc/paths/hosts/get.json new file mode 100644 index 00000000..0ecac1d7 --- /dev/null +++ b/backend/doc/paths/hosts/get.json @@ -0,0 +1,75 @@ +{ + "operationId": "getHosts", + "summary": "Get a list of Hosts", + "tags": [ + "Hosts" + ], + "parameters": [ + { + "in": "query", + "name": "offset", + "schema": { + "type": "number" + }, + "description": "The pagination row offset, default 0", + "example": 0 + }, + { + "in": "query", + "name": "limit", + "schema": { + "type": "number" + }, + "description": "The pagination row limit, default 10", + "example": 10 + }, + { + "in": "query", + "name": "sort", + "schema": { + "type": "string" + }, + "description": "The sorting of the list", + "example": "id,name.asc,value.desc" + } + ], + "responses": { + "200": { + "description": "200 response", + "content": { + "application/json": { + "schema": { + "required": [ + "result" + ], + "properties": { + "result": { + "$ref": "#/components/schemas/HostList" + } + } + }, + "examples": { + "default": { + "value": { + "result": { + "total": 1, + "offset": 0, + "limit": 10, + "sort": [ + { + "field": "name", + "direction": "ASC" + } + ], + "items": [ + "TODO" + ] + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/backend/doc/paths/hosts/hostID/delete.json b/backend/doc/paths/hosts/hostID/delete.json new file mode 100644 index 00000000..4df119ad --- /dev/null +++ b/backend/doc/paths/hosts/hostID/delete.json @@ -0,0 +1,60 @@ +{ + "operationId": "deleteHost", + "summary": "Delete a Host", + "tags": [ + "Hosts" + ], + "parameters": [ + { + "in": "path", + "name": "hostID", + "schema": { + "type": "integer", + "minimum": 1 + }, + "required": true, + "description": "Numeric ID of the Host", + "example": 1 + } + ], + "responses": { + "200": { + "description": "200 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeletedItemResponse" + }, + "examples": { + "default": { + "value": { + "result": true + } + } + } + } + } + }, + "400": { + "description": "400 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeletedItemResponse" + }, + "examples": { + "default": { + "value": { + "result": null, + "error": { + "code": 400, + "message": "You cannot delete a host that is in use!" + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/backend/doc/paths/hosts/hostID/get.json b/backend/doc/paths/hosts/hostID/get.json new file mode 100644 index 00000000..a3782f66 --- /dev/null +++ b/backend/doc/paths/hosts/hostID/get.json @@ -0,0 +1,46 @@ +{ + "operationId": "getHost", + "summary": "Get a Host object by ID", + "tags": [ + "Hosts" + ], + "parameters": [ + { + "in": "path", + "name": "hostID", + "schema": { + "type": "integer", + "minimum": 1 + }, + "required": true, + "description": "ID of the Host", + "example": 1 + } + ], + "responses": { + "200": { + "description": "200 response", + "content": { + "application/json": { + "schema": { + "required": [ + "result" + ], + "properties": { + "result": { + "$ref": "#/components/schemas/HostObject" + } + } + }, + "examples": { + "default": { + "value": { + "result": "TODO" + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/backend/doc/paths/hosts/hostID/put.json b/backend/doc/paths/hosts/hostID/put.json new file mode 100644 index 00000000..5c9401c0 --- /dev/null +++ b/backend/doc/paths/hosts/hostID/put.json @@ -0,0 +1,55 @@ +{ + "operationId": "updateHost", + "summary": "Update an existing Host", + "tags": [ + "Hosts" + ], + "parameters": [ + { + "in": "path", + "name": "hostID", + "schema": { + "type": "integer", + "minimum": 1 + }, + "required": true, + "description": "ID of the Host", + "example": 1 + } + ], + "requestBody": { + "description": "Host details to update", + "required": true, + "content": { + "application/json": { + "schema": "{{schema.UpdateHost}}" + } + } + }, + "responses": { + "200": { + "description": "200 response", + "content": { + "application/json": { + "schema": { + "required": [ + "result" + ], + "properties": { + "result": { + "$ref": "#/components/schemas/HostObject" + } + } + }, + "examples": { + "default": { + "value": { + "result": "TODO" + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/backend/doc/paths/hosts/post.json b/backend/doc/paths/hosts/post.json new file mode 100644 index 00000000..b33d6696 --- /dev/null +++ b/backend/doc/paths/hosts/post.json @@ -0,0 +1,42 @@ +{ + "operationId": "createHost", + "summary": "Create a new Host", + "tags": [ + "Hosts" + ], + "requestBody": { + "description": "Host to Create", + "required": true, + "content": { + "application/json": { + "schema": "{{schema.CreateHost}}" + } + } + }, + "responses": { + "201": { + "description": "201 response", + "content": { + "application/json": { + "schema": { + "required": [ + "result" + ], + "properties": { + "result": { + "$ref": "#/components/schemas/HostObject" + } + } + }, + "examples": { + "default": { + "value": { + "result": "TODO" + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/backend/doc/paths/schema/get.json b/backend/doc/paths/schema/get.json new file mode 100644 index 00000000..e21ae805 --- /dev/null +++ b/backend/doc/paths/schema/get.json @@ -0,0 +1,9 @@ +{ + "operationId": "schema", + "summary": "Returns this swagger API schema", + "responses": { + "200": { + "description": "200 response" + } + } +} \ No newline at end of file diff --git a/backend/doc/paths/settings/get.json b/backend/doc/paths/settings/get.json new file mode 100644 index 00000000..3455b0ff --- /dev/null +++ b/backend/doc/paths/settings/get.json @@ -0,0 +1,84 @@ +{ + "operationId": "getSettings", + "summary": "Get a list of settings", + "tags": [ + "Settings" + ], + "parameters": [ + { + "in": "query", + "name": "offset", + "schema": { + "type": "number" + }, + "description": "The pagination row offset, default 0", + "example": 0 + }, + { + "in": "query", + "name": "limit", + "schema": { + "type": "number" + }, + "description": "The pagination row limit, default 10", + "example": 10 + }, + { + "in": "query", + "name": "sort", + "schema": { + "type": "string" + }, + "description": "The sorting of the list", + "example": "id,name.asc,value.desc" + } + ], + "responses": { + "200": { + "description": "200 response", + "content": { + "application/json": { + "schema": { + "required": [ + "result" + ], + "properties": { + "result": { + "$ref": "#/components/schemas/SettingList" + } + } + }, + "examples": { + "default": { + "value": { + "result": { + "total": 1, + "offset": 0, + "limit": 10, + "sort": [ + { + "field": "name", + "direction": "ASC" + } + ], + "items": [ + { + "id": 1, + "created_on": 1578010090, + "modified_on": 1578010095, + "name": "default-site", + "value": { + "html": "

not found

", + "type": "custom" + } + } + ] + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/backend/doc/paths/settings/name/get.json b/backend/doc/paths/settings/name/get.json new file mode 100644 index 00000000..775a4737 --- /dev/null +++ b/backend/doc/paths/settings/name/get.json @@ -0,0 +1,55 @@ +{ + "operationId": "getSetting", + "summary": "Get a setting object by name", + "tags": [ + "Settings" + ], + "parameters": [ + { + "in": "path", + "name": "name", + "schema": { + "type": "string", + "minLength": 2 + }, + "required": true, + "description": "Name of the setting", + "example": "default-site" + } + ], + "responses": { + "200": { + "description": "200 response", + "content": { + "application/json": { + "schema": { + "required": [ + "result" + ], + "properties": { + "result": { + "$ref": "#/components/schemas/SettingObject" + } + } + }, + "examples": { + "default": { + "value": { + "result": { + "id": 2, + "created_on": 1578010090, + "modified_on": 1578010095, + "name": "default-site", + "value": { + "html": "

not found

", + "type": "custom" + } + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/backend/doc/paths/settings/name/put.json b/backend/doc/paths/settings/name/put.json new file mode 100644 index 00000000..ee741104 --- /dev/null +++ b/backend/doc/paths/settings/name/put.json @@ -0,0 +1,64 @@ +{ + "operationId": "updateSetting", + "summary": "Update an existing Setting", + "tags": [ + "Settings" + ], + "parameters": [ + { + "in": "path", + "name": "name", + "schema": { + "type": "string", + "minLength": 2 + }, + "required": true, + "description": "Name of the setting", + "example": "default-site" + } + ], + "requestBody": { + "description": "Setting details to update", + "required": true, + "content": { + "application/json": { + "schema": "{{schema.UpdateSetting}}" + } + } + }, + "responses": { + "200": { + "description": "200 response", + "content": { + "application/json": { + "schema": { + "required": [ + "result" + ], + "properties": { + "result": { + "$ref": "#/components/schemas/SettingObject" + } + } + }, + "examples": { + "default": { + "value": { + "result": { + "id": 2, + "created_on": 1578010090, + "modified_on": 1578010090, + "name": "default-site", + "value": { + "html": "

not found

", + "type": "custom" + } + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/backend/doc/paths/settings/post.json b/backend/doc/paths/settings/post.json new file mode 100644 index 00000000..63921da2 --- /dev/null +++ b/backend/doc/paths/settings/post.json @@ -0,0 +1,51 @@ +{ + "operationId": "createSetting", + "summary": "Create a new Setting", + "tags": [ + "Settings" + ], + "requestBody": { + "description": "Setting to Create", + "required": true, + "content": { + "application/json": { + "schema": "{{schema.CreateSetting}}" + } + } + }, + "responses": { + "201": { + "description": "201 response", + "content": { + "application/json": { + "schema": { + "required": [ + "result" + ], + "properties": { + "result": { + "$ref": "#/components/schemas/SettingObject" + } + } + }, + "examples": { + "default": { + "value": { + "result": { + "id": 2, + "created_on": 1578010090, + "modified_on": 1578010090, + "name": "default-site", + "value": { + "html": "

not found

", + "type": "custom" + } + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/backend/doc/paths/streams/get.json b/backend/doc/paths/streams/get.json new file mode 100644 index 00000000..482212d1 --- /dev/null +++ b/backend/doc/paths/streams/get.json @@ -0,0 +1,75 @@ +{ + "operationId": "getStreams", + "summary": "Get a list of Streams", + "tags": [ + "Streams" + ], + "parameters": [ + { + "in": "query", + "name": "offset", + "schema": { + "type": "number" + }, + "description": "The pagination row offset, default 0", + "example": 0 + }, + { + "in": "query", + "name": "limit", + "schema": { + "type": "number" + }, + "description": "The pagination row limit, default 10", + "example": 10 + }, + { + "in": "query", + "name": "sort", + "schema": { + "type": "string" + }, + "description": "The sorting of the list", + "example": "id,name.asc,value.desc" + } + ], + "responses": { + "200": { + "description": "200 response", + "content": { + "application/json": { + "schema": { + "required": [ + "result" + ], + "properties": { + "result": { + "$ref": "#/components/schemas/StreamList" + } + } + }, + "examples": { + "default": { + "value": { + "result": { + "total": 1, + "offset": 0, + "limit": 10, + "sort": [ + { + "field": "name", + "direction": "ASC" + } + ], + "items": [ + "TODO" + ] + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/backend/doc/paths/streams/post.json b/backend/doc/paths/streams/post.json new file mode 100644 index 00000000..d1949830 --- /dev/null +++ b/backend/doc/paths/streams/post.json @@ -0,0 +1,42 @@ +{ + "operationId": "createStream", + "summary": "Create a new Stream", + "tags": [ + "Streams" + ], + "requestBody": { + "description": "Host to Create", + "required": true, + "content": { + "application/json": { + "schema": "{{schema.CreateStream}}" + } + } + }, + "responses": { + "201": { + "description": "201 response", + "content": { + "application/json": { + "schema": { + "required": [ + "result" + ], + "properties": { + "result": { + "$ref": "#/components/schemas/StreamObject" + } + } + }, + "examples": { + "default": { + "value": { + "result": "TODO" + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/backend/doc/paths/streams/streamID/delete.json b/backend/doc/paths/streams/streamID/delete.json new file mode 100644 index 00000000..d0f35269 --- /dev/null +++ b/backend/doc/paths/streams/streamID/delete.json @@ -0,0 +1,60 @@ +{ + "operationId": "deleteStream", + "summary": "Delete a Stream", + "tags": [ + "Streams" + ], + "parameters": [ + { + "in": "path", + "name": "streamID", + "schema": { + "type": "integer", + "minimum": 1 + }, + "required": true, + "description": "Numeric ID of the Stream", + "example": 1 + } + ], + "responses": { + "200": { + "description": "200 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeletedItemResponse" + }, + "examples": { + "default": { + "value": { + "result": true + } + } + } + } + } + }, + "400": { + "description": "400 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeletedItemResponse" + }, + "examples": { + "default": { + "value": { + "result": null, + "error": { + "code": 400, + "message": "You cannot delete a Stream that is in use!" + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/backend/doc/paths/streams/streamID/get.json b/backend/doc/paths/streams/streamID/get.json new file mode 100644 index 00000000..94e54c3f --- /dev/null +++ b/backend/doc/paths/streams/streamID/get.json @@ -0,0 +1,46 @@ +{ + "operationId": "getStream", + "summary": "Get a Stream object by ID", + "tags": [ + "Streams" + ], + "parameters": [ + { + "in": "path", + "name": "streamID", + "schema": { + "type": "integer", + "minimum": 1 + }, + "required": true, + "description": "ID of the Stream", + "example": 1 + } + ], + "responses": { + "200": { + "description": "200 response", + "content": { + "application/json": { + "schema": { + "required": [ + "result" + ], + "properties": { + "result": { + "$ref": "#/components/schemas/StreamObject" + } + } + }, + "examples": { + "default": { + "value": { + "result": "TODO" + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/backend/doc/paths/streams/streamID/put.json b/backend/doc/paths/streams/streamID/put.json new file mode 100644 index 00000000..4baa882e --- /dev/null +++ b/backend/doc/paths/streams/streamID/put.json @@ -0,0 +1,55 @@ +{ + "operationId": "updateStream", + "summary": "Update an existing Stream", + "tags": [ + "Streams" + ], + "parameters": [ + { + "in": "path", + "name": "streamID", + "schema": { + "type": "integer", + "minimum": 1 + }, + "required": true, + "description": "ID of the Stream", + "example": 1 + } + ], + "requestBody": { + "description": "Stream details to update", + "required": true, + "content": { + "application/json": { + "schema": "{{schema.UpdateStream}}" + } + } + }, + "responses": { + "200": { + "description": "200 response", + "content": { + "application/json": { + "schema": { + "required": [ + "result" + ], + "properties": { + "result": { + "$ref": "#/components/schemas/StreamObject" + } + } + }, + "examples": { + "default": { + "value": { + "result": "TODO" + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/backend/doc/paths/tokens/get.json b/backend/doc/paths/tokens/get.json new file mode 100644 index 00000000..601697a7 --- /dev/null +++ b/backend/doc/paths/tokens/get.json @@ -0,0 +1,37 @@ +{ + "operationId": "refreshToken", + "summary": "Refresh your access token", + "tags": [ + "Tokens" + ], + "responses": { + "200": { + "description": "200 response", + "content": { + "application/json": { + "schema": { + "required": [ + "result" + ], + "properties": { + "result": { + "$ref": "#/components/schemas/StreamObject" + } + } + }, + "examples": { + "default": { + "value": { + "result": { + "expires": 1566540510, + "token": "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.ey...xaHKYr3Kk6MvkUjcC4", + "scope": "user" + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/backend/doc/paths/tokens/post.json b/backend/doc/paths/tokens/post.json new file mode 100644 index 00000000..44b95b2b --- /dev/null +++ b/backend/doc/paths/tokens/post.json @@ -0,0 +1,79 @@ +{ + "operationId": "requestToken", + "summary": "Request a new access token from credentials", + "tags": [ + "Tokens" + ], + "requestBody": { + "description": "Credentials Payload", + "required": true, + "content": { + "application/json": { + "schema": "{{schema.GetToken}}" + } + } + }, + "responses": { + "200": { + "description": "200 response", + "content": { + "application/json": { + "schema": { + "required": [ + "result" + ], + "properties": { + "result": { + "$ref": "#/components/schemas/StreamObject" + } + } + }, + "examples": { + "default": { + "value": { + "result": { + "expires": 1566540510, + "token": "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.ey...xaHKYr3Kk6MvkUjcC4", + "scope": "user" + } + } + } + } + } + } + }, + "403": { + "description": "403 response", + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": false, + "required": [ + "error" + ], + "properties": { + "result": { + "nullable": true + }, + "error": { + "$ref": "#/components/schemas/ErrorObject" + } + } + }, + "examples": { + "default": { + "value": { + "result": null, + "error": { + "code": 403, + "message": "Not available during setup phase" + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/backend/doc/paths/users/get.json b/backend/doc/paths/users/get.json new file mode 100644 index 00000000..45ee7efb --- /dev/null +++ b/backend/doc/paths/users/get.json @@ -0,0 +1,121 @@ +{ + "operationId": "getUsers", + "summary": "Get a list of users", + "tags": [ + "Users" + ], + "parameters": [ + { + "in": "query", + "name": "offset", + "schema": { + "type": "number" + }, + "description": "The pagination row offset, default 0", + "example": 0 + }, + { + "in": "query", + "name": "limit", + "schema": { + "type": "number" + }, + "description": "The pagination row limit, default 10", + "example": 10 + }, + { + "in": "query", + "name": "sort", + "schema": { + "type": "string" + }, + "description": "The sorting of the list", + "example": "name,nickname.desc,email.asc" + } + ], + "responses": { + "200": { + "description": "200 response", + "content": { + "application/json": { + "schema": { + "required": [ + "result" + ], + "properties": { + "result": { + "$ref": "#/components/schemas/UserList" + } + } + }, + "examples": { + "default": { + "value": { + "result": { + "total": 3, + "offset": 0, + "limit": 100, + "sort": [ + { + "field": "name", + "direction": "ASC" + }, + { + "field": "nickname", + "direction": "DESC" + }, + { + "field": "email", + "direction": "ASC" + } + ], + "items": [ + { + "id": 1, + "name": "Jamie Curnow", + "nickname": "James", + "email": "jc@jc21.com", + "created_on": 1578010090, + "modified_on": 1578010095, + "roles": [ + "admin" + ], + "gravatar_url": "https://www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?d=mm&r=pg&s=128", + "is_disabled": false + }, + { + "id": 2, + "name": "John Doe", + "nickname": "John", + "email": "johdoe@example.com", + "created_on": 1578010100, + "modified_on": 1578010105, + "roles": [ + "admin" + ], + "gravatar_url": "https://www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?d=mm&r=pg&s=128", + "is_disabled": false + }, + { + "id": 3, + "name": "Jane Doe", + "nickname": "Jane", + "email": "janedoe@example.com", + "created_on": 1578010110, + "modified_on": 1578010115, + "roles": [ + "admin" + ], + "gravatar_url": "https://www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?d=mm&r=pg&s=128", + "is_disabled": false + } + ] + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/backend/doc/paths/users/post.json b/backend/doc/paths/users/post.json new file mode 100644 index 00000000..3a524db7 --- /dev/null +++ b/backend/doc/paths/users/post.json @@ -0,0 +1,88 @@ +{ + "operationId": "createUser", + "summary": "Create a new User", + "tags": [ + "Users" + ], + "requestBody": { + "description": "User to Create", + "required": true, + "content": { + "application/json": { + "schema": "{{schema.CreateUser}}" + } + } + }, + "responses": { + "201": { + "description": "201 response", + "content": { + "application/json": { + "schema": { + "required": [ + "result" + ], + "properties": { + "result": { + "$ref": "#/components/schemas/UserObject" + } + } + }, + "examples": { + "default": { + "value": { + "result": { + "id": 1, + "name": "Jamie Curnow", + "nickname": "James", + "email": "jc@jc21.com", + "created_on": 1578010100, + "modified_on": 1578010100, + "roles": [ + "admin" + ], + "gravatar_url": "https://www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?d=mm&r=pg&s=128", + "is_disabled": false, + "auth": { + "id": 1, + "type": "password" + } + } + } + } + } + } + } + }, + "400": { + "description": "400 response", + "content": { + "application/json": { + "schema": { + "required": [ + "error" + ], + "properties": { + "result": { + "nullable": true + }, + "error": { + "$ref": "#/components/schemas/ErrorObject" + } + } + }, + "examples": { + "default": { + "value": { + "error": { + "code": 400, + "message": "An user already exists with this email address" + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/backend/doc/paths/users/userID/auth/post.json b/backend/doc/paths/users/userID/auth/post.json new file mode 100644 index 00000000..08f12f63 --- /dev/null +++ b/backend/doc/paths/users/userID/auth/post.json @@ -0,0 +1,63 @@ +{ + "operationId": "setPassword", + "summary": "Set a User's password", + "tags": [ + "Users" + ], + "parameters": [ + { + "in": "path", + "name": "userID", + "schema": { + "oneOf": [ + { + "type": "integer", + "minimum": 1 + }, + { + "type": "string", + "pattern": "^me$" + } + ] + }, + "required": true, + "description": "Numeric ID of the user or 'me' to get yourself", + "example": 1 + } + ], + "requestBody": { + "description": "Credentials to set", + "required": true, + "content": { + "application/json": { + "schema": "{{schema.SetAuth}}" + } + } + }, + "responses": { + "200": { + "description": "200 response", + "content": { + "application/json": { + "schema": { + "required": [ + "result" + ], + "properties": { + "result": { + "type": "string" + } + } + }, + "examples": { + "default": { + "value": { + "result": "TODO" + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/backend/doc/paths/users/userID/delete.json b/backend/doc/paths/users/userID/delete.json new file mode 100644 index 00000000..0ffa7024 --- /dev/null +++ b/backend/doc/paths/users/userID/delete.json @@ -0,0 +1,60 @@ +{ + "operationId": "deleteUser", + "summary": "Delete a User", + "tags": [ + "Users" + ], + "parameters": [ + { + "in": "path", + "name": "userID", + "schema": { + "type": "integer", + "minimum": 1 + }, + "required": true, + "description": "Numeric ID of the user", + "example": 1 + } + ], + "responses": { + "200": { + "description": "200 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeletedItemResponse" + }, + "examples": { + "default": { + "value": { + "result": true + } + } + } + } + } + }, + "400": { + "description": "400 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeletedItemResponse" + }, + "examples": { + "default": { + "value": { + "result": null, + "error": { + "code": 400, + "message": "You cannot delete yourself!" + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/backend/doc/paths/users/userID/get.json b/backend/doc/paths/users/userID/get.json new file mode 100644 index 00000000..df4b3078 --- /dev/null +++ b/backend/doc/paths/users/userID/get.json @@ -0,0 +1,66 @@ +{ + "operationId": "getUser", + "summary": "Get a user object by ID or 'me'", + "tags": [ + "Users" + ], + "parameters": [ + { + "in": "path", + "name": "userID", + "schema": { + "anyOf": [ + { + "type": "integer", + "minimum": 1 + }, + { + "type": "string", + "pattern": "^me$" + } + ] + }, + "required": true, + "description": "Numeric ID of the user or 'me' to get yourself", + "example": 1 + } + ], + "responses": { + "200": { + "description": "200 response", + "content": { + "application/json": { + "schema": { + "required": [ + "result" + ], + "properties": { + "result": { + "$ref": "#/components/schemas/UserObject" + } + } + }, + "examples": { + "default": { + "value": { + "result": { + "id": 1, + "name": "Jamie Curnow", + "nickname": "James", + "email": "jc@jc21.com", + "created_on": 1578010100, + "modified_on": 1578010105, + "roles": [ + "admin" + ], + "gravatar_url": "https://www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?d=mm&r=pg&s=128", + "is_disabled": false + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/backend/doc/paths/users/userID/put.json b/backend/doc/paths/users/userID/put.json new file mode 100644 index 00000000..7abb5c1d --- /dev/null +++ b/backend/doc/paths/users/userID/put.json @@ -0,0 +1,119 @@ +{ + "operationId": "updateUser", + "summary": "Update an existing User", + "tags": [ + "Users" + ], + "parameters": [ + { + "in": "path", + "name": "userID", + "schema": { + "anyOf": [ + { + "type": "integer", + "minimum": 1 + }, + { + "type": "string", + "pattern": "^me$" + } + ] + }, + "required": true, + "description": "Numeric ID of the user or 'me' to update yourself", + "example": 1 + } + ], + "requestBody": { + "description": "User details to update", + "required": true, + "content": { + "application/json": { + "schema": "{{schema.UpdateUser}}" + } + } + }, + "responses": { + "200": { + "description": "200 response", + "content": { + "application/json": { + "schema": { + "required": [ + "result" + ], + "properties": { + "result": { + "$ref": "#/components/schemas/UserObject" + } + } + }, + "examples": { + "default": { + "value": { + "result": { + "id": 1, + "name": "Jamie Curnow", + "nickname": "James", + "email": "jc@jc21.com", + "created_on": 1578010100, + "modified_on": 1578010110, + "roles": [ + "admin" + ], + "gravatar_url": "https://www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?d=mm&r=pg&s=128", + "is_disabled": false, + "auth": { + "id": 1, + "type": "password" + } + } + } + } + } + } + } + }, + "400": { + "description": "400 response", + "content": { + "application/json": { + "schema": { + "required": [ + "error" + ], + "properties": { + "result": { + "nullable": true + }, + "error": { + "$ref": "#/components/schemas/ErrorObject" + } + } + }, + "examples": { + "duplicateemail": { + "value": { + "result": null, + "error": { + "code": 400, + "message": "A user already exists with this email address" + } + } + }, + "nodisable": { + "value": { + "result": null, + "error": { + "code": 400, + "message": "You cannot disable yourself!" + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/backend/go.mod b/backend/go.mod new file mode 100644 index 00000000..1b004b77 --- /dev/null +++ b/backend/go.mod @@ -0,0 +1,20 @@ +module npm + +go 1.16 + +require ( + github.com/dgrijalva/jwt-go v3.2.0+incompatible + github.com/drexedam/gravatar v0.0.0-20210327211422-e94eea8c338e + github.com/fatih/color v1.10.0 + github.com/getsentry/sentry-go v0.10.0 + github.com/go-chi/chi v4.1.2+incompatible + github.com/go-chi/cors v1.2.0 + github.com/go-chi/jwtauth v4.0.4+incompatible + github.com/jc21/jsref v0.0.0-20210608024405-a97debfc4760 + github.com/jmoiron/sqlx v1.3.3 + github.com/mattn/go-sqlite3 v2.0.3+incompatible + github.com/qri-io/jsonschema v0.2.1 + github.com/stretchr/testify v1.7.0 + github.com/vrischmann/envconfig v1.3.0 + golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf +) diff --git a/backend/go.sum b/backend/go.sum new file mode 100644 index 00000000..9b1d959d --- /dev/null +++ b/backend/go.sum @@ -0,0 +1,265 @@ +github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno= +github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo= +github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= +github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= +github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= +github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= +github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/drexedam/gravatar v0.0.0-20210327211422-e94eea8c338e h1:2R8DvYLNr5DL25eWwpOdPno1eIbTNjJC0d7v8ti5cus= +github.com/drexedam/gravatar v0.0.0-20210327211422-e94eea8c338e/go.mod h1:YjikoytuRI4q+GRd3xrOrKJN+Ayi2dwRomHLDDeMHfs= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= +github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= +github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= +github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg= +github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= +github.com/getsentry/sentry-go v0.10.0 h1:6gwY+66NHKqyZrdi6O2jGdo7wGdo9b3B69E01NFgT5g= +github.com/getsentry/sentry-go v0.10.0/go.mod h1:kELm/9iCblqUYh+ZRML7PNdCvEuw24wBvJPYyi86cws= +github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= +github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= +github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= +github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec= +github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= +github.com/go-chi/cors v1.2.0 h1:tV1g1XENQ8ku4Bq3K9ub2AtgG+p16SmzeMSGTwrOKdE= +github.com/go-chi/cors v1.2.0/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= +github.com/go-chi/jwtauth v4.0.4+incompatible h1:LGIxg6YfvSBzxU2BljXbrzVc1fMlgqSKBQgKOGAVtPY= +github.com/go-chi/jwtauth v4.0.4+incompatible/go.mod h1:Q5EIArY/QnD6BdS+IyDw7B2m6iNbnPxtfd6/BcmtWbs= +github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= +github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= +github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= +github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= +github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= +github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0= +github.com/iris-contrib/jade v1.1.3/go.mod h1:H/geBymxJhShH5kecoiOCSssPX7QWYH7UaeZTSWddIk= +github.com/iris-contrib/pongo2 v0.0.1/go.mod h1:Ssh+00+3GAZqSQb30AvBRNxBx7rf0GqwkjqxNd0u65g= +github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw= +github.com/jc21/jsref v0.0.0-20210608013137-43b07c7d31bd h1:Ag/L5Yc9BeBbi4i8bNAev8Ejtu/jq8Qk/xK+HDHnWNc= +github.com/jc21/jsref v0.0.0-20210608013137-43b07c7d31bd/go.mod h1:yIq2t51OJgVsdRlPY68NAnyVdBH0kYXxDTFtUxOap80= +github.com/jc21/jsref v0.0.0-20210608014024-8bda7cb41eef h1:1jF5nv8PmgH2txfWGmsPium0Hj9PEnGkb96tkZ+4uDU= +github.com/jc21/jsref v0.0.0-20210608014024-8bda7cb41eef/go.mod h1:yIq2t51OJgVsdRlPY68NAnyVdBH0kYXxDTFtUxOap80= +github.com/jc21/jsref v0.0.0-20210608014914-2edd4dea9791 h1:s0hsMFnTiGGytgwDbHo20OvmJj2/+FFMZvLpRNexnvk= +github.com/jc21/jsref v0.0.0-20210608014914-2edd4dea9791/go.mod h1:yIq2t51OJgVsdRlPY68NAnyVdBH0kYXxDTFtUxOap80= +github.com/jc21/jsref v0.0.0-20210608023003-123d7fb98643 h1:ZpDTP4ow7hZMx0ORi06jnLP4ZDGQVa6SayH+5rWWlYg= +github.com/jc21/jsref v0.0.0-20210608023003-123d7fb98643/go.mod h1:yIq2t51OJgVsdRlPY68NAnyVdBH0kYXxDTFtUxOap80= +github.com/jc21/jsref v0.0.0-20210608023437-810a57e5f736 h1:1nZYRLsHvECy8rbOLkqRBK45Y6zKQ5ZRuGPMQalPWVc= +github.com/jc21/jsref v0.0.0-20210608023437-810a57e5f736/go.mod h1:yIq2t51OJgVsdRlPY68NAnyVdBH0kYXxDTFtUxOap80= +github.com/jc21/jsref v0.0.0-20210608024103-9eaa65f76123 h1:pb24Ybg78OdqO4GHh0xcwlVPWKlDYX/ZVnf+wq8D9To= +github.com/jc21/jsref v0.0.0-20210608024103-9eaa65f76123/go.mod h1:yIq2t51OJgVsdRlPY68NAnyVdBH0kYXxDTFtUxOap80= +github.com/jc21/jsref v0.0.0-20210608024405-a97debfc4760 h1:7wxq2DIgtO36KLrFz1RldysO0WVvcYsD49G9tyAs01k= +github.com/jc21/jsref v0.0.0-20210608024405-a97debfc4760/go.mod h1:yIq2t51OJgVsdRlPY68NAnyVdBH0kYXxDTFtUxOap80= +github.com/jmoiron/sqlx v1.3.3 h1:j82X0bf7oQ27XeqxicSZsTU5suPwKElg3oyxNn43iTk= +github.com/jmoiron/sqlx v1.3.3/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= +github.com/kataras/golog v0.0.10/go.mod h1:yJ8YKCmyL+nWjERB90Qwn+bdyBZsaQwU3bTVFgkFIp8= +github.com/kataras/iris/v12 v12.1.8/go.mod h1:LMYy4VlP67TQ3Zgriz8RE2h2kMZV2SgMYbq3UhfoFmE= +github.com/kataras/neffos v0.0.14/go.mod h1:8lqADm8PnbeFfL7CLXh1WHw53dG27MC3pgi2R1rmoTE= +github.com/kataras/pio v0.0.2/go.mod h1:hAoW0t9UmXi4R5Oyq5Z4irTbaTsOemSrDGUtaTl7Dro= +github.com/kataras/sitemap v0.0.5/go.mod h1:KY2eugMKiPwsJgx7+U103YZehfvNGOXURubcGyk0Bz8= +github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kyoh86/richgo v0.3.8/go.mod h1:2C8POkF1H04iTOG2Tp1yyZhspCME9nN3cir3VXJ02II= +github.com/kyoh86/xdg v1.2.0/go.mod h1:/mg8zwu1+qe76oTFUBnyS7rJzk7LLC0VGEzJyJ19DHs= +github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g= +github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= +github.com/lestrrat-go/jspointer v0.0.0-20181205001929-82fadba7561c h1:pGh5EFIfczeDHwgMHgfwjhZzL+8/E3uZF6T7vER/W8c= +github.com/lestrrat-go/jspointer v0.0.0-20181205001929-82fadba7561c/go.mod h1:xw2Gm4Mg+ST9s8fHR1VkUIyOJMJnSloRZlPQB+wyVpY= +github.com/lestrrat-go/option v0.0.0-20210103042652-6f1ecfceda35 h1:lea8Wt+1ePkVrI2/WD+NgQT5r/XsLAzxeqtyFLcEs10= +github.com/lestrrat-go/option v0.0.0-20210103042652-6f1ecfceda35/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= +github.com/lestrrat-go/pdebug/v3 v3.0.1 h1:3G5sX/aw/TbMTtVc9U7IHBWRZtMvwvBziF1e4HoQtv8= +github.com/lestrrat-go/pdebug/v3 v3.0.1/go.mod h1:za+m+Ve24yCxTEhR59N7UlnJomWwCiIqbJRmKeiADU4= +github.com/lestrrat-go/structinfo v0.0.0-20190212233437-acd51874663b h1:YUFRoeHK/mvRjBR0bBRDC7ZGygYchoQ8j1xMENlObro= +github.com/lestrrat-go/structinfo v0.0.0-20190212233437-acd51874663b/go.mod h1:s2U6PowV3/Jobkx/S9d0XiPwOzs6niW3DIouw+7nZC8= +github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA= +github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U= +github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= +github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8= +github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= +github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= +github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= +github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= +github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/qri-io/jsonpointer v0.1.1 h1:prVZBZLL6TW5vsSB9fFHFAMBLI4b0ri5vribQlTJiBA= +github.com/qri-io/jsonpointer v0.1.1/go.mod h1:DnJPaYgiKu56EuDp8TU5wFLdZIcAnb/uH9v37ZaMV64= +github.com/qri-io/jsonschema v0.2.1 h1:NNFoKms+kut6ABPf6xiKNM5214jzxAhDBrPHCJ97Wg0= +github.com/qri-io/jsonschema v0.2.1/go.mod h1:g7DPkiOsK1xv6T/Ao5scXRkd+yTFygcANPBaaqW+VrI= +github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= +github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w= +github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= +github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= +github.com/vrischmann/envconfig v1.3.0 h1:4XIvQTXznxmWMnjouj0ST5lFo/WAYf5Exgl3x82crEk= +github.com/vrischmann/envconfig v1.3.0/go.mod h1:bbvxFYJdRSpXrhS63mBFtKJzkDiNkyArOLXtY6q0kuI= +github.com/wacul/ptr v1.0.0/go.mod h1:BD0gjsZrCwtoR+yWDB9v2hQ8STlq9tT84qKfa+3txOc= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI= +github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= +github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= +github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf h1:B2n+Zi5QeYRDAEodEu72OS36gmTWjgpXr2+cWcBW90o= +golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210326220804-49726bf1d181/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210603125802-9665404d3644 h1:CA1DEQ4NdKphKeL70tvsWNdT5oFh1lOjihRcEDROi0I= +golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= +gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= +gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/backend/index.js b/backend/index.js deleted file mode 100644 index f4f79518..00000000 --- a/backend/index.js +++ /dev/null @@ -1,134 +0,0 @@ -#!/usr/bin/env node - -const logger = require('./logger').global; - -async function appStart () { - // Create config file db settings if environment variables have been set - await createDbConfigFromEnvironment(); - - const migrate = require('./migrate'); - const setup = require('./setup'); - const app = require('./app'); - const apiValidator = require('./lib/validator/api'); - const internalCertificate = require('./internal/certificate'); - const internalIpRanges = require('./internal/ip_ranges'); - - return migrate.latest() - .then(setup) - .then(() => { - return apiValidator.loadSchemas; - }) - .then(internalIpRanges.fetch) - .then(() => { - - internalCertificate.initTimer(); - internalIpRanges.initTimer(); - - const server = app.listen(3000, () => { - logger.info('Backend PID ' + process.pid + ' listening on port 3000 ...'); - - process.on('SIGTERM', () => { - logger.info('PID ' + process.pid + ' received SIGTERM'); - server.close(() => { - logger.info('Stopping.'); - process.exit(0); - }); - }); - }); - }) - .catch((err) => { - logger.error(err.message); - setTimeout(appStart, 1000); - }); -} - -async function createDbConfigFromEnvironment() { - return new Promise((resolve, reject) => { - const envMysqlHost = process.env.DB_MYSQL_HOST || null; - const envMysqlPort = process.env.DB_MYSQL_PORT || null; - const envMysqlUser = process.env.DB_MYSQL_USER || null; - const envMysqlName = process.env.DB_MYSQL_NAME || null; - const envSqliteFile = process.env.DB_SQLITE_FILE || null; - - if ((envMysqlHost && envMysqlPort && envMysqlUser && envMysqlName) || envSqliteFile) { - const fs = require('fs'); - const filename = (process.env.NODE_CONFIG_DIR || './config') + '/' + (process.env.NODE_ENV || 'default') + '.json'; - let configData = {}; - - try { - configData = require(filename); - } catch (err) { - // do nothing - } - - if (configData.database && configData.database.engine && !configData.database.fromEnv) { - logger.info('Manual db configuration already exists, skipping config creation from environment variables'); - resolve(); - return; - } - - if (envMysqlHost && envMysqlPort && envMysqlUser && envMysqlName) { - const newConfig = { - fromEnv: true, - engine: 'mysql', - host: envMysqlHost, - port: envMysqlPort, - user: envMysqlUser, - password: process.env.DB_MYSQL_PASSWORD, - name: envMysqlName, - }; - - if (JSON.stringify(configData.database) === JSON.stringify(newConfig)) { - // Config is unchanged, skip overwrite - resolve(); - return; - } - - logger.info('Generating MySQL db configuration from environment variables'); - configData.database = newConfig; - - } else { - const newConfig = { - fromEnv: true, - engine: 'knex-native', - knex: { - client: 'sqlite3', - connection: { - filename: envSqliteFile - }, - useNullAsDefault: true - } - }; - if (JSON.stringify(configData.database) === JSON.stringify(newConfig)) { - // Config is unchanged, skip overwrite - resolve(); - return; - } - - logger.info('Generating Sqlite db configuration from environment variables'); - configData.database = newConfig; - } - - // Write config - fs.writeFile(filename, JSON.stringify(configData, null, 2), (err) => { - if (err) { - logger.error('Could not write db config to config file: ' + filename); - reject(err); - } else { - logger.info('Wrote db configuration to config file: ' + filename); - resolve(); - } - }); - } else { - resolve(); - } - }); -} - -try { - appStart(); -} catch (err) { - logger.error(err.message, err); - process.exit(1); -} - diff --git a/backend/internal/access-list.js b/backend/internal/access-list.js deleted file mode 100644 index 5b817d03..00000000 --- a/backend/internal/access-list.js +++ /dev/null @@ -1,534 +0,0 @@ -const _ = require('lodash'); -const fs = require('fs'); -const batchflow = require('batchflow'); -const logger = require('../logger').access; -const error = require('../lib/error'); -const accessListModel = require('../models/access_list'); -const accessListAuthModel = require('../models/access_list_auth'); -const accessListClientModel = require('../models/access_list_client'); -const proxyHostModel = require('../models/proxy_host'); -const internalAuditLog = require('./audit-log'); -const internalNginx = require('./nginx'); -const utils = require('../lib/utils'); - -function omissions () { - return ['is_deleted']; -} - -const internalAccessList = { - - /** - * @param {Access} access - * @param {Object} data - * @returns {Promise} - */ - create: (access, data) => { - return access.can('access_lists:create', data) - .then((/*access_data*/) => { - return accessListModel - .query() - .omit(omissions()) - .insertAndFetch({ - name: data.name, - satisfy_any: data.satisfy_any, - pass_auth: data.pass_auth, - owner_user_id: access.token.getUserId(1) - }); - }) - .then((row) => { - data.id = row.id; - - let promises = []; - - // Now add the items - data.items.map((item) => { - promises.push(accessListAuthModel - .query() - .insert({ - access_list_id: row.id, - username: item.username, - password: item.password - }) - ); - }); - - // Now add the clients - if (typeof data.clients !== 'undefined' && data.clients) { - data.clients.map((client) => { - promises.push(accessListClientModel - .query() - .insert({ - access_list_id: row.id, - address: client.address, - directive: client.directive - }) - ); - }); - } - - return Promise.all(promises); - }) - .then(() => { - // re-fetch with expansions - return internalAccessList.get(access, { - id: data.id, - expand: ['owner', 'items', 'clients', 'proxy_hosts.access_list.[clients,items]'] - }, true /* <- skip masking */); - }) - .then((row) => { - // Audit log - data.meta = _.assign({}, data.meta || {}, row.meta); - - return internalAccessList.build(row) - .then(() => { - if (row.proxy_host_count) { - return internalNginx.bulkGenerateConfigs('proxy_host', row.proxy_hosts); - } - }) - .then(() => { - // Add to audit log - return internalAuditLog.add(access, { - action: 'created', - object_type: 'access-list', - object_id: row.id, - meta: internalAccessList.maskItems(data) - }); - }) - .then(() => { - return internalAccessList.maskItems(row); - }); - }); - }, - - /** - * @param {Access} access - * @param {Object} data - * @param {Integer} data.id - * @param {String} [data.name] - * @param {String} [data.items] - * @return {Promise} - */ - update: (access, data) => { - return access.can('access_lists:update', data.id) - .then((/*access_data*/) => { - return internalAccessList.get(access, {id: data.id}); - }) - .then((row) => { - if (row.id !== data.id) { - // Sanity check that something crazy hasn't happened - throw new error.InternalValidationError('Access List could not be updated, IDs do not match: ' + row.id + ' !== ' + data.id); - } - - }) - .then(() => { - // patch name if specified - if (typeof data.name !== 'undefined' && data.name) { - return accessListModel - .query() - .where({id: data.id}) - .patch({ - name: data.name, - satisfy_any: data.satisfy_any, - pass_auth: data.pass_auth, - }); - } - }) - .then(() => { - // Check for items and add/update/remove them - if (typeof data.items !== 'undefined' && data.items) { - let promises = []; - let items_to_keep = []; - - data.items.map(function (item) { - if (item.password) { - promises.push(accessListAuthModel - .query() - .insert({ - access_list_id: data.id, - username: item.username, - password: item.password - }) - ); - } else { - // This was supplied with an empty password, which means keep it but don't change the password - items_to_keep.push(item.username); - } - }); - - let query = accessListAuthModel - .query() - .delete() - .where('access_list_id', data.id); - - if (items_to_keep.length) { - query.andWhere('username', 'NOT IN', items_to_keep); - } - - return query - .then(() => { - // Add new items - if (promises.length) { - return Promise.all(promises); - } - }); - } - }) - .then(() => { - // Check for clients and add/update/remove them - if (typeof data.clients !== 'undefined' && data.clients) { - let promises = []; - - data.clients.map(function (client) { - if (client.address) { - promises.push(accessListClientModel - .query() - .insert({ - access_list_id: data.id, - address: client.address, - directive: client.directive - }) - ); - } - }); - - let query = accessListClientModel - .query() - .delete() - .where('access_list_id', data.id); - - return query - .then(() => { - // Add new items - if (promises.length) { - return Promise.all(promises); - } - }); - } - }) - .then(() => { - // Add to audit log - return internalAuditLog.add(access, { - action: 'updated', - object_type: 'access-list', - object_id: data.id, - meta: internalAccessList.maskItems(data) - }); - }) - .then(() => { - // re-fetch with expansions - return internalAccessList.get(access, { - id: data.id, - expand: ['owner', 'items', 'clients', 'proxy_hosts.access_list.[clients,items]'] - }, true /* <- skip masking */); - }) - .then((row) => { - return internalAccessList.build(row) - .then(() => { - if (row.proxy_host_count) { - return internalNginx.bulkGenerateConfigs('proxy_host', row.proxy_hosts); - } - }) - .then(() => { - return internalAccessList.maskItems(row); - }); - }); - }, - - /** - * @param {Access} access - * @param {Object} data - * @param {Integer} data.id - * @param {Array} [data.expand] - * @param {Array} [data.omit] - * @param {Boolean} [skip_masking] - * @return {Promise} - */ - get: (access, data, skip_masking) => { - if (typeof data === 'undefined') { - data = {}; - } - - return access.can('access_lists:get', data.id) - .then((access_data) => { - let query = accessListModel - .query() - .select('access_list.*', accessListModel.raw('COUNT(proxy_host.id) as proxy_host_count')) - .joinRaw('LEFT JOIN `proxy_host` ON `proxy_host`.`access_list_id` = `access_list`.`id` AND `proxy_host`.`is_deleted` = 0') - .where('access_list.is_deleted', 0) - .andWhere('access_list.id', data.id) - .allowEager('[owner,items,clients,proxy_hosts.[*, access_list.[clients,items]]]') - .omit(['access_list.is_deleted']) - .first(); - - if (access_data.permission_visibility !== 'all') { - query.andWhere('access_list.owner_user_id', access.token.getUserId(1)); - } - - // Custom omissions - if (typeof data.omit !== 'undefined' && data.omit !== null) { - query.omit(data.omit); - } - - if (typeof data.expand !== 'undefined' && data.expand !== null) { - query.eager('[' + data.expand.join(', ') + ']'); - } - - return query; - }) - .then((row) => { - if (row) { - if (!skip_masking && typeof row.items !== 'undefined' && row.items) { - row = internalAccessList.maskItems(row); - } - - return _.omit(row, omissions()); - } else { - throw new error.ItemNotFoundError(data.id); - } - }); - }, - - /** - * @param {Access} access - * @param {Object} data - * @param {Integer} data.id - * @param {String} [data.reason] - * @returns {Promise} - */ - delete: (access, data) => { - return access.can('access_lists:delete', data.id) - .then(() => { - return internalAccessList.get(access, {id: data.id, expand: ['proxy_hosts', 'items', 'clients']}); - }) - .then((row) => { - if (!row) { - throw new error.ItemNotFoundError(data.id); - } - - // 1. update row to be deleted - // 2. update any proxy hosts that were using it (ignoring permissions) - // 3. reconfigure those hosts - // 4. audit log - - // 1. update row to be deleted - return accessListModel - .query() - .where('id', row.id) - .patch({ - is_deleted: 1 - }) - .then(() => { - // 2. update any proxy hosts that were using it (ignoring permissions) - if (row.proxy_hosts) { - return proxyHostModel - .query() - .where('access_list_id', '=', row.id) - .patch({access_list_id: 0}) - .then(() => { - // 3. reconfigure those hosts, then reload nginx - - // set the access_list_id to zero for these items - row.proxy_hosts.map(function (val, idx) { - row.proxy_hosts[idx].access_list_id = 0; - }); - - return internalNginx.bulkGenerateConfigs('proxy_host', row.proxy_hosts); - }) - .then(() => { - return internalNginx.reload(); - }); - } - }) - .then(() => { - // delete the htpasswd file - let htpasswd_file = internalAccessList.getFilename(row); - - try { - fs.unlinkSync(htpasswd_file); - } catch (err) { - // do nothing - } - }) - .then(() => { - // 4. audit log - return internalAuditLog.add(access, { - action: 'deleted', - object_type: 'access-list', - object_id: row.id, - meta: _.omit(internalAccessList.maskItems(row), ['is_deleted', 'proxy_hosts']) - }); - }); - }) - .then(() => { - return true; - }); - }, - - /** - * All Lists - * - * @param {Access} access - * @param {Array} [expand] - * @param {String} [search_query] - * @returns {Promise} - */ - getAll: (access, expand, search_query) => { - return access.can('access_lists:list') - .then((access_data) => { - let query = accessListModel - .query() - .select('access_list.*', accessListModel.raw('COUNT(proxy_host.id) as proxy_host_count')) - .joinRaw('LEFT JOIN `proxy_host` ON `proxy_host`.`access_list_id` = `access_list`.`id` AND `proxy_host`.`is_deleted` = 0') - .where('access_list.is_deleted', 0) - .groupBy('access_list.id') - .omit(['access_list.is_deleted']) - .allowEager('[owner,items,clients]') - .orderBy('access_list.name', 'ASC'); - - if (access_data.permission_visibility !== 'all') { - query.andWhere('access_list.owner_user_id', access.token.getUserId(1)); - } - - // Query is used for searching - if (typeof search_query === 'string') { - query.where(function () { - this.where('name', 'like', '%' + search_query + '%'); - }); - } - - if (typeof expand !== 'undefined' && expand !== null) { - query.eager('[' + expand.join(', ') + ']'); - } - - return query; - }) - .then((rows) => { - if (rows) { - rows.map(function (row, idx) { - if (typeof row.items !== 'undefined' && row.items) { - rows[idx] = internalAccessList.maskItems(row); - } - }); - } - - return rows; - }); - }, - - /** - * Report use - * - * @param {Integer} user_id - * @param {String} visibility - * @returns {Promise} - */ - getCount: (user_id, visibility) => { - let query = accessListModel - .query() - .count('id as count') - .where('is_deleted', 0); - - if (visibility !== 'all') { - query.andWhere('owner_user_id', user_id); - } - - return query.first() - .then((row) => { - return parseInt(row.count, 10); - }); - }, - - /** - * @param {Object} list - * @returns {Object} - */ - maskItems: (list) => { - if (list && typeof list.items !== 'undefined') { - list.items.map(function (val, idx) { - let repeat_for = 8; - let first_char = '*'; - - if (typeof val.password !== 'undefined' && val.password) { - repeat_for = val.password.length - 1; - first_char = val.password.charAt(0); - } - - list.items[idx].hint = first_char + ('*').repeat(repeat_for); - list.items[idx].password = ''; - }); - } - - return list; - }, - - /** - * @param {Object} list - * @param {Integer} list.id - * @returns {String} - */ - getFilename: (list) => { - return '/data/access/' + list.id; - }, - - /** - * @param {Object} list - * @param {Integer} list.id - * @param {String} list.name - * @param {Array} list.items - * @returns {Promise} - */ - build: (list) => { - logger.info('Building Access file #' + list.id + ' for: ' + list.name); - - return new Promise((resolve, reject) => { - let htpasswd_file = internalAccessList.getFilename(list); - - // 1. remove any existing access file - try { - fs.unlinkSync(htpasswd_file); - } catch (err) { - // do nothing - } - - // 2. create empty access file - try { - fs.writeFileSync(htpasswd_file, '', {encoding: 'utf8'}); - resolve(htpasswd_file); - } catch (err) { - reject(err); - } - }) - .then((htpasswd_file) => { - // 3. generate password for each user - if (list.items.length) { - return new Promise((resolve, reject) => { - batchflow(list.items).sequential() - .each((i, item, next) => { - if (typeof item.password !== 'undefined' && item.password.length) { - logger.info('Adding: ' + item.username); - - utils.exec('/usr/bin/htpasswd -b "' + htpasswd_file + '" "' + item.username + '" "' + item.password + '"') - .then((/*result*/) => { - next(); - }) - .catch((err) => { - logger.error(err); - next(err); - }); - } - }) - .error((err) => { - logger.error(err); - reject(err); - }) - .end((results) => { - logger.success('Built Access file #' + list.id + ' for: ' + list.name); - resolve(results); - }); - }); - } - }); - } -}; - -module.exports = internalAccessList; diff --git a/backend/internal/api/context/context.go b/backend/internal/api/context/context.go new file mode 100644 index 00000000..f3bc957b --- /dev/null +++ b/backend/internal/api/context/context.go @@ -0,0 +1,25 @@ +package context + +var ( + // BodyCtxKey is the name of the Body value on the context + BodyCtxKey = &contextKey{"Body"} + // UserIDCtxKey is the name of the UserID value on the context + UserIDCtxKey = &contextKey{"UserID"} + // FiltersCtxKey is the name of the Filters value on the context + FiltersCtxKey = &contextKey{"Filters"} + // PrettyPrintCtxKey is the name of the pretty print context + PrettyPrintCtxKey = &contextKey{"Pretty"} + // ExpansionCtxKey is the name of the expansion context + ExpansionCtxKey = &contextKey{"Expansion"} +) + +// contextKey is a value for use with context.WithValue. It's used as +// a pointer so it fits in an interface{} without allocation. This technique +// for defining context keys was copied from Go 1.7's new use of context in net/http. +type contextKey struct { + name string +} + +func (k *contextKey) String() string { + return "context value: " + k.name +} diff --git a/backend/internal/api/filters/helpers.go b/backend/internal/api/filters/helpers.go new file mode 100644 index 00000000..5f5d5238 --- /dev/null +++ b/backend/internal/api/filters/helpers.go @@ -0,0 +1,208 @@ +package filters + +import ( + "fmt" + "strings" +) + +// NewFilterSchema is the main method to specify a new Filter Schema for use in Middleware +func NewFilterSchema(fieldSchemas []string) string { + return fmt.Sprintf(baseFilterSchema, strings.Join(fieldSchemas, ", ")) +} + +// BoolFieldSchema returns the Field Schema for a Boolean accepted value field +func BoolFieldSchema(fieldName string) string { + return fmt.Sprintf(`{ + "type": "object", + "properties": { + "field": { + "type": "string", + "pattern": "^%s$" + }, + "modifier": %s, + "value": { + "oneOf": [ + %s, + { + "type": "array", + "items": %s + } + ] + } + } + }`, fieldName, boolModifiers, filterBool, filterBool) +} + +// IntFieldSchema returns the Field Schema for a Integer accepted value field +func IntFieldSchema(fieldName string) string { + return fmt.Sprintf(`{ + "type": "object", + "properties": { + "field": { + "type": "string", + "pattern": "^%s$" + }, + "modifier": %s, + "value": { + "oneOf": [ + { + "type": "string", + "pattern": "^[0-9]+$" + }, + { + "type": "array", + "items": { + "type": "string", + "pattern": "^[0-9]+$" + } + } + ] + } + } + }`, fieldName, allModifiers) +} + +// StringFieldSchema returns the Field Schema for a String accepted value field +func StringFieldSchema(fieldName string) string { + return fmt.Sprintf(`{ + "type": "object", + "properties": { + "field": { + "type": "string", + "pattern": "^%s$" + }, + "modifier": %s, + "value": { + "oneOf": [ + %s, + { + "type": "array", + "items": %s + } + ] + } + } + }`, fieldName, stringModifiers, filterString, filterString) +} + +// RegexFieldSchema returns the Field Schema for a String accepted value field matching a Regex +func RegexFieldSchema(fieldName string, regex string) string { + return fmt.Sprintf(`{ + "type": "object", + "properties": { + "field": { + "type": "string", + "pattern": "^%s$" + }, + "modifier": %s, + "value": { + "oneOf": [ + { + "type": "string", + "pattern": "%s" + }, + { + "type": "array", + "items": { + "type": "string", + "pattern": "%s" + } + } + ] + } + } + }`, fieldName, stringModifiers, regex, regex) +} + +// DateFieldSchema returns the Field Schema for a String accepted value field matching a Date format +func DateFieldSchema(fieldName string) string { + return fmt.Sprintf(`{ + "type": "object", + "properties": { + "field": { + "type": "string", + "pattern": "^%s$" + }, + "modifier": %s, + "value": { + "oneOf": [ + { + "type": "string", + "pattern": "^([12]\\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01]))$" + }, + { + "type": "array", + "items": { + "type": "string", + "pattern": "^([12]\\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01]))$" + } + } + ] + } + } + }`, fieldName, allModifiers) +} + +// DateTimeFieldSchema returns the Field Schema for a String accepted value field matching a Date format +// 2020-03-01T10:30:00+10:00 +func DateTimeFieldSchema(fieldName string) string { + return fmt.Sprintf(`{ + "type": "object", + "properties": { + "field": { + "type": "string", + "pattern": "^%s$" + }, + "modifier": %s, + "value": { + "oneOf": [ + { + "type": "string", + "pattern": "^([12]\\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01]))$" + }, + { + "type": "array", + "items": { + "type": "string", + "pattern": "^([12]\\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01]))$" + } + } + ] + } + } + }`, fieldName, allModifiers) +} + +const allModifiers = `{ + "type": "string", + "pattern": "^(equals|not|contains|starts|ends|in|notin|min|max|greater|less)$" +}` + +const boolModifiers = `{ + "type": "string", + "pattern": "^(equals|not)$" +}` + +const stringModifiers = `{ + "type": "string", + "pattern": "^(equals|not|contains|starts|ends|in|notin)$" +}` + +const filterBool = `{ + "type": "string", + "pattern": "^(TRUE|true|t|yes|y|on|1|FALSE|f|false|n|no|off|0)$" +}` + +const filterString = `{ + "type": "string", + "minLength": 1 +}` + +const baseFilterSchema = `{ + "type": "array", + "items": { + "oneOf": [ + %s + ] + } +}` diff --git a/backend/internal/api/handler/auth.go b/backend/internal/api/handler/auth.go new file mode 100644 index 00000000..972022de --- /dev/null +++ b/backend/internal/api/handler/auth.go @@ -0,0 +1,54 @@ +package handler + +import ( + "encoding/json" + "net/http" + + c "npm/internal/api/context" + h "npm/internal/api/http" + "npm/internal/entity/auth" + "npm/internal/logger" +) + +// SetAuth ... +// Route: POST /users/:userID/auth +func SetAuth() func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte) + + // TODO: + // delete old auth for user + // test endpoint + + var newAuth auth.Model + err := json.Unmarshal(bodyBytes, &newAuth) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil) + return + } + + userID, _, userIDErr := getUserIDFromRequest(r) + if userIDErr != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, userIDErr.Error(), nil) + return + } + + newAuth.UserID = userID + if newAuth.Type == auth.TypePassword { + err := newAuth.SetPassword(newAuth.Secret) + if err != nil { + logger.Error("SetPasswordError", err) + } + } + + if err = newAuth.Save(); err != nil { + logger.Error("AuthSaveError", err) + h.ResultErrorJSON(w, r, http.StatusInternalServerError, "Unable to save Authentication for User", nil) + return + } + + newAuth.Secret = "" + + h.ResultResponseJSON(w, r, http.StatusOK, newAuth) + } +} diff --git a/backend/internal/api/handler/certificate_authorities.go b/backend/internal/api/handler/certificate_authorities.go new file mode 100644 index 00000000..e5c453ef --- /dev/null +++ b/backend/internal/api/handler/certificate_authorities.go @@ -0,0 +1,126 @@ +package handler + +import ( + "encoding/json" + "fmt" + "net/http" + + c "npm/internal/api/context" + h "npm/internal/api/http" + "npm/internal/api/middleware" + "npm/internal/entity/certificateauthority" +) + +// GetCertificateAuthorities will return a list of Certificate Authorities +// Route: GET /certificate-authorities +func GetCertificateAuthorities() func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + pageInfo, err := getPageInfoFromRequest(r) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + return + } + + certificates, err := certificateauthority.List(pageInfo, middleware.GetFiltersFromContext(r)) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + } else { + h.ResultResponseJSON(w, r, http.StatusOK, certificates) + } + } +} + +// GetCertificateAuthority will return a single Certificate Authority +// Route: GET /certificate-authorities/{caID} +func GetCertificateAuthority() func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + var err error + var caID int + if caID, err = getURLParamInt(r, "caID"); err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + return + } + + cert, err := certificateauthority.GetByID(caID) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + } else { + h.ResultResponseJSON(w, r, http.StatusOK, cert) + } + } +} + +// CreateCertificateAuthority will create a Certificate Authority +// Route: POST /certificate-authorities +func CreateCertificateAuthority() func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte) + + var newCA certificateauthority.Model + err := json.Unmarshal(bodyBytes, &newCA) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil) + return + } + + if err = newCA.Save(); err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, fmt.Sprintf("Unable to save Certificate Authority: %s", err.Error()), nil) + return + } + + h.ResultResponseJSON(w, r, http.StatusOK, newCA) + } +} + +// UpdateCertificateAuthority ... +// Route: PUT /certificate-authorities/{caID} +func UpdateCertificateAuthority() func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + var err error + var caID int + if caID, err = getURLParamInt(r, "caID"); err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + return + } + + ca, err := certificateauthority.GetByID(caID) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + } else { + + bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte) + err := json.Unmarshal(bodyBytes, &ca) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil) + return + } + + if err = ca.Save(); err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + return + } + + h.ResultResponseJSON(w, r, http.StatusOK, ca) + } + } +} + +// DeleteCertificateAuthority ... +// Route: DELETE /certificate-authorities/{caID} +func DeleteCertificateAuthority() func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + var err error + var caID int + if caID, err = getURLParamInt(r, "caID"); err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + return + } + + cert, err := certificateauthority.GetByID(caID) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + } else { + h.ResultResponseJSON(w, r, http.StatusOK, cert.Delete()) + } + } +} diff --git a/backend/internal/api/handler/certificates.go b/backend/internal/api/handler/certificates.go new file mode 100644 index 00000000..8e70794b --- /dev/null +++ b/backend/internal/api/handler/certificates.go @@ -0,0 +1,145 @@ +package handler + +import ( + "encoding/json" + "fmt" + "net/http" + + c "npm/internal/api/context" + h "npm/internal/api/http" + "npm/internal/api/middleware" + "npm/internal/api/schema" + "npm/internal/entity/certificate" +) + +// GetCertificates will return a list of Certificates +// Route: GET /certificates +func GetCertificates() func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + pageInfo, err := getPageInfoFromRequest(r) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + return + } + + certificates, err := certificate.List(pageInfo, middleware.GetFiltersFromContext(r)) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + } else { + h.ResultResponseJSON(w, r, http.StatusOK, certificates) + } + } +} + +// GetCertificate will return a single Certificate +// Route: GET /certificates/{certificateID} +func GetCertificate() func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + var err error + var certificateID int + if certificateID, err = getURLParamInt(r, "certificateID"); err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + return + } + + cert, err := certificate.GetByID(certificateID) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + } else { + h.ResultResponseJSON(w, r, http.StatusOK, cert) + } + } +} + +// CreateCertificate will create a Certificate +// Route: POST /certificates +func CreateCertificate() func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte) + + var newCertificate certificate.Model + err := json.Unmarshal(bodyBytes, &newCertificate) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil) + return + } + + // Get userID from token + userID, _ := r.Context().Value(c.UserIDCtxKey).(int) + newCertificate.UserID = userID + + if err = newCertificate.Save(); err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, fmt.Sprintf("Unable to save Certificate: %s", err.Error()), nil) + return + } + + h.ResultResponseJSON(w, r, http.StatusOK, newCertificate) + } +} + +// UpdateCertificate ... +// Route: PUT /certificates/{certificateID} +func UpdateCertificate() func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + var err error + var certificateID int + if certificateID, err = getURLParamInt(r, "certificateID"); err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + return + } + + certificateObject, err := certificate.GetByID(certificateID) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + } else { + + // This is a special endpoint, as it needs to verify the schema payload + // based on the certificate type, without being given a type in the payload. + // The middleware would normally handle this. + bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte) + schemaErrors, jsonErr := middleware.CheckRequestSchema(r.Context(), schema.UpdateCertificate(certificateObject.Type), bodyBytes) + if jsonErr != nil { + h.ResultErrorJSON(w, r, http.StatusInternalServerError, fmt.Sprintf("Schema Fatal: %v", jsonErr), nil) + return + } + + if len(schemaErrors) > 0 { + h.ResultSchemaErrorJSON(w, r, schemaErrors) + return + } + + err := json.Unmarshal(bodyBytes, &certificateObject) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil) + return + } + + if err = certificateObject.Save(); err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + return + } + + h.ResultResponseJSON(w, r, http.StatusOK, certificateObject) + } + } +} + +// DeleteCertificate ... +// Route: DELETE /certificates/{certificateID} +func DeleteCertificate() func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + var err error + var certificateID int + if certificateID, err = getURLParamInt(r, "certificateID"); err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + return + } + + cert, err := certificate.GetByID(certificateID) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + } else { + h.ResultResponseJSON(w, r, http.StatusOK, cert.Delete()) + } + } +} diff --git a/backend/internal/api/handler/config.go b/backend/internal/api/handler/config.go new file mode 100644 index 00000000..811d7580 --- /dev/null +++ b/backend/internal/api/handler/config.go @@ -0,0 +1,15 @@ +package handler + +import ( + "net/http" + h "npm/internal/api/http" + "npm/internal/config" +) + +// Config returns the entire configuration, for debug purposes +// Route: GET /config +func Config() func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + h.ResultResponseJSON(w, r, http.StatusOK, config.Configuration) + } +} diff --git a/backend/internal/api/handler/dns_providers.go b/backend/internal/api/handler/dns_providers.go new file mode 100644 index 00000000..045619aa --- /dev/null +++ b/backend/internal/api/handler/dns_providers.go @@ -0,0 +1,129 @@ +package handler + +import ( + "encoding/json" + "fmt" + "net/http" + + c "npm/internal/api/context" + h "npm/internal/api/http" + "npm/internal/api/middleware" + "npm/internal/entity/dnsprovider" +) + +// GetDNSProviders will return a list of DNS Providers +// Route: GET /dns-providers +func GetDNSProviders() func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + pageInfo, err := getPageInfoFromRequest(r) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + return + } + + items, err := dnsprovider.List(pageInfo, middleware.GetFiltersFromContext(r)) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + } else { + h.ResultResponseJSON(w, r, http.StatusOK, items) + } + } +} + +// GetDNSProvider will return a single DNS Provider +// Route: GET /dns-providers/{providerID} +func GetDNSProvider() func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + var err error + var providerID int + if providerID, err = getURLParamInt(r, "providerID"); err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + return + } + + item, err := dnsprovider.GetByID(providerID) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + } else { + h.ResultResponseJSON(w, r, http.StatusOK, item) + } + } +} + +// CreateDNSProvider will create a DNS Provider +// Route: POST /dns-providers +func CreateDNSProvider() func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte) + + var newItem dnsprovider.Model + err := json.Unmarshal(bodyBytes, &newItem) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil) + return + } + + // Get userID from token + userID, _ := r.Context().Value(c.UserIDCtxKey).(int) + newItem.UserID = userID + + if err = newItem.Save(); err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, fmt.Sprintf("Unable to save DNS Provider: %s", err.Error()), nil) + return + } + + h.ResultResponseJSON(w, r, http.StatusOK, newItem) + } +} + +// UpdateDNSProvider ... +// Route: PUT /dns-providers/{providerID} +func UpdateDNSProvider() func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + var err error + var providerID int + if providerID, err = getURLParamInt(r, "providerID"); err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + return + } + + item, err := dnsprovider.GetByID(providerID) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + } else { + bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte) + err := json.Unmarshal(bodyBytes, &item) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil) + return + } + + if err = item.Save(); err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + return + } + + h.ResultResponseJSON(w, r, http.StatusOK, item) + } + } +} + +// DeleteDNSProvider ... +// Route: DELETE /dns-providers/{providerID} +func DeleteDNSProvider() func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + var err error + var providerID int + if providerID, err = getURLParamInt(r, "providerID"); err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + return + } + + item, err := dnsprovider.GetByID(providerID) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + } else { + h.ResultResponseJSON(w, r, http.StatusOK, item.Delete()) + } + } +} diff --git a/backend/internal/api/handler/health.go b/backend/internal/api/handler/health.go new file mode 100644 index 00000000..6e128112 --- /dev/null +++ b/backend/internal/api/handler/health.go @@ -0,0 +1,31 @@ +package handler + +import ( + "net/http" + h "npm/internal/api/http" + "npm/internal/config" +) + +type healthCheckResponse struct { + Version string `json:"version"` + Commit string `json:"commit"` + Healthy bool `json:"healthy"` + IsSetup bool `json:"setup"` + ErrorReporting bool `json:"error_reporting"` +} + +// Health returns the health of the api +// Route: GET /health +func Health() func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + health := healthCheckResponse{ + Version: config.Version, + Commit: config.Commit, + Healthy: true, + IsSetup: config.IsSetup, + ErrorReporting: config.ErrorReporting, + } + + h.ResultResponseJSON(w, r, http.StatusOK, health) + } +} diff --git a/backend/internal/api/handler/helpers.go b/backend/internal/api/handler/helpers.go new file mode 100644 index 00000000..3727269a --- /dev/null +++ b/backend/internal/api/handler/helpers.go @@ -0,0 +1,151 @@ +package handler + +import ( + "fmt" + "net/http" + "strconv" + "strings" + "time" + + "npm/internal/model" + + "github.com/go-chi/chi" +) + +const defaultLimit = 10 + +func getPageInfoFromRequest(r *http.Request) (model.PageInfo, error) { + var pageInfo model.PageInfo + var err error + + pageInfo.FromDate, pageInfo.ToDate, err = getDateRanges(r) + if err != nil { + return pageInfo, err + } + + pageInfo.Offset, pageInfo.Limit, err = getPagination(r) + if err != nil { + return pageInfo, err + } + + pageInfo.Sort = getSortParameter(r) + + return pageInfo, nil +} + +func getDateRanges(r *http.Request) (time.Time, time.Time, error) { + queryValues := r.URL.Query() + from := queryValues.Get("from") + fromDate := time.Now().AddDate(0, -1, 0) // 1 month ago by default + to := queryValues.Get("to") + toDate := time.Now() + + if from != "" { + var fromErr error + fromDate, fromErr = time.Parse(time.RFC3339, from) + if fromErr != nil { + return fromDate, toDate, fmt.Errorf("From date is not in correct format: %v", strings.ReplaceAll(time.RFC3339, "Z", "+")) + } + } + + if to != "" { + var toErr error + toDate, toErr = time.Parse(time.RFC3339, to) + if toErr != nil { + return fromDate, toDate, fmt.Errorf("To date is not in correct format: %v", strings.ReplaceAll(time.RFC3339, "Z", "+")) + } + } + + return fromDate, toDate, nil +} + +func getSortParameter(r *http.Request) []model.Sort { + var sortFields []model.Sort + + queryValues := r.URL.Query() + sortString := queryValues.Get("sort") + if sortString == "" { + return sortFields + } + + // Split sort fields up in to slice + sorts := strings.Split(sortString, ",") + for _, sortItem := range sorts { + if strings.Contains(sortItem, ".") { + theseItems := strings.Split(sortItem, ".") + + switch strings.ToLower(theseItems[1]) { + case "desc": + fallthrough + case "descending": + theseItems[1] = "DESC" + default: + theseItems[1] = "ASC" + } + + sortFields = append(sortFields, model.Sort{ + Field: theseItems[0], + Direction: theseItems[1], + }) + } else { + sortFields = append(sortFields, model.Sort{ + Field: sortItem, + Direction: "ASC", + }) + } + } + + return sortFields +} + +func getQueryVarInt(r *http.Request, varName string, required bool, defaultValue int) (int, error) { + queryValues := r.URL.Query() + varValue := queryValues.Get(varName) + + if varValue == "" && required { + return 0, fmt.Errorf("%v was not supplied in the request", varName) + } else if varValue == "" { + return defaultValue, nil + } + + varInt, intErr := strconv.Atoi(varValue) + if intErr != nil { + return 0, fmt.Errorf("%v is not a valid number", varName) + } + + return varInt, nil +} + +func getURLParamInt(r *http.Request, varName string) (int, error) { + required := true + defaultValue := 0 + paramStr := chi.URLParam(r, varName) + var err error + var paramInt int + + if paramStr == "" && required { + return 0, fmt.Errorf("%v was not supplied in the request", varName) + } else if paramStr == "" { + return defaultValue, nil + } + + if paramInt, err = strconv.Atoi(paramStr); err != nil { + return 0, fmt.Errorf("%v is not a valid number", varName) + } + + return paramInt, nil +} + +func getPagination(r *http.Request) (int, int, error) { + var err error + offset, err := getQueryVarInt(r, "offset", false, 0) + if err != nil { + return 0, 0, err + } + limit, err := getQueryVarInt(r, "limit", false, defaultLimit) + if err != nil { + return 0, 0, err + } + + return offset, limit, nil +} diff --git a/backend/internal/api/handler/hosts.go b/backend/internal/api/handler/hosts.go new file mode 100644 index 00000000..58c75c98 --- /dev/null +++ b/backend/internal/api/handler/hosts.go @@ -0,0 +1,135 @@ +package handler + +import ( + "encoding/json" + "fmt" + "net/http" + + c "npm/internal/api/context" + h "npm/internal/api/http" + "npm/internal/api/middleware" + "npm/internal/entity/host" + "npm/internal/validator" +) + +// GetHosts will return a list of Hosts +// Route: GET /hosts +func GetHosts() func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + pageInfo, err := getPageInfoFromRequest(r) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + return + } + + hosts, err := host.List(pageInfo, middleware.GetFiltersFromContext(r)) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + } else { + h.ResultResponseJSON(w, r, http.StatusOK, hosts) + } + } +} + +// GetHost will return a single Host +// Route: GET /hosts/{hostID} +func GetHost() func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + var err error + var hostID int + if hostID, err = getURLParamInt(r, "hostID"); err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + return + } + + host, err := host.GetByID(hostID) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + } else { + h.ResultResponseJSON(w, r, http.StatusOK, host) + } + } +} + +// CreateHost will create a Host +// Route: POST /hosts +func CreateHost() func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte) + + var newHost host.Model + err := json.Unmarshal(bodyBytes, &newHost) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil) + return + } + + // Get userID from token + userID, _ := r.Context().Value(c.UserIDCtxKey).(int) + newHost.UserID = userID + + if err = validator.ValidateHost(newHost); err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + return + } + + if err = newHost.Save(); err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, fmt.Sprintf("Unable to save Host: %s", err.Error()), nil) + return + } + + h.ResultResponseJSON(w, r, http.StatusOK, newHost) + } +} + +// UpdateHost ... +// Route: PUT /hosts/{hostID} +func UpdateHost() func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + var err error + var hostID int + if hostID, err = getURLParamInt(r, "hostID"); err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + return + } + + host, err := host.GetByID(hostID) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + } else { + bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte) + err := json.Unmarshal(bodyBytes, &host) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil) + return + } + + if err = host.Save(); err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + return + } + + h.ResultResponseJSON(w, r, http.StatusOK, host) + } + } +} + +// DeleteHost ... +// Route: DELETE /hosts/{hostID} +func DeleteHost() func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + var err error + var hostID int + if hostID, err = getURLParamInt(r, "hostID"); err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + return + } + + host, err := host.GetByID(hostID) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + } else { + h.ResultResponseJSON(w, r, http.StatusOK, host.Delete()) + } + } +} diff --git a/backend/internal/api/handler/not_allowed.go b/backend/internal/api/handler/not_allowed.go new file mode 100644 index 00000000..966debab --- /dev/null +++ b/backend/internal/api/handler/not_allowed.go @@ -0,0 +1,14 @@ +package handler + +import ( + "net/http" + + h "npm/internal/api/http" +) + +// NotAllowed is a json error handler for when method is not allowed +func NotAllowed() func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + h.ResultErrorJSON(w, r, http.StatusNotFound, "Not allowed", nil) + } +} diff --git a/backend/internal/api/handler/not_found.go b/backend/internal/api/handler/not_found.go new file mode 100644 index 00000000..04893e9b --- /dev/null +++ b/backend/internal/api/handler/not_found.go @@ -0,0 +1,65 @@ +package handler + +import ( + "embed" + "errors" + "io" + "io/fs" + "mime" + "net/http" + "path/filepath" + "strings" + + h "npm/internal/api/http" +) + +//go:embed assets +var assets embed.FS +var assetsSub fs.FS + +var errIsDir = errors.New("path is dir") + +// NotFound is a json error handler for 404's and method not allowed. +// It also serves the react frontend as embedded files in the golang binary. +func NotFound() func(http.ResponseWriter, *http.Request) { + assetsSub, _ = fs.Sub(assets, "assets") + + return func(w http.ResponseWriter, r *http.Request) { + path := strings.TrimLeft(r.URL.Path, "/") + if path == "" { + path = "index.html" + } + + err := tryRead(assetsSub, path, w) + if err == errIsDir { + err = tryRead(assetsSub, "index.html", w) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusNotFound, "Not found", nil) + } + } else if err == nil { + return + } + + h.ResultErrorJSON(w, r, http.StatusNotFound, "Not found", nil) + } +} + +func tryRead(folder fs.FS, requestedPath string, w http.ResponseWriter) error { + f, err := folder.Open(requestedPath) + if err != nil { + return err + } + + // nolint: errcheck + defer f.Close() + + stat, _ := f.Stat() + if stat.IsDir() { + return errIsDir + } + + contentType := mime.TypeByExtension(filepath.Ext(requestedPath)) + w.Header().Set("Content-Type", contentType) + _, err = io.Copy(w, f) + return err +} diff --git a/backend/internal/api/handler/schema.go b/backend/internal/api/handler/schema.go new file mode 100644 index 00000000..38440f4c --- /dev/null +++ b/backend/internal/api/handler/schema.go @@ -0,0 +1,99 @@ +package handler + +import ( + "encoding/json" + "fmt" + "net/http" + "strings" + + "npm/doc" + "npm/internal/api/schema" + "npm/internal/config" + "npm/internal/logger" + + jsref "github.com/jc21/jsref" + "github.com/jc21/jsref/provider" +) + +var swaggerSchema []byte + +// Schema simply reads the swagger schema from disk and returns is raw +// Route: GET /schema +func Schema() func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(http.StatusOK) + fmt.Fprint(w, string(getSchema())) + } +} + +func getSchema() []byte { + if swaggerSchema == nil { + // nolint:gosec + swaggerSchema, _ = doc.SwaggerFiles.ReadFile("api.swagger.json") + + // Replace {{VERSION}} with Config Version + swaggerSchema = []byte(strings.ReplaceAll(string(swaggerSchema), "{{VERSION}}", config.Version)) + + // Dereference the JSON Schema: + var schema interface{} + if err := json.Unmarshal(swaggerSchema, &schema); err != nil { + logger.Error("SwaggerUnmarshalError", err) + return nil + } + + provider := provider.NewIoFS(doc.SwaggerFiles, "") + resolver := jsref.New() + err := resolver.AddProvider(provider) + if err != nil { + logger.Error("SchemaProviderError", err) + } + + result, err := resolver.Resolve(schema, "", []jsref.Option{jsref.WithRecursiveResolution(true)}...) + if err != nil { + logger.Error("SwaggerResolveError", err) + } else { + var marshalErr error + swaggerSchema, marshalErr = json.MarshalIndent(result, "", " ") + if marshalErr != nil { + logger.Error("SwaggerMarshalError", err) + } + } + // End dereference + + // Replace incoming schemas with those we actually use in code + swaggerSchema = replaceIncomingSchemas(swaggerSchema) + } + return swaggerSchema +} + +func replaceIncomingSchemas(swaggerSchema []byte) []byte { + str := string(swaggerSchema) + + // Remember to include the double quotes in the replacement! + str = strings.ReplaceAll(str, `"{{schema.SetAuth}}"`, schema.SetAuth()) + str = strings.ReplaceAll(str, `"{{schema.GetToken}}"`, schema.GetToken()) + + str = strings.ReplaceAll(str, `"{{schema.CreateCertificateAuthority}}"`, schema.CreateCertificateAuthority()) + str = strings.ReplaceAll(str, `"{{schema.UpdateCertificateAuthority}}"`, schema.UpdateCertificateAuthority()) + + str = strings.ReplaceAll(str, `"{{schema.CreateCertificate}}"`, schema.CreateCertificate()) + str = strings.ReplaceAll(str, `"{{schema.UpdateCertificate}}"`, schema.UpdateCertificate("")) + + str = strings.ReplaceAll(str, `"{{schema.CreateSetting}}"`, schema.CreateSetting()) + str = strings.ReplaceAll(str, `"{{schema.UpdateSetting}}"`, schema.UpdateSetting()) + + str = strings.ReplaceAll(str, `"{{schema.CreateUser}}"`, schema.CreateUser()) + str = strings.ReplaceAll(str, `"{{schema.UpdateUser}}"`, schema.UpdateUser()) + + str = strings.ReplaceAll(str, `"{{schema.CreateHost}}"`, schema.CreateHost()) + str = strings.ReplaceAll(str, `"{{schema.UpdateHost}}"`, schema.UpdateHost()) + + str = strings.ReplaceAll(str, `"{{schema.CreateStream}}"`, schema.CreateStream()) + str = strings.ReplaceAll(str, `"{{schema.UpdateStream}}"`, schema.UpdateStream()) + + str = strings.ReplaceAll(str, `"{{schema.CreateDNSProvider}}"`, schema.CreateDNSProvider()) + str = strings.ReplaceAll(str, `"{{schema.UpdateDNSProvider}}"`, schema.UpdateDNSProvider()) + + return []byte(str) +} diff --git a/backend/internal/api/handler/settings.go b/backend/internal/api/handler/settings.go new file mode 100644 index 00000000..c73e7385 --- /dev/null +++ b/backend/internal/api/handler/settings.go @@ -0,0 +1,98 @@ +package handler + +import ( + "encoding/json" + "fmt" + "net/http" + + c "npm/internal/api/context" + h "npm/internal/api/http" + "npm/internal/api/middleware" + "npm/internal/entity/setting" + + "github.com/go-chi/chi" +) + +// GetSettings will return a list of Settings +// Route: GET /settings +func GetSettings() func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + pageInfo, err := getPageInfoFromRequest(r) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + return + } + + settings, err := setting.List(pageInfo, middleware.GetFiltersFromContext(r)) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + } else { + h.ResultResponseJSON(w, r, http.StatusOK, settings) + } + } +} + +// GetSetting will return a single Setting +// Route: GET /settings/{name} +func GetSetting() func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + name := chi.URLParam(r, "name") + + sett, err := setting.GetByName(name) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + } else { + h.ResultResponseJSON(w, r, http.StatusOK, sett) + } + } +} + +// CreateSetting will create a Setting +// Route: POST /settings +func CreateSetting() func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte) + + var newSetting setting.Model + err := json.Unmarshal(bodyBytes, &newSetting) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil) + return + } + + if err = newSetting.Save(); err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, fmt.Sprintf("Unable to save Setting: %s", err.Error()), nil) + return + } + + h.ResultResponseJSON(w, r, http.StatusOK, newSetting) + } +} + +// UpdateSetting ... +// Route: PUT /settings/{name} +func UpdateSetting() func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + settingName := chi.URLParam(r, "name") + + setting, err := setting.GetByName(settingName) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + } else { + + bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte) + err := json.Unmarshal(bodyBytes, &setting) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil) + return + } + + if err = setting.Save(); err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + return + } + + h.ResultResponseJSON(w, r, http.StatusOK, setting) + } + } +} diff --git a/backend/internal/api/handler/streams.go b/backend/internal/api/handler/streams.go new file mode 100644 index 00000000..04b277b6 --- /dev/null +++ b/backend/internal/api/handler/streams.go @@ -0,0 +1,129 @@ +package handler + +import ( + "encoding/json" + "fmt" + "net/http" + + c "npm/internal/api/context" + h "npm/internal/api/http" + "npm/internal/api/middleware" + "npm/internal/entity/stream" +) + +// GetStreams will return a list of Streams +// Route: GET /hosts/streams +func GetStreams() func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + pageInfo, err := getPageInfoFromRequest(r) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + return + } + + hosts, err := stream.List(pageInfo, middleware.GetFiltersFromContext(r)) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + } else { + h.ResultResponseJSON(w, r, http.StatusOK, hosts) + } + } +} + +// GetStream will return a single Streams +// Route: GET /hosts/streams/{hostID} +func GetStream() func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + var err error + var hostID int + if hostID, err = getURLParamInt(r, "hostID"); err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + return + } + + host, err := stream.GetByID(hostID) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + } else { + h.ResultResponseJSON(w, r, http.StatusOK, host) + } + } +} + +// CreateStream will create a Stream +// Route: POST /hosts/steams +func CreateStream() func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte) + + var newHost stream.Model + err := json.Unmarshal(bodyBytes, &newHost) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil) + return + } + + // Get userID from token + userID, _ := r.Context().Value(c.UserIDCtxKey).(int) + newHost.UserID = userID + + if err = newHost.Save(); err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, fmt.Sprintf("Unable to save Stream: %s", err.Error()), nil) + return + } + + h.ResultResponseJSON(w, r, http.StatusOK, newHost) + } +} + +// UpdateStream ... +// Route: PUT /hosts/streams/{hostID} +func UpdateStream() func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + var err error + var hostID int + if hostID, err = getURLParamInt(r, "hostID"); err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + return + } + + host, err := stream.GetByID(hostID) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + } else { + bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte) + err := json.Unmarshal(bodyBytes, &host) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil) + return + } + + if err = host.Save(); err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + return + } + + h.ResultResponseJSON(w, r, http.StatusOK, host) + } + } +} + +// DeleteStream ... +// Route: DELETE /hosts/streams/{hostID} +func DeleteStream() func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + var err error + var hostID int + if hostID, err = getURLParamInt(r, "hostID"); err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + return + } + + host, err := stream.GetByID(hostID) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + } else { + h.ResultResponseJSON(w, r, http.StatusOK, host.Delete()) + } + } +} diff --git a/backend/internal/api/handler/tokens.go b/backend/internal/api/handler/tokens.go new file mode 100644 index 00000000..8851e97e --- /dev/null +++ b/backend/internal/api/handler/tokens.go @@ -0,0 +1,77 @@ +package handler + +import ( + "encoding/json" + "net/http" + h "npm/internal/api/http" + + c "npm/internal/api/context" + "npm/internal/entity/auth" + "npm/internal/entity/user" + njwt "npm/internal/jwt" +) + +// tokenPayload is the structure we expect from a incoming login request +type tokenPayload struct { + Type string `json:"type"` + Identity string `json:"identity"` + Secret string `json:"secret"` +} + +// NewToken Also known as a Login, requesting a new token with credentials +// Route: POST /tokens +func NewToken() func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + // Read the bytes from the body + bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte) + + var payload tokenPayload + err := json.Unmarshal(bodyBytes, &payload) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil) + return + } + + // Find user + userObj, userErr := user.GetByEmail(payload.Identity) + if userErr != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, userErr.Error(), nil) + return + } + + // Get Auth + authObj, authErr := auth.GetByUserIDType(userObj.ID, payload.Type) + if userErr != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, authErr.Error(), nil) + return + } + + // Verify Auth + validateErr := authObj.ValidateSecret(payload.Secret) + if validateErr != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, validateErr.Error(), nil) + return + } + + if response, err := njwt.Generate(&userObj); err != nil { + h.ResultErrorJSON(w, r, http.StatusInternalServerError, err.Error(), nil) + } else { + h.ResultResponseJSON(w, r, http.StatusOK, response) + } + } +} + +// RefreshToken an existing token by given them a new one with the same claims +// Route: GET /tokens +func RefreshToken() func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + // TODO: Use your own methods to verify an existing user is + // able to refresh their token and then give them a new one + userObj, _ := user.GetByEmail("jc@jc21.com") + if response, err := njwt.Generate(&userObj); err != nil { + h.ResultErrorJSON(w, r, http.StatusInternalServerError, err.Error(), nil) + } else { + h.ResultResponseJSON(w, r, http.StatusOK, response) + } + } +} diff --git a/backend/internal/api/handler/users.go b/backend/internal/api/handler/users.go new file mode 100644 index 00000000..d2988966 --- /dev/null +++ b/backend/internal/api/handler/users.go @@ -0,0 +1,206 @@ +package handler + +import ( + "encoding/json" + "net/http" + + c "npm/internal/api/context" + h "npm/internal/api/http" + "npm/internal/api/middleware" + "npm/internal/config" + "npm/internal/entity/auth" + "npm/internal/entity/user" + "npm/internal/errors" + "npm/internal/logger" + + "github.com/go-chi/chi" +) + +// GetUsers ... +// Route: GET /users +func GetUsers() func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + pageInfo, err := getPageInfoFromRequest(r) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + return + } + + users, err := user.List(pageInfo, middleware.GetFiltersFromContext(r)) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + } else { + h.ResultResponseJSON(w, r, http.StatusOK, users) + } + } +} + +// GetUser ... +// Route: GET /users/{userID} +func GetUser() func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + + userID, _, userIDErr := getUserIDFromRequest(r) + if userIDErr != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, userIDErr.Error(), nil) + return + } + + user, err := user.GetByID(userID) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + } else { + h.ResultResponseJSON(w, r, http.StatusOK, user) + } + } +} + +// UpdateUser ... +// Route: PUT /users/{userID} +func UpdateUser() func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + userID, self, userIDErr := getUserIDFromRequest(r) + if userIDErr != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, userIDErr.Error(), nil) + return + } + + user, err := user.GetByID(userID) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + } else { + + bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte) + err := json.Unmarshal(bodyBytes, &user) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil) + return + } + + if user.IsDisabled && self { + h.ResultErrorJSON(w, r, http.StatusBadRequest, "You cannot disable yourself!", nil) + return + } + + if err = user.Save(); err != nil { + if err == errors.ErrDuplicateEmailUser { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + } else { + h.ResultErrorJSON(w, r, http.StatusInternalServerError, "Unable to save User", nil) + } + return + } + + h.ResultResponseJSON(w, r, http.StatusOK, user) + } + } +} + +// DeleteUser ... +// Route: DELETE /users/{userID} +func DeleteUser() func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + var userID int + var err error + if userID, err = getURLParamInt(r, "userID"); err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + return + } + + myUserID, _ := r.Context().Value(c.UserIDCtxKey).(int) + if myUserID == userID { + h.ResultErrorJSON(w, r, http.StatusBadRequest, "You cannot delete yourself!", nil) + return + } + + user, err := user.GetByID(userID) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + } else { + h.ResultResponseJSON(w, r, http.StatusOK, user.Delete()) + } + } +} + +// CreateUser ... +// Route: POST /users +func CreateUser() func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte) + + var newUser user.Model + err := json.Unmarshal(bodyBytes, &newUser) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil) + return + } + + if err = newUser.Save(); err != nil { + if err == errors.ErrDuplicateEmailUser { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + } else { + h.ResultErrorJSON(w, r, http.StatusInternalServerError, "Unable to save User", nil) + } + return + } + + // newUser has been saved, now save their auth + if newUser.Auth.Secret != "" && newUser.Auth.ID == 0 { + newUser.Auth.UserID = newUser.ID + if newUser.Auth.Type == auth.TypePassword { + err = newUser.Auth.SetPassword(newUser.Auth.Secret) + if err != nil { + logger.Error("SetPasswordError", err) + } + } + + if err = newUser.Auth.Save(); err != nil { + h.ResultErrorJSON(w, r, http.StatusInternalServerError, "Unable to save Authentication for User", nil) + return + } + + newUser.Auth.Secret = "" + } + + if !config.IsSetup { + config.IsSetup = true + logger.Info("A new user was created, leaving Setup Mode") + } + + h.ResultResponseJSON(w, r, http.StatusOK, newUser) + } +} + +// DeleteUsers is only available in debug mode for cypress tests +// Route: DELETE /users +func DeleteUsers() func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + err := user.DeleteAll() + if err != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil) + } else { + // also change setup to true + config.IsSetup = false + logger.Info("Users have been wiped, entering Setup Mode") + h.ResultResponseJSON(w, r, http.StatusOK, true) + } + } +} + +func getUserIDFromRequest(r *http.Request) (int, bool, error) { + userIDstr := chi.URLParam(r, "userID") + + var userID int + self := false + if userIDstr == "me" { + // Get user id from Token + userID, _ = r.Context().Value(c.UserIDCtxKey).(int) + self = true + } else { + var userIDerr error + if userID, userIDerr = getURLParamInt(r, "userID"); userIDerr != nil { + return 0, false, userIDerr + } + } + return userID, self, nil +} diff --git a/backend/internal/api/http/requests.go b/backend/internal/api/http/requests.go new file mode 100644 index 00000000..c07d3fa6 --- /dev/null +++ b/backend/internal/api/http/requests.go @@ -0,0 +1,46 @@ +package http + +import ( + "context" + "encoding/json" + "errors" + + "github.com/qri-io/jsonschema" +) + +var ( + // ErrInvalidJSON ... + ErrInvalidJSON = errors.New("JSON is invalid") + // ErrInvalidPayload ... + ErrInvalidPayload = errors.New("Payload is invalid") +) + +// ValidateRequestSchema takes a Schema and the Content to validate against it +func ValidateRequestSchema(schema string, requestBody []byte) ([]jsonschema.KeyError, error) { + var jsonErrors []jsonschema.KeyError + var schemaBytes = []byte(schema) + + // Make sure the body is valid JSON + if !isJSON(requestBody) { + return jsonErrors, ErrInvalidJSON + } + + rs := &jsonschema.Schema{} + if err := json.Unmarshal(schemaBytes, rs); err != nil { + return jsonErrors, err + } + + var validationErr error + ctx := context.TODO() + if jsonErrors, validationErr = rs.ValidateBytes(ctx, requestBody); len(jsonErrors) > 0 { + return jsonErrors, validationErr + } + + // Valid + return nil, nil +} + +func isJSON(bytes []byte) bool { + var js map[string]interface{} + return json.Unmarshal(bytes, &js) == nil +} diff --git a/backend/internal/api/http/responses.go b/backend/internal/api/http/responses.go new file mode 100644 index 00000000..fa2d9767 --- /dev/null +++ b/backend/internal/api/http/responses.go @@ -0,0 +1,90 @@ +package http + +import ( + "encoding/json" + "fmt" + "net/http" + "reflect" + + c "npm/internal/api/context" + "npm/internal/logger" + + "github.com/qri-io/jsonschema" +) + +// Response interface for standard API results +type Response struct { + Result interface{} `json:"result"` + Error interface{} `json:"error,omitempty"` +} + +// ErrorResponse interface for errors returned via the API +type ErrorResponse struct { + Code interface{} `json:"code"` + Message interface{} `json:"message"` + Invalid interface{} `json:"invalid,omitempty"` +} + +// ResultResponseJSON will write the result as json to the http output +func ResultResponseJSON(w http.ResponseWriter, r *http.Request, status int, result interface{}) { + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(status) + + var response Response + resultClass := fmt.Sprintf("%v", reflect.TypeOf(result)) + + if resultClass == "http.ErrorResponse" { + response = Response{ + Error: result, + } + } else { + response = Response{ + Result: result, + } + } + + var payload []byte + var err error + if getPrettyPrintFromContext(r) { + payload, err = json.MarshalIndent(response, "", " ") + } else { + payload, err = json.Marshal(response) + } + + if err != nil { + logger.Error("ResponseMarshalError", err) + } + + fmt.Fprint(w, string(payload)) +} + +// ResultSchemaErrorJSON will format the result as a standard error object and send it for output +func ResultSchemaErrorJSON(w http.ResponseWriter, r *http.Request, errors []jsonschema.KeyError) { + errorResponse := ErrorResponse{ + Code: http.StatusBadRequest, + Message: "Request failed validation", + Invalid: errors, + } + + ResultResponseJSON(w, r, http.StatusBadRequest, errorResponse) +} + +// ResultErrorJSON will format the result as a standard error object and send it for output +func ResultErrorJSON(w http.ResponseWriter, r *http.Request, status int, message string, extended interface{}) { + errorResponse := ErrorResponse{ + Code: status, + Message: message, + Invalid: extended, + } + + ResultResponseJSON(w, r, status, errorResponse) +} + +// getPrettyPrintFromContext returns the PrettyPrint setting +func getPrettyPrintFromContext(r *http.Request) bool { + pretty, ok := r.Context().Value(c.PrettyPrintCtxKey).(bool) + if !ok { + return false + } + return pretty +} diff --git a/backend/internal/api/middleware/access_control.go b/backend/internal/api/middleware/access_control.go new file mode 100644 index 00000000..18bca31b --- /dev/null +++ b/backend/internal/api/middleware/access_control.go @@ -0,0 +1,13 @@ +package middleware + +import ( + "net/http" +) + +// AccessControl sets http headers for responses +func AccessControl(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Access-Control-Allow-Origin", "*") + next.ServeHTTP(w, r) + }) +} diff --git a/backend/internal/api/middleware/auth.go b/backend/internal/api/middleware/auth.go new file mode 100644 index 00000000..925c7992 --- /dev/null +++ b/backend/internal/api/middleware/auth.go @@ -0,0 +1,64 @@ +package middleware + +import ( + "context" + "net/http" + + c "npm/internal/api/context" + h "npm/internal/api/http" + "npm/internal/config" + "npm/internal/entity/user" + njwt "npm/internal/jwt" + "npm/internal/logger" + + "github.com/go-chi/jwtauth" +) + +// DecodeAuth ... +func DecodeAuth() func(http.Handler) http.Handler { + privateKey, privateKeyParseErr := njwt.GetPrivateKey() + if privateKeyParseErr != nil && privateKey == nil { + logger.Error("PrivateKeyParseError", privateKeyParseErr) + } + + publicKey, publicKeyParseErr := njwt.GetPublicKey() + if publicKeyParseErr != nil && publicKey == nil { + logger.Error("PublicKeyParseError", publicKeyParseErr) + } + + tokenAuth := jwtauth.New("RS256", privateKey, publicKey) + return jwtauth.Verifier(tokenAuth) +} + +// Enforce is a authentication middleware to enforce access from the +// jwtauth.Verifier middleware request context values. The Authenticator sends a 401 Unauthorised +// response for any unverified tokens and passes the good ones through. +func Enforce() func(http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + if config.IsSetup { + token, claims, err := jwtauth.FromContext(ctx) + + if err != nil { + h.ResultErrorJSON(w, r, http.StatusUnauthorized, err.Error(), nil) + return + } + + userID := int(claims["uid"].(float64)) + _, enabled := user.IsEnabled(userID) + if token == nil || !token.Valid || !enabled { + h.ResultErrorJSON(w, r, http.StatusUnauthorized, "Unauthorised", nil) + return + } + + // Add claims to context + ctx = context.WithValue(ctx, c.UserIDCtxKey, userID) + } + + // Token is authenticated, continue as normal + next.ServeHTTP(w, r.WithContext(ctx)) + }) + } +} diff --git a/backend/internal/api/middleware/body_context.go b/backend/internal/api/middleware/body_context.go new file mode 100644 index 00000000..68cfaa08 --- /dev/null +++ b/backend/internal/api/middleware/body_context.go @@ -0,0 +1,26 @@ +package middleware + +import ( + "context" + "io/ioutil" + "net/http" + + c "npm/internal/api/context" +) + +// BodyContext simply adds the body data to a context item +func BodyContext() func(http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // Grab the Body Data + var body []byte + if r.Body != nil { + body, _ = ioutil.ReadAll(r.Body) + } + // Add it to the context + ctx := r.Context() + ctx = context.WithValue(ctx, c.BodyCtxKey, body) + next.ServeHTTP(w, r.WithContext(ctx)) + }) + } +} diff --git a/backend/internal/api/middleware/cors.go b/backend/internal/api/middleware/cors.go new file mode 100644 index 00000000..a3b2fbb9 --- /dev/null +++ b/backend/internal/api/middleware/cors.go @@ -0,0 +1,88 @@ +package middleware + +import ( + "fmt" + "net/http" + "strings" + + "github.com/go-chi/chi" +) + +var methodMap = []string{ + http.MethodGet, + http.MethodHead, + http.MethodPost, + http.MethodPut, + http.MethodPatch, + http.MethodDelete, + http.MethodConnect, + http.MethodTrace, +} + +func getRouteMethods(routes chi.Router, path string) []string { + var methods []string + tctx := chi.NewRouteContext() + for _, method := range methodMap { + if routes.Match(tctx, method, path) { + methods = append(methods, method) + } + } + return methods +} + +var headersAllowedByCORS = []string{ + "Authorization", + "Host", + "Content-Type", + "Connection", + "User-Agent", + "Cache-Control", + "Accept-Encoding", + "X-Jumbo-AppKey", + "X-Jumbo-SKey", + "X-Jumbo-SV", + "X-Jumbo-Timestamp", + "X-Jumbo-Version", + "X-Jumbo-Customer-Id", +} + +// Cors ... +func Cors(routes chi.Router) func(http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + methods := getRouteMethods(routes, r.URL.Path) + if len(methods) == 0 { + // no route no cors + next.ServeHTTP(w, r) + return + } + methods = append(methods, http.MethodOptions) + w.Header().Set("Access-Control-Allow-Methods", strings.Join(methods, ",")) + w.Header().Set("Access-Control-Allow-Headers", + strings.Join(headersAllowedByCORS, ","), + ) + next.ServeHTTP(w, r) + }) + } +} + +// Options ... +func Options(routes chi.Router) func(http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + methods := getRouteMethods(routes, r.URL.Path) + if len(methods) == 0 { + // no route shouldn't have options + next.ServeHTTP(w, r) + return + } + if r.Method == http.MethodOptions { + w.Header().Set("Access-Control-Allow-Origin", "*") + w.Header().Set("Content-Type", "application/json") + fmt.Fprint(w, "{}") + return + } + next.ServeHTTP(w, r) + }) + } +} diff --git a/backend/internal/api/middleware/enforce_setup.go b/backend/internal/api/middleware/enforce_setup.go new file mode 100644 index 00000000..3c75ccd9 --- /dev/null +++ b/backend/internal/api/middleware/enforce_setup.go @@ -0,0 +1,28 @@ +package middleware + +import ( + "fmt" + "net/http" + + h "npm/internal/api/http" + "npm/internal/config" +) + +// EnforceSetup will error if the config setup doesn't match what is required +func EnforceSetup(shouldBeSetup bool) func(http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if config.IsSetup != shouldBeSetup { + state := "during" + if config.IsSetup { + state = "after" + } + h.ResultErrorJSON(w, r, http.StatusForbidden, fmt.Sprintf("Not available %s setup phase", state), nil) + return + } + + // All good + next.ServeHTTP(w, r) + }) + } +} diff --git a/backend/internal/api/middleware/filters.go b/backend/internal/api/middleware/filters.go new file mode 100644 index 00000000..8a3f84cf --- /dev/null +++ b/backend/internal/api/middleware/filters.go @@ -0,0 +1,114 @@ +package middleware + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + + c "npm/internal/api/context" + h "npm/internal/api/http" + "npm/internal/model" + "npm/internal/util" + "strings" + + "github.com/qri-io/jsonschema" +) + +// Filters will accept a pre-defined schemaData to validate against the GET query params +// passed in to this endpoint. This will ensure that the filters are not injecting SQL. +// After we have determined what the Filters are to be, they are saved on the Context +// to be used later in other endpoints. +func Filters(schemaData string) func(http.Handler) http.Handler { + reservedFilterKeys := []string{ + "limit", + "offset", + "sort", + "order", + "t", // This is used as a timestamp paramater in some clients and can be ignored + } + + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var filters []model.Filter + for key, val := range r.URL.Query() { + key = strings.ToLower(key) + + // Split out the modifier from the field name and set a default modifier + var keyParts []string + keyParts = strings.Split(key, ":") + if len(keyParts) == 1 { + // Default modifier + keyParts = append(keyParts, "equals") + } + + // Only use this filter if it's not a reserved get param + if !util.SliceContainsItem(reservedFilterKeys, keyParts[0]) { + for _, valItem := range val { + // Check that the val isn't empty + if len(strings.TrimSpace(valItem)) > 0 { + valSlice := []string{valItem} + if keyParts[1] == "in" || keyParts[1] == "notin" { + valSlice = strings.Split(valItem, ",") + } + + filters = append(filters, model.Filter{ + Field: keyParts[0], + Modifier: keyParts[1], + Value: valSlice, + }) + } + } + } + } + + // Only validate schema if there are filters to validate + if len(filters) > 0 { + ctx := r.Context() + + // Marshal the Filters in to a JSON string so that the Schema Validation works against it + filterData, marshalErr := json.MarshalIndent(filters, "", " ") + if marshalErr != nil { + h.ResultErrorJSON(w, r, http.StatusInternalServerError, fmt.Sprintf("Schema Fatal: %v", marshalErr), nil) + return + } + + // Create root schema + rs := &jsonschema.Schema{} + if err := json.Unmarshal([]byte(schemaData), rs); err != nil { + h.ResultErrorJSON(w, r, http.StatusInternalServerError, fmt.Sprintf("Schema Fatal: %v", err), nil) + return + } + + // Validate it + errors, jsonError := rs.ValidateBytes(ctx, filterData) + if jsonError != nil { + h.ResultErrorJSON(w, r, http.StatusBadRequest, jsonError.Error(), nil) + return + } + + if len(errors) > 0 { + h.ResultErrorJSON(w, r, http.StatusBadRequest, "Invalid Filters", errors) + return + } + + ctx = context.WithValue(ctx, c.FiltersCtxKey, filters) + next.ServeHTTP(w, r.WithContext(ctx)) + + } else { + next.ServeHTTP(w, r) + } + }) + } +} + +// GetFiltersFromContext returns the Filters +func GetFiltersFromContext(r *http.Request) []model.Filter { + filters, ok := r.Context().Value(c.FiltersCtxKey).([]model.Filter) + if !ok { + // the assertion failed + var emptyFilters []model.Filter + return emptyFilters + } + return filters +} diff --git a/backend/internal/api/middleware/pretty_print.go b/backend/internal/api/middleware/pretty_print.go new file mode 100644 index 00000000..270d2a24 --- /dev/null +++ b/backend/internal/api/middleware/pretty_print.go @@ -0,0 +1,23 @@ +package middleware + +import ( + "context" + "net/http" + + c "npm/internal/api/context" +) + +// PrettyPrint will determine whether the request should be pretty printed in output +// with ?pretty=1 or ?pretty=true +func PrettyPrint(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + prettyStr := r.URL.Query().Get("pretty") + if prettyStr == "1" || prettyStr == "true" { + ctx := r.Context() + ctx = context.WithValue(ctx, c.PrettyPrintCtxKey, true) + next.ServeHTTP(w, r.WithContext(ctx)) + } else { + next.ServeHTTP(w, r) + } + }) +} diff --git a/backend/internal/api/middleware/schema.go b/backend/internal/api/middleware/schema.go new file mode 100644 index 00000000..d3dba570 --- /dev/null +++ b/backend/internal/api/middleware/schema.go @@ -0,0 +1,55 @@ +package middleware + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + + c "npm/internal/api/context" + h "npm/internal/api/http" + + "github.com/qri-io/jsonschema" +) + +// CheckRequestSchema ... +func CheckRequestSchema(ctx context.Context, schemaData string, payload []byte) ([]jsonschema.KeyError, error) { + // Create root schema + rs := &jsonschema.Schema{} + if err := json.Unmarshal([]byte(schemaData), rs); err != nil { + return nil, fmt.Errorf("Schema Fatal: %v", err) + } + + // Validate it + schemaErrors, jsonError := rs.ValidateBytes(ctx, payload) + if jsonError != nil { + return nil, jsonError + } + + return schemaErrors, nil +} + +// EnforceRequestSchema accepts a schema and validates the request body against it +func EnforceRequestSchema(schemaData string) func(http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + + // Get content from context + bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte) + + schemaErrors, err := CheckRequestSchema(r.Context(), schemaData, bodyBytes) + if err != nil { + h.ResultErrorJSON(w, r, http.StatusInternalServerError, fmt.Sprintf("Schema Fatal: %v", err), nil) + return + } + + if len(schemaErrors) > 0 { + h.ResultSchemaErrorJSON(w, r, schemaErrors) + return + } + + // All good + next.ServeHTTP(w, r) + }) + } +} diff --git a/backend/internal/api/router.go b/backend/internal/api/router.go new file mode 100644 index 00000000..944f993b --- /dev/null +++ b/backend/internal/api/router.go @@ -0,0 +1,171 @@ +package api + +import ( + "net/http" + "time" + + "npm/internal/api/handler" + "npm/internal/api/middleware" + "npm/internal/api/schema" + "npm/internal/config" + "npm/internal/entity/certificate" + "npm/internal/entity/certificateauthority" + "npm/internal/entity/dnsprovider" + "npm/internal/entity/host" + "npm/internal/entity/setting" + "npm/internal/entity/stream" + "npm/internal/entity/user" + "npm/internal/logger" + + "github.com/go-chi/chi" + chiMiddleware "github.com/go-chi/chi/middleware" + "github.com/go-chi/cors" +) + +// NewRouter returns a new router object +func NewRouter() http.Handler { + // Cors + cors := cors.New(cors.Options{ + AllowedOrigins: []string{"*"}, + AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}, + AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-Requested-With"}, + AllowCredentials: true, + MaxAge: 300, + }) + + r := chi.NewRouter() + r.Use( + middleware.AccessControl, + middleware.Cors(r), + middleware.Options(r), + cors.Handler, + chiMiddleware.RealIP, + chiMiddleware.Recoverer, + chiMiddleware.Throttle(5), + chiMiddleware.Timeout(30*time.Second), + middleware.PrettyPrint, + middleware.DecodeAuth(), + middleware.BodyContext(), + ) + + return applyRoutes(r) +} + +// applyRoutes is where the magic happens +func applyRoutes(r chi.Router) chi.Router { + r.NotFound(handler.NotFound()) + r.MethodNotAllowed(handler.NotAllowed()) + + // API + r.Route("/api", func(r chi.Router) { + r.Get("/", handler.Health()) + r.Get("/schema", handler.Schema()) + r.With(middleware.EnforceSetup(true), middleware.Enforce()). + Get("/config", handler.Config()) + + // Tokens + r.With(middleware.EnforceSetup(true)).Route("/tokens", func(r chi.Router) { + r.With(middleware.EnforceRequestSchema(schema.GetToken())). + Post("/", handler.NewToken()) + r.With(middleware.Enforce()). + Get("/", handler.RefreshToken()) + }) + + // Users + r.With(middleware.Enforce()).Route("/users", func(r chi.Router) { + r.With(middleware.EnforceSetup(true)).Get("/{userID:(?:[0-9]+|me)}", handler.GetUser()) + r.With(middleware.EnforceSetup(true)).Delete("/{userID:(?:[0-9]+|me)}", handler.DeleteUser()) + r.With(middleware.EnforceSetup(true)).With(middleware.Filters(user.GetFilterSchema())). + Get("/", handler.GetUsers()) + r.With(middleware.EnforceRequestSchema(schema.CreateUser())). + Post("/", handler.CreateUser()) + r.With(middleware.EnforceSetup(true)).With(middleware.EnforceRequestSchema(schema.UpdateUser())). + Put("/{userID:(?:[0-9]+|me)}", handler.UpdateUser()) + + // Auth + r.With(middleware.EnforceSetup(true)).With(middleware.EnforceRequestSchema(schema.SetAuth())). + Post("/{userID:(?:[0-9]+|me)}/auth", handler.SetAuth()) + }) + + // Only available in debug mode: delete users without auth + if config.GetLogLevel() == logger.DebugLevel { + r.Delete("/users", handler.DeleteUsers()) + } + + // Settings + r.With(middleware.EnforceSetup(true), middleware.Enforce()).Route("/settings", func(r chi.Router) { + r.With(middleware.Filters(setting.GetFilterSchema())). + Get("/", handler.GetSettings()) + r.Get("/{name}", handler.GetSetting()) + r.With(middleware.EnforceRequestSchema(schema.CreateSetting())). + Post("/", handler.CreateSetting()) + r.With(middleware.EnforceRequestSchema(schema.UpdateSetting())). + Put("/{name}", handler.UpdateSetting()) + }) + + // DNS Providers + r.With(middleware.EnforceSetup(true), middleware.Enforce()).Route("/dns-providers", func(r chi.Router) { + r.With(middleware.Filters(dnsprovider.GetFilterSchema())). + Get("/", handler.GetDNSProviders()) + r.Get("/{providerID:[0-9]+}", handler.GetDNSProvider()) + r.Delete("/{providerID:[0-9]+}", handler.DeleteDNSProvider()) + r.With(middleware.EnforceRequestSchema(schema.CreateDNSProvider())). + Post("/", handler.CreateDNSProvider()) + r.With(middleware.EnforceRequestSchema(schema.UpdateDNSProvider())). + Put("/{providerID:[0-9]+}", handler.UpdateDNSProvider()) + }) + + // Certificate Authorities + r.With(middleware.EnforceSetup(true), middleware.Enforce()).Route("/certificate-authorities", func(r chi.Router) { + r.With(middleware.Filters(certificateauthority.GetFilterSchema())). + Get("/", handler.GetCertificateAuthorities()) + r.Get("/{caID:[0-9]+}", handler.GetCertificateAuthority()) + r.Delete("/{caID:[0-9]+}", handler.DeleteCertificateAuthority()) + r.With(middleware.EnforceRequestSchema(schema.CreateCertificateAuthority())). + Post("/", handler.CreateCertificateAuthority()) + r.With(middleware.EnforceRequestSchema(schema.UpdateCertificateAuthority())). + Put("/{caID:[0-9]+}", handler.UpdateCertificateAuthority()) + }) + + // Certificates + r.With(middleware.EnforceSetup(true), middleware.Enforce()).Route("/certificates", func(r chi.Router) { + r.With(middleware.Filters(certificate.GetFilterSchema())). + Get("/", handler.GetCertificates()) + r.Get("/{certificateID:[0-9]+}", handler.GetCertificate()) + r.Delete("/{certificateID:[0-9]+}", handler.DeleteCertificate()) + r.With(middleware.EnforceRequestSchema(schema.CreateCertificate())). + Post("/", handler.CreateCertificate()) + /* + r.With(middleware.EnforceRequestSchema(schema.UpdateCertificate())). + Put("/{certificateID:[0-9]+}", handler.UpdateCertificate()) + */ + r.Put("/{certificateID:[0-9]+}", handler.UpdateCertificate()) + }) + + // Hosts + r.With(middleware.EnforceSetup(true), middleware.Enforce()).Route("/hosts", func(r chi.Router) { + r.With(middleware.Filters(host.GetFilterSchema())). + Get("/", handler.GetHosts()) + r.Get("/{hostID:[0-9]+}", handler.GetHost()) + r.Delete("/{hostID:[0-9]+}", handler.DeleteHost()) + r.With(middleware.EnforceRequestSchema(schema.CreateHost())). + Post("/", handler.CreateHost()) + r.With(middleware.EnforceRequestSchema(schema.UpdateHost())). + Put("/{hostID:[0-9]+}", handler.UpdateHost()) + }) + + // Streams + r.With(middleware.EnforceSetup(true), middleware.Enforce()).Route("/streams", func(r chi.Router) { + r.With(middleware.Filters(stream.GetFilterSchema())). + Get("/", handler.GetStreams()) + r.Get("/{hostID:[0-9]+}", handler.GetStream()) + r.Delete("/{hostID:[0-9]+}", handler.DeleteStream()) + r.With(middleware.EnforceRequestSchema(schema.CreateStream())). + Post("/", handler.CreateStream()) + r.With(middleware.EnforceRequestSchema(schema.UpdateStream())). + Put("/{hostID:[0-9]+}", handler.UpdateStream()) + }) + }) + + return r +} diff --git a/backend/internal/api/router_test.go b/backend/internal/api/router_test.go new file mode 100644 index 00000000..78ec784f --- /dev/null +++ b/backend/internal/api/router_test.go @@ -0,0 +1,44 @@ +package api + +import ( + "net/http" + "net/http/httptest" + "os" + "testing" + + "npm/internal/config" + + "github.com/stretchr/testify/assert" +) + +var ( + r = NewRouter() + version = "3.0.0" + commit = "abcdefgh" + sentryDSN = "" +) + +// Tear up/down +func TestMain(m *testing.M) { + config.Init(&version, &commit, &sentryDSN) + code := m.Run() + os.Exit(code) +} + +func TestGetHealthz(t *testing.T) { + respRec := httptest.NewRecorder() + req, _ := http.NewRequest("GET", "/api/", nil) + + r.ServeHTTP(respRec, req) + assert.Equal(t, http.StatusOK, respRec.Code) + assert.Contains(t, respRec.Body.String(), "healthy") +} + +func TestNonExistent(t *testing.T) { + respRec := httptest.NewRecorder() + req, _ := http.NewRequest("GET", "/non-existent-endpoint", nil) + + r.ServeHTTP(respRec, req) + assert.Equal(t, http.StatusNotFound, respRec.Code) + assert.Equal(t, respRec.Body.String(), `{"result":null,"error":{"code":404,"message":"Not found"}}`, "404 Message should match") +} diff --git a/backend/internal/api/schema/certificates.go b/backend/internal/api/schema/certificates.go new file mode 100644 index 00000000..5014b636 --- /dev/null +++ b/backend/internal/api/schema/certificates.go @@ -0,0 +1,191 @@ +package schema + +import ( + "fmt" + + "npm/internal/entity/certificate" +) + +// This validation is strictly for Custom certificates +// and the combination of values that must be defined +func createCertificateCustom() string { + return fmt.Sprintf(` + { + "type": "object", + "required": [ + "type", + "name", + "domain_names" + ], + "properties": { + "type": %s, + "name": %s, + "domain_names": %s, + "meta": { + "type": "object" + } + } + }`, strictString("custom"), stringMinMax(1, 100), domainNames()) +} + +// This validation is strictly for HTTP certificates +// and the combination of values that must be defined +func createCertificateHTTP() string { + return fmt.Sprintf(` + { + "type": "object", + "required": [ + "type", + "certificate_authority_id", + "name", + "domain_names" + ], + "properties": { + "type": %s, + "certificate_authority_id": %s, + "name": %s, + "domain_names": %s, + "meta": { + "type": "object" + } + } + }`, strictString("http"), intMinOne, stringMinMax(1, 100), domainNames()) +} + +// This validation is strictly for DNS certificates +// and the combination of values that must be defined +func createCertificateDNS() string { + return fmt.Sprintf(` + { + "type": "object", + "required": [ + "type", + "certificate_authority_id", + "dns_provider_id", + "name", + "domain_names" + ], + "properties": { + "type": %s, + "certificate_authority_id": %s, + "dns_provider_id": %s, + "name": %s, + "domain_names": %s, + "meta": { + "type": "object" + } + } + }`, strictString("dns"), intMinOne, intMinOne, stringMinMax(1, 100), domainNames()) +} + +// This validation is strictly for MKCERT certificates +// and the combination of values that must be defined +func createCertificateMkcert() string { + return fmt.Sprintf(` + { + "type": "object", + "required": [ + "type", + "name", + "domain_names" + ], + "properties": { + "type": %s, + "name": %s, + "domain_names": %s, + "meta": { + "type": "object" + } + } + }`, strictString("mkcert"), stringMinMax(1, 100), domainNames()) +} + +func updateCertificateHTTP() string { + return fmt.Sprintf(` + { + "type": "object", + "minProperties": 1, + "properties": { + "certificate_authority_id": %s, + "name": %s, + "domain_names": %s, + "meta": { + "type": "object" + } + } + }`, intMinOne, stringMinMax(1, 100), domainNames()) +} + +func updateCertificateDNS() string { + return fmt.Sprintf(` + { + "type": "object", + "minProperties": 1, + "properties": { + "certificate_authority_id": %s, + "dns_provider_id": %s, + "name": %s, + "domain_names": %s, + "meta": { + "type": "object" + } + } + }`, intMinOne, intMinOne, stringMinMax(1, 100), domainNames()) +} + +func updateCertificateCustom() string { + return fmt.Sprintf(` + { + "type": "object", + "minProperties": 1, + "properties": { + "name": %s, + "domain_names": %s, + "meta": { + "type": "object" + } + } + }`, stringMinMax(1, 100), domainNames()) +} + +func updateCertificateMkcert() string { + return fmt.Sprintf(` + { + "type": "object", + "minProperties": 1, + "properties": { + "name": %s, + "domain_names": %s, + "meta": { + "type": "object" + } + } + }`, stringMinMax(1, 100), domainNames()) +} + +// CreateCertificate is the schema for incoming data validation +func CreateCertificate() string { + return fmt.Sprintf(` + { + "oneOf": [%s, %s, %s, %s] + }`, createCertificateHTTP(), createCertificateDNS(), createCertificateCustom(), createCertificateMkcert()) +} + +// UpdateCertificate is the schema for incoming data validation +func UpdateCertificate(certificateType string) string { + switch certificateType { + case certificate.TypeHTTP: + return updateCertificateHTTP() + case certificate.TypeDNS: + return updateCertificateDNS() + case certificate.TypeCustom: + return updateCertificateCustom() + case certificate.TypeMkcert: + return updateCertificateMkcert() + default: + return fmt.Sprintf(` + { + "oneOf": [%s, %s, %s, %s] + }`, updateCertificateHTTP(), updateCertificateDNS(), updateCertificateCustom(), updateCertificateMkcert()) + } +} diff --git a/backend/internal/api/schema/common.go b/backend/internal/api/schema/common.go new file mode 100644 index 00000000..dda1b9be --- /dev/null +++ b/backend/internal/api/schema/common.go @@ -0,0 +1,61 @@ +package schema + +import "fmt" + +func strictString(value string) string { + return fmt.Sprintf(`{ + "type": "string", + "pattern": "^%s$" + }`, value) +} + +const intMinOne = ` +{ + "type": "integer", + "minimum": 1 +} +` + +func stringMinMax(minLength, maxLength int) string { + return fmt.Sprintf(`{ + "type": "string", + "minLength": %d, + "maxLength": %d + }`, minLength, maxLength) +} + +func userRoles() string { + return fmt.Sprintf(` + { + "type": "array", + "items": %s + }`, stringMinMax(2, 50)) +} + +func domainNames() string { + return fmt.Sprintf(` + { + "type": "array", + "minItems": 1, + "items": %s + }`, stringMinMax(4, 255)) +} + +const anyType = ` +{ + "anyOf": [ + { + "type": "array" + }, + { + "type": "boolean" + }, + { + "type": "object" + }, + { + "type": "integer" + } + ] +} +` diff --git a/backend/internal/api/schema/create_certificate_authority.go b/backend/internal/api/schema/create_certificate_authority.go new file mode 100644 index 00000000..a4b69bb3 --- /dev/null +++ b/backend/internal/api/schema/create_certificate_authority.go @@ -0,0 +1,21 @@ +package schema + +import "fmt" + +// CreateCertificateAuthority is the schema for incoming data validation +func CreateCertificateAuthority() string { + return fmt.Sprintf(` + { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "acme2_url" + ], + "properties": { + "name": %s, + "acme2_url": %s + } + } + `, stringMinMax(1, 100), stringMinMax(8, 255)) +} diff --git a/backend/internal/api/schema/create_dns_provider.go b/backend/internal/api/schema/create_dns_provider.go new file mode 100644 index 00000000..6da73e72 --- /dev/null +++ b/backend/internal/api/schema/create_dns_provider.go @@ -0,0 +1,25 @@ +package schema + +import "fmt" + +// CreateDNSProvider is the schema for incoming data validation +func CreateDNSProvider() string { + return fmt.Sprintf(` + { + "type": "object", + "additionalProperties": false, + "required": [ + "provider_key", + "name", + "meta" + ], + "properties": { + "provider_key": %s, + "name": %s, + "meta": { + "type": "object" + } + } + } + `, stringMinMax(2, 100), stringMinMax(1, 100)) +} diff --git a/backend/internal/api/schema/create_host.go b/backend/internal/api/schema/create_host.go new file mode 100644 index 00000000..c87d393b --- /dev/null +++ b/backend/internal/api/schema/create_host.go @@ -0,0 +1,75 @@ +package schema + +import "fmt" + +// CreateHost is the schema for incoming data validation +// This schema supports 3 possible types with different data combinations: +// - proxy +// - redirection +// - dead +func CreateHost() string { + return fmt.Sprintf(` + { + "oneOf": [ + { + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "domain_names" + ], + "properties": { + "type": { + "type": "string", + "pattern": "^proxy$" + }, + "listen_interface": %s, + "domain_names": %s, + "upstream_id": { + "type": "integer" + }, + "certificate_id": { + "type": "integer" + }, + "access_list_id": { + "type": "integer" + }, + "ssl_forced": { + "type": "boolean" + }, + "caching_enabled": { + "type": "boolean" + }, + "block_exploits": { + "type": "boolean" + }, + "allow_websocket_upgrade": { + "type": "boolean" + }, + "http2_support": { + "type": "boolean" + }, + "hsts_enabled": { + "type": "boolean" + }, + "hsts_subdomains": { + "type": "boolean" + }, + "paths": { + "type": "string" + }, + "upstream_options": { + "type": "string" + }, + "advanced_config": { + "type": "string" + }, + "is_disabled": { + "type": "boolean" + } + } + } + ] + } + `, stringMinMax(0, 255), domainNames()) +} diff --git a/backend/internal/api/schema/create_setting.go b/backend/internal/api/schema/create_setting.go new file mode 100644 index 00000000..dca3869c --- /dev/null +++ b/backend/internal/api/schema/create_setting.go @@ -0,0 +1,21 @@ +package schema + +import "fmt" + +// CreateSetting is the schema for incoming data validation +func CreateSetting() string { + return fmt.Sprintf(` + { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "value" + ], + "properties": { + "name": %s, + "value": %s + } + } + `, stringMinMax(2, 100), anyType) +} diff --git a/backend/internal/api/schema/create_stream.go b/backend/internal/api/schema/create_stream.go new file mode 100644 index 00000000..792b8818 --- /dev/null +++ b/backend/internal/api/schema/create_stream.go @@ -0,0 +1,27 @@ +package schema + +import "fmt" + +// CreateStream is the schema for incoming data validation +func CreateStream() string { + return fmt.Sprintf(` + { + "type": "object", + "additionalProperties": false, + "required": [ + "provider", + "name", + "domain_names" + ], + "properties": { + "provider": %s, + "name": %s, + "domain_names": %s, + "expires_on": %s, + "meta": { + "type": "object" + } + } + } + `, stringMinMax(2, 100), stringMinMax(1, 100), domainNames(), intMinOne) +} diff --git a/backend/internal/api/schema/create_user.go b/backend/internal/api/schema/create_user.go new file mode 100644 index 00000000..b46a8621 --- /dev/null +++ b/backend/internal/api/schema/create_user.go @@ -0,0 +1,42 @@ +package schema + +import "fmt" + +// CreateUser is the schema for incoming data validation +func CreateUser() string { + return fmt.Sprintf(` + { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "email", + "roles", + "is_disabled" + ], + "properties": { + "name": %s, + "nickname": %s, + "email": %s, + "roles": %s, + "is_disabled": { + "type": "boolean" + }, + "auth": { + "type": "object", + "required": [ + "type", + "secret" + ], + "properties": { + "type": { + "type": "string", + "pattern": "^password$" + }, + "secret": %s + } + } + } + } + `, stringMinMax(2, 100), stringMinMax(2, 100), stringMinMax(5, 150), userRoles(), stringMinMax(8, 255)) +} diff --git a/backend/internal/api/schema/get_token.go b/backend/internal/api/schema/get_token.go new file mode 100644 index 00000000..fe1a9502 --- /dev/null +++ b/backend/internal/api/schema/get_token.go @@ -0,0 +1,28 @@ +package schema + +import "fmt" + +// GetToken is the schema for incoming data validation +// nolint: gosec +func GetToken() string { + stdField := stringMinMax(1, 255) + return fmt.Sprintf(` + { + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "identity", + "secret" + ], + "properties": { + "type": { + "type": "string", + "pattern": "^password$" + }, + "identity": %s, + "secret": %s + } + } + `, stdField, stdField) +} diff --git a/backend/internal/api/schema/set_auth.go b/backend/internal/api/schema/set_auth.go new file mode 100644 index 00000000..5e6ea975 --- /dev/null +++ b/backend/internal/api/schema/set_auth.go @@ -0,0 +1,21 @@ +package schema + +import "fmt" + +// SetAuth is the schema for incoming data validation +func SetAuth() string { + return fmt.Sprintf(` + { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "value" + ], + "properties": { + "name": %s, + "value": %s + } + } + `, stringMinMax(2, 100), anyType) +} diff --git a/backend/internal/api/schema/update_certificate_authority.go b/backend/internal/api/schema/update_certificate_authority.go new file mode 100644 index 00000000..9bdeadc5 --- /dev/null +++ b/backend/internal/api/schema/update_certificate_authority.go @@ -0,0 +1,17 @@ +package schema + +import "fmt" + +// UpdateCertificateAuthority is the schema for incoming data validation +func UpdateCertificateAuthority() string { + return fmt.Sprintf(` + { + "type": "object", + "additionalProperties": false, + "properties": { + "name": %s, + "acme2_url": %s + } + } + `, stringMinMax(1, 100), stringMinMax(8, 255)) +} diff --git a/backend/internal/api/schema/update_dns_provider.go b/backend/internal/api/schema/update_dns_provider.go new file mode 100644 index 00000000..10d77145 --- /dev/null +++ b/backend/internal/api/schema/update_dns_provider.go @@ -0,0 +1,19 @@ +package schema + +import "fmt" + +// UpdateDNSProvider is the schema for incoming data validation +func UpdateDNSProvider() string { + return fmt.Sprintf(` + { + "type": "object", + "additionalProperties": false, + "properties": { + "name": %s, + "meta": { + "type": "object" + } + } + } + `, stringMinMax(1, 100)) +} diff --git a/backend/internal/api/schema/update_host.go b/backend/internal/api/schema/update_host.go new file mode 100644 index 00000000..33e411c5 --- /dev/null +++ b/backend/internal/api/schema/update_host.go @@ -0,0 +1,22 @@ +package schema + +import "fmt" + +// UpdateHost is the schema for incoming data validation +func UpdateHost() string { + return fmt.Sprintf(` + { + "type": "object", + "additionalProperties": false, + "properties": { + "provider": %s, + "name": %s, + "domain_names": %s, + "expires_on": %s, + "meta": { + "type": "object" + } + } + } + `, stringMinMax(2, 100), stringMinMax(1, 100), domainNames(), intMinOne) +} diff --git a/backend/internal/api/schema/update_setting.go b/backend/internal/api/schema/update_setting.go new file mode 100644 index 00000000..98686935 --- /dev/null +++ b/backend/internal/api/schema/update_setting.go @@ -0,0 +1,16 @@ +package schema + +import "fmt" + +// UpdateSetting is the schema for incoming data validation +func UpdateSetting() string { + return fmt.Sprintf(` + { + "type": "object", + "additionalProperties": false, + "properties": { + "value": %s + } + } + `, anyType) +} diff --git a/backend/internal/api/schema/update_stream.go b/backend/internal/api/schema/update_stream.go new file mode 100644 index 00000000..8b2763a6 --- /dev/null +++ b/backend/internal/api/schema/update_stream.go @@ -0,0 +1,22 @@ +package schema + +import "fmt" + +// UpdateStream is the schema for incoming data validation +func UpdateStream() string { + return fmt.Sprintf(` + { + "type": "object", + "additionalProperties": false, + "properties": { + "provider": %s, + "name": %s, + "domain_names": %s, + "expires_on": %s, + "meta": { + "type": "object" + } + } + } + `, stringMinMax(2, 100), stringMinMax(1, 100), domainNames(), intMinOne) +} diff --git a/backend/internal/api/schema/update_user.go b/backend/internal/api/schema/update_user.go new file mode 100644 index 00000000..cf17bd7d --- /dev/null +++ b/backend/internal/api/schema/update_user.go @@ -0,0 +1,22 @@ +package schema + +import "fmt" + +// UpdateUser is the schema for incoming data validation +func UpdateUser() string { + return fmt.Sprintf(` + { + "type": "object", + "additionalProperties": false, + "properties": { + "name": %s, + "nickname": %s, + "email": %s, + "roles": %s, + "is_disabled": { + "type": "boolean" + } + } + } + `, stringMinMax(2, 100), stringMinMax(2, 100), stringMinMax(5, 150), userRoles()) +} diff --git a/backend/internal/api/server.go b/backend/internal/api/server.go new file mode 100644 index 00000000..c64de44a --- /dev/null +++ b/backend/internal/api/server.go @@ -0,0 +1,19 @@ +package api + +import ( + "fmt" + "net/http" + + "npm/internal/logger" +) + +const httpPort = 3000 + +// StartServer creates a http server +func StartServer() { + logger.Info("Server starting on port %v", httpPort) + err := http.ListenAndServe(fmt.Sprintf(":%v", httpPort), NewRouter()) + if err != nil { + logger.Error("HttpListenError", err) + } +} diff --git a/backend/internal/audit-log.js b/backend/internal/audit-log.js deleted file mode 100644 index 422b4f46..00000000 --- a/backend/internal/audit-log.js +++ /dev/null @@ -1,78 +0,0 @@ -const error = require('../lib/error'); -const auditLogModel = require('../models/audit-log'); - -const internalAuditLog = { - - /** - * All logs - * - * @param {Access} access - * @param {Array} [expand] - * @param {String} [search_query] - * @returns {Promise} - */ - getAll: (access, expand, search_query) => { - return access.can('auditlog:list') - .then(() => { - let query = auditLogModel - .query() - .orderBy('created_on', 'DESC') - .orderBy('id', 'DESC') - .limit(100) - .allowEager('[user]'); - - // Query is used for searching - if (typeof search_query === 'string') { - query.where(function () { - this.where('meta', 'like', '%' + search_query + '%'); - }); - } - - if (typeof expand !== 'undefined' && expand !== null) { - query.eager('[' + expand.join(', ') + ']'); - } - - return query; - }); - }, - - /** - * This method should not be publicly used, it doesn't check certain things. It will be assumed - * that permission to add to audit log is already considered, however the access token is used for - * default user id determination. - * - * @param {Access} access - * @param {Object} data - * @param {String} data.action - * @param {Number} [data.user_id] - * @param {Number} [data.object_id] - * @param {Number} [data.object_type] - * @param {Object} [data.meta] - * @returns {Promise} - */ - add: (access, data) => { - return new Promise((resolve, reject) => { - // Default the user id - if (typeof data.user_id === 'undefined' || !data.user_id) { - data.user_id = access.token.getUserId(1); - } - - if (typeof data.action === 'undefined' || !data.action) { - reject(new error.InternalValidationError('Audit log entry must contain an Action')); - } else { - // Make sure at least 1 of the IDs are set and action - resolve(auditLogModel - .query() - .insert({ - user_id: data.user_id, - action: data.action, - object_type: data.object_type || '', - object_id: data.object_id || 0, - meta: data.meta || {} - })); - } - }); - } -}; - -module.exports = internalAuditLog; diff --git a/backend/internal/cache/cache.go b/backend/internal/cache/cache.go new file mode 100644 index 00000000..347ce92f --- /dev/null +++ b/backend/internal/cache/cache.go @@ -0,0 +1,51 @@ +package cache + +import ( + "time" + + "npm/internal/entity/setting" + "npm/internal/logger" +) + +// Cache is a memory cache +type Cache struct { + Settings *map[string]setting.Model +} + +// Status is the status of last update +type Status struct { + LastUpdate time.Time + Valid bool +} + +// NewCache will create and return a new Cache object +func NewCache() *Cache { + return &Cache{ + Settings: nil, + } +} + +// Refresh will refresh all cache items +func (c *Cache) Refresh() { + c.RefreshSettings() +} + +// Clear will clear the cache +func (c *Cache) Clear() { + c.Settings = nil +} + +// RefreshSettings will refresh the settings from db +func (c *Cache) RefreshSettings() { + logger.Info("Cache refreshing Settings") + /* + c.ProductOffers = client.GetProductOffers() + + if c.ProductOffers != nil { + c.Status["product_offers"] = Status{ + LastUpdate: time.Now(), + Valid: true, + } + } + */ +} diff --git a/backend/internal/certificate.js b/backend/internal/certificate.js deleted file mode 100644 index c887e681..00000000 --- a/backend/internal/certificate.js +++ /dev/null @@ -1,1073 +0,0 @@ -const fs = require('fs'); -const _ = require('lodash'); -const logger = require('../logger').ssl; -const error = require('../lib/error'); -const certificateModel = require('../models/certificate'); -const internalAuditLog = require('./audit-log'); -const tempWrite = require('temp-write'); -const utils = require('../lib/utils'); -const moment = require('moment'); -const debug_mode = process.env.NODE_ENV !== 'production' || !!process.env.DEBUG; -const le_staging = process.env.NODE_ENV !== 'production'; -const internalNginx = require('./nginx'); -const internalHost = require('./host'); -const certbot_command = '/opt/certbot/bin/certbot'; -const le_config = '/etc/letsencrypt.ini'; -const dns_plugins = require('../global/certbot-dns-plugins'); - -function omissions() { - return ['is_deleted']; -} - -const internalCertificate = { - - allowed_ssl_files: ['certificate', 'certificate_key', 'intermediate_certificate'], - interval_timeout: 1000 * 60 * 60, // 1 hour - interval: null, - interval_processing: false, - - initTimer: () => { - logger.info('Let\'s Encrypt Renewal Timer initialized'); - internalCertificate.interval = setInterval(internalCertificate.processExpiringHosts, internalCertificate.interval_timeout); - // And do this now as well - internalCertificate.processExpiringHosts(); - }, - - /** - * Triggered by a timer, this will check for expiring hosts and renew their ssl certs if required - */ - processExpiringHosts: () => { - if (!internalCertificate.interval_processing) { - internalCertificate.interval_processing = true; - logger.info('Renewing SSL certs close to expiry...'); - - let cmd = certbot_command + ' renew --non-interactive --quiet ' + - '--config "' + le_config + '" ' + - '--preferred-challenges "dns,http" ' + - '--disable-hook-validation ' + - (le_staging ? '--staging' : ''); - - return utils.exec(cmd) - .then((result) => { - if (result) { - logger.info('Renew Result: ' + result); - } - - return internalNginx.reload() - .then(() => { - logger.info('Renew Complete'); - return result; - }); - }) - .then(() => { - // Now go and fetch all the letsencrypt certs from the db and query the files and update expiry times - return certificateModel - .query() - .where('is_deleted', 0) - .andWhere('provider', 'letsencrypt') - .then((certificates) => { - if (certificates && certificates.length) { - let promises = []; - - certificates.map(function (certificate) { - promises.push( - internalCertificate.getCertificateInfoFromFile('/etc/letsencrypt/live/npm-' + certificate.id + '/fullchain.pem') - .then((cert_info) => { - return certificateModel - .query() - .where('id', certificate.id) - .andWhere('provider', 'letsencrypt') - .patch({ - expires_on: moment(cert_info.dates.to, 'X').format('YYYY-MM-DD HH:mm:ss') - }); - }) - .catch((err) => { - // Don't want to stop the train here, just log the error - logger.error(err.message); - }) - ); - }); - - return Promise.all(promises); - } - }); - }) - .then(() => { - internalCertificate.interval_processing = false; - }) - .catch((err) => { - logger.error(err); - internalCertificate.interval_processing = false; - }); - } - }, - - /** - * @param {Access} access - * @param {Object} data - * @returns {Promise} - */ - create: (access, data) => { - return access.can('certificates:create', data) - .then(() => { - data.owner_user_id = access.token.getUserId(1); - - if (data.provider === 'letsencrypt') { - data.nice_name = data.domain_names.sort().join(', '); - } - - return certificateModel - .query() - .omit(omissions()) - .insertAndFetch(data); - }) - .then((certificate) => { - if (certificate.provider === 'letsencrypt') { - // Request a new Cert from LE. Let the fun begin. - - // 1. Find out any hosts that are using any of the hostnames in this cert - // 2. Disable them in nginx temporarily - // 3. Generate the LE config - // 4. Request cert - // 5. Remove LE config - // 6. Re-instate previously disabled hosts - - // 1. Find out any hosts that are using any of the hostnames in this cert - return internalHost.getHostsWithDomains(certificate.domain_names) - .then((in_use_result) => { - // 2. Disable them in nginx temporarily - return internalCertificate.disableInUseHosts(in_use_result) - .then(() => { - return in_use_result; - }); - }) - .then((in_use_result) => { - // With DNS challenge no config is needed, so skip 3 and 5. - if (certificate.meta.dns_challenge) { - return internalNginx.reload().then(() => { - // 4. Request cert - return internalCertificate.requestLetsEncryptSslWithDnsChallenge(certificate); - }) - .then(internalNginx.reload) - .then(() => { - // 6. Re-instate previously disabled hosts - return internalCertificate.enableInUseHosts(in_use_result); - }) - .then(() => { - return certificate; - }) - .catch((err) => { - // In the event of failure, revert things and throw err back - return internalCertificate.enableInUseHosts(in_use_result) - .then(internalNginx.reload) - .then(() => { - throw err; - }); - }); - } else { - // 3. Generate the LE config - return internalNginx.generateLetsEncryptRequestConfig(certificate) - .then(internalNginx.reload) - .then(() => { - // 4. Request cert - return internalCertificate.requestLetsEncryptSsl(certificate); - }) - .then(() => { - // 5. Remove LE config - return internalNginx.deleteLetsEncryptRequestConfig(certificate); - }) - .then(internalNginx.reload) - .then(() => { - // 6. Re-instate previously disabled hosts - return internalCertificate.enableInUseHosts(in_use_result); - }) - .then(() => { - return certificate; - }) - .catch((err) => { - // In the event of failure, revert things and throw err back - return internalNginx.deleteLetsEncryptRequestConfig(certificate) - .then(() => { - return internalCertificate.enableInUseHosts(in_use_result); - }) - .then(internalNginx.reload) - .then(() => { - throw err; - }); - }); - } - }) - .then(() => { - // At this point, the letsencrypt cert should exist on disk. - // Lets get the expiry date from the file and update the row silently - return internalCertificate.getCertificateInfoFromFile('/etc/letsencrypt/live/npm-' + certificate.id + '/fullchain.pem') - .then((cert_info) => { - return certificateModel - .query() - .patchAndFetchById(certificate.id, { - expires_on: moment(cert_info.dates.to, 'X').format('YYYY-MM-DD HH:mm:ss') - }) - .then((saved_row) => { - // Add cert data for audit log - saved_row.meta = _.assign({}, saved_row.meta, { - letsencrypt_certificate: cert_info - }); - - return saved_row; - }); - }); - }).catch(async (error) => { - // Delete the certificate from the database if it was not created successfully - await certificateModel - .query() - .deleteById(certificate.id); - - throw error; - }); - } else { - return certificate; - } - }).then((certificate) => { - - data.meta = _.assign({}, data.meta || {}, certificate.meta); - - // Add to audit log - return internalAuditLog.add(access, { - action: 'created', - object_type: 'certificate', - object_id: certificate.id, - meta: data - }) - .then(() => { - return certificate; - }); - }); - }, - - /** - * @param {Access} access - * @param {Object} data - * @param {Number} data.id - * @param {String} [data.email] - * @param {String} [data.name] - * @return {Promise} - */ - update: (access, data) => { - return access.can('certificates:update', data.id) - .then((/*access_data*/) => { - return internalCertificate.get(access, {id: data.id}); - }) - .then((row) => { - if (row.id !== data.id) { - // Sanity check that something crazy hasn't happened - throw new error.InternalValidationError('Certificate could not be updated, IDs do not match: ' + row.id + ' !== ' + data.id); - } - - return certificateModel - .query() - .omit(omissions()) - .patchAndFetchById(row.id, data) - .then((saved_row) => { - saved_row.meta = internalCertificate.cleanMeta(saved_row.meta); - data.meta = internalCertificate.cleanMeta(data.meta); - - // Add row.nice_name for custom certs - if (saved_row.provider === 'other') { - data.nice_name = saved_row.nice_name; - } - - // Add to audit log - return internalAuditLog.add(access, { - action: 'updated', - object_type: 'certificate', - object_id: row.id, - meta: _.omit(data, ['expires_on']) // this prevents json circular reference because expires_on might be raw - }) - .then(() => { - return _.omit(saved_row, omissions()); - }); - }); - }); - }, - - /** - * @param {Access} access - * @param {Object} data - * @param {Number} data.id - * @param {Array} [data.expand] - * @param {Array} [data.omit] - * @return {Promise} - */ - get: (access, data) => { - if (typeof data === 'undefined') { - data = {}; - } - - return access.can('certificates:get', data.id) - .then((access_data) => { - let query = certificateModel - .query() - .where('is_deleted', 0) - .andWhere('id', data.id) - .allowEager('[owner]') - .first(); - - if (access_data.permission_visibility !== 'all') { - query.andWhere('owner_user_id', access.token.getUserId(1)); - } - - // Custom omissions - if (typeof data.omit !== 'undefined' && data.omit !== null) { - query.omit(data.omit); - } - - if (typeof data.expand !== 'undefined' && data.expand !== null) { - query.eager('[' + data.expand.join(', ') + ']'); - } - - return query; - }) - .then((row) => { - if (row) { - return _.omit(row, omissions()); - } else { - throw new error.ItemNotFoundError(data.id); - } - }); - }, - - /** - * @param {Access} access - * @param {Object} data - * @param {Number} data.id - * @param {String} [data.reason] - * @returns {Promise} - */ - delete: (access, data) => { - return access.can('certificates:delete', data.id) - .then(() => { - return internalCertificate.get(access, {id: data.id}); - }) - .then((row) => { - if (!row) { - throw new error.ItemNotFoundError(data.id); - } - - return certificateModel - .query() - .where('id', row.id) - .patch({ - is_deleted: 1 - }) - .then(() => { - // Add to audit log - row.meta = internalCertificate.cleanMeta(row.meta); - - return internalAuditLog.add(access, { - action: 'deleted', - object_type: 'certificate', - object_id: row.id, - meta: _.omit(row, omissions()) - }); - }) - .then(() => { - if (row.provider === 'letsencrypt') { - // Revoke the cert - return internalCertificate.revokeLetsEncryptSsl(row); - } - }); - }) - .then(() => { - return true; - }); - }, - - /** - * All Certs - * - * @param {Access} access - * @param {Array} [expand] - * @param {String} [search_query] - * @returns {Promise} - */ - getAll: (access, expand, search_query) => { - return access.can('certificates:list') - .then((access_data) => { - let query = certificateModel - .query() - .where('is_deleted', 0) - .groupBy('id') - .omit(['is_deleted']) - .allowEager('[owner]') - .orderBy('nice_name', 'ASC'); - - if (access_data.permission_visibility !== 'all') { - query.andWhere('owner_user_id', access.token.getUserId(1)); - } - - // Query is used for searching - if (typeof search_query === 'string') { - query.where(function () { - this.where('name', 'like', '%' + search_query + '%'); - }); - } - - if (typeof expand !== 'undefined' && expand !== null) { - query.eager('[' + expand.join(', ') + ']'); - } - - return query; - }); - }, - - /** - * Report use - * - * @param {Number} user_id - * @param {String} visibility - * @returns {Promise} - */ - getCount: (user_id, visibility) => { - let query = certificateModel - .query() - .count('id as count') - .where('is_deleted', 0); - - if (visibility !== 'all') { - query.andWhere('owner_user_id', user_id); - } - - return query.first() - .then((row) => { - return parseInt(row.count, 10); - }); - }, - - /** - * @param {Object} certificate - * @returns {Promise} - */ - writeCustomCert: (certificate) => { - if (debug_mode) { - logger.info('Writing Custom Certificate:', certificate); - } - - let dir = '/data/custom_ssl/npm-' + certificate.id; - - return new Promise((resolve, reject) => { - if (certificate.provider === 'letsencrypt') { - reject(new Error('Refusing to write letsencrypt certs here')); - return; - } - - let cert_data = certificate.meta.certificate; - if (typeof certificate.meta.intermediate_certificate !== 'undefined') { - cert_data = cert_data + '\n' + certificate.meta.intermediate_certificate; - } - - try { - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir); - } - } catch (err) { - reject(err); - return; - } - - fs.writeFile(dir + '/fullchain.pem', cert_data, function (err) { - if (err) { - reject(err); - } else { - resolve(); - } - }); - }) - .then(() => { - return new Promise((resolve, reject) => { - fs.writeFile(dir + '/privkey.pem', certificate.meta.certificate_key, function (err) { - if (err) { - reject(err); - } else { - resolve(); - } - }); - }); - }); - }, - - /** - * @param {Access} access - * @param {Object} data - * @param {Array} data.domain_names - * @param {String} data.meta.letsencrypt_email - * @param {Boolean} data.meta.letsencrypt_agree - * @returns {Promise} - */ - createQuickCertificate: (access, data) => { - return internalCertificate.create(access, { - provider: 'letsencrypt', - domain_names: data.domain_names, - meta: data.meta - }); - }, - - /** - * Validates that the certs provided are good. - * No access required here, nothing is changed or stored. - * - * @param {Object} data - * @param {Object} data.files - * @returns {Promise} - */ - validate: (data) => { - return new Promise((resolve) => { - // Put file contents into an object - let files = {}; - _.map(data.files, (file, name) => { - if (internalCertificate.allowed_ssl_files.indexOf(name) !== -1) { - files[name] = file.data.toString(); - } - }); - - resolve(files); - }) - .then((files) => { - // For each file, create a temp file and write the contents to it - // Then test it depending on the file type - let promises = []; - _.map(files, (content, type) => { - promises.push(new Promise((resolve) => { - if (type === 'certificate_key') { - resolve(internalCertificate.checkPrivateKey(content)); - } else { - // this should handle `certificate` and intermediate certificate - resolve(internalCertificate.getCertificateInfo(content, true)); - } - }).then((res) => { - return {[type]: res}; - })); - }); - - return Promise.all(promises) - .then((files) => { - let data = {}; - - _.each(files, (file) => { - data = _.assign({}, data, file); - }); - - return data; - }); - }); - }, - - /** - * @param {Access} access - * @param {Object} data - * @param {Number} data.id - * @param {Object} data.files - * @returns {Promise} - */ - upload: (access, data) => { - return internalCertificate.get(access, {id: data.id}) - .then((row) => { - if (row.provider !== 'other') { - throw new error.ValidationError('Cannot upload certificates for this type of provider'); - } - - return internalCertificate.validate(data) - .then((validations) => { - if (typeof validations.certificate === 'undefined') { - throw new error.ValidationError('Certificate file was not provided'); - } - - _.map(data.files, (file, name) => { - if (internalCertificate.allowed_ssl_files.indexOf(name) !== -1) { - row.meta[name] = file.data.toString(); - } - }); - - // TODO: This uses a mysql only raw function that won't translate to postgres - return internalCertificate.update(access, { - id: data.id, - expires_on: moment(validations.certificate.dates.to, 'X').format('YYYY-MM-DD HH:mm:ss'), - domain_names: [validations.certificate.cn], - meta: _.clone(row.meta) // Prevent the update method from changing this value that we'll use later - }) - .then((certificate) => { - console.log('ROWMETA:', row.meta); - certificate.meta = row.meta; - return internalCertificate.writeCustomCert(certificate); - }); - }) - .then(() => { - return _.pick(row.meta, internalCertificate.allowed_ssl_files); - }); - }); - }, - - /** - * Uses the openssl command to validate the private key. - * It will save the file to disk first, then run commands on it, then delete the file. - * - * @param {String} private_key This is the entire key contents as a string - */ - checkPrivateKey: (private_key) => { - return tempWrite(private_key, '/tmp') - .then((filepath) => { - return new Promise((resolve, reject) => { - const failTimeout = setTimeout(() => { - reject(new error.ValidationError('Result Validation Error: Validation timed out. This could be due to the key being passphrase-protected.')); - }, 10000); - utils - .exec('openssl pkey -in ' + filepath + ' -check -noout 2>&1 ') - .then((result) => { - clearTimeout(failTimeout); - if (!result.toLowerCase().includes('key is valid')) { - reject(new error.ValidationError('Result Validation Error: ' + result)); - } - fs.unlinkSync(filepath); - resolve(true); - }) - .catch((err) => { - clearTimeout(failTimeout); - fs.unlinkSync(filepath); - reject(new error.ValidationError('Certificate Key is not valid (' + err.message + ')', err)); - }); - }); - }); - }, - - /** - * Uses the openssl command to both validate and get info out of the certificate. - * It will save the file to disk first, then run commands on it, then delete the file. - * - * @param {String} certificate This is the entire cert contents as a string - * @param {Boolean} [throw_expired] Throw when the certificate is out of date - */ - getCertificateInfo: (certificate, throw_expired) => { - return tempWrite(certificate, '/tmp') - .then((filepath) => { - return internalCertificate.getCertificateInfoFromFile(filepath, throw_expired) - .then((cert_data) => { - fs.unlinkSync(filepath); - return cert_data; - }).catch((err) => { - fs.unlinkSync(filepath); - throw err; - }); - }); - }, - - /** - * Uses the openssl command to both validate and get info out of the certificate. - * It will save the file to disk first, then run commands on it, then delete the file. - * - * @param {String} certificate_file The file location on disk - * @param {Boolean} [throw_expired] Throw when the certificate is out of date - */ - getCertificateInfoFromFile: (certificate_file, throw_expired) => { - let cert_data = {}; - - return utils.exec('openssl x509 -in ' + certificate_file + ' -subject -noout') - .then((result) => { - // subject=CN = something.example.com - let regex = /(?:subject=)?[^=]+=\s+(\S+)/gim; - let match = regex.exec(result); - - if (typeof match[1] === 'undefined') { - throw new error.ValidationError('Could not determine subject from certificate: ' + result); - } - - cert_data['cn'] = match[1]; - }) - .then(() => { - return utils.exec('openssl x509 -in ' + certificate_file + ' -issuer -noout'); - }) - .then((result) => { - // issuer=C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3 - let regex = /^(?:issuer=)?(.*)$/gim; - let match = regex.exec(result); - - if (typeof match[1] === 'undefined') { - throw new error.ValidationError('Could not determine issuer from certificate: ' + result); - } - - cert_data['issuer'] = match[1]; - }) - .then(() => { - return utils.exec('openssl x509 -in ' + certificate_file + ' -dates -noout'); - }) - .then((result) => { - // notBefore=Jul 14 04:04:29 2018 GMT - // notAfter=Oct 12 04:04:29 2018 GMT - let valid_from = null; - let valid_to = null; - - let lines = result.split('\n'); - lines.map(function (str) { - let regex = /^(\S+)=(.*)$/gim; - let match = regex.exec(str.trim()); - - if (match && typeof match[2] !== 'undefined') { - let date = parseInt(moment(match[2], 'MMM DD HH:mm:ss YYYY z').format('X'), 10); - - if (match[1].toLowerCase() === 'notbefore') { - valid_from = date; - } else if (match[1].toLowerCase() === 'notafter') { - valid_to = date; - } - } - }); - - if (!valid_from || !valid_to) { - throw new error.ValidationError('Could not determine dates from certificate: ' + result); - } - - if (throw_expired && valid_to < parseInt(moment().format('X'), 10)) { - throw new error.ValidationError('Certificate has expired'); - } - - cert_data['dates'] = { - from: valid_from, - to: valid_to - }; - - return cert_data; - }).catch((err) => { - throw new error.ValidationError('Certificate is not valid (' + err.message + ')', err); - }); - }, - - /** - * Cleans the ssl keys from the meta object and sets them to "true" - * - * @param {Object} meta - * @param {Boolean} [remove] - * @returns {Object} - */ - cleanMeta: function (meta, remove) { - internalCertificate.allowed_ssl_files.map((key) => { - if (typeof meta[key] !== 'undefined' && meta[key]) { - if (remove) { - delete meta[key]; - } else { - meta[key] = true; - } - } - }); - - return meta; - }, - - /** - * @param {Object} certificate the certificate row - * @returns {Promise} - */ - requestLetsEncryptSsl: (certificate) => { - logger.info('Requesting Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', ')); - - let cmd = certbot_command + ' certonly --non-interactive ' + - '--config "' + le_config + '" ' + - '--cert-name "npm-' + certificate.id + '" ' + - '--agree-tos ' + - '--email "' + certificate.meta.letsencrypt_email + '" ' + - '--preferred-challenges "dns,http" ' + - '--domains "' + certificate.domain_names.join(',') + '" ' + - (le_staging ? '--staging' : ''); - - if (debug_mode) { - logger.info('Command:', cmd); - } - - return utils.exec(cmd) - .then((result) => { - logger.success(result); - return result; - }); - }, - - /** - * @param {Object} certificate the certificate row - * @param {String} dns_provider the dns provider name (key used in `certbot-dns-plugins.js`) - * @param {String | null} credentials the content of this providers credentials file - * @param {String} propagation_seconds the cloudflare api token - * @returns {Promise} - */ - requestLetsEncryptSslWithDnsChallenge: (certificate) => { - const dns_plugin = dns_plugins[certificate.meta.dns_provider]; - - if (!dns_plugin) { - throw Error(`Unknown DNS provider '${certificate.meta.dns_provider}'`); - } - - logger.info(`Requesting Let'sEncrypt certificates via ${dns_plugin.display_name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`); - - const credentials_loc = '/etc/letsencrypt/credentials/credentials-' + certificate.id; - const credentials_cmd = 'mkdir -p /etc/letsencrypt/credentials 2> /dev/null; echo \'' + certificate.meta.dns_provider_credentials.replace('\'', '\\\'') + '\' > \'' + credentials_loc + '\' && chmod 600 \'' + credentials_loc + '\''; - const prepare_cmd = 'pip install ' + dns_plugin.package_name + '==' + dns_plugin.package_version + ' ' + dns_plugin.dependencies; - - // Whether the plugin has a ---credentials argument - const has_config_arg = certificate.meta.dns_provider !== 'route53'; - - let main_cmd = - certbot_command + ' certonly --non-interactive ' + - '--cert-name "npm-' + certificate.id + '" ' + - '--agree-tos ' + - '--email "' + certificate.meta.letsencrypt_email + '" ' + - '--domains "' + certificate.domain_names.join(',') + '" ' + - '--authenticator ' + dns_plugin.full_plugin_name + ' ' + - ( - has_config_arg - ? '--' + dns_plugin.full_plugin_name + '-credentials "' + credentials_loc + '"' - : '' - ) + - ( - certificate.meta.propagation_seconds !== undefined - ? ' --' + dns_plugin.full_plugin_name + '-propagation-seconds ' + certificate.meta.propagation_seconds - : '' - ) + - (le_staging ? ' --staging' : ''); - - // Prepend the path to the credentials file as an environment variable - if (certificate.meta.dns_provider === 'route53') { - main_cmd = 'AWS_CONFIG_FILE=\'' + credentials_loc + '\' ' + main_cmd; - } - - if (debug_mode) { - logger.info('Command:', `${credentials_cmd} && ${prepare_cmd} && ${main_cmd}`); - } - - return utils.exec(credentials_cmd) - .then(() => { - return utils.exec(prepare_cmd) - .then(() => { - return utils.exec(main_cmd) - .then(async (result) => { - logger.info(result); - return result; - }); - }); - }).catch(async (err) => { - // Don't fail if file does not exist - const delete_credentials_cmd = `rm -f '${credentials_loc}' || true`; - await utils.exec(delete_credentials_cmd); - throw err; - }); - }, - - - /** - * @param {Access} access - * @param {Object} data - * @param {Number} data.id - * @returns {Promise} - */ - renew: (access, data) => { - return access.can('certificates:update', data) - .then(() => { - return internalCertificate.get(access, data); - }) - .then((certificate) => { - if (certificate.provider === 'letsencrypt') { - let renewMethod = certificate.meta.dns_challenge ? internalCertificate.renewLetsEncryptSslWithDnsChallenge : internalCertificate.renewLetsEncryptSsl; - - return renewMethod(certificate) - .then(() => { - return internalCertificate.getCertificateInfoFromFile('/etc/letsencrypt/live/npm-' + certificate.id + '/fullchain.pem'); - }) - .then((cert_info) => { - return certificateModel - .query() - .patchAndFetchById(certificate.id, { - expires_on: moment(cert_info.dates.to, 'X').format('YYYY-MM-DD HH:mm:ss') - }); - }) - .then((updated_certificate) => { - // Add to audit log - return internalAuditLog.add(access, { - action: 'renewed', - object_type: 'certificate', - object_id: updated_certificate.id, - meta: updated_certificate - }) - .then(() => { - return updated_certificate; - }); - }); - } else { - throw new error.ValidationError('Only Let\'sEncrypt certificates can be renewed'); - } - }); - }, - - /** - * @param {Object} certificate the certificate row - * @returns {Promise} - */ - renewLetsEncryptSsl: (certificate) => { - logger.info('Renewing Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', ')); - - let cmd = certbot_command + ' renew --non-interactive ' + - '--config "' + le_config + '" ' + - '--cert-name "npm-' + certificate.id + '" ' + - '--preferred-challenges "dns,http" ' + - '--disable-hook-validation ' + - (le_staging ? '--staging' : ''); - - if (debug_mode) { - logger.info('Command:', cmd); - } - - return utils.exec(cmd) - .then((result) => { - logger.info(result); - return result; - }); - }, - - /** - * @param {Object} certificate the certificate row - * @returns {Promise} - */ - renewLetsEncryptSslWithDnsChallenge: (certificate) => { - const dns_plugin = dns_plugins[certificate.meta.dns_provider]; - - if (!dns_plugin) { - throw Error(`Unknown DNS provider '${certificate.meta.dns_provider}'`); - } - - logger.info(`Renewing Let'sEncrypt certificates via ${dns_plugin.display_name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`); - - let main_cmd = - certbot_command + ' renew --non-interactive ' + - '--cert-name "npm-' + certificate.id + '" ' + - '--disable-hook-validation' + - (le_staging ? ' --staging' : ''); - - // Prepend the path to the credentials file as an environment variable - if (certificate.meta.dns_provider === 'route53') { - const credentials_loc = '/etc/letsencrypt/credentials/credentials-' + certificate.id; - main_cmd = 'AWS_CONFIG_FILE=\'' + credentials_loc + '\' ' + main_cmd; - } - - if (debug_mode) { - logger.info('Command:', main_cmd); - } - - return utils.exec(main_cmd) - .then(async (result) => { - logger.info(result); - return result; - }); - }, - - /** - * @param {Object} certificate the certificate row - * @param {Boolean} [throw_errors] - * @returns {Promise} - */ - revokeLetsEncryptSsl: (certificate, throw_errors) => { - logger.info('Revoking Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', ')); - - const main_cmd = certbot_command + ' revoke --non-interactive ' + - '--cert-path "/etc/letsencrypt/live/npm-' + certificate.id + '/fullchain.pem" ' + - '--delete-after-revoke ' + - (le_staging ? '--staging' : ''); - - // Don't fail command if file does not exist - const delete_credentials_cmd = `rm -f '/etc/letsencrypt/credentials/credentials-${certificate.id}' || true`; - - if (debug_mode) { - logger.info('Command:', main_cmd + '; ' + delete_credentials_cmd); - } - - return utils.exec(main_cmd) - .then(async (result) => { - await utils.exec(delete_credentials_cmd); - logger.info(result); - return result; - }) - .catch((err) => { - if (debug_mode) { - logger.error(err.message); - } - - if (throw_errors) { - throw err; - } - }); - }, - - /** - * @param {Object} certificate - * @returns {Boolean} - */ - hasLetsEncryptSslCerts: (certificate) => { - let le_path = '/etc/letsencrypt/live/npm-' + certificate.id; - - return fs.existsSync(le_path + '/fullchain.pem') && fs.existsSync(le_path + '/privkey.pem'); - }, - - /** - * @param {Object} in_use_result - * @param {Number} in_use_result.total_count - * @param {Array} in_use_result.proxy_hosts - * @param {Array} in_use_result.redirection_hosts - * @param {Array} in_use_result.dead_hosts - */ - disableInUseHosts: (in_use_result) => { - if (in_use_result.total_count) { - let promises = []; - - if (in_use_result.proxy_hosts.length) { - promises.push(internalNginx.bulkDeleteConfigs('proxy_host', in_use_result.proxy_hosts)); - } - - if (in_use_result.redirection_hosts.length) { - promises.push(internalNginx.bulkDeleteConfigs('redirection_host', in_use_result.redirection_hosts)); - } - - if (in_use_result.dead_hosts.length) { - promises.push(internalNginx.bulkDeleteConfigs('dead_host', in_use_result.dead_hosts)); - } - - return Promise.all(promises); - - } else { - return Promise.resolve(); - } - }, - - /** - * @param {Object} in_use_result - * @param {Number} in_use_result.total_count - * @param {Array} in_use_result.proxy_hosts - * @param {Array} in_use_result.redirection_hosts - * @param {Array} in_use_result.dead_hosts - */ - enableInUseHosts: (in_use_result) => { - if (in_use_result.total_count) { - let promises = []; - - if (in_use_result.proxy_hosts.length) { - promises.push(internalNginx.bulkGenerateConfigs('proxy_host', in_use_result.proxy_hosts)); - } - - if (in_use_result.redirection_hosts.length) { - promises.push(internalNginx.bulkGenerateConfigs('redirection_host', in_use_result.redirection_hosts)); - } - - if (in_use_result.dead_hosts.length) { - promises.push(internalNginx.bulkGenerateConfigs('dead_host', in_use_result.dead_hosts)); - } - - return Promise.all(promises); - - } else { - return Promise.resolve(); - } - } -}; - -module.exports = internalCertificate; diff --git a/backend/internal/config/config.go b/backend/internal/config/config.go new file mode 100644 index 00000000..1eeb5ee8 --- /dev/null +++ b/backend/internal/config/config.go @@ -0,0 +1,78 @@ +package config + +import ( + "fmt" + golog "log" + "runtime" + + "npm/internal/logger" + + "github.com/getsentry/sentry-go" + "github.com/vrischmann/envconfig" +) + +// Init will parse environment variables into the Env struct +func Init(version, commit, sentryDSN *string) { + // ErrorReporting is enabled until we load the status of it from the DB later + ErrorReporting = true + + Version = *version + Commit = *commit + + if err := envconfig.Init(&Configuration); err != nil { + fmt.Printf("%+v\n", err) + } + + initLogger(*sentryDSN) + logger.Info("Build Version: %s (%s)", Version, Commit) + loadKeys() +} + +// Init initialises the Log object and return it +func initLogger(sentryDSN string) { + // this removes timestamp prefixes from logs + golog.SetFlags(0) + + switch Configuration.Log.Level { + case "debug": + logLevel = logger.DebugLevel + case "warn": + logLevel = logger.WarnLevel + case "error": + logLevel = logger.ErrorLevel + default: + logLevel = logger.InfoLevel + } + + err := logger.Configure(&logger.Config{ + LogThreshold: logLevel, + Formatter: Configuration.Log.Format, + SentryConfig: sentry.ClientOptions{ + // This is the jc21 NginxProxyManager Sentry project, + // errors will be reported here (if error reporting is enable) + // and this project is private. No personal information should + // be sent in any error messages, only stacktraces. + Dsn: sentryDSN, + Release: Commit, + Dist: Version, + Environment: fmt.Sprintf("%s-%s", runtime.GOOS, runtime.GOARCH), + }, + }) + + if err != nil { + logger.Error("LoggerConfigurationError", err) + } +} + +// GetLogLevel returns the logger const level +func GetLogLevel() logger.Level { + return logLevel +} + +func isError(errorClass string, err error) bool { + if err != nil { + logger.Error(errorClass, err) + return true + } + return false +} diff --git a/backend/internal/config/keys.go b/backend/internal/config/keys.go new file mode 100644 index 00000000..9ac3e903 --- /dev/null +++ b/backend/internal/config/keys.go @@ -0,0 +1,112 @@ +package config + +import ( + "bytes" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/asn1" + "encoding/pem" + "fmt" + "io/ioutil" + "os" + + "npm/internal/logger" +) + +var keysFolder string +var publicKeyFile string +var privateKeyFile string + +func loadKeys() { + // check if keys folder exists in data folder + keysFolder = fmt.Sprintf("%s/keys", Configuration.DataFolder) + publicKeyFile = fmt.Sprintf("%s/public.key", keysFolder) + privateKeyFile = fmt.Sprintf("%s/private.key", keysFolder) + + if _, err := os.Stat(keysFolder); os.IsNotExist(err) { + // nolint:errcheck,gosec + os.Mkdir(keysFolder, 0700) + } + + // check if keys exist on disk + _, publicKeyErr := os.Stat(publicKeyFile) + _, privateKeyErr := os.Stat(privateKeyFile) + + // generate keys if either one doesn't exist + if os.IsNotExist(publicKeyErr) || os.IsNotExist(privateKeyErr) { + generateKeys() + saveKeys() + } + + // Load keys from disk + // nolint:gosec + publicKeyBytes, publicKeyBytesErr := ioutil.ReadFile(publicKeyFile) + // nolint:gosec + privateKeyBytes, privateKeyBytesErr := ioutil.ReadFile(privateKeyFile) + PublicKey = string(publicKeyBytes) + PrivateKey = string(privateKeyBytes) + + if isError("PublicKeyReadError", publicKeyBytesErr) || isError("PrivateKeyReadError", privateKeyBytesErr) || PublicKey == "" || PrivateKey == "" { + logger.Warn("There was an error loading keys, proceeding to generate new RSA keys") + generateKeys() + saveKeys() + } +} + +func generateKeys() { + reader := rand.Reader + bitSize := 4096 + + key, err := rsa.GenerateKey(reader, bitSize) + if isError("RSAGenerateError", err) { + return + } + + privateKey := &pem.Block{ + Type: "PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(key), + } + + privateKeyBuffer := new(bytes.Buffer) + err = pem.Encode(privateKeyBuffer, privateKey) + if isError("PrivatePEMEncodeError", err) { + return + } + + asn1Bytes, err2 := asn1.Marshal(key.PublicKey) + if isError("RSAMarshalError", err2) { + return + } + + publicKey := &pem.Block{ + Type: "PUBLIC KEY", + Bytes: asn1Bytes, + } + + publicKeyBuffer := new(bytes.Buffer) + err = pem.Encode(publicKeyBuffer, publicKey) + if isError("PublicPEMEncodeError", err) { + return + } + + PublicKey = publicKeyBuffer.String() + PrivateKey = privateKeyBuffer.String() + logger.Info("Generated new RSA keys") +} + +func saveKeys() { + err := ioutil.WriteFile(publicKeyFile, []byte(PublicKey), 0600) + if err != nil { + logger.Error("PublicKeyWriteError", err) + } else { + logger.Info("Saved Public Key: %s", publicKeyFile) + } + + err = ioutil.WriteFile(privateKeyFile, []byte(PrivateKey), 0600) + if err != nil { + logger.Error("PrivateKeyWriteError", err) + } else { + logger.Info("Saved Private Key: %s", privateKeyFile) + } +} diff --git a/backend/internal/config/vars.go b/backend/internal/config/vars.go new file mode 100644 index 00000000..f856a940 --- /dev/null +++ b/backend/internal/config/vars.go @@ -0,0 +1,34 @@ +package config + +import "npm/internal/logger" + +// Version is the version set by ldflags +var Version string + +// Commit is the git commit set by ldflags +var Commit string + +// IsSetup defines whether we have an admin user or not +var IsSetup bool + +// ErrorReporting defines whether we will send errors to Sentry +var ErrorReporting bool + +// PublicKey ... +var PublicKey string + +// PrivateKey ... +var PrivateKey string + +var logLevel logger.Level + +type log struct { + Level string `json:"level" envconfig:"optional,default=info"` + Format string `json:"format" envconfig:"optional,default=nice"` +} + +// Configuration ... +var Configuration struct { + DataFolder string `json:"data_folder" envconfig:"optional,default=/data"` + Log log `json:"log"` +} diff --git a/backend/internal/database/helpers.go b/backend/internal/database/helpers.go new file mode 100644 index 00000000..7553af3b --- /dev/null +++ b/backend/internal/database/helpers.go @@ -0,0 +1,46 @@ +package database + +import ( + "fmt" + "strings" + + "npm/internal/errors" + "npm/internal/model" + "npm/internal/util" +) + +const ( + // DateFormat for DateFormat + DateFormat = "2006-01-02" + // DateTimeFormat for DateTimeFormat + DateTimeFormat = "2006-01-02T15:04:05" +) + +// GetByQuery returns a row given a query, populating the model given +func GetByQuery(model interface{}, query string, params []interface{}) error { + db := GetInstance() + if db != nil { + err := db.Get(model, query, params...) + return err + } + + return errors.ErrDatabaseUnavailable +} + +// BuildOrderBySQL takes a `Sort` slice and constructs a query fragment +func BuildOrderBySQL(columns []string, sort *[]model.Sort) (string, []model.Sort) { + var sortStrings []string + var newSort []model.Sort + for _, sortItem := range *sort { + if util.SliceContainsItem(columns, sortItem.Field) { + sortStrings = append(sortStrings, fmt.Sprintf("`%s` %s", sortItem.Field, sortItem.Direction)) + newSort = append(newSort, sortItem) + } + } + + if len(sortStrings) > 0 { + return fmt.Sprintf("ORDER BY %s", strings.Join(sortStrings, ", ")), newSort + } + + return "", newSort +} diff --git a/backend/internal/database/setup.go b/backend/internal/database/setup.go new file mode 100644 index 00000000..16f64559 --- /dev/null +++ b/backend/internal/database/setup.go @@ -0,0 +1,38 @@ +package database + +import ( + "database/sql" + + "npm/internal/config" + "npm/internal/errors" + "npm/internal/logger" +) + +// CheckSetup Quick check by counting the number of users in the database +func CheckSetup() { + query := `SELECT COUNT(*) FROM "user" WHERE is_deleted = $1 and is_disabled = $2` + db := GetInstance() + + if db != nil { + row := db.QueryRowx(query, false, false) + var totalRows int + queryErr := row.Scan(&totalRows) + if queryErr != nil && queryErr != sql.ErrNoRows { + logger.Error("SetupError", queryErr) + return + } + if totalRows == 0 { + logger.Warn("No users found, starting in Setup Mode") + } else { + config.IsSetup = true + logger.Info("Application is setup") + } + + if config.ErrorReporting { + logger.Warn("Error reporting is enabled - Application Errors WILL be sent to Sentry, you can disable this in the Settings interface") + } + + } else { + logger.Error("DatabaseError", errors.ErrDatabaseUnavailable) + } +} diff --git a/backend/internal/database/sqlite.go b/backend/internal/database/sqlite.go new file mode 100644 index 00000000..57528b49 --- /dev/null +++ b/backend/internal/database/sqlite.go @@ -0,0 +1,73 @@ +package database + +import ( + "fmt" + "os" + + "npm/internal/config" + "npm/internal/logger" + + "github.com/jmoiron/sqlx" + + // Blank import for Sqlite + _ "github.com/mattn/go-sqlite3" +) + +var dbInstance *sqlx.DB + +// NewDB creates a new connection +func NewDB() { + logger.Info("Creating new DB instance") + db := SqliteDB() + if db != nil { + dbInstance = db + } +} + +// GetInstance returns an existing or new instance +func GetInstance() *sqlx.DB { + if dbInstance == nil { + NewDB() + } else if err := dbInstance.Ping(); err != nil { + NewDB() + } + + return dbInstance +} + +// SqliteDB Create sqlite client +func SqliteDB() *sqlx.DB { + dbFile := fmt.Sprintf("%s/nginxproxymanager.db", config.Configuration.DataFolder) + autocreate(dbFile) + db, err := sqlx.Open("sqlite3", dbFile) + if err != nil { + logger.Error("SqliteError", err) + return nil + } + + return db +} + +// Commit will close and reopen the db file +func Commit() *sqlx.DB { + if dbInstance != nil { + err := dbInstance.Close() + if err != nil { + logger.Error("DatabaseCloseError", err) + } + } + NewDB() + return dbInstance +} + +func autocreate(dbFile string) { + if _, err := os.Stat(dbFile); os.IsNotExist(err) { + // Create it + logger.Info("Creating Sqlite DB: %s", dbFile) + _, err = os.Create(dbFile) + if err != nil { + logger.Error("FileCreateError", err) + } + Commit() + } +} diff --git a/backend/internal/dead-host.js b/backend/internal/dead-host.js deleted file mode 100644 index d35fec25..00000000 --- a/backend/internal/dead-host.js +++ /dev/null @@ -1,461 +0,0 @@ -const _ = require('lodash'); -const error = require('../lib/error'); -const deadHostModel = require('../models/dead_host'); -const internalHost = require('./host'); -const internalNginx = require('./nginx'); -const internalAuditLog = require('./audit-log'); -const internalCertificate = require('./certificate'); - -function omissions () { - return ['is_deleted']; -} - -const internalDeadHost = { - - /** - * @param {Access} access - * @param {Object} data - * @returns {Promise} - */ - create: (access, data) => { - let create_certificate = data.certificate_id === 'new'; - - if (create_certificate) { - delete data.certificate_id; - } - - return access.can('dead_hosts:create', data) - .then((/*access_data*/) => { - // Get a list of the domain names and check each of them against existing records - let domain_name_check_promises = []; - - data.domain_names.map(function (domain_name) { - domain_name_check_promises.push(internalHost.isHostnameTaken(domain_name)); - }); - - return Promise.all(domain_name_check_promises) - .then((check_results) => { - check_results.map(function (result) { - if (result.is_taken) { - throw new error.ValidationError(result.hostname + ' is already in use'); - } - }); - }); - }) - .then(() => { - // At this point the domains should have been checked - data.owner_user_id = access.token.getUserId(1); - data = internalHost.cleanSslHstsData(data); - - return deadHostModel - .query() - .omit(omissions()) - .insertAndFetch(data); - }) - .then((row) => { - if (create_certificate) { - return internalCertificate.createQuickCertificate(access, data) - .then((cert) => { - // update host with cert id - return internalDeadHost.update(access, { - id: row.id, - certificate_id: cert.id - }); - }) - .then(() => { - return row; - }); - } else { - return row; - } - }) - .then((row) => { - // re-fetch with cert - return internalDeadHost.get(access, { - id: row.id, - expand: ['certificate', 'owner'] - }); - }) - .then((row) => { - // Configure nginx - return internalNginx.configure(deadHostModel, 'dead_host', row) - .then(() => { - return row; - }); - }) - .then((row) => { - data.meta = _.assign({}, data.meta || {}, row.meta); - - // Add to audit log - return internalAuditLog.add(access, { - action: 'created', - object_type: 'dead-host', - object_id: row.id, - meta: data - }) - .then(() => { - return row; - }); - }); - }, - - /** - * @param {Access} access - * @param {Object} data - * @param {Number} data.id - * @return {Promise} - */ - update: (access, data) => { - let create_certificate = data.certificate_id === 'new'; - - if (create_certificate) { - delete data.certificate_id; - } - - return access.can('dead_hosts:update', data.id) - .then((/*access_data*/) => { - // Get a list of the domain names and check each of them against existing records - let domain_name_check_promises = []; - - if (typeof data.domain_names !== 'undefined') { - data.domain_names.map(function (domain_name) { - domain_name_check_promises.push(internalHost.isHostnameTaken(domain_name, 'dead', data.id)); - }); - - return Promise.all(domain_name_check_promises) - .then((check_results) => { - check_results.map(function (result) { - if (result.is_taken) { - throw new error.ValidationError(result.hostname + ' is already in use'); - } - }); - }); - } - }) - .then(() => { - return internalDeadHost.get(access, {id: data.id}); - }) - .then((row) => { - if (row.id !== data.id) { - // Sanity check that something crazy hasn't happened - throw new error.InternalValidationError('404 Host could not be updated, IDs do not match: ' + row.id + ' !== ' + data.id); - } - - if (create_certificate) { - return internalCertificate.createQuickCertificate(access, { - domain_names: data.domain_names || row.domain_names, - meta: _.assign({}, row.meta, data.meta) - }) - .then((cert) => { - // update host with cert id - data.certificate_id = cert.id; - }) - .then(() => { - return row; - }); - } else { - return row; - } - }) - .then((row) => { - // Add domain_names to the data in case it isn't there, so that the audit log renders correctly. The order is important here. - data = _.assign({}, { - domain_names: row.domain_names - }, data); - - data = internalHost.cleanSslHstsData(data, row); - - return deadHostModel - .query() - .where({id: data.id}) - .patch(data) - .then((saved_row) => { - // Add to audit log - return internalAuditLog.add(access, { - action: 'updated', - object_type: 'dead-host', - object_id: row.id, - meta: data - }) - .then(() => { - return _.omit(saved_row, omissions()); - }); - }); - }) - .then(() => { - return internalDeadHost.get(access, { - id: data.id, - expand: ['owner', 'certificate'] - }) - .then((row) => { - // Configure nginx - return internalNginx.configure(deadHostModel, 'dead_host', row) - .then((new_meta) => { - row.meta = new_meta; - row = internalHost.cleanRowCertificateMeta(row); - return _.omit(row, omissions()); - }); - }); - }); - }, - - /** - * @param {Access} access - * @param {Object} data - * @param {Number} data.id - * @param {Array} [data.expand] - * @param {Array} [data.omit] - * @return {Promise} - */ - get: (access, data) => { - if (typeof data === 'undefined') { - data = {}; - } - - return access.can('dead_hosts:get', data.id) - .then((access_data) => { - let query = deadHostModel - .query() - .where('is_deleted', 0) - .andWhere('id', data.id) - .allowEager('[owner,certificate]') - .first(); - - if (access_data.permission_visibility !== 'all') { - query.andWhere('owner_user_id', access.token.getUserId(1)); - } - - // Custom omissions - if (typeof data.omit !== 'undefined' && data.omit !== null) { - query.omit(data.omit); - } - - if (typeof data.expand !== 'undefined' && data.expand !== null) { - query.eager('[' + data.expand.join(', ') + ']'); - } - - return query; - }) - .then((row) => { - if (row) { - row = internalHost.cleanRowCertificateMeta(row); - return _.omit(row, omissions()); - } else { - throw new error.ItemNotFoundError(data.id); - } - }); - }, - - /** - * @param {Access} access - * @param {Object} data - * @param {Number} data.id - * @param {String} [data.reason] - * @returns {Promise} - */ - delete: (access, data) => { - return access.can('dead_hosts:delete', data.id) - .then(() => { - return internalDeadHost.get(access, {id: data.id}); - }) - .then((row) => { - if (!row) { - throw new error.ItemNotFoundError(data.id); - } - - return deadHostModel - .query() - .where('id', row.id) - .patch({ - is_deleted: 1 - }) - .then(() => { - // Delete Nginx Config - return internalNginx.deleteConfig('dead_host', row) - .then(() => { - return internalNginx.reload(); - }); - }) - .then(() => { - // Add to audit log - return internalAuditLog.add(access, { - action: 'deleted', - object_type: 'dead-host', - object_id: row.id, - meta: _.omit(row, omissions()) - }); - }); - }) - .then(() => { - return true; - }); - }, - - /** - * @param {Access} access - * @param {Object} data - * @param {Number} data.id - * @param {String} [data.reason] - * @returns {Promise} - */ - enable: (access, data) => { - return access.can('dead_hosts:update', data.id) - .then(() => { - return internalDeadHost.get(access, { - id: data.id, - expand: ['certificate', 'owner'] - }); - }) - .then((row) => { - if (!row) { - throw new error.ItemNotFoundError(data.id); - } else if (row.enabled) { - throw new error.ValidationError('Host is already enabled'); - } - - row.enabled = 1; - - return deadHostModel - .query() - .where('id', row.id) - .patch({ - enabled: 1 - }) - .then(() => { - // Configure nginx - return internalNginx.configure(deadHostModel, 'dead_host', row); - }) - .then(() => { - // Add to audit log - return internalAuditLog.add(access, { - action: 'enabled', - object_type: 'dead-host', - object_id: row.id, - meta: _.omit(row, omissions()) - }); - }); - }) - .then(() => { - return true; - }); - }, - - /** - * @param {Access} access - * @param {Object} data - * @param {Number} data.id - * @param {String} [data.reason] - * @returns {Promise} - */ - disable: (access, data) => { - return access.can('dead_hosts:update', data.id) - .then(() => { - return internalDeadHost.get(access, {id: data.id}); - }) - .then((row) => { - if (!row) { - throw new error.ItemNotFoundError(data.id); - } else if (!row.enabled) { - throw new error.ValidationError('Host is already disabled'); - } - - row.enabled = 0; - - return deadHostModel - .query() - .where('id', row.id) - .patch({ - enabled: 0 - }) - .then(() => { - // Delete Nginx Config - return internalNginx.deleteConfig('dead_host', row) - .then(() => { - return internalNginx.reload(); - }); - }) - .then(() => { - // Add to audit log - return internalAuditLog.add(access, { - action: 'disabled', - object_type: 'dead-host', - object_id: row.id, - meta: _.omit(row, omissions()) - }); - }); - }) - .then(() => { - return true; - }); - }, - - /** - * All Hosts - * - * @param {Access} access - * @param {Array} [expand] - * @param {String} [search_query] - * @returns {Promise} - */ - getAll: (access, expand, search_query) => { - return access.can('dead_hosts:list') - .then((access_data) => { - let query = deadHostModel - .query() - .where('is_deleted', 0) - .groupBy('id') - .omit(['is_deleted']) - .allowEager('[owner,certificate]') - .orderBy('domain_names', 'ASC'); - - if (access_data.permission_visibility !== 'all') { - query.andWhere('owner_user_id', access.token.getUserId(1)); - } - - // Query is used for searching - if (typeof search_query === 'string') { - query.where(function () { - this.where('domain_names', 'like', '%' + search_query + '%'); - }); - } - - if (typeof expand !== 'undefined' && expand !== null) { - query.eager('[' + expand.join(', ') + ']'); - } - - return query; - }) - .then((rows) => { - if (typeof expand !== 'undefined' && expand !== null && expand.indexOf('certificate') !== -1) { - return internalHost.cleanAllRowsCertificateMeta(rows); - } - - return rows; - }); - }, - - /** - * Report use - * - * @param {Number} user_id - * @param {String} visibility - * @returns {Promise} - */ - getCount: (user_id, visibility) => { - let query = deadHostModel - .query() - .count('id as count') - .where('is_deleted', 0); - - if (visibility !== 'all') { - query.andWhere('owner_user_id', user_id); - } - - return query.first() - .then((row) => { - return parseInt(row.count, 10); - }); - } -}; - -module.exports = internalDeadHost; diff --git a/backend/internal/entity/auth/methods.go b/backend/internal/entity/auth/methods.go new file mode 100644 index 00000000..9cfb7faa --- /dev/null +++ b/backend/internal/entity/auth/methods.go @@ -0,0 +1,82 @@ +package auth + +import ( + goerrors "errors" + "fmt" + + "npm/internal/database" +) + +// GetByID finds a auth by ID +func GetByID(id int) (Model, error) { + var m Model + err := m.LoadByID(id) + return m, err +} + +// GetByUserIDType finds a user by email +func GetByUserIDType(userID int, authType string) (Model, error) { + var m Model + err := m.LoadByUserIDType(userID, authType) + return m, err +} + +// Create will create a Auth from this model +func Create(auth *Model) (int, error) { + if auth.ID != 0 { + return 0, goerrors.New("Cannot create auth when model already has an ID") + } + + auth.Touch(true) + + db := database.GetInstance() + // nolint: gosec + result, err := db.NamedExec(`INSERT INTO `+fmt.Sprintf("`%s`", tableName)+` ( + created_on, + modified_on, + user_id, + type, + secret, + is_deleted + ) VALUES ( + :created_on, + :modified_on, + :user_id, + :type, + :secret, + :is_deleted + )`, auth) + + if err != nil { + return 0, err + } + + last, lastErr := result.LastInsertId() + if lastErr != nil { + return 0, lastErr + } + + return int(last), nil +} + +// Update will Update a Auth from this model +func Update(auth *Model) error { + if auth.ID == 0 { + return goerrors.New("Cannot update auth when model doesn't have an ID") + } + + auth.Touch(false) + + db := database.GetInstance() + // nolint: gosec + _, err := db.NamedExec(`UPDATE `+fmt.Sprintf("`%s`", tableName)+` SET + created_on = :created_on, + modified_on = :modified_on, + user_id = :user_id, + type = :type, + secret = :secret, + is_deleted = :is_deleted + WHERE id = :id`, auth) + + return err +} diff --git a/backend/internal/entity/auth/model.go b/backend/internal/entity/auth/model.go new file mode 100644 index 00000000..b5640bbf --- /dev/null +++ b/backend/internal/entity/auth/model.go @@ -0,0 +1,98 @@ +package auth + +import ( + goerrors "errors" + "fmt" + "time" + + "npm/internal/database" + "npm/internal/types" + + "golang.org/x/crypto/bcrypt" +) + +const ( + tableName = "auth" + + // TypePassword is the Password Type + TypePassword = "password" +) + +// Model is the user model +type Model struct { + ID int `json:"id" db:"id"` + UserID int `json:"user_id" db:"user_id"` + Type string `json:"type" db:"type"` + Secret string `json:"secret,omitempty" db:"secret"` + CreatedOn types.DBDate `json:"created_on" db:"created_on"` + ModifiedOn types.DBDate `json:"modified_on" db:"modified_on"` + IsDeleted bool `json:"is_deleted,omitempty" db:"is_deleted"` +} + +func (m *Model) getByQuery(query string, params []interface{}) error { + return database.GetByQuery(m, query, params) +} + +// LoadByID will load from an ID +func (m *Model) LoadByID(id int) error { + query := fmt.Sprintf("SELECT * FROM `%s` WHERE id = ? LIMIT 1", tableName) + params := []interface{}{id} + return m.getByQuery(query, params) +} + +// LoadByUserIDType will load from an ID +func (m *Model) LoadByUserIDType(userID int, authType string) error { + query := fmt.Sprintf("SELECT * FROM `%s` WHERE user_id = ? AND type = ? LIMIT 1", tableName) + params := []interface{}{userID, authType} + return m.getByQuery(query, params) +} + +// Touch will update model's timestamp(s) +func (m *Model) Touch(created bool) { + var d types.DBDate + d.Time = time.Now() + if created { + m.CreatedOn = d + } + m.ModifiedOn = d +} + +// Save will save this model to the DB +func (m *Model) Save() error { + var err error + + if m.ID == 0 { + m.ID, err = Create(m) + } else { + err = Update(m) + } + + return err +} + +// SetPassword will generate a hashed password based on given string +func (m *Model) SetPassword(password string) error { + hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.MinCost+2) + if err != nil { + return err + } + + m.Type = TypePassword + m.Secret = string(hash) + + return nil +} + +// ValidateSecret will check if a given secret matches the encrypted secret +func (m *Model) ValidateSecret(secret string) error { + if m.Type != TypePassword { + return goerrors.New("Could not validate Secret, auth type is not a Password") + } + + err := bcrypt.CompareHashAndPassword([]byte(m.Secret), []byte(secret)) + if err != nil { + return goerrors.New("Invalid Password") + } + + return nil +} diff --git a/backend/internal/entity/certificate/filters.go b/backend/internal/entity/certificate/filters.go new file mode 100644 index 00000000..5e321af8 --- /dev/null +++ b/backend/internal/entity/certificate/filters.go @@ -0,0 +1,25 @@ +package certificate + +import ( + "npm/internal/entity" +) + +var filterMapFunctions = make(map[string]entity.FilterMapFunction) + +// getFilterMapFunctions is a map of functions that should be executed +// during the filtering process, if a field is defined here then the value in +// the filter will be given to the defined function and it will return a new +// value for use in the sql query. +func getFilterMapFunctions() map[string]entity.FilterMapFunction { + // if len(filterMapFunctions) == 0 { + // TODO: See internal/model/file_item.go:620 for an example + // } + + return filterMapFunctions +} + +// GetFilterSchema ... +func GetFilterSchema() string { + var m Model + return entity.GetFilterSchema(m) +} diff --git a/backend/internal/entity/certificate/methods.go b/backend/internal/entity/certificate/methods.go new file mode 100644 index 00000000..27ae7d78 --- /dev/null +++ b/backend/internal/entity/certificate/methods.go @@ -0,0 +1,173 @@ +package certificate + +import ( + "database/sql" + goerrors "errors" + "fmt" + + "npm/internal/database" + "npm/internal/entity" + "npm/internal/errors" + "npm/internal/logger" + "npm/internal/model" +) + +// GetByID finds a row by ID +func GetByID(id int) (Model, error) { + var m Model + err := m.LoadByID(id) + return m, err +} + +// Create will create a row from this model +func Create(certificate *Model) (int, error) { + if certificate.ID != 0 { + return 0, goerrors.New("Cannot create certificate when model already has an ID") + } + + certificate.Touch(true) + + db := database.GetInstance() + // nolint: gosec + result, err := db.NamedExec(`INSERT INTO `+fmt.Sprintf("`%s`", tableName)+` ( + created_on, + modified_on, + user_id, + type, + certificate_authority_id, + dns_provider_id, + name, + domain_names, + expires_on, + status, + meta, + is_deleted + ) VALUES ( + :created_on, + :modified_on, + :user_id, + :type, + :certificate_authority_id, + :dns_provider_id, + :name, + :domain_names, + :expires_on, + :status, + :meta, + :is_deleted + )`, certificate) + + if err != nil { + return 0, err + } + + last, lastErr := result.LastInsertId() + if lastErr != nil { + return 0, lastErr + } + + return int(last), nil +} + +// Update will Update a Auth from this model +func Update(certificate *Model) error { + if certificate.ID == 0 { + return goerrors.New("Cannot update certificate when model doesn't have an ID") + } + + certificate.Touch(false) + + db := database.GetInstance() + // nolint: gosec + _, err := db.NamedExec(`UPDATE `+fmt.Sprintf("`%s`", tableName)+` SET + created_on = :created_on, + modified_on = :modified_on, + type = :type, + user_id = :user_id, + certificate_authority_id = :certificate_authority_id, + dns_provider_id = :dns_provider_id, + name = :name, + domain_names = :domain_names, + expires_on = :expires_on, + status = :status, + meta = :meta, + is_deleted = :is_deleted + WHERE id = :id`, certificate) + + return err +} + +// List will return a list of certificates +func List(pageInfo model.PageInfo, filters []model.Filter) (ListResponse, error) { + var result ListResponse + var exampleModel Model + + defaultSort := model.Sort{ + Field: "name", + Direction: "ASC", + } + + db := database.GetInstance() + if db == nil { + return result, errors.ErrDatabaseUnavailable + } + + // Get count of items in this search + query, params := entity.ListQueryBuilder(exampleModel, tableName, &pageInfo, defaultSort, filters, getFilterMapFunctions(), true) + countRow := db.QueryRowx(query, params...) + var totalRows int + queryErr := countRow.Scan(&totalRows) + if queryErr != nil && queryErr != sql.ErrNoRows { + logger.Debug("%s -- %+v", query, params) + return result, queryErr + } + + // Get rows + var items []Model + query, params = entity.ListQueryBuilder(exampleModel, tableName, &pageInfo, defaultSort, filters, getFilterMapFunctions(), false) + err := db.Select(&items, query, params...) + if err != nil { + logger.Debug("%s -- %+v", query, params) + return result, err + } + + result = ListResponse{ + Items: items, + Total: totalRows, + Limit: pageInfo.Limit, + Offset: pageInfo.Offset, + Sort: pageInfo.Sort, + Filter: filters, + } + + return result, nil +} + +// GetByStatus will select rows that are ready for requesting +func GetByStatus(status string) ([]Model, error) { + models := make([]Model, 0) + db := database.GetInstance() + + query := fmt.Sprintf(` + SELECT + t.* + FROM "%s" t + INNER JOIN "dns_provider" d ON d."id" = t."dns_provider_id" + INNER JOIN "certificate_authority" c ON c."id" = t."certificate_authority_id" + WHERE + t."type" IN ("http", "dns") AND + t."status" = ? AND + t."certificate_authority_id" > 0 AND + t."dns_provider_id" > 0 AND + t."is_deleted" = 0 + `, tableName) + + params := []interface{}{StatusReady} + err := db.Select(&models, query, params...) + if err != nil && err != sql.ErrNoRows { + logger.Error("GetByStatusError", err) + logger.Debug("Query: %s -- %+v", query, params) + } + + return models, err +} diff --git a/backend/internal/entity/certificate/model.go b/backend/internal/entity/certificate/model.go new file mode 100644 index 00000000..dc0dec5f --- /dev/null +++ b/backend/internal/entity/certificate/model.go @@ -0,0 +1,178 @@ +package certificate + +import ( + "fmt" + "time" + + "npm/internal/database" + "npm/internal/entity/certificateauthority" + "npm/internal/entity/dnsprovider" + "npm/internal/types" +) + +const ( + tableName = "certificate" + + // TypeCustom ... + TypeCustom = "custom" + // TypeHTTP ... + TypeHTTP = "http" + // TypeDNS ... + TypeDNS = "dns" + // TypeMkcert ... + TypeMkcert = "mkcert" + // StatusReady is ready for certificate to be requested + StatusReady = "ready" + // StatusRequesting is process of being requested + StatusRequesting = "requesting" + // StatusFailed is a certicifate that failed to request + StatusFailed = "failed" + // StatusProvided is a certificate provided and ready for actual use + StatusProvided = "provided" +) + +// Model is the user model +type Model struct { + ID int `json:"id" db:"id" filter:"id,integer"` + CreatedOn types.DBDate `json:"created_on" db:"created_on" filter:"created_on,integer"` + ModifiedOn types.DBDate `json:"modified_on" db:"modified_on" filter:"modified_on,integer"` + ExpiresOn types.NullableDBDate `json:"expires_on" db:"expires_on" filter:"expires_on,integer"` + Type string `json:"type" db:"type" filter:"type,string"` + UserID int `json:"user_id" db:"user_id" filter:"user_id,integer"` + CertificateAuthorityID int `json:"certificate_authority_id" db:"certificate_authority_id" filter:"certificate_authority_id,integer"` + DNSProviderID int `json:"dns_provider_id" db:"dns_provider_id" filter:"dns_provider_id,integer"` + Name string `json:"name" db:"name" filter:"name,string"` + DomainNames types.JSONB `json:"domain_names" db:"domain_names" filter:"domain_names,string"` + Status string `json:"status" db:"status" filter:"status,string"` + ErrorMessage string `json:"error_message,omitempty" db:"error_message" filter:"error_message,string"` + Meta types.JSONB `json:"-" db:"meta"` + IsDeleted bool `json:"is_deleted,omitempty" db:"is_deleted"` + // Expansions: + CertificateAuthority *certificateauthority.Model `json:"certificate_authority,omitempty"` + DNSProvider *dnsprovider.Model `json:"dns_provider,omitempty"` +} + +func (m *Model) getByQuery(query string, params []interface{}) error { + return database.GetByQuery(m, query, params) +} + +// LoadByID will load from an ID +func (m *Model) LoadByID(id int) error { + query := fmt.Sprintf("SELECT * FROM `%s` WHERE id = ? AND is_deleted = ? LIMIT 1", tableName) + params := []interface{}{id, 0} + return m.getByQuery(query, params) +} + +// Touch will update model's timestamp(s) +func (m *Model) Touch(created bool) { + var d types.DBDate + d.Time = time.Now() + if created { + m.CreatedOn = d + } + m.ModifiedOn = d +} + +// Save will save this model to the DB +func (m *Model) Save() error { + var err error + + if m.UserID == 0 { + return fmt.Errorf("User ID must be specified") + } + + if !m.Validate() { + return fmt.Errorf("Certificate data is incorrect or incomplete for this type") + } + + m.setDefaultStatus() + + if m.ID == 0 { + m.ID, err = Create(m) + } else { + err = Update(m) + } + + return err +} + +// Delete will mark a certificate as deleted +func (m *Model) Delete() bool { + m.Touch(false) + m.IsDeleted = true + if err := m.Save(); err != nil { + return false + } + return true +} + +// Validate will make sure the data given is expected. This object is a bit complicated, +// as there could be multiple combinations of values. +func (m *Model) Validate() bool { + switch m.Type { + case TypeCustom: + // TODO: make sure meta contains required fields + return m.DNSProviderID == 0 && m.CertificateAuthorityID == 0 + + case TypeHTTP: + return m.DNSProviderID == 0 && m.CertificateAuthorityID > 0 + + case TypeDNS: + return m.DNSProviderID > 0 && m.CertificateAuthorityID > 0 + + case TypeMkcert: + return true + + default: + return false + } +} + +func (m *Model) setDefaultStatus() { + if m.ID == 0 { + // It's a new certificate + if m.Type == TypeCustom { + m.Status = StatusProvided + } else { + m.Status = StatusReady + } + } +} + +// Expand will populate attached objects for the model +func (m *Model) Expand() { + if m.CertificateAuthorityID > 0 { + certificateAuthority, _ := certificateauthority.GetByID(m.CertificateAuthorityID) + m.CertificateAuthority = &certificateAuthority + } + if m.DNSProviderID > 0 { + dnsProvider, _ := dnsprovider.GetByID(m.DNSProviderID) + m.DNSProvider = &dnsProvider + } +} + +// Request ... +func (m *Model) Request() error { + m.Expand() + m.Status = StatusRequesting + if err := m.Save(); err != nil { + return err + } + + // If error + m.Status = StatusFailed + m.ErrorMessage = "something" + if err := m.Save(); err != nil { + return err + } + + // If done + m.Status = StatusProvided + t := time.Now() + m.ExpiresOn.Time = &t + if err := m.Save(); err != nil { + return err + } + + return nil +} diff --git a/backend/internal/entity/certificate/structs.go b/backend/internal/entity/certificate/structs.go new file mode 100644 index 00000000..a9b99369 --- /dev/null +++ b/backend/internal/entity/certificate/structs.go @@ -0,0 +1,15 @@ +package certificate + +import ( + "npm/internal/model" +) + +// ListResponse is the JSON response for users list +type ListResponse struct { + Total int `json:"total"` + Offset int `json:"offset"` + Limit int `json:"limit"` + Sort []model.Sort `json:"sort"` + Filter []model.Filter `json:"filter,omitempty"` + Items []Model `json:"items,omitempty"` +} diff --git a/backend/internal/entity/certificateauthority/filters.go b/backend/internal/entity/certificateauthority/filters.go new file mode 100644 index 00000000..89d2d70f --- /dev/null +++ b/backend/internal/entity/certificateauthority/filters.go @@ -0,0 +1,25 @@ +package certificateauthority + +import ( + "npm/internal/entity" +) + +var filterMapFunctions = make(map[string]entity.FilterMapFunction) + +// getFilterMapFunctions is a map of functions that should be executed +// during the filtering process, if a field is defined here then the value in +// the filter will be given to the defined function and it will return a new +// value for use in the sql query. +func getFilterMapFunctions() map[string]entity.FilterMapFunction { + // if len(filterMapFunctions) == 0 { + // TODO: See internal/model/file_item.go:620 for an example + // } + + return filterMapFunctions +} + +// GetFilterSchema ... +func GetFilterSchema() string { + var m Model + return entity.GetFilterSchema(m) +} diff --git a/backend/internal/entity/certificateauthority/methods.go b/backend/internal/entity/certificateauthority/methods.go new file mode 100644 index 00000000..6a22b02f --- /dev/null +++ b/backend/internal/entity/certificateauthority/methods.go @@ -0,0 +1,125 @@ +package certificateauthority + +import ( + "database/sql" + goerrors "errors" + "fmt" + + "npm/internal/database" + "npm/internal/entity" + "npm/internal/errors" + "npm/internal/logger" + "npm/internal/model" +) + +// GetByID finds a row by ID +func GetByID(id int) (Model, error) { + var m Model + err := m.LoadByID(id) + return m, err +} + +// Create will create a row from this model +func Create(ca *Model) (int, error) { + if ca.ID != 0 { + return 0, goerrors.New("Cannot create certificate authority when model already has an ID") + } + + ca.Touch(true) + + db := database.GetInstance() + // nolint: gosec + result, err := db.NamedExec(`INSERT INTO `+fmt.Sprintf("`%s`", tableName)+` ( + created_on, + modified_on, + name, + acme2_url, + is_deleted + ) VALUES ( + :created_on, + :modified_on, + :name, + :acme2_url, + :is_deleted + )`, ca) + + if err != nil { + return 0, err + } + + last, lastErr := result.LastInsertId() + if lastErr != nil { + return 0, lastErr + } + + return int(last), nil +} + +// Update will Update a row from this model +func Update(ca *Model) error { + if ca.ID == 0 { + return goerrors.New("Cannot update certificate authority when model doesn't have an ID") + } + + ca.Touch(false) + + db := database.GetInstance() + // nolint: gosec + _, err := db.NamedExec(`UPDATE `+fmt.Sprintf("`%s`", tableName)+` SET + created_on = :created_on, + modified_on = :modified_on, + name = :name, + acme2_url = :acme2_url, + is_deleted = :is_deleted + WHERE id = :id`, ca) + + return err +} + +// List will return a list of certificates +func List(pageInfo model.PageInfo, filters []model.Filter) (ListResponse, error) { + var result ListResponse + var exampleModel Model + + defaultSort := model.Sort{ + Field: "name", + Direction: "ASC", + } + + db := database.GetInstance() + if db == nil { + return result, errors.ErrDatabaseUnavailable + } + + // Get count of items in this search + query, params := entity.ListQueryBuilder(exampleModel, tableName, &pageInfo, defaultSort, filters, getFilterMapFunctions(), true) + countRow := db.QueryRowx(query, params...) + var totalRows int + queryErr := countRow.Scan(&totalRows) + if queryErr != nil && queryErr != sql.ErrNoRows { + logger.Error("ListCertificateAuthoritiesError", queryErr) + logger.Debug("%s -- %+v", query, params) + return result, queryErr + } + + // Get rows + var items []Model + query, params = entity.ListQueryBuilder(exampleModel, tableName, &pageInfo, defaultSort, filters, getFilterMapFunctions(), false) + err := db.Select(&items, query, params...) + if err != nil { + logger.Error("ListCertificateAuthoritiesError", err) + logger.Debug("%s -- %+v", query, params) + return result, err + } + + result = ListResponse{ + Items: items, + Total: totalRows, + Limit: pageInfo.Limit, + Offset: pageInfo.Offset, + Sort: pageInfo.Sort, + Filter: filters, + } + + return result, nil +} diff --git a/backend/internal/entity/certificateauthority/model.go b/backend/internal/entity/certificateauthority/model.go new file mode 100644 index 00000000..8a3ce47b --- /dev/null +++ b/backend/internal/entity/certificateauthority/model.go @@ -0,0 +1,67 @@ +package certificateauthority + +import ( + "fmt" + "time" + + "npm/internal/database" + "npm/internal/types" +) + +const ( + tableName = "certificate_authority" +) + +// Model is the user model +type Model struct { + ID int `json:"id" db:"id" filter:"id,integer"` + CreatedOn types.DBDate `json:"created_on" db:"created_on" filter:"created_on,integer"` + ModifiedOn types.DBDate `json:"modified_on" db:"modified_on" filter:"modified_on,integer"` + Name string `json:"name" db:"name" filter:"name,string"` + Acme2URL string `json:"acme2_url" db:"acme2_url" filter:"acme2_url,string"` + IsDeleted bool `json:"is_deleted,omitempty" db:"is_deleted"` +} + +func (m *Model) getByQuery(query string, params []interface{}) error { + return database.GetByQuery(m, query, params) +} + +// LoadByID will load from an ID +func (m *Model) LoadByID(id int) error { + query := fmt.Sprintf("SELECT * FROM `%s` WHERE id = ? AND is_deleted = ? LIMIT 1", tableName) + params := []interface{}{id, 0} + return m.getByQuery(query, params) +} + +// Touch will update model's timestamp(s) +func (m *Model) Touch(created bool) { + var d types.DBDate + d.Time = time.Now() + if created { + m.CreatedOn = d + } + m.ModifiedOn = d +} + +// Save will save this model to the DB +func (m *Model) Save() error { + var err error + + if m.ID == 0 { + m.ID, err = Create(m) + } else { + err = Update(m) + } + + return err +} + +// Delete will mark a certificate as deleted +func (m *Model) Delete() bool { + m.Touch(false) + m.IsDeleted = true + if err := m.Save(); err != nil { + return false + } + return true +} diff --git a/backend/internal/entity/certificateauthority/structs.go b/backend/internal/entity/certificateauthority/structs.go new file mode 100644 index 00000000..85e3521a --- /dev/null +++ b/backend/internal/entity/certificateauthority/structs.go @@ -0,0 +1,15 @@ +package certificateauthority + +import ( + "npm/internal/model" +) + +// ListResponse is the JSON response for users list +type ListResponse struct { + Total int `json:"total"` + Offset int `json:"offset"` + Limit int `json:"limit"` + Sort []model.Sort `json:"sort"` + Filter []model.Filter `json:"filter,omitempty"` + Items []Model `json:"items,omitempty"` +} diff --git a/backend/internal/entity/dnsprovider/filters.go b/backend/internal/entity/dnsprovider/filters.go new file mode 100644 index 00000000..683e8ec6 --- /dev/null +++ b/backend/internal/entity/dnsprovider/filters.go @@ -0,0 +1,25 @@ +package dnsprovider + +import ( + "npm/internal/entity" +) + +var filterMapFunctions = make(map[string]entity.FilterMapFunction) + +// getFilterMapFunctions is a map of functions that should be executed +// during the filtering process, if a field is defined here then the value in +// the filter will be given to the defined function and it will return a new +// value for use in the sql query. +func getFilterMapFunctions() map[string]entity.FilterMapFunction { + // if len(filterMapFunctions) == 0 { + // TODO: See internal/model/file_item.go:620 for an example + // } + + return filterMapFunctions +} + +// GetFilterSchema ... +func GetFilterSchema() string { + var m Model + return entity.GetFilterSchema(m) +} diff --git a/backend/internal/entity/dnsprovider/methods.go b/backend/internal/entity/dnsprovider/methods.go new file mode 100644 index 00000000..1eca9ab9 --- /dev/null +++ b/backend/internal/entity/dnsprovider/methods.go @@ -0,0 +1,131 @@ +package dnsprovider + +import ( + "database/sql" + goerrors "errors" + "fmt" + + "npm/internal/database" + "npm/internal/entity" + "npm/internal/errors" + "npm/internal/logger" + "npm/internal/model" +) + +// GetByID finds a row by ID +func GetByID(id int) (Model, error) { + var m Model + err := m.LoadByID(id) + return m, err +} + +// Create will create a row from this model +func Create(provider *Model) (int, error) { + if provider.ID != 0 { + return 0, goerrors.New("Cannot create dns provider when model already has an ID") + } + + provider.Touch(true) + + db := database.GetInstance() + // nolint: gosec + result, err := db.NamedExec(`INSERT INTO `+fmt.Sprintf("`%s`", tableName)+` ( + created_on, + modified_on, + user_id, + provider_key, + name, + meta, + is_deleted + ) VALUES ( + :created_on, + :modified_on, + :user_id, + :provider_key, + :name, + :meta, + :is_deleted + )`, provider) + + if err != nil { + return 0, err + } + + last, lastErr := result.LastInsertId() + if lastErr != nil { + return 0, lastErr + } + + return int(last), nil +} + +// Update will Update a row from this model +func Update(provider *Model) error { + if provider.ID == 0 { + return goerrors.New("Cannot update dns provider when model doesn't have an ID") + } + + provider.Touch(false) + + db := database.GetInstance() + // nolint: gosec + _, err := db.NamedExec(`UPDATE `+fmt.Sprintf("`%s`", tableName)+` SET + created_on = :created_on, + modified_on = :modified_on, + user_id = :user_id, + provider_key = :provider_key, + name = :name, + meta = :meta, + is_deleted = :is_deleted + WHERE id = :id`, provider) + + return err +} + +// List will return a list of certificates +func List(pageInfo model.PageInfo, filters []model.Filter) (ListResponse, error) { + var result ListResponse + var exampleModel Model + + defaultSort := model.Sort{ + Field: "name", + Direction: "ASC", + } + + db := database.GetInstance() + if db == nil { + return result, errors.ErrDatabaseUnavailable + } + + // Get count of items in this search + query, params := entity.ListQueryBuilder(exampleModel, tableName, &pageInfo, defaultSort, filters, getFilterMapFunctions(), true) + countRow := db.QueryRowx(query, params...) + var totalRows int + queryErr := countRow.Scan(&totalRows) + if queryErr != nil && queryErr != sql.ErrNoRows { + logger.Error("ListDnsProvidersError", queryErr) + logger.Debug("%s -- %+v", query, params) + return result, queryErr + } + + // Get rows + var items []Model + query, params = entity.ListQueryBuilder(exampleModel, tableName, &pageInfo, defaultSort, filters, getFilterMapFunctions(), false) + err := db.Select(&items, query, params...) + if err != nil { + logger.Error("ListDnsProvidersError", err) + logger.Debug("%s -- %+v", query, params) + return result, err + } + + result = ListResponse{ + Items: items, + Total: totalRows, + Limit: pageInfo.Limit, + Offset: pageInfo.Offset, + Sort: pageInfo.Sort, + Filter: filters, + } + + return result, nil +} diff --git a/backend/internal/entity/dnsprovider/model.go b/backend/internal/entity/dnsprovider/model.go new file mode 100644 index 00000000..70eb22cb --- /dev/null +++ b/backend/internal/entity/dnsprovider/model.go @@ -0,0 +1,73 @@ +package dnsprovider + +import ( + "fmt" + "time" + + "npm/internal/database" + "npm/internal/types" +) + +const ( + tableName = "dns_provider" +) + +// Model is the user model +type Model struct { + ID int `json:"id" db:"id" filter:"id,integer"` + CreatedOn types.DBDate `json:"created_on" db:"created_on" filter:"created_on,integer"` + ModifiedOn types.DBDate `json:"modified_on" db:"modified_on" filter:"modified_on,integer"` + UserID int `json:"user_id" db:"user_id" filter:"user_id,integer"` + ProviderKey string `json:"provider_key" db:"provider_key" filter:"provider_key,string"` + Name string `json:"name" db:"name" filter:"name,string"` + Meta types.JSONB `json:"meta" db:"meta"` + IsDeleted bool `json:"is_deleted,omitempty" db:"is_deleted"` +} + +func (m *Model) getByQuery(query string, params []interface{}) error { + return database.GetByQuery(m, query, params) +} + +// LoadByID will load from an ID +func (m *Model) LoadByID(id int) error { + query := fmt.Sprintf("SELECT * FROM `%s` WHERE id = ? AND is_deleted = ? LIMIT 1", tableName) + params := []interface{}{id, 0} + return m.getByQuery(query, params) +} + +// Touch will update model's timestamp(s) +func (m *Model) Touch(created bool) { + var d types.DBDate + d.Time = time.Now() + if created { + m.CreatedOn = d + } + m.ModifiedOn = d +} + +// Save will save this model to the DB +func (m *Model) Save() error { + var err error + + if m.UserID == 0 { + return fmt.Errorf("User ID must be specified") + } + + if m.ID == 0 { + m.ID, err = Create(m) + } else { + err = Update(m) + } + + return err +} + +// Delete will mark a certificate as deleted +func (m *Model) Delete() bool { + m.Touch(false) + m.IsDeleted = true + if err := m.Save(); err != nil { + return false + } + return true +} diff --git a/backend/internal/entity/dnsprovider/structs.go b/backend/internal/entity/dnsprovider/structs.go new file mode 100644 index 00000000..835c947b --- /dev/null +++ b/backend/internal/entity/dnsprovider/structs.go @@ -0,0 +1,15 @@ +package dnsprovider + +import ( + "npm/internal/model" +) + +// ListResponse is the JSON response for the list +type ListResponse struct { + Total int `json:"total"` + Offset int `json:"offset"` + Limit int `json:"limit"` + Sort []model.Sort `json:"sort"` + Filter []model.Filter `json:"filter,omitempty"` + Items []Model `json:"items,omitempty"` +} diff --git a/backend/internal/entity/filters.go b/backend/internal/entity/filters.go new file mode 100644 index 00000000..55c5464f --- /dev/null +++ b/backend/internal/entity/filters.go @@ -0,0 +1,158 @@ +package entity + +import ( + "fmt" + "reflect" + "strings" + + "npm/internal/model" +) + +// FilterMapFunction is a filter map function +type FilterMapFunction func(value []string) []string + +// FilterTagName ... +const FilterTagName = "filter" + +// DBTagName ... +const DBTagName = "db" + +// GenerateSQLFromFilters will return a Query and params for use as WHERE clause in SQL queries +// This will use a AND where clause approach. +func GenerateSQLFromFilters(filters []model.Filter, fieldMap map[string]string, fieldMapFunctions map[string]FilterMapFunction) (string, []interface{}) { + clauses := make([]string, 0) + params := make([]interface{}, 0) + + for _, filter := range filters { + // Lookup this filter field from the functions map + if _, ok := fieldMapFunctions[filter.Field]; ok { + filter.Value = fieldMapFunctions[filter.Field](filter.Value) + } + + // Lookup this filter field from the name map + if _, ok := fieldMap[filter.Field]; ok { + filter.Field = fieldMap[filter.Field] + } + + // Special case for LIKE queries, the column needs to be uppercase for comparison + fieldName := fmt.Sprintf("`%s`", filter.Field) + if strings.ToLower(filter.Modifier) == "contains" || strings.ToLower(filter.Modifier) == "starts" || strings.ToLower(filter.Modifier) == "ends" { + fieldName = fmt.Sprintf("UPPER(`%s`)", filter.Field) + } + + clauses = append(clauses, fmt.Sprintf("%s %s", fieldName, getSQLAssignmentFromModifier(filter, ¶ms))) + } + + return strings.Join(clauses, " AND "), params +} + +func getSQLAssignmentFromModifier(filter model.Filter, params *[]interface{}) string { + var clause string + + // Quick hacks + if filter.Modifier == "in" && len(filter.Value) == 1 { + filter.Modifier = "equals" + } else if filter.Modifier == "notin" && len(filter.Value) == 1 { + filter.Modifier = "not" + } + + switch strings.ToLower(filter.Modifier) { + default: + clause = "= ?" + case "not": + clause = "!= ?" + case "min": + clause = ">= ?" + case "max": + clause = "<= ?" + case "greater": + clause = "> ?" + case "lesser": + clause = "< ?" + + // LIKE modifiers: + case "contains": + *params = append(*params, strings.ToUpper(filter.Value[0])) + return "LIKE '%' || ? || '%'" + case "starts": + *params = append(*params, strings.ToUpper(filter.Value[0])) + return "LIKE ? || '%'" + case "ends": + *params = append(*params, strings.ToUpper(filter.Value[0])) + return "LIKE '%' || ?" + + // Array parameter modifiers: + case "in": + s, p := buildInArray(filter.Value) + *params = append(*params, p...) + return fmt.Sprintf("IN (%s)", s) + case "notin": + s, p := buildInArray(filter.Value) + *params = append(*params, p...) + return fmt.Sprintf("NOT IN (%s)", s) + } + + *params = append(*params, filter.Value[0]) + return clause +} + +// GetFilterMap ... +func GetFilterMap(m interface{}) map[string]string { + var filterMap = make(map[string]string) + + // TypeOf returns the reflection Type that represents the dynamic type of variable. + // If variable is a nil interface value, TypeOf returns nil. + t := reflect.TypeOf(m) + + // Iterate over all available fields and read the tag value + for i := 0; i < t.NumField(); i++ { + // Get the field, returns https://golang.org/pkg/reflect/#StructField + field := t.Field(i) + + // Get the field tag value + filterTag := field.Tag.Get(FilterTagName) + dbTag := field.Tag.Get(DBTagName) + if filterTag != "" && dbTag != "" && dbTag != "-" && filterTag != "-" { + // Filter tag can be a 2 part thing: name,type + // ie: account_id,integer + // So we need to split and use the first part + parts := strings.Split(filterTag, ",") + filterMap[parts[0]] = dbTag + filterMap[filterTag] = dbTag + } + } + + return filterMap +} + +// GetDBColumns ... +func GetDBColumns(m interface{}) []string { + var columns []string + t := reflect.TypeOf(m) + + for i := 0; i < t.NumField(); i++ { + field := t.Field(i) + dbTag := field.Tag.Get(DBTagName) + if dbTag != "" && dbTag != "-" { + columns = append(columns, dbTag) + } + } + + return columns +} + +func buildInArray(items []string) (string, []interface{}) { + // Query string placeholder + strs := make([]string, len(items)) + for i := 0; i < len(items); i++ { + strs[i] = "?" + } + + // Params as interface + params := make([]interface{}, len(items)) + for i, v := range items { + params[i] = v + } + + return strings.Join(strs, ", "), params +} diff --git a/backend/internal/entity/filters_schema.go b/backend/internal/entity/filters_schema.go new file mode 100644 index 00000000..9686293b --- /dev/null +++ b/backend/internal/entity/filters_schema.go @@ -0,0 +1,223 @@ +package entity + +import ( + "fmt" + "reflect" + "strings" +) + +// GetFilterSchema creates a jsonschema for validating filters, based on the model +// object given and by reading the struct "filter" tags. +func GetFilterSchema(m interface{}) string { + var schemas []string + t := reflect.TypeOf(m) + + for i := 0; i < t.NumField(); i++ { + field := t.Field(i) + filterTag := field.Tag.Get(FilterTagName) + + if filterTag != "" && filterTag != "-" { + // split out tag value "field,filtreType" + // with a default filter type of string + items := strings.Split(filterTag, ",") + if len(items) == 1 { + items = append(items, "string") + } + + switch items[1] { + case "int": + fallthrough + case "integer": + schemas = append(schemas, intFieldSchema(items[0])) + case "bool": + fallthrough + case "boolean": + schemas = append(schemas, boolFieldSchema(items[0])) + case "date": + schemas = append(schemas, dateFieldSchema(items[0])) + case "regex": + if len(items) < 3 { + items = append(items, ".*") + } + schemas = append(schemas, regexFieldSchema(items[0], items[2])) + + default: + schemas = append(schemas, stringFieldSchema(items[0])) + } + } + } + + return newFilterSchema(schemas) +} + +// newFilterSchema is the main method to specify a new Filter Schema for use in Middleware +func newFilterSchema(fieldSchemas []string) string { + return fmt.Sprintf(baseFilterSchema, strings.Join(fieldSchemas, ", ")) +} + +// boolFieldSchema returns the Field Schema for a Boolean accepted value field +func boolFieldSchema(fieldName string) string { + return fmt.Sprintf(`{ + "type": "object", + "properties": { + "field": { + "type": "string", + "pattern": "^%s$" + }, + "modifier": %s, + "value": { + "oneOf": [ + %s, + { + "type": "array", + "items": %s + } + ] + } + } + }`, fieldName, boolModifiers, filterBool, filterBool) +} + +// intFieldSchema returns the Field Schema for a Integer accepted value field +func intFieldSchema(fieldName string) string { + return fmt.Sprintf(`{ + "type": "object", + "properties": { + "field": { + "type": "string", + "pattern": "^%s$" + }, + "modifier": %s, + "value": { + "oneOf": [ + { + "type": "string", + "pattern": "^[0-9]+$" + }, + { + "type": "array", + "items": { + "type": "string", + "pattern": "^[0-9]+$" + } + } + ] + } + } + }`, fieldName, allModifiers) +} + +// stringFieldSchema returns the Field Schema for a String accepted value field +func stringFieldSchema(fieldName string) string { + return fmt.Sprintf(`{ + "type": "object", + "properties": { + "field": { + "type": "string", + "pattern": "^%s$" + }, + "modifier": %s, + "value": { + "oneOf": [ + %s, + { + "type": "array", + "items": %s + } + ] + } + } + }`, fieldName, stringModifiers, filterString, filterString) +} + +// regexFieldSchema returns the Field Schema for a String accepted value field matching a Regex +func regexFieldSchema(fieldName string, regex string) string { + return fmt.Sprintf(`{ + "type": "object", + "properties": { + "field": { + "type": "string", + "pattern": "^%s$" + }, + "modifier": %s, + "value": { + "oneOf": [ + { + "type": "string", + "pattern": "%s" + }, + { + "type": "array", + "items": { + "type": "string", + "pattern": "%s" + } + } + ] + } + } + }`, fieldName, stringModifiers, regex, regex) +} + +// dateFieldSchema returns the Field Schema for a String accepted value field matching a Date format +func dateFieldSchema(fieldName string) string { + return fmt.Sprintf(`{ + "type": "object", + "properties": { + "field": { + "type": "string", + "pattern": "^%s$" + }, + "modifier": %s, + "value": { + "oneOf": [ + { + "type": "string", + "pattern": "^([12]\\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01]))$" + }, + { + "type": "array", + "items": { + "type": "string", + "pattern": "^([12]\\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01]))$" + } + } + ] + } + } + }`, fieldName, allModifiers) +} + +const allModifiers = `{ + "type": "string", + "pattern": "^(equals|not|contains|starts|ends|in|notin|min|max|greater|less)$" +}` + +const boolModifiers = `{ + "type": "string", + "pattern": "^(equals|not)$" +}` + +const stringModifiers = `{ + "type": "string", + "pattern": "^(equals|not|contains|starts|ends|in|notin)$" +}` + +const filterBool = `{ + "type": "string", + "pattern": "^(TRUE|true|t|yes|y|on|1|FALSE|f|false|n|no|off|0)$" +}` + +const filterString = `{ + "type": "string", + "minLength": 1 +}` + +const baseFilterSchema = `{ + "type": "array", + "items": { + "oneOf": [ + %s + ] + } +}` diff --git a/backend/internal/entity/host/filters.go b/backend/internal/entity/host/filters.go new file mode 100644 index 00000000..643a417d --- /dev/null +++ b/backend/internal/entity/host/filters.go @@ -0,0 +1,25 @@ +package host + +import ( + "npm/internal/entity" +) + +var filterMapFunctions = make(map[string]entity.FilterMapFunction) + +// getFilterMapFunctions is a map of functions that should be executed +// during the filtering process, if a field is defined here then the value in +// the filter will be given to the defined function and it will return a new +// value for use in the sql query. +func getFilterMapFunctions() map[string]entity.FilterMapFunction { + // if len(filterMapFunctions) == 0 { + // TODO: See internal/model/file_item.go:620 for an example + // } + + return filterMapFunctions +} + +// GetFilterSchema ... +func GetFilterSchema() string { + var m Model + return entity.GetFilterSchema(m) +} diff --git a/backend/internal/entity/host/methods.go b/backend/internal/entity/host/methods.go new file mode 100644 index 00000000..1c6cf7ed --- /dev/null +++ b/backend/internal/entity/host/methods.go @@ -0,0 +1,171 @@ +package host + +import ( + "database/sql" + goerrors "errors" + "fmt" + + "npm/internal/database" + "npm/internal/entity" + "npm/internal/errors" + "npm/internal/logger" + "npm/internal/model" +) + +// GetByID finds a Host by ID +func GetByID(id int) (Model, error) { + var m Model + err := m.LoadByID(id) + return m, err +} + +// Create will create a Host from this model +func Create(host *Model) (int, error) { + if host.ID != 0 { + return 0, goerrors.New("Cannot create host when model already has an ID") + } + + host.Touch(true) + + db := database.GetInstance() + // nolint: gosec + result, err := db.NamedExec(`INSERT INTO `+fmt.Sprintf("`%s`", tableName)+` ( + created_on, + modified_on, + user_id, + type, + listen_interface, + domain_names, + upstream_id, + certificate_id, + access_list_id, + ssl_forced, + caching_enabled, + block_exploits, + allow_websocket_upgrade, + http2_support, + hsts_enabled, + hsts_subdomains, + paths, + upstream_options, + advanced_config, + is_disabled, + is_deleted + ) VALUES ( + :created_on, + :modified_on, + :user_id, + :type, + :listen_interface, + :domain_names, + :upstream_id, + :certificate_id, + :access_list_id, + :ssl_forced, + :caching_enabled, + :block_exploits, + :allow_websocket_upgrade, + :http2_support, + :hsts_enabled, + :hsts_subdomains, + :paths, + :upstream_options, + :advanced_config, + :is_disabled, + :is_deleted + )`, host) + + if err != nil { + return 0, err + } + + last, lastErr := result.LastInsertId() + if lastErr != nil { + return 0, lastErr + } + + return int(last), nil +} + +// Update will Update a Host from this model +func Update(host *Model) error { + if host.ID == 0 { + return goerrors.New("Cannot update host when model doesn't have an ID") + } + + host.Touch(false) + + db := database.GetInstance() + // nolint: gosec + _, err := db.NamedExec(`UPDATE `+fmt.Sprintf("`%s`", tableName)+` SET + created_on = :created_on, + modified_on = :modified_on, + user_id = :user_id, + type = :type, + listen_interface = :listen_interface, + domain_names = :domain_names, + upstream_id = :upstream_id, + certificate_id = :certificate_id, + access_list_id = :access_list_id, + ssl_forced = :ssl_forced, + caching_enabled = :caching_enabled, + block_exploits = :block_exploits, + allow_websocket_upgrade = :allow_websocket_upgrade, + http2_support = :http2_support, + hsts_enabled = :hsts_enabled, + hsts_subdomains = :hsts_subdomains, + paths = :paths, + upstream_options = :upstream_options, + advanced_config = :advanced_config, + is_disabled = :is_disabled, + is_deleted = :is_deleted + WHERE id = :id`, host) + + return err +} + +// List will return a list of hosts +func List(pageInfo model.PageInfo, filters []model.Filter) (ListResponse, error) { + var result ListResponse + var exampleModel Model + + defaultSort := model.Sort{ + Field: "domain_names", + Direction: "ASC", + } + + db := database.GetInstance() + if db == nil { + return result, errors.ErrDatabaseUnavailable + } + + // Get count of items in this search + query, params := entity.ListQueryBuilder(exampleModel, tableName, &pageInfo, defaultSort, filters, getFilterMapFunctions(), true) + countRow := db.QueryRowx(query, params...) + var totalRows int + queryErr := countRow.Scan(&totalRows) + if queryErr != nil && queryErr != sql.ErrNoRows { + logger.Debug("%s -- %+v", query, params) + return result, queryErr + } + + // Get rows + var items []Model + query, params = entity.ListQueryBuilder(exampleModel, tableName, &pageInfo, defaultSort, filters, getFilterMapFunctions(), false) + err := db.Select(&items, query, params...) + if err != nil { + logger.Debug("%s -- %+v", query, params) + return result, err + } + + result = ListResponse{ + Items: items, + Total: totalRows, + Limit: pageInfo.Limit, + Offset: pageInfo.Offset, + Sort: pageInfo.Sort, + Filter: filters, + } + + return result, nil +} diff --git a/backend/internal/entity/host/model.go b/backend/internal/entity/host/model.go new file mode 100644 index 00000000..8d2f851d --- /dev/null +++ b/backend/internal/entity/host/model.go @@ -0,0 +1,94 @@ +package host + +import ( + "fmt" + "time" + + "npm/internal/database" + "npm/internal/types" +) + +const ( + tableName = "host" + + // ProxyHostType ... + ProxyHostType = "proxy" + // RedirectionHostType ... + RedirectionHostType = "redirection" + // DeadHostType ... + DeadHostType = "dead" +) + +// Model is the user model +type Model struct { + ID int `json:"id" db:"id" filter:"id,integer"` + CreatedOn types.DBDate `json:"created_on" db:"created_on" filter:"created_on,integer"` + ModifiedOn types.DBDate `json:"modified_on" db:"modified_on" filter:"modified_on,integer"` + UserID int `json:"user_id" db:"user_id" filter:"user_id,integer"` + Type string `json:"type" db:"type" filter:"type,string"` + ListenInterface string `json:"listen_interface" db:"listen_interface" filter:"listen_interface,string"` + DomainNames types.JSONB `json:"domain_names" db:"domain_names" filter:"domain_names,string"` + UpstreamID int `json:"upstream_id" db:"upstream_id" filter:"upstream_id,integer"` + CertificateID int `json:"certificate_id" db:"certificate_id" filter:"certificate_id,integer"` + AccessListID int `json:"access_list_id" db:"access_list_id" filter:"access_list_id,integer"` + SSLForced bool `json:"ssl_forced" db:"ssl_forced" filter:"ssl_forced,boolean"` + CachingEnabled bool `json:"caching_enabled" db:"caching_enabled" filter:"caching_enabled,boolean"` + BlockExploits bool `json:"block_exploits" db:"block_exploits" filter:"block_exploits,boolean"` + AllowWebsocketUpgrade bool `json:"allow_websocket_upgrade" db:"allow_websocket_upgrade" filter:"allow_websocket_upgrade,boolean"` + HTTP2Support bool `json:"http2_support" db:"http2_support" filter:"http2_support,boolean"` + HSTSEnabled bool `json:"hsts_enabled" db:"hsts_enabled" filter:"hsts_enabled,boolean"` + HSTSSubdomains bool `json:"hsts_subdomains" db:"hsts_subdomains" filter:"hsts_subdomains,boolean"` + Paths string `json:"paths" db:"paths" filter:"paths,string"` + UpstreamOptions string `json:"upstream_options" db:"upstream_options" filter:"upstream_options,string"` + AdvancedConfig string `json:"advanced_config" db:"advanced_config" filter:"advanced_config,string"` + IsDisabled bool `json:"is_disabled" db:"is_disabled" filter:"is_disabled,boolean"` + IsDeleted bool `json:"is_deleted,omitempty" db:"is_deleted"` +} + +func (m *Model) getByQuery(query string, params []interface{}) error { + return database.GetByQuery(m, query, params) +} + +// LoadByID will load from an ID +func (m *Model) LoadByID(id int) error { + query := fmt.Sprintf("SELECT * FROM `%s` WHERE id = ? AND is_deleted = ? LIMIT 1", tableName) + params := []interface{}{id, 0} + return m.getByQuery(query, params) +} + +// Touch will update model's timestamp(s) +func (m *Model) Touch(created bool) { + var d types.DBDate + d.Time = time.Now() + if created { + m.CreatedOn = d + } + m.ModifiedOn = d +} + +// Save will save this model to the DB +func (m *Model) Save() error { + var err error + + if m.UserID == 0 { + return fmt.Errorf("User ID must be specified") + } + + if m.ID == 0 { + m.ID, err = Create(m) + } else { + err = Update(m) + } + + return err +} + +// Delete will mark a host as deleted +func (m *Model) Delete() bool { + m.Touch(false) + m.IsDeleted = true + if err := m.Save(); err != nil { + return false + } + return true +} diff --git a/backend/internal/entity/host/structs.go b/backend/internal/entity/host/structs.go new file mode 100644 index 00000000..dda3d6fe --- /dev/null +++ b/backend/internal/entity/host/structs.go @@ -0,0 +1,15 @@ +package host + +import ( + "npm/internal/model" +) + +// ListResponse is the JSON response for this list +type ListResponse struct { + Total int `json:"total"` + Offset int `json:"offset"` + Limit int `json:"limit"` + Sort []model.Sort `json:"sort"` + Filter []model.Filter `json:"filter,omitempty"` + Items []Model `json:"items,omitempty"` +} diff --git a/backend/internal/entity/lists_query.go b/backend/internal/entity/lists_query.go new file mode 100644 index 00000000..c277f675 --- /dev/null +++ b/backend/internal/entity/lists_query.go @@ -0,0 +1,80 @@ +package entity + +import ( + "fmt" + "reflect" + "strings" + + "npm/internal/database" + "npm/internal/model" +) + +// ListQueryBuilder should be able to return the query and params to get items agnostically based +// on given params. +func ListQueryBuilder(modelExample interface{}, tableName string, pageInfo *model.PageInfo, defaultSort model.Sort, filters []model.Filter, filterMapFunctions map[string]FilterMapFunction, returnCount bool) (string, []interface{}) { + var queryStrings []string + var whereStrings []string + var params []interface{} + + if returnCount { + queryStrings = append(queryStrings, "SELECT COUNT(*)") + } else { + queryStrings = append(queryStrings, "SELECT *") + } + + // nolint: gosec + queryStrings = append(queryStrings, fmt.Sprintf("FROM `%s`", tableName)) + + // Append filters to where clause: + if filters != nil { + filterMap := GetFilterMap(modelExample) + filterQuery, filterParams := GenerateSQLFromFilters(filters, filterMap, filterMapFunctions) + whereStrings = []string{filterQuery} + params = append(params, filterParams...) + } + + // Add is deletee check if model has the field + if hasDeletedField(modelExample) { + params = append(params, 0) + whereStrings = append(whereStrings, "`is_deleted` = ?") + } + + // Append where clauses to query + if len(whereStrings) > 0 { + // nolint: gosec + queryStrings = append(queryStrings, fmt.Sprintf("WHERE %s", strings.Join(whereStrings, " AND "))) + } + + if !returnCount { + var orderBy string + columns := GetDBColumns(modelExample) + orderBy, pageInfo.Sort = database.BuildOrderBySQL(columns, &pageInfo.Sort) + + if orderBy != "" { + queryStrings = append(queryStrings, orderBy) + } else { + pageInfo.Sort = append(pageInfo.Sort, defaultSort) + queryStrings = append(queryStrings, fmt.Sprintf("ORDER BY `%v` %v", defaultSort.Field, defaultSort.Direction)) + } + + params = append(params, pageInfo.Offset) + params = append(params, pageInfo.Limit) + queryStrings = append(queryStrings, "LIMIT ?, ?") + } + + return strings.Join(queryStrings, " "), params +} + +func hasDeletedField(modelExample interface{}) bool { + t := reflect.TypeOf(modelExample) + + for i := 0; i < t.NumField(); i++ { + field := t.Field(i) + dbTag := field.Tag.Get(DBTagName) + if dbTag == "is_deleted" { + return true + } + } + + return false +} diff --git a/backend/internal/entity/setting/apply.go b/backend/internal/entity/setting/apply.go new file mode 100644 index 00000000..72211a8e --- /dev/null +++ b/backend/internal/entity/setting/apply.go @@ -0,0 +1,15 @@ +package setting + +import ( + "npm/internal/config" + "npm/internal/logger" +) + +// ApplySettings will load settings from the DB and apply them where required +func ApplySettings() { + logger.Debug("Applying Settings") + + // Error-reporting + m, _ := GetByName("error-reporting") + config.ErrorReporting = m.Value.Decoded.(bool) +} diff --git a/backend/internal/entity/setting/filters.go b/backend/internal/entity/setting/filters.go new file mode 100644 index 00000000..0c4d6371 --- /dev/null +++ b/backend/internal/entity/setting/filters.go @@ -0,0 +1,25 @@ +package setting + +import ( + "npm/internal/entity" +) + +var filterMapFunctions = make(map[string]entity.FilterMapFunction) + +// getFilterMapFunctions is a map of functions that should be executed +// during the filtering process, if a field is defined here then the value in +// the filter will be given to the defined function and it will return a new +// value for use in the sql query. +func getFilterMapFunctions() map[string]entity.FilterMapFunction { + // if len(filterMapFunctions) == 0 { + // TODO: See internal/model/file_item.go:620 for an example + // } + + return filterMapFunctions +} + +// GetFilterSchema ... +func GetFilterSchema() string { + var m Model + return entity.GetFilterSchema(m) +} diff --git a/backend/internal/entity/setting/methods.go b/backend/internal/entity/setting/methods.go new file mode 100644 index 00000000..02211a03 --- /dev/null +++ b/backend/internal/entity/setting/methods.go @@ -0,0 +1,124 @@ +package setting + +import ( + "database/sql" + goerrors "errors" + "fmt" + + "npm/internal/database" + "npm/internal/entity" + "npm/internal/errors" + "npm/internal/model" +) + +// GetByID finds a setting by ID +func GetByID(id int) (Model, error) { + var m Model + err := m.LoadByID(id) + return m, err +} + +// GetByName finds a setting by name +func GetByName(name string) (Model, error) { + var m Model + err := m.LoadByName(name) + return m, err +} + +// Create will Create a Setting from this model +func Create(setting *Model) (int, error) { + if setting.ID != 0 { + return 0, goerrors.New("Cannot create setting when model already has an ID") + } + + setting.Touch(true) + + db := database.GetInstance() + // nolint: gosec + result, err := db.NamedExec(`INSERT INTO `+fmt.Sprintf("`%s`", tableName)+` ( + created_on, + modified_on, + name, + value + ) VALUES ( + :created_on, + :modified_on, + :name, + :value + )`, setting) + + if err != nil { + return 0, err + } + + last, lastErr := result.LastInsertId() + if lastErr != nil { + return 0, lastErr + } + + return int(last), nil +} + +// Update will Update a Setting from this model +func Update(setting *Model) error { + if setting.ID == 0 { + return goerrors.New("Cannot update setting when model doesn't have an ID") + } + + setting.Touch(false) + + db := database.GetInstance() + // nolint: gosec + _, err := db.NamedExec(`UPDATE `+fmt.Sprintf("`%s`", tableName)+` SET + created_on = :created_on, + modified_on = :modified_on, + name = :name, + value = :value + WHERE id = :id`, setting) + + return err +} + +// List will return a list of settings +func List(pageInfo model.PageInfo, filters []model.Filter) (ListResponse, error) { + var result ListResponse + var exampleModel Model + + defaultSort := model.Sort{ + Field: "name", + Direction: "ASC", + } + + db := database.GetInstance() + if db == nil { + return result, errors.ErrDatabaseUnavailable + } + + // Get count of items in this search + query, params := entity.ListQueryBuilder(exampleModel, tableName, &pageInfo, defaultSort, filters, getFilterMapFunctions(), true) + countRow := db.QueryRowx(query, params...) + var totalRows int + queryErr := countRow.Scan(&totalRows) + if queryErr != nil && queryErr != sql.ErrNoRows { + return result, queryErr + } + + // Get rows + var items []Model + query, params = entity.ListQueryBuilder(exampleModel, tableName, &pageInfo, defaultSort, filters, getFilterMapFunctions(), false) + err := db.Select(&items, query, params...) + if err != nil { + return result, err + } + + result = ListResponse{ + Items: items, + Total: totalRows, + Limit: pageInfo.Limit, + Offset: pageInfo.Offset, + Sort: pageInfo.Sort, + Filter: filters, + } + + return result, nil +} diff --git a/backend/internal/entity/setting/model.go b/backend/internal/entity/setting/model.go new file mode 100644 index 00000000..303268ae --- /dev/null +++ b/backend/internal/entity/setting/model.go @@ -0,0 +1,69 @@ +package setting + +import ( + "fmt" + "strings" + "time" + + "npm/internal/database" + "npm/internal/types" +) + +const ( + tableName = "setting" +) + +// Model is the user model +type Model struct { + ID int `json:"id" db:"id" filter:"id,integer"` + CreatedOn types.DBDate `json:"created_on" db:"created_on" filter:"created_on,integer"` + ModifiedOn types.DBDate `json:"modified_on" db:"modified_on" filter:"modified_on,integer"` + Name string `json:"name" db:"name" filter:"name,string"` + Value types.JSONB `json:"value" db:"value"` +} + +func (m *Model) getByQuery(query string, params []interface{}) error { + return database.GetByQuery(m, query, params) +} + +// LoadByID will load from an ID +func (m *Model) LoadByID(id int) error { + query := fmt.Sprintf("SELECT * FROM `%s` WHERE `id` = ? LIMIT 1", tableName) + params := []interface{}{id} + return m.getByQuery(query, params) +} + +// LoadByName will load from a Name +func (m *Model) LoadByName(name string) error { + query := fmt.Sprintf("SELECT * FROM `%s` WHERE LOWER(`name`) = ? LIMIT 1", tableName) + params := []interface{}{strings.TrimSpace(strings.ToLower(name))} + return m.getByQuery(query, params) +} + +// Touch will update model's timestamp(s) +func (m *Model) Touch(created bool) { + var d types.DBDate + d.Time = time.Now() + if created { + m.CreatedOn = d + } + m.ModifiedOn = d +} + +// Save will save this model to the DB +func (m *Model) Save() error { + var err error + + if m.ID == 0 { + m.ID, err = Create(m) + } else { + err = Update(m) + } + + // Reapply settings + if err == nil { + ApplySettings() + } + + return err +} diff --git a/backend/internal/entity/setting/structs.go b/backend/internal/entity/setting/structs.go new file mode 100644 index 00000000..d585e9a1 --- /dev/null +++ b/backend/internal/entity/setting/structs.go @@ -0,0 +1,15 @@ +package setting + +import ( + "npm/internal/model" +) + +// ListResponse is the JSON response for users list +type ListResponse struct { + Total int `json:"total"` + Offset int `json:"offset"` + Limit int `json:"limit"` + Sort []model.Sort `json:"sort"` + Filter []model.Filter `json:"filter,omitempty"` + Items []Model `json:"items,omitempty"` +} diff --git a/backend/internal/entity/stream/filters.go b/backend/internal/entity/stream/filters.go new file mode 100644 index 00000000..f0078894 --- /dev/null +++ b/backend/internal/entity/stream/filters.go @@ -0,0 +1,25 @@ +package stream + +import ( + "npm/internal/entity" +) + +var filterMapFunctions = make(map[string]entity.FilterMapFunction) + +// getFilterMapFunctions is a map of functions that should be executed +// during the filtering process, if a field is defined here then the value in +// the filter will be given to the defined function and it will return a new +// value for use in the sql query. +func getFilterMapFunctions() map[string]entity.FilterMapFunction { + // if len(filterMapFunctions) == 0 { + // TODO: See internal/model/file_item.go:620 for an example + // } + + return filterMapFunctions +} + +// GetFilterSchema ... +func GetFilterSchema() string { + var m Model + return entity.GetFilterSchema(m) +} diff --git a/backend/internal/entity/stream/methods.go b/backend/internal/entity/stream/methods.go new file mode 100644 index 00000000..02ddd4d6 --- /dev/null +++ b/backend/internal/entity/stream/methods.go @@ -0,0 +1,135 @@ +package stream + +import ( + "database/sql" + goerrors "errors" + "fmt" + + "npm/internal/database" + "npm/internal/entity" + "npm/internal/errors" + "npm/internal/logger" + "npm/internal/model" +) + +// GetByID finds a auth by ID +func GetByID(id int) (Model, error) { + var m Model + err := m.LoadByID(id) + return m, err +} + +// Create will create a Auth from this model +func Create(host *Model) (int, error) { + if host.ID != 0 { + return 0, goerrors.New("Cannot create stream when model already has an ID") + } + + host.Touch(true) + + db := database.GetInstance() + // nolint: gosec + result, err := db.NamedExec(`INSERT INTO `+fmt.Sprintf("`%s`", tableName)+` ( + created_on, + modified_on, + user_id, + provider, + name, + domain_names, + expires_on, + meta, + is_deleted + ) VALUES ( + :created_on, + :modified_on, + :user_id, + :provider, + :name, + :domain_names, + :expires_on, + :meta, + :is_deleted + )`, host) + + if err != nil { + return 0, err + } + + last, lastErr := result.LastInsertId() + if lastErr != nil { + return 0, lastErr + } + + return int(last), nil +} + +// Update will Update a Host from this model +func Update(host *Model) error { + if host.ID == 0 { + return goerrors.New("Cannot update stream when model doesn't have an ID") + } + + host.Touch(false) + + db := database.GetInstance() + // nolint: gosec + _, err := db.NamedExec(`UPDATE `+fmt.Sprintf("`%s`", tableName)+` SET + created_on = :created_on, + modified_on = :modified_on, + user_id = :user_id, + provider = :provider, + name = :name, + domain_names = :domain_names, + expires_on = :expires_on, + meta = :meta, + is_deleted = :is_deleted + WHERE id = :id`, host) + + return err +} + +// List will return a list of hosts +func List(pageInfo model.PageInfo, filters []model.Filter) (ListResponse, error) { + var result ListResponse + var exampleModel Model + + defaultSort := model.Sort{ + Field: "name", + Direction: "ASC", + } + + db := database.GetInstance() + if db == nil { + return result, errors.ErrDatabaseUnavailable + } + + // Get count of items in this search + query, params := entity.ListQueryBuilder(exampleModel, tableName, &pageInfo, defaultSort, filters, getFilterMapFunctions(), true) + countRow := db.QueryRowx(query, params...) + var totalRows int + queryErr := countRow.Scan(&totalRows) + if queryErr != nil && queryErr != sql.ErrNoRows { + logger.Debug("%s -- %+v", query, params) + return result, queryErr + } + + // Get rows + var items []Model + query, params = entity.ListQueryBuilder(exampleModel, tableName, &pageInfo, defaultSort, filters, getFilterMapFunctions(), false) + err := db.Select(&items, query, params...) + if err != nil { + logger.Debug("%s -- %+v", query, params) + return result, err + } + + result = ListResponse{ + Items: items, + Total: totalRows, + Limit: pageInfo.Limit, + Offset: pageInfo.Offset, + Sort: pageInfo.Sort, + Filter: filters, + } + + return result, nil +} diff --git a/backend/internal/entity/stream/model.go b/backend/internal/entity/stream/model.go new file mode 100644 index 00000000..60f47f75 --- /dev/null +++ b/backend/internal/entity/stream/model.go @@ -0,0 +1,75 @@ +package stream + +import ( + "fmt" + "time" + + "npm/internal/database" + "npm/internal/types" +) + +const ( + tableName = "stream" +) + +// Model is the user model +type Model struct { + ID int `json:"id" db:"id" filter:"id,integer"` + CreatedOn types.DBDate `json:"created_on" db:"created_on" filter:"created_on,integer"` + ModifiedOn types.DBDate `json:"modified_on" db:"modified_on" filter:"modified_on,integer"` + ExpiresOn types.DBDate `json:"expires_on" db:"expires_on" filter:"expires_on,integer"` + UserID int `json:"user_id" db:"user_id" filter:"user_id,integer"` + Provider string `json:"provider" db:"provider" filter:"provider,string"` + Name string `json:"name" db:"name" filter:"name,string"` + DomainNames types.JSONB `json:"domain_names" db:"domain_names" filter:"domain_names,string"` + Meta types.JSONB `json:"-" db:"meta"` + IsDeleted bool `json:"is_deleted,omitempty" db:"is_deleted"` +} + +func (m *Model) getByQuery(query string, params []interface{}) error { + return database.GetByQuery(m, query, params) +} + +// LoadByID will load from an ID +func (m *Model) LoadByID(id int) error { + query := fmt.Sprintf("SELECT * FROM `%s` WHERE id = ? AND is_deleted = ? LIMIT 1", tableName) + params := []interface{}{id, 0} + return m.getByQuery(query, params) +} + +// Touch will update model's timestamp(s) +func (m *Model) Touch(created bool) { + var d types.DBDate + d.Time = time.Now() + if created { + m.CreatedOn = d + } + m.ModifiedOn = d +} + +// Save will save this model to the DB +func (m *Model) Save() error { + var err error + + if m.UserID == 0 { + return fmt.Errorf("User ID must be specified") + } + + if m.ID == 0 { + m.ID, err = Create(m) + } else { + err = Update(m) + } + + return err +} + +// Delete will mark a host as deleted +func (m *Model) Delete() bool { + m.Touch(false) + m.IsDeleted = true + if err := m.Save(); err != nil { + return false + } + return true +} diff --git a/backend/internal/entity/stream/structs.go b/backend/internal/entity/stream/structs.go new file mode 100644 index 00000000..ec732c13 --- /dev/null +++ b/backend/internal/entity/stream/structs.go @@ -0,0 +1,15 @@ +package stream + +import ( + "npm/internal/model" +) + +// ListResponse is the JSON response for this list +type ListResponse struct { + Total int `json:"total"` + Offset int `json:"offset"` + Limit int `json:"limit"` + Sort []model.Sort `json:"sort"` + Filter []model.Filter `json:"filter,omitempty"` + Items []Model `json:"items,omitempty"` +} diff --git a/backend/internal/entity/user/filters.go b/backend/internal/entity/user/filters.go new file mode 100644 index 00000000..ea1c1640 --- /dev/null +++ b/backend/internal/entity/user/filters.go @@ -0,0 +1,25 @@ +package user + +import ( + "npm/internal/entity" +) + +var filterMapFunctions = make(map[string]entity.FilterMapFunction) + +// getFilterMapFunctions is a map of functions that should be executed +// during the filtering process, if a field is defined here then the value in +// the filter will be given to the defined function and it will return a new +// value for use in the sql query. +func getFilterMapFunctions() map[string]entity.FilterMapFunction { + // if len(filterMapFunctions) == 0 { + // TODO: See internal/model/file_item.go:620 for an example + // } + + return filterMapFunctions +} + +// GetFilterSchema ... +func GetFilterSchema() string { + var m Model + return entity.GetFilterSchema(m) +} diff --git a/backend/internal/entity/user/methods.go b/backend/internal/entity/user/methods.go new file mode 100644 index 00000000..211cadd0 --- /dev/null +++ b/backend/internal/entity/user/methods.go @@ -0,0 +1,181 @@ +package user + +import ( + "database/sql" + goerrors "errors" + "fmt" + + "npm/internal/database" + "npm/internal/entity" + "npm/internal/errors" + "npm/internal/logger" + "npm/internal/model" +) + +// GetByID finds a user by ID +func GetByID(id int) (Model, error) { + var m Model + err := m.LoadByID(id) + return m, err +} + +// GetByEmail finds a user by email +func GetByEmail(email string) (Model, error) { + var m Model + err := m.LoadByEmail(email) + return m, err +} + +// Create will create a User from given model +func Create(user *Model) (int, error) { + // We need to ensure that a user can't be created with the same email + // as an existing non-deleted user. Usually you would do this with the + // database schema, but it's a bit more complex because of the is_deleted field. + + if user.ID != 0 { + return 0, goerrors.New("Cannot create user when model already has an ID") + } + + // Check if an existing user with this email exists + _, err := GetByEmail(user.Email) + if err == nil { + return 0, errors.ErrDuplicateEmailUser + } + + user.Touch(true) + + db := database.GetInstance() + // nolint: gosec + result, err := db.NamedExec(`INSERT INTO `+fmt.Sprintf("`%s`", tableName)+` ( + created_on, + modified_on, + name, + nickname, + email, + roles, + is_disabled + ) VALUES ( + :created_on, + :modified_on, + :name, + :nickname, + :email, + :roles, + :is_disabled + )`, user) + + if err != nil { + return 0, err + } + + last, lastErr := result.LastInsertId() + if lastErr != nil { + return 0, lastErr + } + + return int(last), nil +} + +// Update will Update a User from this model +func Update(user *Model) error { + if user.ID == 0 { + return goerrors.New("Cannot update user when model doesn't have an ID") + } + + // Check that the email address isn't associated with another user + if existingUser, _ := GetByEmail(user.Email); existingUser.ID != 0 && existingUser.ID != user.ID { + return errors.ErrDuplicateEmailUser + } + + user.Touch(false) + + db := database.GetInstance() + // nolint: gosec + _, err := db.NamedExec(`UPDATE `+fmt.Sprintf("`%s`", tableName)+` SET + created_on = :created_on, + modified_on = :modified_on, + name = :name, + nickname = :nickname, + email = :email, + roles = :roles, + is_disabled = :is_disabled, + is_deleted = :is_deleted + WHERE id = :id`, user) + + return err +} + +// IsEnabled is used by middleware to ensure the user is still enabled +// returns (userExist, isEnabled) +func IsEnabled(userID int) (bool, bool) { + // nolint: gosec + query := `SELECT is_disabled FROM ` + fmt.Sprintf("`%s`", tableName) + ` WHERE id = ? AND is_deleted = ?` + disabled := true + db := database.GetInstance() + err := db.QueryRowx(query, userID, 0).Scan(&disabled) + + if err == sql.ErrNoRows { + return false, false + } else if err != nil { + logger.Error("QueryError", err) + } + + return true, !disabled +} + +// List will return a list of users +func List(pageInfo model.PageInfo, filters []model.Filter) (ListResponse, error) { + var result ListResponse + var exampleModel Model + + defaultSort := model.Sort{ + Field: "name", + Direction: "ASC", + } + + db := database.GetInstance() + if db == nil { + return result, errors.ErrDatabaseUnavailable + } + + // Get count of items in this search + query, params := entity.ListQueryBuilder(exampleModel, tableName, &pageInfo, defaultSort, filters, getFilterMapFunctions(), true) + logger.Debug("Query: %s -- %+v", query, params) + countRow := db.QueryRowx(query, params...) + var totalRows int + queryErr := countRow.Scan(&totalRows) + if queryErr != nil && queryErr != sql.ErrNoRows { + return result, queryErr + } + + // Get rows + var items []Model + query, params = entity.ListQueryBuilder(exampleModel, tableName, &pageInfo, defaultSort, filters, getFilterMapFunctions(), false) + logger.Debug("Query: %s -- %+v", query, params) + err := db.Select(&items, query, params...) + if err != nil { + return result, err + } + + for idx := range items { + items[idx].generateGravatar() + } + + result = ListResponse{ + Items: items, + Total: totalRows, + Limit: pageInfo.Limit, + Offset: pageInfo.Offset, + Sort: pageInfo.Sort, + Filter: filters, + } + + return result, nil +} + +// DeleteAll will do just that, and should only be used for testing purposes. +func DeleteAll() error { + db := database.GetInstance() + _, err := db.Exec(fmt.Sprintf("DELETE FROM `%s`", tableName)) + return err +} diff --git a/backend/internal/entity/user/model.go b/backend/internal/entity/user/model.go new file mode 100644 index 00000000..0e605dd1 --- /dev/null +++ b/backend/internal/entity/user/model.go @@ -0,0 +1,97 @@ +package user + +import ( + "fmt" + "strings" + "time" + + "npm/internal/database" + "npm/internal/entity/auth" + "npm/internal/types" + + "github.com/drexedam/gravatar" +) + +const ( + tableName = "user" +) + +// Model is the user model +type Model struct { + ID int `json:"id" db:"id" filter:"id,integer"` + Name string `json:"name" db:"name" filter:"name,string"` + Nickname string `json:"nickname" db:"nickname" filter:"nickname,string"` + Email string `json:"email" db:"email" filter:"email,email"` + CreatedOn types.DBDate `json:"created_on" db:"created_on" filter:"created_on,integer"` + ModifiedOn types.DBDate `json:"modified_on" db:"modified_on" filter:"modified_on,integer"` + Roles types.Roles `json:"roles" db:"roles"` + GravatarURL string `json:"gravatar_url"` + IsDisabled bool `json:"is_disabled" db:"is_disabled" filter:"is_disabled,boolean"` + IsDeleted bool `json:"is_deleted,omitempty" db:"is_deleted"` + // Expansions + Auth *auth.Model `json:"auth,omitempty" db:"-"` +} + +func (m *Model) getByQuery(query string, params []interface{}) error { + err := database.GetByQuery(m, query, params) + m.generateGravatar() + return err +} + +// LoadByID will load from an ID +func (m *Model) LoadByID(id int) error { + query := fmt.Sprintf("SELECT * FROM `%s` WHERE id = ? AND is_deleted = ? LIMIT 1", tableName) + params := []interface{}{id, false} + return m.getByQuery(query, params) +} + +// LoadByEmail will load from an Email +func (m *Model) LoadByEmail(email string) error { + query := fmt.Sprintf("SELECT * FROM `%s` WHERE email = ? AND is_deleted = ? LIMIT 1", tableName) + params := []interface{}{strings.TrimSpace(strings.ToLower(email)), false} + return m.getByQuery(query, params) +} + +// Touch will update model's timestamp(s) +func (m *Model) Touch(created bool) { + var d types.DBDate + d.Time = time.Now() + if created { + m.CreatedOn = d + } + m.ModifiedOn = d + m.generateGravatar() +} + +// Save will save this model to the DB +func (m *Model) Save() error { + var err error + // Ensure email is nice + m.Email = strings.TrimSpace(strings.ToLower(m.Email)) + + if m.ID == 0 { + m.ID, err = Create(m) + } else { + err = Update(m) + } + + return err +} + +// Delete will mark a user as deleted +func (m *Model) Delete() bool { + m.Touch(false) + m.IsDeleted = true + if err := m.Save(); err != nil { + return false + } + return true +} + +func (m *Model) generateGravatar() { + m.GravatarURL = gravatar.New(m.Email). + Size(128). + Default(gravatar.MysteryMan). + Rating(gravatar.Pg). + AvatarURL() +} diff --git a/backend/internal/entity/user/structs.go b/backend/internal/entity/user/structs.go new file mode 100644 index 00000000..f9f4490e --- /dev/null +++ b/backend/internal/entity/user/structs.go @@ -0,0 +1,15 @@ +package user + +import ( + "npm/internal/model" +) + +// ListResponse is the JSON response for users list +type ListResponse struct { + Total int `json:"total"` + Offset int `json:"offset"` + Limit int `json:"limit"` + Sort []model.Sort `json:"sort"` + Filter []model.Filter `json:"filter,omitempty"` + Items []Model `json:"items,omitempty"` +} diff --git a/backend/internal/errors/errors.go b/backend/internal/errors/errors.go new file mode 100644 index 00000000..3336e64a --- /dev/null +++ b/backend/internal/errors/errors.go @@ -0,0 +1,10 @@ +package errors + +import "errors" + +// All error messages used by the service package to report +// problems back to calling clients +var ( + ErrDatabaseUnavailable = errors.New("Database is unavailable") + ErrDuplicateEmailUser = errors.New("A user already exists with this email address") +) diff --git a/backend/internal/host.js b/backend/internal/host.js deleted file mode 100644 index 58e1d09a..00000000 --- a/backend/internal/host.js +++ /dev/null @@ -1,235 +0,0 @@ -const _ = require('lodash'); -const proxyHostModel = require('../models/proxy_host'); -const redirectionHostModel = require('../models/redirection_host'); -const deadHostModel = require('../models/dead_host'); - -const internalHost = { - - /** - * Makes sure that the ssl_* and hsts_* fields play nicely together. - * ie: if there is no cert, then force_ssl is off. - * if force_ssl is off, then hsts_enabled is definitely off. - * - * @param {object} data - * @param {object} [existing_data] - * @returns {object} - */ - cleanSslHstsData: function (data, existing_data) { - existing_data = existing_data === undefined ? {} : existing_data; - - let combined_data = _.assign({}, existing_data, data); - - if (!combined_data.certificate_id) { - combined_data.ssl_forced = false; - combined_data.http2_support = false; - } - - if (!combined_data.ssl_forced) { - combined_data.hsts_enabled = false; - } - - if (!combined_data.hsts_enabled) { - combined_data.hsts_subdomains = false; - } - - return combined_data; - }, - - /** - * used by the getAll functions of hosts, this removes the certificate meta if present - * - * @param {Array} rows - * @returns {Array} - */ - cleanAllRowsCertificateMeta: function (rows) { - rows.map(function (row, idx) { - if (typeof rows[idx].certificate !== 'undefined' && rows[idx].certificate) { - rows[idx].certificate.meta = {}; - } - }); - - return rows; - }, - - /** - * used by the get/update functions of hosts, this removes the certificate meta if present - * - * @param {Object} row - * @returns {Object} - */ - cleanRowCertificateMeta: function (row) { - if (typeof row.certificate !== 'undefined' && row.certificate) { - row.certificate.meta = {}; - } - - return row; - }, - - /** - * This returns all the host types with any domain listed in the provided domain_names array. - * This is used by the certificates to temporarily disable any host that is using the domain - * - * @param {Array} domain_names - * @returns {Promise} - */ - getHostsWithDomains: function (domain_names) { - let promises = [ - proxyHostModel - .query() - .where('is_deleted', 0), - redirectionHostModel - .query() - .where('is_deleted', 0), - deadHostModel - .query() - .where('is_deleted', 0) - ]; - - return Promise.all(promises) - .then((promises_results) => { - let response_object = { - total_count: 0, - dead_hosts: [], - proxy_hosts: [], - redirection_hosts: [] - }; - - if (promises_results[0]) { - // Proxy Hosts - response_object.proxy_hosts = internalHost._getHostsWithDomains(promises_results[0], domain_names); - response_object.total_count += response_object.proxy_hosts.length; - } - - if (promises_results[1]) { - // Redirection Hosts - response_object.redirection_hosts = internalHost._getHostsWithDomains(promises_results[1], domain_names); - response_object.total_count += response_object.redirection_hosts.length; - } - - if (promises_results[2]) { - // Dead Hosts - response_object.dead_hosts = internalHost._getHostsWithDomains(promises_results[2], domain_names); - response_object.total_count += response_object.dead_hosts.length; - } - - return response_object; - }); - }, - - /** - * Internal use only, checks to see if the domain is already taken by any other record - * - * @param {String} hostname - * @param {String} [ignore_type] 'proxy', 'redirection', 'dead' - * @param {Integer} [ignore_id] Must be supplied if type was also supplied - * @returns {Promise} - */ - isHostnameTaken: function (hostname, ignore_type, ignore_id) { - let promises = [ - proxyHostModel - .query() - .where('is_deleted', 0) - .andWhere('domain_names', 'like', '%' + hostname + '%'), - redirectionHostModel - .query() - .where('is_deleted', 0) - .andWhere('domain_names', 'like', '%' + hostname + '%'), - deadHostModel - .query() - .where('is_deleted', 0) - .andWhere('domain_names', 'like', '%' + hostname + '%') - ]; - - return Promise.all(promises) - .then((promises_results) => { - let is_taken = false; - - if (promises_results[0]) { - // Proxy Hosts - if (internalHost._checkHostnameRecordsTaken(hostname, promises_results[0], ignore_type === 'proxy' && ignore_id ? ignore_id : 0)) { - is_taken = true; - } - } - - if (promises_results[1]) { - // Redirection Hosts - if (internalHost._checkHostnameRecordsTaken(hostname, promises_results[1], ignore_type === 'redirection' && ignore_id ? ignore_id : 0)) { - is_taken = true; - } - } - - if (promises_results[2]) { - // Dead Hosts - if (internalHost._checkHostnameRecordsTaken(hostname, promises_results[2], ignore_type === 'dead' && ignore_id ? ignore_id : 0)) { - is_taken = true; - } - } - - return { - hostname: hostname, - is_taken: is_taken - }; - }); - }, - - /** - * Private call only - * - * @param {String} hostname - * @param {Array} existing_rows - * @param {Integer} [ignore_id] - * @returns {Boolean} - */ - _checkHostnameRecordsTaken: function (hostname, existing_rows, ignore_id) { - let is_taken = false; - - if (existing_rows && existing_rows.length) { - existing_rows.map(function (existing_row) { - existing_row.domain_names.map(function (existing_hostname) { - // Does this domain match? - if (existing_hostname.toLowerCase() === hostname.toLowerCase()) { - if (!ignore_id || ignore_id !== existing_row.id) { - is_taken = true; - } - } - }); - }); - } - - return is_taken; - }, - - /** - * Private call only - * - * @param {Array} hosts - * @param {Array} domain_names - * @returns {Array} - */ - _getHostsWithDomains: function (hosts, domain_names) { - let response = []; - - if (hosts && hosts.length) { - hosts.map(function (host) { - let host_matches = false; - - domain_names.map(function (domain_name) { - host.domain_names.map(function (host_domain_name) { - if (domain_name.toLowerCase() === host_domain_name.toLowerCase()) { - host_matches = true; - } - }); - }); - - if (host_matches) { - response.push(host); - } - }); - } - - return response; - } - -}; - -module.exports = internalHost; diff --git a/backend/internal/ip_ranges.js b/backend/internal/ip_ranges.js deleted file mode 100644 index edf5c3a0..00000000 --- a/backend/internal/ip_ranges.js +++ /dev/null @@ -1,147 +0,0 @@ -const https = require('https'); -const fs = require('fs'); -const logger = require('../logger').ip_ranges; -const error = require('../lib/error'); -const internalNginx = require('./nginx'); -const { Liquid } = require('liquidjs'); - -const CLOUDFRONT_URL = 'https://ip-ranges.amazonaws.com/ip-ranges.json'; -const CLOUDFARE_V4_URL = 'https://www.cloudflare.com/ips-v4'; -const CLOUDFARE_V6_URL = 'https://www.cloudflare.com/ips-v6'; - -const internalIpRanges = { - - interval_timeout: 1000 * 60 * 60 * 6, // 6 hours - interval: null, - interval_processing: false, - iteration_count: 0, - - initTimer: () => { - logger.info('IP Ranges Renewal Timer initialized'); - internalIpRanges.interval = setInterval(internalIpRanges.fetch, internalIpRanges.interval_timeout); - }, - - fetchUrl: (url) => { - return new Promise((resolve, reject) => { - logger.info('Fetching ' + url); - return https.get(url, (res) => { - res.setEncoding('utf8'); - let raw_data = ''; - res.on('data', (chunk) => { - raw_data += chunk; - }); - - res.on('end', () => { - resolve(raw_data); - }); - }).on('error', (err) => { - reject(err); - }); - }); - }, - - /** - * Triggered at startup and then later by a timer, this will fetch the ip ranges from services and apply them to nginx. - */ - fetch: () => { - if (!internalIpRanges.interval_processing) { - internalIpRanges.interval_processing = true; - logger.info('Fetching IP Ranges from online services...'); - - let ip_ranges = []; - - return internalIpRanges.fetchUrl(CLOUDFRONT_URL) - .then((cloudfront_data) => { - let data = JSON.parse(cloudfront_data); - - if (data && typeof data.prefixes !== 'undefined') { - data.prefixes.map((item) => { - if (item.service === 'CLOUDFRONT') { - ip_ranges.push(item.ip_prefix); - } - }); - } - - if (data && typeof data.ipv6_prefixes !== 'undefined') { - data.ipv6_prefixes.map((item) => { - if (item.service === 'CLOUDFRONT') { - ip_ranges.push(item.ipv6_prefix); - } - }); - } - }) - .then(() => { - return internalIpRanges.fetchUrl(CLOUDFARE_V4_URL); - }) - .then((cloudfare_data) => { - let items = cloudfare_data.split('\n'); - ip_ranges = [... ip_ranges, ... items]; - }) - .then(() => { - return internalIpRanges.fetchUrl(CLOUDFARE_V6_URL); - }) - .then((cloudfare_data) => { - let items = cloudfare_data.split('\n'); - ip_ranges = [... ip_ranges, ... items]; - }) - .then(() => { - let clean_ip_ranges = []; - ip_ranges.map((range) => { - if (range) { - clean_ip_ranges.push(range); - } - }); - - return internalIpRanges.generateConfig(clean_ip_ranges) - .then(() => { - if (internalIpRanges.iteration_count) { - // Reload nginx - return internalNginx.reload(); - } - }); - }) - .then(() => { - internalIpRanges.interval_processing = false; - internalIpRanges.iteration_count++; - }) - .catch((err) => { - logger.error(err.message); - internalIpRanges.interval_processing = false; - }); - } - }, - - /** - * @param {Array} ip_ranges - * @returns {Promise} - */ - generateConfig: (ip_ranges) => { - let renderEngine = new Liquid({ - root: __dirname + '/../templates/' - }); - - return new Promise((resolve, reject) => { - let template = null; - let filename = '/etc/nginx/conf.d/include/ip_ranges.conf'; - try { - template = fs.readFileSync(__dirname + '/../templates/ip_ranges.conf', {encoding: 'utf8'}); - } catch (err) { - reject(new error.ConfigurationError(err.message)); - return; - } - - renderEngine - .parseAndRender(template, {ip_ranges: ip_ranges}) - .then((config_text) => { - fs.writeFileSync(filename, config_text, {encoding: 'utf8'}); - resolve(true); - }) - .catch((err) => { - logger.warn('Could not write ' + filename + ':', err.message); - reject(new error.ConfigurationError(err.message)); - }); - }); - } -}; - -module.exports = internalIpRanges; diff --git a/backend/internal/jwt/jwt.go b/backend/internal/jwt/jwt.go new file mode 100644 index 00000000..4c786f26 --- /dev/null +++ b/backend/internal/jwt/jwt.go @@ -0,0 +1,60 @@ +package jwt + +import ( + "fmt" + "time" + + "npm/internal/entity/user" + "npm/internal/logger" + + "github.com/dgrijalva/jwt-go" +) + +// UserJWTClaims is the structure of a JWT for a User +type UserJWTClaims struct { + UserID int `json:"uid"` + Roles []string `json:"roles"` + jwt.StandardClaims +} + +// GeneratedResponse is the response of a generated token, usually used in http response +type GeneratedResponse struct { + Expires int64 `json:"expires"` + Token string `json:"token"` +} + +// Generate will create a JWT +func Generate(userObj *user.Model) (GeneratedResponse, error) { + var response GeneratedResponse + + key, _ := GetPrivateKey() + expires := time.Now().AddDate(0, 0, 1) // 1 day + + // Create the Claims + claims := UserJWTClaims{ + userObj.ID, + userObj.Roles, + jwt.StandardClaims{ + IssuedAt: time.Now().Unix(), + ExpiresAt: expires.Unix(), + Issuer: "api", + }, + } + + // Create a new token object, specifying signing method and the claims + // you would like it to contain. + token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims) + var err error + token.Signature, err = token.SignedString(key) + if err != nil { + logger.Error("JWTError", fmt.Errorf("Error signing token: %v", err)) + return response, err + } + + response = GeneratedResponse{ + Expires: expires.Unix(), + Token: token.Signature, + } + + return response, nil +} diff --git a/backend/internal/jwt/keys.go b/backend/internal/jwt/keys.go new file mode 100644 index 00000000..e48c334d --- /dev/null +++ b/backend/internal/jwt/keys.go @@ -0,0 +1,86 @@ +package jwt + +import ( + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "errors" + + "npm/internal/config" +) + +var ( + privateKey *rsa.PrivateKey + publicKey *rsa.PublicKey +) + +// GetPrivateKey will load the key from config package and return a usable object +// It should only load from file once per program execution +func GetPrivateKey() (*rsa.PrivateKey, error) { + if privateKey == nil { + var blankKey *rsa.PrivateKey + + if config.PrivateKey == "" { + return blankKey, errors.New("Could not get Private Key from configuration") + } + + var err error + privateKey, err = LoadPemPrivateKey(config.PrivateKey) + if err != nil { + return blankKey, err + } + } + + pub, pubErr := GetPublicKey() + if pubErr != nil { + return privateKey, pubErr + } + + privateKey.PublicKey = *pub + + return privateKey, pubErr +} + +// GetPublicKey will load the key from config package and return a usable object +// It should only load once per program execution +func GetPublicKey() (*rsa.PublicKey, error) { + if publicKey == nil { + var blankKey *rsa.PublicKey + + if config.PublicKey == "" { + return blankKey, errors.New("Could not get Public Key filename, check environment variables") + } + + var err error + publicKey, err = LoadPemPublicKey(config.PublicKey) + if err != nil { + return blankKey, err + } + } + + return publicKey, nil +} + +// LoadPemPrivateKey reads a key from a PEM encoded string and returns a private key +func LoadPemPrivateKey(content string) (*rsa.PrivateKey, error) { + var key *rsa.PrivateKey + data, _ := pem.Decode([]byte(content)) + var err error + key, err = x509.ParsePKCS1PrivateKey(data.Bytes) + if err != nil { + return key, err + } + return key, nil +} + +// LoadPemPublicKey reads a key from a PEM encoded string and returns a public key +func LoadPemPublicKey(content string) (*rsa.PublicKey, error) { + var key *rsa.PublicKey + data, _ := pem.Decode([]byte(content)) + publicKeyFileImported, err := x509.ParsePKCS1PublicKey(data.Bytes) + if err != nil { + return key, err + } + + return publicKeyFileImported, nil +} diff --git a/backend/internal/logger/config.go b/backend/internal/logger/config.go new file mode 100644 index 00000000..c0f8a35a --- /dev/null +++ b/backend/internal/logger/config.go @@ -0,0 +1,40 @@ +package logger + +import "github.com/getsentry/sentry-go" + +// Level type +type Level int + +// Log level definitions +const ( + // DebugLevel usually only enabled when debugging. Very verbose logging. + DebugLevel Level = 10 + // InfoLevel general operational entries about what's going on inside the application. + InfoLevel Level = 20 + // WarnLevel non-critical entries that deserve eyes. + WarnLevel Level = 30 + // ErrorLevel used for errors that should definitely be noted. + ErrorLevel Level = 40 +) + +// Config options for the logger. +type Config struct { + LogThreshold Level + Formatter string + SentryConfig sentry.ClientOptions +} + +// Interface for a logger +type Interface interface { + GetLogLevel() Level + Debug(format string, args ...interface{}) + Info(format string, args ...interface{}) + Warn(format string, args ...interface{}) + Error(errorClass string, err error, args ...interface{}) + Errorf(errorClass, format string, err error, args ...interface{}) +} + +// ConfigurableLogger is an interface for a logger that can be configured +type ConfigurableLogger interface { + Configure(c *Config) error +} diff --git a/backend/internal/logger/logger.go b/backend/internal/logger/logger.go new file mode 100644 index 00000000..f82145ee --- /dev/null +++ b/backend/internal/logger/logger.go @@ -0,0 +1,242 @@ +package logger + +import ( + "encoding/json" + "fmt" + stdlog "log" + "os" + "runtime/debug" + "sync" + "time" + + "github.com/fatih/color" + "github.com/getsentry/sentry-go" +) + +var colorReset, colorGray, colorYellow, colorBlue, colorRed, colorMagenta, colorBlack, colorWhite *color.Color + +// Log message structure. +type Log struct { + Timestamp string `json:"timestamp"` + Level string `json:"level"` + Message string `json:"message"` + Pid int `json:"pid"` + Summary string `json:"summary,omitempty"` + Caller string `json:"caller,omitempty"` + StackTrace []string `json:"stack_trace,omitempty"` +} + +// Logger instance +type Logger struct { + Config + mux sync.Mutex +} + +// global logging configuration. +var logger = NewLogger() + +// NewLogger creates a new logger instance +func NewLogger() *Logger { + color.NoColor = false + colorReset = color.New(color.Reset) + colorGray = color.New(color.FgWhite) + colorYellow = color.New(color.Bold, color.FgYellow) + colorBlue = color.New(color.Bold, color.FgBlue) + colorRed = color.New(color.Bold, color.FgRed) + colorMagenta = color.New(color.Bold, color.FgMagenta) + colorBlack = color.New(color.Bold, color.FgBlack) + colorWhite = color.New(color.Bold, color.FgWhite) + + return &Logger{ + Config: NewConfig(), + } +} + +// NewConfig returns the default config +func NewConfig() Config { + return Config{ + LogThreshold: InfoLevel, + Formatter: "json", + } +} + +// Configure logger and will return error if missing required fields. +func Configure(c *Config) error { + return logger.Configure(c) +} + +// GetLogLevel currently configured +func GetLogLevel() Level { + return logger.GetLogLevel() +} + +// Debug logs if the log level is set to DebugLevel or below. Arguments are handled in the manner of fmt.Printf. +func Debug(format string, args ...interface{}) { + logger.Debug(format, args...) +} + +// Info logs if the log level is set to InfoLevel or below. Arguments are handled in the manner of fmt.Printf. +func Info(format string, args ...interface{}) { + logger.Info(format, args...) +} + +// Warn logs if the log level is set to WarnLevel or below. Arguments are handled in the manner of fmt.Printf. +func Warn(format string, args ...interface{}) { + logger.Warn(format, args...) +} + +// Error logs error given if the log level is set to ErrorLevel or below. Arguments are not logged. +// Attempts to log to bugsang. +func Error(errorClass string, err error) { + logger.Error(errorClass, err) +} + +// Configure logger and will return error if missing required fields. +func (l *Logger) Configure(c *Config) error { + // ensure updates to the config are atomic + l.mux.Lock() + defer l.mux.Unlock() + + if c == nil { + return fmt.Errorf("a non nil Config is mandatory") + } + + if err := c.LogThreshold.validate(); err != nil { + return err + } + + l.LogThreshold = c.LogThreshold + l.Formatter = c.Formatter + l.SentryConfig = c.SentryConfig + + if c.SentryConfig.Dsn != "" { + if sentryErr := sentry.Init(c.SentryConfig); sentryErr != nil { + fmt.Printf("Sentry initialization failed: %v\n", sentryErr) + } + } + + stdlog.SetFlags(0) // this removes timestamp prefixes from logs + return nil +} + +// validate the log level is in the accepted list. +func (l Level) validate() error { + switch l { + case DebugLevel, InfoLevel, WarnLevel, ErrorLevel: + return nil + default: + return fmt.Errorf("invalid \"Level\" %d", l) + } +} + +var logLevels = map[Level]string{ + DebugLevel: "DEBUG", + InfoLevel: "INFO", + WarnLevel: "WARN", + ErrorLevel: "ERROR", +} + +func (l *Logger) logLevel(logLevel Level, format string, args ...interface{}) { + if logLevel < l.LogThreshold { + return + } + + errorClass := "" + if logLevel == ErrorLevel { + // First arg is the errorClass + errorClass = args[0].(string) + if len(args) > 1 { + args = args[1:] + } else { + args = []interface{}{} + } + } + + stringMessage := fmt.Sprintf(format, args...) + + if l.Formatter == "json" { + // JSON Log Format + jsonLog, _ := json.Marshal( + Log{ + Timestamp: time.Now().Format(time.RFC3339Nano), + Level: logLevels[logLevel], + Message: stringMessage, + Pid: os.Getpid(), + }, + ) + + stdlog.Println(string(jsonLog)) + } else { + // Nice Log Format + var colorLevel *color.Color + switch logLevel { + case DebugLevel: + colorLevel = colorMagenta + case InfoLevel: + colorLevel = colorBlue + case WarnLevel: + colorLevel = colorYellow + case ErrorLevel: + colorLevel = colorRed + stringMessage = fmt.Sprintf("%s: %s", errorClass, stringMessage) + } + + t := time.Now() + stdlog.Println( + colorBlack.Sprint("["), + colorWhite.Sprint(t.Format("2006-01-02 15:04:05")), + colorBlack.Sprint("] "), + colorLevel.Sprintf("%-8v", logLevels[logLevel]), + colorGray.Sprint(stringMessage), + colorReset.Sprint(""), + ) + + if logLevel == ErrorLevel && l.LogThreshold == DebugLevel { + // Print a stack trace too + debug.PrintStack() + } + } +} + +// GetLogLevel currently configured +func (l *Logger) GetLogLevel() Level { + return l.LogThreshold +} + +// Debug logs if the log level is set to DebugLevel or below. Arguments are handled in the manner of fmt.Printf. +func (l *Logger) Debug(format string, args ...interface{}) { + l.logLevel(DebugLevel, format, args...) +} + +// Info logs if the log level is set to InfoLevel or below. Arguments are handled in the manner of fmt.Printf. +func (l *Logger) Info(format string, args ...interface{}) { + l.logLevel(InfoLevel, format, args...) +} + +// Warn logs if the log level is set to WarnLevel or below. Arguments are handled in the manner of fmt.Printf. +func (l *Logger) Warn(format string, args ...interface{}) { + l.logLevel(WarnLevel, format, args...) +} + +// Error logs error given if the log level is set to ErrorLevel or below. Arguments are not logged. +// Attempts to log to bugsang. +func (l *Logger) Error(errorClass string, err error) { + l.logLevel(ErrorLevel, err.Error(), errorClass) + l.notifySentry(errorClass, err) +} + +func (l *Logger) notifySentry(errorClass string, err error) { + if l.SentryConfig.Dsn != "" && l.SentryConfig.Dsn != "-" { + + sentry.ConfigureScope(func(scope *sentry.Scope) { + scope.SetLevel(sentry.LevelError) + scope.SetTag("service", "backend") + scope.SetTag("error_class", errorClass) + }) + + sentry.CaptureException(err) + // Since sentry emits events in the background we need to make sure + // they are sent before we shut down + sentry.Flush(time.Second * 5) + } +} diff --git a/backend/internal/logger/logger_test.go b/backend/internal/logger/logger_test.go new file mode 100644 index 00000000..e0019696 --- /dev/null +++ b/backend/internal/logger/logger_test.go @@ -0,0 +1,168 @@ +package logger + +import ( + "bytes" + "errors" + "fmt" + "io/ioutil" + "log" + "os" + "testing" + + "github.com/getsentry/sentry-go" + "github.com/stretchr/testify/assert" +) + +func TestGetLogLevel(t *testing.T) { + assert.Equal(t, InfoLevel, GetLogLevel()) +} + +func TestThreshold(t *testing.T) { + buf := new(bytes.Buffer) + log.SetOutput(buf) + defer func() { + log.SetOutput(os.Stderr) + }() + + assert.NoError(t, Configure(&Config{ + LogThreshold: InfoLevel, + })) + + Debug("this should not display") + assert.Empty(t, buf.String()) + + Info("this should display") + assert.NotEmpty(t, buf.String()) + + Error("ErrorClass", errors.New("this should display")) + assert.NotEmpty(t, buf.String()) +} + +func TestDebug(t *testing.T) { + buf := new(bytes.Buffer) + log.SetOutput(buf) + defer func() { + log.SetOutput(os.Stderr) + }() + + assert.NoError(t, Configure(&Config{ + LogThreshold: DebugLevel, + })) + + Debug("This is a %s message", "test") + assert.Contains(t, buf.String(), "DEBUG") + assert.Contains(t, buf.String(), "This is a test message") +} + +func TestInfo(t *testing.T) { + buf := new(bytes.Buffer) + log.SetOutput(buf) + defer func() { + log.SetOutput(os.Stderr) + }() + + assert.NoError(t, Configure(&Config{ + LogThreshold: InfoLevel, + })) + + Info("This is a %s message", "test") + assert.Contains(t, buf.String(), "INFO") + assert.Contains(t, buf.String(), "This is a test message") +} + +func TestWarn(t *testing.T) { + buf := new(bytes.Buffer) + log.SetOutput(buf) + defer func() { + log.SetOutput(os.Stderr) + }() + + assert.NoError(t, Configure(&Config{ + LogThreshold: InfoLevel, + })) + + Warn("This is a %s message", "test") + assert.Contains(t, buf.String(), "WARN") + assert.Contains(t, buf.String(), "This is a test message") +} + +func TestError(t *testing.T) { + buf := new(bytes.Buffer) + log.SetOutput(buf) + defer func() { + log.SetOutput(os.Stderr) + }() + + assert.NoError(t, Configure(&Config{ + LogThreshold: ErrorLevel, + })) + + Error("TestErrorClass", fmt.Errorf("this is a %s error", "test")) + assert.Contains(t, buf.String(), "ERROR") + assert.Contains(t, buf.String(), "this is a test error") +} + +func TestConfigure(t *testing.T) { + type args struct { + c *Config + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "configure", + args: args{ + &Config{ + LogThreshold: InfoLevel, + SentryConfig: sentry.ClientOptions{}, + }, + }, + wantErr: false, + }, + { + name: "invalid log level", + args: args{ + &Config{ + SentryConfig: sentry.ClientOptions{}, + }, + }, + wantErr: true, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + if err := Configure(tt.args.c); (err != nil) != tt.wantErr { + t.Errorf("Configure() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func BenchmarkLogLevelBelowThreshold(b *testing.B) { + l := NewLogger() + + log.SetOutput(ioutil.Discard) + defer func() { + log.SetOutput(os.Stderr) + }() + + for i := 0; i < b.N; i++ { + l.logLevel(DebugLevel, "benchmark %d", i) + } +} + +func BenchmarkLogLevelAboveThreshold(b *testing.B) { + l := NewLogger() + + log.SetOutput(ioutil.Discard) + defer func() { + log.SetOutput(os.Stderr) + }() + + for i := 0; i < b.N; i++ { + l.logLevel(InfoLevel, "benchmark %d", i) + } +} diff --git a/backend/internal/model/filter.go b/backend/internal/model/filter.go new file mode 100644 index 00000000..8b88b70c --- /dev/null +++ b/backend/internal/model/filter.go @@ -0,0 +1,8 @@ +package model + +// Filter is the structure of a field/modifier/value item +type Filter struct { + Field string `json:"field"` + Modifier string `json:"modifier"` + Value []string `json:"value"` +} diff --git a/backend/internal/model/pageinfo.go b/backend/internal/model/pageinfo.go new file mode 100644 index 00000000..129a0a14 --- /dev/null +++ b/backend/internal/model/pageinfo.go @@ -0,0 +1,22 @@ +package model + +import ( + "time" +) + +// PageInfo is the model used by Api Handlers and passed on to other parts +// of the application +type PageInfo struct { + FromDate time.Time `json:"from_date"` + ToDate time.Time `json:"to_date"` + Sort []Sort `json:"sort"` + Offset int `json:"offset"` + Limit int `json:"limit"` + Expand []string `json:"expand"` +} + +// Sort ... +type Sort struct { + Field string `json:"field"` + Direction string `json:"direction"` +} diff --git a/backend/internal/nginx.js b/backend/internal/nginx.js deleted file mode 100644 index 52bdd66d..00000000 --- a/backend/internal/nginx.js +++ /dev/null @@ -1,435 +0,0 @@ -const _ = require('lodash'); -const fs = require('fs'); -const logger = require('../logger').nginx; -const utils = require('../lib/utils'); -const error = require('../lib/error'); -const { Liquid } = require('liquidjs'); -const debug_mode = process.env.NODE_ENV !== 'production' || !!process.env.DEBUG; - -const internalNginx = { - - /** - * This will: - * - test the nginx config first to make sure it's OK - * - create / recreate the config for the host - * - test again - * - IF OK: update the meta with online status - * - IF BAD: update the meta with offline status and remove the config entirely - * - then reload nginx - * - * @param {Object|String} model - * @param {String} host_type - * @param {Object} host - * @returns {Promise} - */ - configure: (model, host_type, host) => { - let combined_meta = {}; - - return internalNginx.test() - .then(() => { - // Nginx is OK - // We're deleting this config regardless. - return internalNginx.deleteConfig(host_type, host); // Don't throw errors, as the file may not exist at all - }) - .then(() => { - return internalNginx.generateConfig(host_type, host); - }) - .then(() => { - // Test nginx again and update meta with result - return internalNginx.test() - .then(() => { - // nginx is ok - combined_meta = _.assign({}, host.meta, { - nginx_online: true, - nginx_err: null - }); - - return model - .query() - .where('id', host.id) - .patch({ - meta: combined_meta - }); - }) - .catch((err) => { - // Remove the error_log line because it's a docker-ism false positive that doesn't need to be reported. - // It will always look like this: - // nginx: [alert] could not open error log file: open() "/var/log/nginx/error.log" failed (6: No such device or address) - - let valid_lines = []; - let err_lines = err.message.split('\n'); - err_lines.map(function (line) { - if (line.indexOf('/var/log/nginx/error.log') === -1) { - valid_lines.push(line); - } - }); - - if (debug_mode) { - logger.error('Nginx test failed:', valid_lines.join('\n')); - } - - // config is bad, update meta and delete config - combined_meta = _.assign({}, host.meta, { - nginx_online: false, - nginx_err: valid_lines.join('\n') - }); - - return model - .query() - .where('id', host.id) - .patch({ - meta: combined_meta - }) - .then(() => { - return internalNginx.deleteConfig(host_type, host, true); - }); - }); - }) - .then(() => { - return internalNginx.reload(); - }) - .then(() => { - return combined_meta; - }); - }, - - /** - * @returns {Promise} - */ - test: () => { - if (debug_mode) { - logger.info('Testing Nginx configuration'); - } - - return utils.exec('/usr/sbin/nginx -t -g "error_log off;"'); - }, - - /** - * @returns {Promise} - */ - reload: () => { - return internalNginx.test() - .then(() => { - logger.info('Reloading Nginx'); - return utils.exec('/usr/sbin/nginx -s reload'); - }); - }, - - /** - * @param {String} host_type - * @param {Integer} host_id - * @returns {String} - */ - getConfigName: (host_type, host_id) => { - host_type = host_type.replace(new RegExp('-', 'g'), '_'); - - if (host_type === 'default') { - return '/data/nginx/default_host/site.conf'; - } - - return '/data/nginx/' + host_type + '/' + host_id + '.conf'; - }, - - /** - * Generates custom locations - * @param {Object} host - * @returns {Promise} - */ - renderLocations: (host) => { - - //logger.info('host = ' + JSON.stringify(host, null, 2)); - return new Promise((resolve, reject) => { - let template; - - try { - template = fs.readFileSync(__dirname + '/../templates/_location.conf', {encoding: 'utf8'}); - } catch (err) { - reject(new error.ConfigurationError(err.message)); - return; - } - - let renderer = new Liquid({ - root: __dirname + '/../templates/' - }); - let renderedLocations = ''; - - const locationRendering = async () => { - for (let i = 0; i < host.locations.length; i++) { - let locationCopy = Object.assign({}, {access_list_id: host.access_list_id}, {certificate_id: host.certificate_id}, - {ssl_forced: host.ssl_forced}, {caching_enabled: host.caching_enabled}, {block_exploits: host.block_exploits}, - {allow_websocket_upgrade: host.allow_websocket_upgrade}, {http2_support: host.http2_support}, - {hsts_enabled: host.hsts_enabled}, {hsts_subdomains: host.hsts_subdomains}, {access_list: host.access_list}, - {certificate: host.certificate}, host.locations[i]); - - if (locationCopy.forward_host.indexOf('/') > -1) { - const splitted = locationCopy.forward_host.split('/'); - - locationCopy.forward_host = splitted.shift(); - locationCopy.forward_path = `/${splitted.join('/')}`; - } - - //logger.info('locationCopy = ' + JSON.stringify(locationCopy, null, 2)); - - // eslint-disable-next-line - renderedLocations += await renderer.parseAndRender(template, locationCopy); - } - - }; - - locationRendering().then(() => resolve(renderedLocations)); - - }); - }, - - /** - * @param {String} host_type - * @param {Object} host - * @returns {Promise} - */ - generateConfig: (host_type, host) => { - host_type = host_type.replace(new RegExp('-', 'g'), '_'); - - if (debug_mode) { - logger.info('Generating ' + host_type + ' Config:', host); - } - - // logger.info('host = ' + JSON.stringify(host, null, 2)); - - let renderEngine = new Liquid({ - root: __dirname + '/../templates/' - }); - - return new Promise((resolve, reject) => { - let template = null; - let filename = internalNginx.getConfigName(host_type, host.id); - - try { - template = fs.readFileSync(__dirname + '/../templates/' + host_type + '.conf', {encoding: 'utf8'}); - } catch (err) { - reject(new error.ConfigurationError(err.message)); - return; - } - - let locationsPromise; - let origLocations; - - // Manipulate the data a bit before sending it to the template - if (host_type !== 'default') { - host.use_default_location = true; - if (typeof host.advanced_config !== 'undefined' && host.advanced_config) { - host.use_default_location = !internalNginx.advancedConfigHasDefaultLocation(host.advanced_config); - } - } - - if (host.locations) { - //logger.info ('host.locations = ' + JSON.stringify(host.locations, null, 2)); - origLocations = [].concat(host.locations); - locationsPromise = internalNginx.renderLocations(host).then((renderedLocations) => { - host.locations = renderedLocations; - }); - - // Allow someone who is using / custom location path to use it, and skip the default / location - _.map(host.locations, (location) => { - if (location.path === '/') { - host.use_default_location = false; - } - }); - - } else { - locationsPromise = Promise.resolve(); - } - - // Set the IPv6 setting for the host - host.ipv6 = internalNginx.ipv6Enabled(); - - locationsPromise.then(() => { - renderEngine - .parseAndRender(template, host) - .then((config_text) => { - fs.writeFileSync(filename, config_text, {encoding: 'utf8'}); - - if (debug_mode) { - logger.success('Wrote config:', filename, config_text); - } - - // Restore locations array - host.locations = origLocations; - - resolve(true); - }) - .catch((err) => { - if (debug_mode) { - logger.warn('Could not write ' + filename + ':', err.message); - } - - reject(new error.ConfigurationError(err.message)); - }); - }); - }); - }, - - /** - * This generates a temporary nginx config listening on port 80 for the domain names listed - * in the certificate setup. It allows the letsencrypt acme challenge to be requested by letsencrypt - * when requesting a certificate without having a hostname set up already. - * - * @param {Object} certificate - * @returns {Promise} - */ - generateLetsEncryptRequestConfig: (certificate) => { - if (debug_mode) { - logger.info('Generating LetsEncrypt Request Config:', certificate); - } - - let renderEngine = new Liquid({ - root: __dirname + '/../templates/' - }); - - return new Promise((resolve, reject) => { - let template = null; - let filename = '/data/nginx/temp/letsencrypt_' + certificate.id + '.conf'; - - try { - template = fs.readFileSync(__dirname + '/../templates/letsencrypt-request.conf', {encoding: 'utf8'}); - } catch (err) { - reject(new error.ConfigurationError(err.message)); - return; - } - - certificate.ipv6 = internalNginx.ipv6Enabled(); - - renderEngine - .parseAndRender(template, certificate) - .then((config_text) => { - fs.writeFileSync(filename, config_text, {encoding: 'utf8'}); - - if (debug_mode) { - logger.success('Wrote config:', filename, config_text); - } - - resolve(true); - }) - .catch((err) => { - if (debug_mode) { - logger.warn('Could not write ' + filename + ':', err.message); - } - - reject(new error.ConfigurationError(err.message)); - }); - }); - }, - - /** - * This removes the temporary nginx config file generated by `generateLetsEncryptRequestConfig` - * - * @param {Object} certificate - * @param {Boolean} [throw_errors] - * @returns {Promise} - */ - deleteLetsEncryptRequestConfig: (certificate, throw_errors) => { - return new Promise((resolve, reject) => { - try { - let config_file = '/data/nginx/temp/letsencrypt_' + certificate.id + '.conf'; - - if (debug_mode) { - logger.warn('Deleting nginx config: ' + config_file); - } - - fs.unlinkSync(config_file); - } catch (err) { - if (debug_mode) { - logger.warn('Could not delete config:', err.message); - } - - if (throw_errors) { - reject(err); - } - } - - resolve(); - }); - }, - - /** - * @param {String} host_type - * @param {Object} [host] - * @param {Boolean} [throw_errors] - * @returns {Promise} - */ - deleteConfig: (host_type, host, throw_errors) => { - host_type = host_type.replace(new RegExp('-', 'g'), '_'); - - return new Promise((resolve, reject) => { - try { - let config_file = internalNginx.getConfigName(host_type, typeof host === 'undefined' ? 0 : host.id); - - if (debug_mode) { - logger.warn('Deleting nginx config: ' + config_file); - } - - fs.unlinkSync(config_file); - } catch (err) { - if (debug_mode) { - logger.warn('Could not delete config:', err.message); - } - - if (throw_errors) { - reject(err); - } - } - - resolve(); - }); - }, - - /** - * @param {String} host_type - * @param {Array} hosts - * @returns {Promise} - */ - bulkGenerateConfigs: (host_type, hosts) => { - let promises = []; - hosts.map(function (host) { - promises.push(internalNginx.generateConfig(host_type, host)); - }); - - return Promise.all(promises); - }, - - /** - * @param {String} host_type - * @param {Array} hosts - * @param {Boolean} [throw_errors] - * @returns {Promise} - */ - bulkDeleteConfigs: (host_type, hosts, throw_errors) => { - let promises = []; - hosts.map(function (host) { - promises.push(internalNginx.deleteConfig(host_type, host, throw_errors)); - }); - - return Promise.all(promises); - }, - - /** - * @param {string} config - * @returns {boolean} - */ - advancedConfigHasDefaultLocation: function (config) { - return !!config.match(/^(?:.*;)?\s*?location\s*?\/\s*?{/im); - }, - - /** - * @returns {boolean} - */ - ipv6Enabled: function () { - if (typeof process.env.DISABLE_IPV6 !== 'undefined') { - const disabled = process.env.DISABLE_IPV6.toLowerCase(); - return !(disabled === 'on' || disabled === 'true' || disabled === '1' || disabled === 'yes'); - } - - return true; - } -}; - -module.exports = internalNginx; diff --git a/backend/internal/proxy-host.js b/backend/internal/proxy-host.js deleted file mode 100644 index 09b8bca5..00000000 --- a/backend/internal/proxy-host.js +++ /dev/null @@ -1,466 +0,0 @@ -const _ = require('lodash'); -const error = require('../lib/error'); -const proxyHostModel = require('../models/proxy_host'); -const internalHost = require('./host'); -const internalNginx = require('./nginx'); -const internalAuditLog = require('./audit-log'); -const internalCertificate = require('./certificate'); - -function omissions () { - return ['is_deleted']; -} - -const internalProxyHost = { - - /** - * @param {Access} access - * @param {Object} data - * @returns {Promise} - */ - create: (access, data) => { - let create_certificate = data.certificate_id === 'new'; - - if (create_certificate) { - delete data.certificate_id; - } - - return access.can('proxy_hosts:create', data) - .then(() => { - // Get a list of the domain names and check each of them against existing records - let domain_name_check_promises = []; - - data.domain_names.map(function (domain_name) { - domain_name_check_promises.push(internalHost.isHostnameTaken(domain_name)); - }); - - return Promise.all(domain_name_check_promises) - .then((check_results) => { - check_results.map(function (result) { - if (result.is_taken) { - throw new error.ValidationError(result.hostname + ' is already in use'); - } - }); - }); - }) - .then(() => { - // At this point the domains should have been checked - data.owner_user_id = access.token.getUserId(1); - data = internalHost.cleanSslHstsData(data); - - return proxyHostModel - .query() - .omit(omissions()) - .insertAndFetch(data); - }) - .then((row) => { - if (create_certificate) { - return internalCertificate.createQuickCertificate(access, data) - .then((cert) => { - // update host with cert id - return internalProxyHost.update(access, { - id: row.id, - certificate_id: cert.id - }); - }) - .then(() => { - return row; - }); - } else { - return row; - } - }) - .then((row) => { - // re-fetch with cert - return internalProxyHost.get(access, { - id: row.id, - expand: ['certificate', 'owner', 'access_list.[clients,items]'] - }); - }) - .then((row) => { - // Configure nginx - return internalNginx.configure(proxyHostModel, 'proxy_host', row) - .then(() => { - return row; - }); - }) - .then((row) => { - // Audit log - data.meta = _.assign({}, data.meta || {}, row.meta); - - // Add to audit log - return internalAuditLog.add(access, { - action: 'created', - object_type: 'proxy-host', - object_id: row.id, - meta: data - }) - .then(() => { - return row; - }); - }); - }, - - /** - * @param {Access} access - * @param {Object} data - * @param {Number} data.id - * @return {Promise} - */ - update: (access, data) => { - let create_certificate = data.certificate_id === 'new'; - - if (create_certificate) { - delete data.certificate_id; - } - - return access.can('proxy_hosts:update', data.id) - .then((/*access_data*/) => { - // Get a list of the domain names and check each of them against existing records - let domain_name_check_promises = []; - - if (typeof data.domain_names !== 'undefined') { - data.domain_names.map(function (domain_name) { - domain_name_check_promises.push(internalHost.isHostnameTaken(domain_name, 'proxy', data.id)); - }); - - return Promise.all(domain_name_check_promises) - .then((check_results) => { - check_results.map(function (result) { - if (result.is_taken) { - throw new error.ValidationError(result.hostname + ' is already in use'); - } - }); - }); - } - }) - .then(() => { - return internalProxyHost.get(access, {id: data.id}); - }) - .then((row) => { - if (row.id !== data.id) { - // Sanity check that something crazy hasn't happened - throw new error.InternalValidationError('Proxy Host could not be updated, IDs do not match: ' + row.id + ' !== ' + data.id); - } - - if (create_certificate) { - return internalCertificate.createQuickCertificate(access, { - domain_names: data.domain_names || row.domain_names, - meta: _.assign({}, row.meta, data.meta) - }) - .then((cert) => { - // update host with cert id - data.certificate_id = cert.id; - }) - .then(() => { - return row; - }); - } else { - return row; - } - }) - .then((row) => { - // Add domain_names to the data in case it isn't there, so that the audit log renders correctly. The order is important here. - data = _.assign({}, { - domain_names: row.domain_names - }, data); - - data = internalHost.cleanSslHstsData(data, row); - - return proxyHostModel - .query() - .where({id: data.id}) - .patch(data) - .then((saved_row) => { - // Add to audit log - return internalAuditLog.add(access, { - action: 'updated', - object_type: 'proxy-host', - object_id: row.id, - meta: data - }) - .then(() => { - return _.omit(saved_row, omissions()); - }); - }); - }) - .then(() => { - return internalProxyHost.get(access, { - id: data.id, - expand: ['owner', 'certificate', 'access_list.[clients,items]'] - }) - .then((row) => { - if (!row.enabled) { - // No need to add nginx config if host is disabled - return row; - } - // Configure nginx - return internalNginx.configure(proxyHostModel, 'proxy_host', row) - .then((new_meta) => { - row.meta = new_meta; - row = internalHost.cleanRowCertificateMeta(row); - return _.omit(row, omissions()); - }); - }); - }); - }, - - /** - * @param {Access} access - * @param {Object} data - * @param {Number} data.id - * @param {Array} [data.expand] - * @param {Array} [data.omit] - * @return {Promise} - */ - get: (access, data) => { - if (typeof data === 'undefined') { - data = {}; - } - - return access.can('proxy_hosts:get', data.id) - .then((access_data) => { - let query = proxyHostModel - .query() - .where('is_deleted', 0) - .andWhere('id', data.id) - .allowEager('[owner,access_list,access_list.[clients,items],certificate]') - .first(); - - if (access_data.permission_visibility !== 'all') { - query.andWhere('owner_user_id', access.token.getUserId(1)); - } - - // Custom omissions - if (typeof data.omit !== 'undefined' && data.omit !== null) { - query.omit(data.omit); - } - - if (typeof data.expand !== 'undefined' && data.expand !== null) { - query.eager('[' + data.expand.join(', ') + ']'); - } - - return query; - }) - .then((row) => { - if (row) { - row = internalHost.cleanRowCertificateMeta(row); - return _.omit(row, omissions()); - } else { - throw new error.ItemNotFoundError(data.id); - } - }); - }, - - /** - * @param {Access} access - * @param {Object} data - * @param {Number} data.id - * @param {String} [data.reason] - * @returns {Promise} - */ - delete: (access, data) => { - return access.can('proxy_hosts:delete', data.id) - .then(() => { - return internalProxyHost.get(access, {id: data.id}); - }) - .then((row) => { - if (!row) { - throw new error.ItemNotFoundError(data.id); - } - - return proxyHostModel - .query() - .where('id', row.id) - .patch({ - is_deleted: 1 - }) - .then(() => { - // Delete Nginx Config - return internalNginx.deleteConfig('proxy_host', row) - .then(() => { - return internalNginx.reload(); - }); - }) - .then(() => { - // Add to audit log - return internalAuditLog.add(access, { - action: 'deleted', - object_type: 'proxy-host', - object_id: row.id, - meta: _.omit(row, omissions()) - }); - }); - }) - .then(() => { - return true; - }); - }, - - /** - * @param {Access} access - * @param {Object} data - * @param {Number} data.id - * @param {String} [data.reason] - * @returns {Promise} - */ - enable: (access, data) => { - return access.can('proxy_hosts:update', data.id) - .then(() => { - return internalProxyHost.get(access, { - id: data.id, - expand: ['certificate', 'owner', 'access_list'] - }); - }) - .then((row) => { - if (!row) { - throw new error.ItemNotFoundError(data.id); - } else if (row.enabled) { - throw new error.ValidationError('Host is already enabled'); - } - - row.enabled = 1; - - return proxyHostModel - .query() - .where('id', row.id) - .patch({ - enabled: 1 - }) - .then(() => { - // Configure nginx - return internalNginx.configure(proxyHostModel, 'proxy_host', row); - }) - .then(() => { - // Add to audit log - return internalAuditLog.add(access, { - action: 'enabled', - object_type: 'proxy-host', - object_id: row.id, - meta: _.omit(row, omissions()) - }); - }); - }) - .then(() => { - return true; - }); - }, - - /** - * @param {Access} access - * @param {Object} data - * @param {Number} data.id - * @param {String} [data.reason] - * @returns {Promise} - */ - disable: (access, data) => { - return access.can('proxy_hosts:update', data.id) - .then(() => { - return internalProxyHost.get(access, {id: data.id}); - }) - .then((row) => { - if (!row) { - throw new error.ItemNotFoundError(data.id); - } else if (!row.enabled) { - throw new error.ValidationError('Host is already disabled'); - } - - row.enabled = 0; - - return proxyHostModel - .query() - .where('id', row.id) - .patch({ - enabled: 0 - }) - .then(() => { - // Delete Nginx Config - return internalNginx.deleteConfig('proxy_host', row) - .then(() => { - return internalNginx.reload(); - }); - }) - .then(() => { - // Add to audit log - return internalAuditLog.add(access, { - action: 'disabled', - object_type: 'proxy-host', - object_id: row.id, - meta: _.omit(row, omissions()) - }); - }); - }) - .then(() => { - return true; - }); - }, - - /** - * All Hosts - * - * @param {Access} access - * @param {Array} [expand] - * @param {String} [search_query] - * @returns {Promise} - */ - getAll: (access, expand, search_query) => { - return access.can('proxy_hosts:list') - .then((access_data) => { - let query = proxyHostModel - .query() - .where('is_deleted', 0) - .groupBy('id') - .omit(['is_deleted']) - .allowEager('[owner,access_list,certificate]') - .orderBy('domain_names', 'ASC'); - - if (access_data.permission_visibility !== 'all') { - query.andWhere('owner_user_id', access.token.getUserId(1)); - } - - // Query is used for searching - if (typeof search_query === 'string') { - query.where(function () { - this.where('domain_names', 'like', '%' + search_query + '%'); - }); - } - - if (typeof expand !== 'undefined' && expand !== null) { - query.eager('[' + expand.join(', ') + ']'); - } - - return query; - }) - .then((rows) => { - if (typeof expand !== 'undefined' && expand !== null && expand.indexOf('certificate') !== -1) { - return internalHost.cleanAllRowsCertificateMeta(rows); - } - - return rows; - }); - }, - - /** - * Report use - * - * @param {Number} user_id - * @param {String} visibility - * @returns {Promise} - */ - getCount: (user_id, visibility) => { - let query = proxyHostModel - .query() - .count('id as count') - .where('is_deleted', 0); - - if (visibility !== 'all') { - query.andWhere('owner_user_id', user_id); - } - - return query.first() - .then((row) => { - return parseInt(row.count, 10); - }); - } -}; - -module.exports = internalProxyHost; diff --git a/backend/internal/redirection-host.js b/backend/internal/redirection-host.js deleted file mode 100644 index f22c3668..00000000 --- a/backend/internal/redirection-host.js +++ /dev/null @@ -1,461 +0,0 @@ -const _ = require('lodash'); -const error = require('../lib/error'); -const redirectionHostModel = require('../models/redirection_host'); -const internalHost = require('./host'); -const internalNginx = require('./nginx'); -const internalAuditLog = require('./audit-log'); -const internalCertificate = require('./certificate'); - -function omissions () { - return ['is_deleted']; -} - -const internalRedirectionHost = { - - /** - * @param {Access} access - * @param {Object} data - * @returns {Promise} - */ - create: (access, data) => { - let create_certificate = data.certificate_id === 'new'; - - if (create_certificate) { - delete data.certificate_id; - } - - return access.can('redirection_hosts:create', data) - .then((/*access_data*/) => { - // Get a list of the domain names and check each of them against existing records - let domain_name_check_promises = []; - - data.domain_names.map(function (domain_name) { - domain_name_check_promises.push(internalHost.isHostnameTaken(domain_name)); - }); - - return Promise.all(domain_name_check_promises) - .then((check_results) => { - check_results.map(function (result) { - if (result.is_taken) { - throw new error.ValidationError(result.hostname + ' is already in use'); - } - }); - }); - }) - .then(() => { - // At this point the domains should have been checked - data.owner_user_id = access.token.getUserId(1); - data = internalHost.cleanSslHstsData(data); - - return redirectionHostModel - .query() - .omit(omissions()) - .insertAndFetch(data); - }) - .then((row) => { - if (create_certificate) { - return internalCertificate.createQuickCertificate(access, data) - .then((cert) => { - // update host with cert id - return internalRedirectionHost.update(access, { - id: row.id, - certificate_id: cert.id - }); - }) - .then(() => { - return row; - }); - } else { - return row; - } - }) - .then((row) => { - // re-fetch with cert - return internalRedirectionHost.get(access, { - id: row.id, - expand: ['certificate', 'owner'] - }); - }) - .then((row) => { - // Configure nginx - return internalNginx.configure(redirectionHostModel, 'redirection_host', row) - .then(() => { - return row; - }); - }) - .then((row) => { - data.meta = _.assign({}, data.meta || {}, row.meta); - - // Add to audit log - return internalAuditLog.add(access, { - action: 'created', - object_type: 'redirection-host', - object_id: row.id, - meta: data - }) - .then(() => { - return row; - }); - }); - }, - - /** - * @param {Access} access - * @param {Object} data - * @param {Number} data.id - * @return {Promise} - */ - update: (access, data) => { - let create_certificate = data.certificate_id === 'new'; - - if (create_certificate) { - delete data.certificate_id; - } - - return access.can('redirection_hosts:update', data.id) - .then((/*access_data*/) => { - // Get a list of the domain names and check each of them against existing records - let domain_name_check_promises = []; - - if (typeof data.domain_names !== 'undefined') { - data.domain_names.map(function (domain_name) { - domain_name_check_promises.push(internalHost.isHostnameTaken(domain_name, 'redirection', data.id)); - }); - - return Promise.all(domain_name_check_promises) - .then((check_results) => { - check_results.map(function (result) { - if (result.is_taken) { - throw new error.ValidationError(result.hostname + ' is already in use'); - } - }); - }); - } - }) - .then(() => { - return internalRedirectionHost.get(access, {id: data.id}); - }) - .then((row) => { - if (row.id !== data.id) { - // Sanity check that something crazy hasn't happened - throw new error.InternalValidationError('Redirection Host could not be updated, IDs do not match: ' + row.id + ' !== ' + data.id); - } - - if (create_certificate) { - return internalCertificate.createQuickCertificate(access, { - domain_names: data.domain_names || row.domain_names, - meta: _.assign({}, row.meta, data.meta) - }) - .then((cert) => { - // update host with cert id - data.certificate_id = cert.id; - }) - .then(() => { - return row; - }); - } else { - return row; - } - }) - .then((row) => { - // Add domain_names to the data in case it isn't there, so that the audit log renders correctly. The order is important here. - data = _.assign({}, { - domain_names: row.domain_names - }, data); - - data = internalHost.cleanSslHstsData(data, row); - - return redirectionHostModel - .query() - .where({id: data.id}) - .patch(data) - .then((saved_row) => { - // Add to audit log - return internalAuditLog.add(access, { - action: 'updated', - object_type: 'redirection-host', - object_id: row.id, - meta: data - }) - .then(() => { - return _.omit(saved_row, omissions()); - }); - }); - }) - .then(() => { - return internalRedirectionHost.get(access, { - id: data.id, - expand: ['owner', 'certificate'] - }) - .then((row) => { - // Configure nginx - return internalNginx.configure(redirectionHostModel, 'redirection_host', row) - .then((new_meta) => { - row.meta = new_meta; - row = internalHost.cleanRowCertificateMeta(row); - return _.omit(row, omissions()); - }); - }); - }); - }, - - /** - * @param {Access} access - * @param {Object} data - * @param {Number} data.id - * @param {Array} [data.expand] - * @param {Array} [data.omit] - * @return {Promise} - */ - get: (access, data) => { - if (typeof data === 'undefined') { - data = {}; - } - - return access.can('redirection_hosts:get', data.id) - .then((access_data) => { - let query = redirectionHostModel - .query() - .where('is_deleted', 0) - .andWhere('id', data.id) - .allowEager('[owner,certificate]') - .first(); - - if (access_data.permission_visibility !== 'all') { - query.andWhere('owner_user_id', access.token.getUserId(1)); - } - - // Custom omissions - if (typeof data.omit !== 'undefined' && data.omit !== null) { - query.omit(data.omit); - } - - if (typeof data.expand !== 'undefined' && data.expand !== null) { - query.eager('[' + data.expand.join(', ') + ']'); - } - - return query; - }) - .then((row) => { - if (row) { - row = internalHost.cleanRowCertificateMeta(row); - return _.omit(row, omissions()); - } else { - throw new error.ItemNotFoundError(data.id); - } - }); - }, - - /** - * @param {Access} access - * @param {Object} data - * @param {Number} data.id - * @param {String} [data.reason] - * @returns {Promise} - */ - delete: (access, data) => { - return access.can('redirection_hosts:delete', data.id) - .then(() => { - return internalRedirectionHost.get(access, {id: data.id}); - }) - .then((row) => { - if (!row) { - throw new error.ItemNotFoundError(data.id); - } - - return redirectionHostModel - .query() - .where('id', row.id) - .patch({ - is_deleted: 1 - }) - .then(() => { - // Delete Nginx Config - return internalNginx.deleteConfig('redirection_host', row) - .then(() => { - return internalNginx.reload(); - }); - }) - .then(() => { - // Add to audit log - return internalAuditLog.add(access, { - action: 'deleted', - object_type: 'redirection-host', - object_id: row.id, - meta: _.omit(row, omissions()) - }); - }); - }) - .then(() => { - return true; - }); - }, - - /** - * @param {Access} access - * @param {Object} data - * @param {Number} data.id - * @param {String} [data.reason] - * @returns {Promise} - */ - enable: (access, data) => { - return access.can('redirection_hosts:update', data.id) - .then(() => { - return internalRedirectionHost.get(access, { - id: data.id, - expand: ['certificate', 'owner'] - }); - }) - .then((row) => { - if (!row) { - throw new error.ItemNotFoundError(data.id); - } else if (row.enabled) { - throw new error.ValidationError('Host is already enabled'); - } - - row.enabled = 1; - - return redirectionHostModel - .query() - .where('id', row.id) - .patch({ - enabled: 1 - }) - .then(() => { - // Configure nginx - return internalNginx.configure(redirectionHostModel, 'redirection_host', row); - }) - .then(() => { - // Add to audit log - return internalAuditLog.add(access, { - action: 'enabled', - object_type: 'redirection-host', - object_id: row.id, - meta: _.omit(row, omissions()) - }); - }); - }) - .then(() => { - return true; - }); - }, - - /** - * @param {Access} access - * @param {Object} data - * @param {Number} data.id - * @param {String} [data.reason] - * @returns {Promise} - */ - disable: (access, data) => { - return access.can('redirection_hosts:update', data.id) - .then(() => { - return internalRedirectionHost.get(access, {id: data.id}); - }) - .then((row) => { - if (!row) { - throw new error.ItemNotFoundError(data.id); - } else if (!row.enabled) { - throw new error.ValidationError('Host is already disabled'); - } - - row.enabled = 0; - - return redirectionHostModel - .query() - .where('id', row.id) - .patch({ - enabled: 0 - }) - .then(() => { - // Delete Nginx Config - return internalNginx.deleteConfig('redirection_host', row) - .then(() => { - return internalNginx.reload(); - }); - }) - .then(() => { - // Add to audit log - return internalAuditLog.add(access, { - action: 'disabled', - object_type: 'redirection-host', - object_id: row.id, - meta: _.omit(row, omissions()) - }); - }); - }) - .then(() => { - return true; - }); - }, - - /** - * All Hosts - * - * @param {Access} access - * @param {Array} [expand] - * @param {String} [search_query] - * @returns {Promise} - */ - getAll: (access, expand, search_query) => { - return access.can('redirection_hosts:list') - .then((access_data) => { - let query = redirectionHostModel - .query() - .where('is_deleted', 0) - .groupBy('id') - .omit(['is_deleted']) - .allowEager('[owner,certificate]') - .orderBy('domain_names', 'ASC'); - - if (access_data.permission_visibility !== 'all') { - query.andWhere('owner_user_id', access.token.getUserId(1)); - } - - // Query is used for searching - if (typeof search_query === 'string') { - query.where(function () { - this.where('domain_names', 'like', '%' + search_query + '%'); - }); - } - - if (typeof expand !== 'undefined' && expand !== null) { - query.eager('[' + expand.join(', ') + ']'); - } - - return query; - }) - .then((rows) => { - if (typeof expand !== 'undefined' && expand !== null && expand.indexOf('certificate') !== -1) { - return internalHost.cleanAllRowsCertificateMeta(rows); - } - - return rows; - }); - }, - - /** - * Report use - * - * @param {Number} user_id - * @param {String} visibility - * @returns {Promise} - */ - getCount: (user_id, visibility) => { - let query = redirectionHostModel - .query() - .count('id as count') - .where('is_deleted', 0); - - if (visibility !== 'all') { - query.andWhere('owner_user_id', user_id); - } - - return query.first() - .then((row) => { - return parseInt(row.count, 10); - }); - } -}; - -module.exports = internalRedirectionHost; diff --git a/backend/internal/report.js b/backend/internal/report.js deleted file mode 100644 index 4dde659b..00000000 --- a/backend/internal/report.js +++ /dev/null @@ -1,38 +0,0 @@ -const internalProxyHost = require('./proxy-host'); -const internalRedirectionHost = require('./redirection-host'); -const internalDeadHost = require('./dead-host'); -const internalStream = require('./stream'); - -const internalReport = { - - /** - * @param {Access} access - * @return {Promise} - */ - getHostsReport: (access) => { - return access.can('reports:hosts', 1) - .then((access_data) => { - let user_id = access.token.getUserId(1); - - let promises = [ - internalProxyHost.getCount(user_id, access_data.visibility), - internalRedirectionHost.getCount(user_id, access_data.visibility), - internalStream.getCount(user_id, access_data.visibility), - internalDeadHost.getCount(user_id, access_data.visibility) - ]; - - return Promise.all(promises); - }) - .then((counts) => { - return { - proxy: counts.shift(), - redirection: counts.shift(), - stream: counts.shift(), - dead: counts.shift() - }; - }); - - } -}; - -module.exports = internalReport; diff --git a/backend/internal/setting.js b/backend/internal/setting.js deleted file mode 100644 index d4ac67d8..00000000 --- a/backend/internal/setting.js +++ /dev/null @@ -1,133 +0,0 @@ -const fs = require('fs'); -const error = require('../lib/error'); -const settingModel = require('../models/setting'); -const internalNginx = require('./nginx'); - -const internalSetting = { - - /** - * @param {Access} access - * @param {Object} data - * @param {String} data.id - * @return {Promise} - */ - update: (access, data) => { - return access.can('settings:update', data.id) - .then((/*access_data*/) => { - return internalSetting.get(access, {id: data.id}); - }) - .then((row) => { - if (row.id !== data.id) { - // Sanity check that something crazy hasn't happened - throw new error.InternalValidationError('Setting could not be updated, IDs do not match: ' + row.id + ' !== ' + data.id); - } - - return settingModel - .query() - .where({id: data.id}) - .patch(data); - }) - .then(() => { - return internalSetting.get(access, { - id: data.id - }); - }) - .then((row) => { - if (row.id === 'default-site') { - // write the html if we need to - if (row.value === 'html') { - fs.writeFileSync('/data/nginx/default_www/index.html', row.meta.html, {encoding: 'utf8'}); - } - - // Configure nginx - return internalNginx.deleteConfig('default') - .then(() => { - return internalNginx.generateConfig('default', row); - }) - .then(() => { - return internalNginx.test(); - }) - .then(() => { - return internalNginx.reload(); - }) - .then(() => { - return row; - }) - .catch((/*err*/) => { - internalNginx.deleteConfig('default') - .then(() => { - return internalNginx.test(); - }) - .then(() => { - return internalNginx.reload(); - }) - .then(() => { - // I'm being slack here I know.. - throw new error.ValidationError('Could not reconfigure Nginx. Please check logs.'); - }); - }); - } else { - return row; - } - }); - }, - - /** - * @param {Access} access - * @param {Object} data - * @param {String} data.id - * @return {Promise} - */ - get: (access, data) => { - return access.can('settings:get', data.id) - .then(() => { - return settingModel - .query() - .where('id', data.id) - .first(); - }) - .then((row) => { - if (row) { - return row; - } else { - throw new error.ItemNotFoundError(data.id); - } - }); - }, - - /** - * This will only count the settings - * - * @param {Access} access - * @returns {*} - */ - getCount: (access) => { - return access.can('settings:list') - .then(() => { - return settingModel - .query() - .count('id as count') - .first(); - }) - .then((row) => { - return parseInt(row.count, 10); - }); - }, - - /** - * All settings - * - * @param {Access} access - * @returns {Promise} - */ - getAll: (access) => { - return access.can('settings:list') - .then(() => { - return settingModel - .query() - .orderBy('description', 'ASC'); - }); - } -}; - -module.exports = internalSetting; diff --git a/backend/internal/state/state.go b/backend/internal/state/state.go new file mode 100644 index 00000000..e09867a8 --- /dev/null +++ b/backend/internal/state/state.go @@ -0,0 +1,31 @@ +package state + +import ( + "sync" +) + +// AppState holds pointers to channels and waitGroups +// shared by all goroutines of the application +type AppState struct { + waitGroup sync.WaitGroup + termSig chan bool +} + +// NewState ... +func NewState() *AppState { + state := &AppState{ + // buffered channel + termSig: make(chan bool, 1), + } + return state +} + +// GetWaitGroup ... +func (state *AppState) GetWaitGroup() *sync.WaitGroup { + return &state.waitGroup +} + +// GetTermSig ... +func (state *AppState) GetTermSig() chan bool { + return state.termSig +} diff --git a/backend/internal/stream.js b/backend/internal/stream.js deleted file mode 100644 index 9c458a10..00000000 --- a/backend/internal/stream.js +++ /dev/null @@ -1,348 +0,0 @@ -const _ = require('lodash'); -const error = require('../lib/error'); -const streamModel = require('../models/stream'); -const internalNginx = require('./nginx'); -const internalAuditLog = require('./audit-log'); - -function omissions () { - return ['is_deleted']; -} - -const internalStream = { - - /** - * @param {Access} access - * @param {Object} data - * @returns {Promise} - */ - create: (access, data) => { - return access.can('streams:create', data) - .then((/*access_data*/) => { - // TODO: At this point the existing ports should have been checked - data.owner_user_id = access.token.getUserId(1); - - if (typeof data.meta === 'undefined') { - data.meta = {}; - } - - return streamModel - .query() - .omit(omissions()) - .insertAndFetch(data); - }) - .then((row) => { - // Configure nginx - return internalNginx.configure(streamModel, 'stream', row) - .then(() => { - return internalStream.get(access, {id: row.id, expand: ['owner']}); - }); - }) - .then((row) => { - // Add to audit log - return internalAuditLog.add(access, { - action: 'created', - object_type: 'stream', - object_id: row.id, - meta: data - }) - .then(() => { - return row; - }); - }); - }, - - /** - * @param {Access} access - * @param {Object} data - * @param {Number} data.id - * @return {Promise} - */ - update: (access, data) => { - return access.can('streams:update', data.id) - .then((/*access_data*/) => { - // TODO: at this point the existing streams should have been checked - return internalStream.get(access, {id: data.id}); - }) - .then((row) => { - if (row.id !== data.id) { - // Sanity check that something crazy hasn't happened - throw new error.InternalValidationError('Stream could not be updated, IDs do not match: ' + row.id + ' !== ' + data.id); - } - - return streamModel - .query() - .omit(omissions()) - .patchAndFetchById(row.id, data) - .then((saved_row) => { - return internalNginx.configure(streamModel, 'stream', saved_row) - .then(() => { - return internalStream.get(access, {id: row.id, expand: ['owner']}); - }); - }) - .then((saved_row) => { - // Add to audit log - return internalAuditLog.add(access, { - action: 'updated', - object_type: 'stream', - object_id: row.id, - meta: data - }) - .then(() => { - return _.omit(saved_row, omissions()); - }); - }); - }); - }, - - /** - * @param {Access} access - * @param {Object} data - * @param {Number} data.id - * @param {Array} [data.expand] - * @param {Array} [data.omit] - * @return {Promise} - */ - get: (access, data) => { - if (typeof data === 'undefined') { - data = {}; - } - - return access.can('streams:get', data.id) - .then((access_data) => { - let query = streamModel - .query() - .where('is_deleted', 0) - .andWhere('id', data.id) - .allowEager('[owner]') - .first(); - - if (access_data.permission_visibility !== 'all') { - query.andWhere('owner_user_id', access.token.getUserId(1)); - } - - // Custom omissions - if (typeof data.omit !== 'undefined' && data.omit !== null) { - query.omit(data.omit); - } - - if (typeof data.expand !== 'undefined' && data.expand !== null) { - query.eager('[' + data.expand.join(', ') + ']'); - } - - return query; - }) - .then((row) => { - if (row) { - return _.omit(row, omissions()); - } else { - throw new error.ItemNotFoundError(data.id); - } - }); - }, - - /** - * @param {Access} access - * @param {Object} data - * @param {Number} data.id - * @param {String} [data.reason] - * @returns {Promise} - */ - delete: (access, data) => { - return access.can('streams:delete', data.id) - .then(() => { - return internalStream.get(access, {id: data.id}); - }) - .then((row) => { - if (!row) { - throw new error.ItemNotFoundError(data.id); - } - - return streamModel - .query() - .where('id', row.id) - .patch({ - is_deleted: 1 - }) - .then(() => { - // Delete Nginx Config - return internalNginx.deleteConfig('stream', row) - .then(() => { - return internalNginx.reload(); - }); - }) - .then(() => { - // Add to audit log - return internalAuditLog.add(access, { - action: 'deleted', - object_type: 'stream', - object_id: row.id, - meta: _.omit(row, omissions()) - }); - }); - }) - .then(() => { - return true; - }); - }, - - /** - * @param {Access} access - * @param {Object} data - * @param {Number} data.id - * @param {String} [data.reason] - * @returns {Promise} - */ - enable: (access, data) => { - return access.can('streams:update', data.id) - .then(() => { - return internalStream.get(access, { - id: data.id, - expand: ['owner'] - }); - }) - .then((row) => { - if (!row) { - throw new error.ItemNotFoundError(data.id); - } else if (row.enabled) { - throw new error.ValidationError('Host is already enabled'); - } - - row.enabled = 1; - - return streamModel - .query() - .where('id', row.id) - .patch({ - enabled: 1 - }) - .then(() => { - // Configure nginx - return internalNginx.configure(streamModel, 'stream', row); - }) - .then(() => { - // Add to audit log - return internalAuditLog.add(access, { - action: 'enabled', - object_type: 'stream', - object_id: row.id, - meta: _.omit(row, omissions()) - }); - }); - }) - .then(() => { - return true; - }); - }, - - /** - * @param {Access} access - * @param {Object} data - * @param {Number} data.id - * @param {String} [data.reason] - * @returns {Promise} - */ - disable: (access, data) => { - return access.can('streams:update', data.id) - .then(() => { - return internalStream.get(access, {id: data.id}); - }) - .then((row) => { - if (!row) { - throw new error.ItemNotFoundError(data.id); - } else if (!row.enabled) { - throw new error.ValidationError('Host is already disabled'); - } - - row.enabled = 0; - - return streamModel - .query() - .where('id', row.id) - .patch({ - enabled: 0 - }) - .then(() => { - // Delete Nginx Config - return internalNginx.deleteConfig('stream', row) - .then(() => { - return internalNginx.reload(); - }); - }) - .then(() => { - // Add to audit log - return internalAuditLog.add(access, { - action: 'disabled', - object_type: 'stream-host', - object_id: row.id, - meta: _.omit(row, omissions()) - }); - }); - }) - .then(() => { - return true; - }); - }, - - /** - * All Streams - * - * @param {Access} access - * @param {Array} [expand] - * @param {String} [search_query] - * @returns {Promise} - */ - getAll: (access, expand, search_query) => { - return access.can('streams:list') - .then((access_data) => { - let query = streamModel - .query() - .where('is_deleted', 0) - .groupBy('id') - .omit(['is_deleted']) - .allowEager('[owner]') - .orderBy('incoming_port', 'ASC'); - - if (access_data.permission_visibility !== 'all') { - query.andWhere('owner_user_id', access.token.getUserId(1)); - } - - // Query is used for searching - if (typeof search_query === 'string') { - query.where(function () { - this.where('incoming_port', 'like', '%' + search_query + '%'); - }); - } - - if (typeof expand !== 'undefined' && expand !== null) { - query.eager('[' + expand.join(', ') + ']'); - } - - return query; - }); - }, - - /** - * Report use - * - * @param {Number} user_id - * @param {String} visibility - * @returns {Promise} - */ - getCount: (user_id, visibility) => { - let query = streamModel - .query() - .count('id as count') - .where('is_deleted', 0); - - if (visibility !== 'all') { - query.andWhere('owner_user_id', user_id); - } - - return query.first() - .then((row) => { - return parseInt(row.count, 10); - }); - } -}; - -module.exports = internalStream; diff --git a/backend/internal/token.js b/backend/internal/token.js deleted file mode 100644 index a64b9010..00000000 --- a/backend/internal/token.js +++ /dev/null @@ -1,162 +0,0 @@ -const _ = require('lodash'); -const error = require('../lib/error'); -const userModel = require('../models/user'); -const authModel = require('../models/auth'); -const helpers = require('../lib/helpers'); -const TokenModel = require('../models/token'); - -module.exports = { - - /** - * @param {Object} data - * @param {String} data.identity - * @param {String} data.secret - * @param {String} [data.scope] - * @param {String} [data.expiry] - * @param {String} [issuer] - * @returns {Promise} - */ - getTokenFromEmail: (data, issuer) => { - let Token = new TokenModel(); - - data.scope = data.scope || 'user'; - data.expiry = data.expiry || '1d'; - - return userModel - .query() - .where('email', data.identity) - .andWhere('is_deleted', 0) - .andWhere('is_disabled', 0) - .first() - .then((user) => { - if (user) { - // Get auth - return authModel - .query() - .where('user_id', '=', user.id) - .where('type', '=', 'password') - .first() - .then((auth) => { - if (auth) { - return auth.verifyPassword(data.secret) - .then((valid) => { - if (valid) { - - if (data.scope !== 'user' && _.indexOf(user.roles, data.scope) === -1) { - // The scope requested doesn't exist as a role against the user, - // you shall not pass. - throw new error.AuthError('Invalid scope: ' + data.scope); - } - - // Create a moment of the expiry expression - let expiry = helpers.parseDatePeriod(data.expiry); - if (expiry === null) { - throw new error.AuthError('Invalid expiry time: ' + data.expiry); - } - - return Token.create({ - iss: issuer || 'api', - attrs: { - id: user.id - }, - scope: [data.scope], - expiresIn: data.expiry - }) - .then((signed) => { - return { - token: signed.token, - expires: expiry.toISOString() - }; - }); - } else { - throw new error.AuthError('Invalid password'); - } - }); - } else { - throw new error.AuthError('No password auth for user'); - } - }); - } else { - throw new error.AuthError('No relevant user found'); - } - }); - }, - - /** - * @param {Access} access - * @param {Object} [data] - * @param {String} [data.expiry] - * @param {String} [data.scope] Only considered if existing token scope is admin - * @returns {Promise} - */ - getFreshToken: (access, data) => { - let Token = new TokenModel(); - - data = data || {}; - data.expiry = data.expiry || '1d'; - - if (access && access.token.getUserId(0)) { - - // Create a moment of the expiry expression - let expiry = helpers.parseDatePeriod(data.expiry); - if (expiry === null) { - throw new error.AuthError('Invalid expiry time: ' + data.expiry); - } - - let token_attrs = { - id: access.token.getUserId(0) - }; - - // Only admins can request otherwise scoped tokens - let scope = access.token.get('scope'); - if (data.scope && access.token.hasScope('admin')) { - scope = [data.scope]; - - if (data.scope === 'job-board' || data.scope === 'worker') { - token_attrs.id = 0; - } - } - - return Token.create({ - iss: 'api', - scope: scope, - attrs: token_attrs, - expiresIn: data.expiry - }) - .then((signed) => { - return { - token: signed.token, - expires: expiry.toISOString() - }; - }); - } else { - throw new error.AssertionFailedError('Existing token contained invalid user data'); - } - }, - - /** - * @param {Object} user - * @returns {Promise} - */ - getTokenFromUser: (user) => { - const expire = '1d'; - const Token = new TokenModel(); - const expiry = helpers.parseDatePeriod(expire); - - return Token.create({ - iss: 'api', - attrs: { - id: user.id - }, - scope: ['user'], - expiresIn: expire - }) - .then((signed) => { - return { - token: signed.token, - expires: expiry.toISOString(), - user: user - }; - }); - } -}; diff --git a/backend/internal/types/db_date.go b/backend/internal/types/db_date.go new file mode 100644 index 00000000..9339868e --- /dev/null +++ b/backend/internal/types/db_date.go @@ -0,0 +1,39 @@ +package types + +import ( + "database/sql/driver" + "encoding/json" + "time" +) + +// DBDate is a date time +// type DBDate time.Time +type DBDate struct { + Time time.Time +} + +// Value encodes the type ready for the database +func (d DBDate) Value() (driver.Value, error) { + return driver.Value(d.Time.Unix()), nil +} + +// Scan takes data from the database and modifies it for Go Types +func (d *DBDate) Scan(src interface{}) error { + d.Time = time.Unix(src.(int64), 0) + return nil +} + +// UnmarshalJSON will unmarshal both database and post given values +func (d *DBDate) UnmarshalJSON(data []byte) error { + var u int64 + if err := json.Unmarshal(data, &u); err != nil { + return err + } + d.Time = time.Unix(u, 0) + return nil +} + +// MarshalJSON will marshal for output in api responses +func (d DBDate) MarshalJSON() ([]byte, error) { + return json.Marshal(d.Time.Unix()) +} diff --git a/backend/internal/types/jsonb.go b/backend/internal/types/jsonb.go new file mode 100644 index 00000000..d0fa2ae5 --- /dev/null +++ b/backend/internal/types/jsonb.go @@ -0,0 +1,58 @@ +package types + +import ( + "database/sql/driver" + "encoding/json" + "fmt" +) + +// JSONB can be anything +type JSONB struct { + Encoded string `json:"decoded"` + Decoded interface{} `json:"encoded"` +} + +// Value encodes the type ready for the database +func (j JSONB) Value() (driver.Value, error) { + json, err := json.Marshal(j.Decoded) + return driver.Value(string(json)), err +} + +// Scan takes data from the database and modifies it for Go Types +func (j *JSONB) Scan(src interface{}) error { + var jsonb JSONB + var srcString string + switch v := src.(type) { + case string: + srcString = src.(string) + case []uint8: + srcString = string(src.([]uint8)) + default: + return fmt.Errorf("Incompatible type for JSONB: %v", v) + } + + jsonb.Encoded = srcString + + if err := json.Unmarshal([]byte(srcString), &jsonb.Decoded); err != nil { + return err + } + + *j = jsonb + return nil +} + +// UnmarshalJSON will unmarshal both database and post given values +func (j *JSONB) UnmarshalJSON(data []byte) error { + var jsonb JSONB + jsonb.Encoded = string(data) + if err := json.Unmarshal(data, &jsonb.Decoded); err != nil { + return err + } + *j = jsonb + return nil +} + +// MarshalJSON will marshal for output in api responses +func (j JSONB) MarshalJSON() ([]byte, error) { + return json.Marshal(j.Decoded) +} diff --git a/backend/internal/types/nullable_db_date.go b/backend/internal/types/nullable_db_date.go new file mode 100644 index 00000000..9a0022c2 --- /dev/null +++ b/backend/internal/types/nullable_db_date.go @@ -0,0 +1,54 @@ +package types + +import ( + "database/sql/driver" + "encoding/json" + "time" +) + +// NullableDBDate is a date time that can be null in the db +// type DBDate time.Time +type NullableDBDate struct { + Time *time.Time +} + +// Value encodes the type ready for the database +func (d NullableDBDate) Value() (driver.Value, error) { + if d.Time == nil { + return nil, nil + } + return driver.Value(d.Time.Unix()), nil +} + +// Scan takes data from the database and modifies it for Go Types +func (d *NullableDBDate) Scan(src interface{}) error { + var tme time.Time + if src != nil { + tme = time.Unix(src.(int64), 0) + } + + d.Time = &tme + return nil +} + +// UnmarshalJSON will unmarshal both database and post given values +func (d *NullableDBDate) UnmarshalJSON(data []byte) error { + var t time.Time + var u int64 + if err := json.Unmarshal(data, &u); err != nil { + d.Time = &t + return nil + } + t = time.Unix(u, 0) + d.Time = &t + return nil +} + +// MarshalJSON will marshal for output in api responses +func (d NullableDBDate) MarshalJSON() ([]byte, error) { + if d.Time == nil || d.Time.IsZero() { + return json.Marshal(nil) + } + + return json.Marshal(d.Time.Unix()) +} diff --git a/backend/internal/types/roles.go b/backend/internal/types/roles.go new file mode 100644 index 00000000..2cb79006 --- /dev/null +++ b/backend/internal/types/roles.go @@ -0,0 +1,36 @@ +package types + +import ( + "database/sql/driver" + "encoding/json" + "fmt" +) + +// Roles is an array of strings +type Roles []string + +// Value encodes the type ready for the database +func (r Roles) Value() (driver.Value, error) { + roles, err := json.Marshal(r) + return driver.Value(string(roles)), err +} + +// Scan takes data from the database and modifies it for Go Types +func (r *Roles) Scan(src interface{}) error { + var roles Roles + var srcString string + switch v := src.(type) { + case string: + srcString = src.(string) + case []uint8: + srcString = string(src.([]uint8)) + default: + return fmt.Errorf("Incompatible type for Roles: %v", v) + } + + if err := json.Unmarshal([]byte(srcString), &roles); err != nil { + return err + } + *r = roles + return nil +} diff --git a/backend/internal/user.js b/backend/internal/user.js deleted file mode 100644 index 2e2d8abf..00000000 --- a/backend/internal/user.js +++ /dev/null @@ -1,518 +0,0 @@ -const _ = require('lodash'); -const error = require('../lib/error'); -const userModel = require('../models/user'); -const userPermissionModel = require('../models/user_permission'); -const authModel = require('../models/auth'); -const gravatar = require('gravatar'); -const internalToken = require('./token'); -const internalAuditLog = require('./audit-log'); - -function omissions () { - return ['is_deleted']; -} - -const internalUser = { - - /** - * @param {Access} access - * @param {Object} data - * @returns {Promise} - */ - create: (access, data) => { - let auth = data.auth || null; - delete data.auth; - - data.avatar = data.avatar || ''; - data.roles = data.roles || []; - - if (typeof data.is_disabled !== 'undefined') { - data.is_disabled = data.is_disabled ? 1 : 0; - } - - return access.can('users:create', data) - .then(() => { - data.avatar = gravatar.url(data.email, {default: 'mm'}); - - return userModel - .query() - .omit(omissions()) - .insertAndFetch(data); - }) - .then((user) => { - if (auth) { - return authModel - .query() - .insert({ - user_id: user.id, - type: auth.type, - secret: auth.secret, - meta: {} - }) - .then(() => { - return user; - }); - } else { - return user; - } - }) - .then((user) => { - // Create permissions row as well - let is_admin = data.roles.indexOf('admin') !== -1; - - return userPermissionModel - .query() - .insert({ - user_id: user.id, - visibility: is_admin ? 'all' : 'user', - proxy_hosts: 'manage', - redirection_hosts: 'manage', - dead_hosts: 'manage', - streams: 'manage', - access_lists: 'manage', - certificates: 'manage' - }) - .then(() => { - return internalUser.get(access, {id: user.id, expand: ['permissions']}); - }); - }) - .then((user) => { - // Add to audit log - return internalAuditLog.add(access, { - action: 'created', - object_type: 'user', - object_id: user.id, - meta: user - }) - .then(() => { - return user; - }); - }); - }, - - /** - * @param {Access} access - * @param {Object} data - * @param {Integer} data.id - * @param {String} [data.email] - * @param {String} [data.name] - * @return {Promise} - */ - update: (access, data) => { - if (typeof data.is_disabled !== 'undefined') { - data.is_disabled = data.is_disabled ? 1 : 0; - } - - return access.can('users:update', data.id) - .then(() => { - - // Make sure that the user being updated doesn't change their email to another user that is already using it - // 1. get user we want to update - return internalUser.get(access, {id: data.id}) - .then((user) => { - - // 2. if email is to be changed, find other users with that email - if (typeof data.email !== 'undefined') { - data.email = data.email.toLowerCase().trim(); - - if (user.email !== data.email) { - return internalUser.isEmailAvailable(data.email, data.id) - .then((available) => { - if (!available) { - throw new error.ValidationError('Email address already in use - ' + data.email); - } - - return user; - }); - } - } - - // No change to email: - return user; - }); - }) - .then((user) => { - if (user.id !== data.id) { - // Sanity check that something crazy hasn't happened - throw new error.InternalValidationError('User could not be updated, IDs do not match: ' + user.id + ' !== ' + data.id); - } - - data.avatar = gravatar.url(data.email || user.email, {default: 'mm'}); - - return userModel - .query() - .omit(omissions()) - .patchAndFetchById(user.id, data) - .then((saved_user) => { - return _.omit(saved_user, omissions()); - }); - }) - .then(() => { - return internalUser.get(access, {id: data.id}); - }) - .then((user) => { - // Add to audit log - return internalAuditLog.add(access, { - action: 'updated', - object_type: 'user', - object_id: user.id, - meta: data - }) - .then(() => { - return user; - }); - }); - }, - - /** - * @param {Access} access - * @param {Object} [data] - * @param {Integer} [data.id] Defaults to the token user - * @param {Array} [data.expand] - * @param {Array} [data.omit] - * @return {Promise} - */ - get: (access, data) => { - if (typeof data === 'undefined') { - data = {}; - } - - if (typeof data.id === 'undefined' || !data.id) { - data.id = access.token.getUserId(0); - } - - return access.can('users:get', data.id) - .then(() => { - let query = userModel - .query() - .where('is_deleted', 0) - .andWhere('id', data.id) - .allowEager('[permissions]') - .first(); - - // Custom omissions - if (typeof data.omit !== 'undefined' && data.omit !== null) { - query.omit(data.omit); - } - - if (typeof data.expand !== 'undefined' && data.expand !== null) { - query.eager('[' + data.expand.join(', ') + ']'); - } - - return query; - }) - .then((row) => { - if (row) { - return _.omit(row, omissions()); - } else { - throw new error.ItemNotFoundError(data.id); - } - }); - }, - - /** - * Checks if an email address is available, but if a user_id is supplied, it will ignore checking - * against that user. - * - * @param email - * @param user_id - */ - isEmailAvailable: (email, user_id) => { - let query = userModel - .query() - .where('email', '=', email.toLowerCase().trim()) - .where('is_deleted', 0) - .first(); - - if (typeof user_id !== 'undefined') { - query.where('id', '!=', user_id); - } - - return query - .then((user) => { - return !user; - }); - }, - - /** - * @param {Access} access - * @param {Object} data - * @param {Integer} data.id - * @param {String} [data.reason] - * @returns {Promise} - */ - delete: (access, data) => { - return access.can('users:delete', data.id) - .then(() => { - return internalUser.get(access, {id: data.id}); - }) - .then((user) => { - if (!user) { - throw new error.ItemNotFoundError(data.id); - } - - // Make sure user can't delete themselves - if (user.id === access.token.getUserId(0)) { - throw new error.PermissionError('You cannot delete yourself.'); - } - - return userModel - .query() - .where('id', user.id) - .patch({ - is_deleted: 1 - }) - .then(() => { - // Add to audit log - return internalAuditLog.add(access, { - action: 'deleted', - object_type: 'user', - object_id: user.id, - meta: _.omit(user, omissions()) - }); - }); - }) - .then(() => { - return true; - }); - }, - - /** - * This will only count the users - * - * @param {Access} access - * @param {String} [search_query] - * @returns {*} - */ - getCount: (access, search_query) => { - return access.can('users:list') - .then(() => { - let query = userModel - .query() - .count('id as count') - .where('is_deleted', 0) - .first(); - - // Query is used for searching - if (typeof search_query === 'string') { - query.where(function () { - this.where('user.name', 'like', '%' + search_query + '%') - .orWhere('user.email', 'like', '%' + search_query + '%'); - }); - } - - return query; - }) - .then((row) => { - return parseInt(row.count, 10); - }); - }, - - /** - * All users - * - * @param {Access} access - * @param {Array} [expand] - * @param {String} [search_query] - * @returns {Promise} - */ - getAll: (access, expand, search_query) => { - return access.can('users:list') - .then(() => { - let query = userModel - .query() - .where('is_deleted', 0) - .groupBy('id') - .omit(['is_deleted']) - .allowEager('[permissions]') - .orderBy('name', 'ASC'); - - // Query is used for searching - if (typeof search_query === 'string') { - query.where(function () { - this.where('name', 'like', '%' + search_query + '%') - .orWhere('email', 'like', '%' + search_query + '%'); - }); - } - - if (typeof expand !== 'undefined' && expand !== null) { - query.eager('[' + expand.join(', ') + ']'); - } - - return query; - }); - }, - - /** - * @param {Access} access - * @param {Integer} [id_requested] - * @returns {[String]} - */ - getUserOmisionsByAccess: (access, id_requested) => { - let response = []; // Admin response - - if (!access.token.hasScope('admin') && access.token.getUserId(0) !== id_requested) { - response = ['roles', 'is_deleted']; // Restricted response - } - - return response; - }, - - /** - * @param {Access} access - * @param {Object} data - * @param {Integer} data.id - * @param {String} data.type - * @param {String} data.secret - * @return {Promise} - */ - setPassword: (access, data) => { - return access.can('users:password', data.id) - .then(() => { - return internalUser.get(access, {id: data.id}); - }) - .then((user) => { - if (user.id !== data.id) { - // Sanity check that something crazy hasn't happened - throw new error.InternalValidationError('User could not be updated, IDs do not match: ' + user.id + ' !== ' + data.id); - } - - if (user.id === access.token.getUserId(0)) { - // they're setting their own password. Make sure their current password is correct - if (typeof data.current === 'undefined' || !data.current) { - throw new error.ValidationError('Current password was not supplied'); - } - - return internalToken.getTokenFromEmail({ - identity: user.email, - secret: data.current - }) - .then(() => { - return user; - }); - } - - return user; - }) - .then((user) => { - // Get auth, patch if it exists - return authModel - .query() - .where('user_id', user.id) - .andWhere('type', data.type) - .first() - .then((existing_auth) => { - if (existing_auth) { - // patch - return authModel - .query() - .where('user_id', user.id) - .andWhere('type', data.type) - .patch({ - type: data.type, // This is required for the model to encrypt on save - secret: data.secret - }); - } else { - // insert - return authModel - .query() - .insert({ - user_id: user.id, - type: data.type, - secret: data.secret, - meta: {} - }); - } - }) - .then(() => { - // Add to Audit Log - return internalAuditLog.add(access, { - action: 'updated', - object_type: 'user', - object_id: user.id, - meta: { - name: user.name, - password_changed: true, - auth_type: data.type - } - }); - }); - }) - .then(() => { - return true; - }); - }, - - /** - * @param {Access} access - * @param {Object} data - * @return {Promise} - */ - setPermissions: (access, data) => { - return access.can('users:permissions', data.id) - .then(() => { - return internalUser.get(access, {id: data.id}); - }) - .then((user) => { - if (user.id !== data.id) { - // Sanity check that something crazy hasn't happened - throw new error.InternalValidationError('User could not be updated, IDs do not match: ' + user.id + ' !== ' + data.id); - } - - return user; - }) - .then((user) => { - // Get perms row, patch if it exists - return userPermissionModel - .query() - .where('user_id', user.id) - .first() - .then((existing_auth) => { - if (existing_auth) { - // patch - return userPermissionModel - .query() - .where('user_id', user.id) - .patchAndFetchById(existing_auth.id, _.assign({user_id: user.id}, data)); - } else { - // insert - return userPermissionModel - .query() - .insertAndFetch(_.assign({user_id: user.id}, data)); - } - }) - .then((permissions) => { - // Add to Audit Log - return internalAuditLog.add(access, { - action: 'updated', - object_type: 'user', - object_id: user.id, - meta: { - name: user.name, - permissions: permissions - } - }); - - }); - }) - .then(() => { - return true; - }); - }, - - /** - * @param {Access} access - * @param {Object} data - * @param {Integer} data.id - */ - loginAs: (access, data) => { - return access.can('users:loginas', data.id) - .then(() => { - return internalUser.get(access, data); - }) - .then((user) => { - return internalToken.getTokenFromUser(user); - }); - } -}; - -module.exports = internalUser; diff --git a/backend/internal/util/maps.go b/backend/internal/util/maps.go new file mode 100644 index 00000000..1ff211ec --- /dev/null +++ b/backend/internal/util/maps.go @@ -0,0 +1,9 @@ +package util + +// MapContainsKey is fairly self explanatory +func MapContainsKey(dict map[string]interface{}, key string) bool { + if _, ok := dict[key]; ok { + return true + } + return false +} diff --git a/backend/internal/util/maps_test.go b/backend/internal/util/maps_test.go new file mode 100644 index 00000000..fdb5d2ff --- /dev/null +++ b/backend/internal/util/maps_test.go @@ -0,0 +1,45 @@ +package util + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +type rect struct { + width int + height int +} + +func TestMapContainsKey(t *testing.T) { + var r rect + r.width = 5 + r.height = 5 + m := map[string]interface{}{ + "rect_width": r.width, + "rect_height": r.height, + } + tests := []struct { + name string + pass string + want bool + }{ + { + name: "exists", + pass: "rect_width", + want: true, + }, + { + name: "Does not exist", + pass: "rect_perimeter", + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := MapContainsKey(m, tt.pass) + + assert.Equal(t, result, tt.want) + }) + } +} diff --git a/backend/internal/util/slices.go b/backend/internal/util/slices.go new file mode 100644 index 00000000..c078f1c8 --- /dev/null +++ b/backend/internal/util/slices.go @@ -0,0 +1,35 @@ +package util + +import ( + "strconv" + "strings" +) + +// SliceContainsItem returns whether the slice given contains the item given +func SliceContainsItem(slice []string, item string) bool { + for _, a := range slice { + if a == item { + return true + } + } + return false +} + +// SliceContainsInt returns whether the slice given contains the item given +func SliceContainsInt(slice []int, item int) bool { + for _, a := range slice { + if a == item { + return true + } + } + return false +} + +// ConvertIntSliceToString returns a comma separated string of all items in the slice +func ConvertIntSliceToString(slice []int) string { + strs := []string{} + for _, item := range slice { + strs = append(strs, strconv.Itoa(item)) + } + return strings.Join(strs, ",") +} diff --git a/backend/internal/util/slices_test.go b/backend/internal/util/slices_test.go new file mode 100644 index 00000000..f2f18714 --- /dev/null +++ b/backend/internal/util/slices_test.go @@ -0,0 +1,92 @@ +package util + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestSliceContainsItem(t *testing.T) { + type want struct { + result bool + } + tests := []struct { + name string + inputString string + inputArray []string + want want + }{ + { + name: "In array", + inputString: "test", + inputArray: []string{"no", "more", "tests", "test"}, + want: want{ + result: true, + }, + }, + { + name: "Not in array", + inputString: "test", + inputArray: []string{"no", "more", "tests"}, + want: want{ + result: false, + }, + }, + { + name: "Case sensitive", + inputString: "test", + inputArray: []string{"no", "TEST", "more"}, + want: want{ + result: false, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := SliceContainsItem(tt.inputArray, tt.inputString) + assert.Equal(t, tt.want.result, got) + }) + } +} + +func TestSliceContainsInt(t *testing.T) { + type want struct { + result bool + } + tests := []struct { + name string + inputInt int + inputArray []int + want want + }{ + { + name: "In array", + inputInt: 1, + inputArray: []int{1, 2, 3, 4}, + want: want{ + result: true, + }, + }, + { + name: "Not in array", + inputInt: 1, + inputArray: []int{10, 2, 3, 4}, + want: want{ + result: false, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := SliceContainsInt(tt.inputArray, tt.inputInt) + assert.Equal(t, tt.want.result, got) + }) + } +} + +func TestConvertIntSliceToString(t *testing.T) { + items := []int{1, 2, 3, 4, 5, 6, 7} + expectedStr := "1,2,3,4,5,6,7" + str := ConvertIntSliceToString(items) + assert.Equal(t, expectedStr, str) +} diff --git a/backend/internal/validator/hosts.go b/backend/internal/validator/hosts.go new file mode 100644 index 00000000..6453fa49 --- /dev/null +++ b/backend/internal/validator/hosts.go @@ -0,0 +1,23 @@ +package validator + +import ( + "fmt" + + "npm/internal/entity/certificate" + "npm/internal/entity/host" +) + +// ValidateHost will check if associated objects exist and other checks +// will return a nil error if things are OK +func ValidateHost(h host.Model) error { + if h.CertificateID > 0 { + // Check certificate exists and is valid + // This will not determine if the certificate is Ready to use, + // as this validation only cares that the row exists. + if _, cErr := certificate.GetByID(h.CertificateID); cErr != nil { + return fmt.Errorf("Certificate #%d does not exist", h.CertificateID) + } + } + + return nil +} diff --git a/backend/internal/worker/certificate.go b/backend/internal/worker/certificate.go new file mode 100644 index 00000000..157bdb67 --- /dev/null +++ b/backend/internal/worker/certificate.go @@ -0,0 +1,61 @@ +package worker + +import ( + "time" + + "npm/internal/entity/certificate" + "npm/internal/logger" + "npm/internal/state" +) + +type certificateWorker struct { + state *state.AppState +} + +// StartCertificateWorker starts the CertificateWorker +func StartCertificateWorker(state *state.AppState) { + worker := newCertificateWorker(state) + logger.Info("CertificateWorker Started") + worker.Run() +} + +func newCertificateWorker(state *state.AppState) *certificateWorker { + return &certificateWorker{ + state: state, + } +} + +// Run the CertificateWorker +func (w *certificateWorker) Run() { + // global wait group + gwg := w.state.GetWaitGroup() + gwg.Add(1) + + ticker := time.NewTicker(15 * time.Second) +mainLoop: + for { + select { + case _, more := <-w.state.GetTermSig(): + if !more { + logger.Info("Terminating CertificateWorker ... ") + break mainLoop + } + case <-ticker.C: + requestCertificates() + } + } +} + +func requestCertificates() { + rows, err := certificate.GetByStatus(certificate.StatusReady) + if err != nil { + logger.Error("requestCertificatesError", err) + return + } + + for _, row := range rows { + if err := row.Request(); err != nil { + logger.Error("CertificateRequestError", err) + } + } +} diff --git a/backend/knexfile.js b/backend/knexfile.js deleted file mode 100644 index 391ca005..00000000 --- a/backend/knexfile.js +++ /dev/null @@ -1,19 +0,0 @@ -module.exports = { - development: { - client: 'mysql', - migrations: { - tableName: 'migrations', - stub: 'lib/migrate_template.js', - directory: 'migrations' - } - }, - - production: { - client: 'mysql', - migrations: { - tableName: 'migrations', - stub: 'lib/migrate_template.js', - directory: 'migrations' - } - } -}; diff --git a/backend/lib/access.js b/backend/lib/access.js deleted file mode 100644 index 9d7329d9..00000000 --- a/backend/lib/access.js +++ /dev/null @@ -1,314 +0,0 @@ -/** - * Some Notes: This is a friggin complicated piece of code. - * - * "scope" in this file means "where did this token come from and what is using it", so 99% of the time - * the "scope" is going to be "user" because it would be a user token. This is not to be confused with - * the "role" which could be "user" or "admin". The scope in fact, could be "worker" or anything else. - * - * - */ - -const _ = require('lodash'); -const logger = require('../logger').access; -const validator = require('ajv'); -const error = require('./error'); -const userModel = require('../models/user'); -const proxyHostModel = require('../models/proxy_host'); -const TokenModel = require('../models/token'); -const roleSchema = require('./access/roles.json'); -const permsSchema = require('./access/permissions.json'); - -module.exports = function (token_string) { - let Token = new TokenModel(); - let token_data = null; - let initialised = false; - let object_cache = {}; - let allow_internal_access = false; - let user_roles = []; - let permissions = {}; - - /** - * Loads the Token object from the token string - * - * @returns {Promise} - */ - this.init = () => { - return new Promise((resolve, reject) => { - if (initialised) { - resolve(); - } else if (!token_string) { - reject(new error.PermissionError('Permission Denied')); - } else { - resolve(Token.load(token_string) - .then((data) => { - token_data = data; - - // At this point we need to load the user from the DB and make sure they: - // - exist (and not soft deleted) - // - still have the appropriate scopes for this token - // This is only required when the User ID is supplied or if the token scope has `user` - - if (token_data.attrs.id || (typeof token_data.scope !== 'undefined' && _.indexOf(token_data.scope, 'user') !== -1)) { - // Has token user id or token user scope - return userModel - .query() - .where('id', token_data.attrs.id) - .andWhere('is_deleted', 0) - .andWhere('is_disabled', 0) - .allowEager('[permissions]') - .eager('[permissions]') - .first() - .then((user) => { - if (user) { - // make sure user has all scopes of the token - // The `user` role is not added against the user row, so we have to just add it here to get past this check. - user.roles.push('user'); - - let is_ok = true; - _.forEach(token_data.scope, (scope_item) => { - if (_.indexOf(user.roles, scope_item) === -1) { - is_ok = false; - } - }); - - if (!is_ok) { - throw new error.AuthError('Invalid token scope for User'); - } else { - initialised = true; - user_roles = user.roles; - permissions = user.permissions; - } - - } else { - throw new error.AuthError('User cannot be loaded for Token'); - } - }); - } else { - initialised = true; - } - })); - } - }); - }; - - /** - * Fetches the object ids from the database, only once per object type, for this token. - * This only applies to USER token scopes, as all other tokens are not really bound - * by object scopes - * - * @param {String} object_type - * @returns {Promise} - */ - this.loadObjects = (object_type) => { - return new Promise((resolve, reject) => { - if (Token.hasScope('user')) { - if (typeof token_data.attrs.id === 'undefined' || !token_data.attrs.id) { - reject(new error.AuthError('User Token supplied without a User ID')); - } else { - let token_user_id = token_data.attrs.id ? token_data.attrs.id : 0; - let query; - - if (typeof object_cache[object_type] === 'undefined') { - switch (object_type) { - - // USERS - should only return yourself - case 'users': - resolve(token_user_id ? [token_user_id] : []); - break; - - // Proxy Hosts - case 'proxy_hosts': - query = proxyHostModel - .query() - .select('id') - .andWhere('is_deleted', 0); - - if (permissions.visibility === 'user') { - query.andWhere('owner_user_id', token_user_id); - } - - resolve(query - .then((rows) => { - let result = []; - _.forEach(rows, (rule_row) => { - result.push(rule_row.id); - }); - - // enum should not have less than 1 item - if (!result.length) { - result.push(0); - } - - return result; - }) - ); - break; - - // DEFAULT: null - default: - resolve(null); - break; - } - } else { - resolve(object_cache[object_type]); - } - } - } else { - resolve(null); - } - }) - .then((objects) => { - object_cache[object_type] = objects; - return objects; - }); - }; - - /** - * Creates a schema object on the fly with the IDs and other values required to be checked against the permissionSchema - * - * @param {String} permission_label - * @returns {Object} - */ - this.getObjectSchema = (permission_label) => { - let base_object_type = permission_label.split(':').shift(); - - let schema = { - $id: 'objects', - $schema: 'http://json-schema.org/draft-07/schema#', - description: 'Actor Properties', - type: 'object', - additionalProperties: false, - properties: { - user_id: { - anyOf: [ - { - type: 'number', - enum: [Token.get('attrs').id] - } - ] - }, - scope: { - type: 'string', - pattern: '^' + Token.get('scope') + '$' - } - } - }; - - return this.loadObjects(base_object_type) - .then((object_result) => { - if (typeof object_result === 'object' && object_result !== null) { - schema.properties[base_object_type] = { - type: 'number', - enum: object_result, - minimum: 1 - }; - } else { - schema.properties[base_object_type] = { - type: 'number', - minimum: 1 - }; - } - - return schema; - }); - }; - - return { - - token: Token, - - /** - * - * @param {Boolean} [allow_internal] - * @returns {Promise} - */ - load: (allow_internal) => { - return new Promise(function (resolve/*, reject*/) { - if (token_string) { - resolve(Token.load(token_string)); - } else { - allow_internal_access = allow_internal; - resolve(allow_internal_access || null); - } - }); - }, - - reloadObjects: this.loadObjects, - - /** - * - * @param {String} permission - * @param {*} [data] - * @returns {Promise} - */ - can: (permission, data) => { - if (allow_internal_access === true) { - return Promise.resolve(true); - //return true; - } else { - return this.init() - .then(() => { - // Initialised, token decoded ok - return this.getObjectSchema(permission) - .then((objectSchema) => { - let data_schema = { - [permission]: { - data: data, - scope: Token.get('scope'), - roles: user_roles, - permission_visibility: permissions.visibility, - permission_proxy_hosts: permissions.proxy_hosts, - permission_redirection_hosts: permissions.redirection_hosts, - permission_dead_hosts: permissions.dead_hosts, - permission_streams: permissions.streams, - permission_access_lists: permissions.access_lists, - permission_certificates: permissions.certificates - } - }; - - let permissionSchema = { - $schema: 'http://json-schema.org/draft-07/schema#', - $async: true, - $id: 'permissions', - additionalProperties: false, - properties: {} - }; - - permissionSchema.properties[permission] = require('./access/' + permission.replace(/:/gim, '-') + '.json'); - - // logger.info('objectSchema', JSON.stringify(objectSchema, null, 2)); - // logger.info('permissionSchema', JSON.stringify(permissionSchema, null, 2)); - // logger.info('data_schema', JSON.stringify(data_schema, null, 2)); - - let ajv = validator({ - verbose: true, - allErrors: true, - format: 'full', - missingRefs: 'fail', - breakOnError: true, - coerceTypes: true, - schemas: [ - roleSchema, - permsSchema, - objectSchema, - permissionSchema - ] - }); - - return ajv.validate('permissions', data_schema) - .then(() => { - return data_schema[permission]; - }); - }); - }) - .catch((err) => { - err.permission = permission; - err.permission_data = data; - logger.error(permission, data, err.message); - - throw new error.PermissionError('Permission Denied', err); - }); - } - } - }; -}; diff --git a/backend/lib/access/access_lists-create.json b/backend/lib/access/access_lists-create.json deleted file mode 100644 index 5a16a864..00000000 --- a/backend/lib/access/access_lists-create.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "anyOf": [ - { - "$ref": "roles#/definitions/admin" - }, - { - "type": "object", - "required": ["permission_access_lists", "roles"], - "properties": { - "permission_access_lists": { - "$ref": "perms#/definitions/manage" - }, - "roles": { - "type": "array", - "items": { - "type": "string", - "enum": ["user"] - } - } - } - } - ] -} diff --git a/backend/lib/access/access_lists-delete.json b/backend/lib/access/access_lists-delete.json deleted file mode 100644 index 5a16a864..00000000 --- a/backend/lib/access/access_lists-delete.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "anyOf": [ - { - "$ref": "roles#/definitions/admin" - }, - { - "type": "object", - "required": ["permission_access_lists", "roles"], - "properties": { - "permission_access_lists": { - "$ref": "perms#/definitions/manage" - }, - "roles": { - "type": "array", - "items": { - "type": "string", - "enum": ["user"] - } - } - } - } - ] -} diff --git a/backend/lib/access/access_lists-get.json b/backend/lib/access/access_lists-get.json deleted file mode 100644 index 8f6dd8cc..00000000 --- a/backend/lib/access/access_lists-get.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "anyOf": [ - { - "$ref": "roles#/definitions/admin" - }, - { - "type": "object", - "required": ["permission_access_lists", "roles"], - "properties": { - "permission_access_lists": { - "$ref": "perms#/definitions/view" - }, - "roles": { - "type": "array", - "items": { - "type": "string", - "enum": ["user"] - } - } - } - } - ] -} diff --git a/backend/lib/access/access_lists-list.json b/backend/lib/access/access_lists-list.json deleted file mode 100644 index 8f6dd8cc..00000000 --- a/backend/lib/access/access_lists-list.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "anyOf": [ - { - "$ref": "roles#/definitions/admin" - }, - { - "type": "object", - "required": ["permission_access_lists", "roles"], - "properties": { - "permission_access_lists": { - "$ref": "perms#/definitions/view" - }, - "roles": { - "type": "array", - "items": { - "type": "string", - "enum": ["user"] - } - } - } - } - ] -} diff --git a/backend/lib/access/access_lists-update.json b/backend/lib/access/access_lists-update.json deleted file mode 100644 index 5a16a864..00000000 --- a/backend/lib/access/access_lists-update.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "anyOf": [ - { - "$ref": "roles#/definitions/admin" - }, - { - "type": "object", - "required": ["permission_access_lists", "roles"], - "properties": { - "permission_access_lists": { - "$ref": "perms#/definitions/manage" - }, - "roles": { - "type": "array", - "items": { - "type": "string", - "enum": ["user"] - } - } - } - } - ] -} diff --git a/backend/lib/access/auditlog-list.json b/backend/lib/access/auditlog-list.json deleted file mode 100644 index aeadc94b..00000000 --- a/backend/lib/access/auditlog-list.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "anyOf": [ - { - "$ref": "roles#/definitions/admin" - } - ] -} diff --git a/backend/lib/access/certificates-create.json b/backend/lib/access/certificates-create.json deleted file mode 100644 index bcdf6674..00000000 --- a/backend/lib/access/certificates-create.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "anyOf": [ - { - "$ref": "roles#/definitions/admin" - }, - { - "type": "object", - "required": ["permission_certificates", "roles"], - "properties": { - "permission_certificates": { - "$ref": "perms#/definitions/manage" - }, - "roles": { - "type": "array", - "items": { - "type": "string", - "enum": ["user"] - } - } - } - } - ] -} diff --git a/backend/lib/access/certificates-delete.json b/backend/lib/access/certificates-delete.json deleted file mode 100644 index bcdf6674..00000000 --- a/backend/lib/access/certificates-delete.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "anyOf": [ - { - "$ref": "roles#/definitions/admin" - }, - { - "type": "object", - "required": ["permission_certificates", "roles"], - "properties": { - "permission_certificates": { - "$ref": "perms#/definitions/manage" - }, - "roles": { - "type": "array", - "items": { - "type": "string", - "enum": ["user"] - } - } - } - } - ] -} diff --git a/backend/lib/access/certificates-get.json b/backend/lib/access/certificates-get.json deleted file mode 100644 index 9ccfa4f1..00000000 --- a/backend/lib/access/certificates-get.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "anyOf": [ - { - "$ref": "roles#/definitions/admin" - }, - { - "type": "object", - "required": ["permission_certificates", "roles"], - "properties": { - "permission_certificates": { - "$ref": "perms#/definitions/view" - }, - "roles": { - "type": "array", - "items": { - "type": "string", - "enum": ["user"] - } - } - } - } - ] -} diff --git a/backend/lib/access/certificates-list.json b/backend/lib/access/certificates-list.json deleted file mode 100644 index 9ccfa4f1..00000000 --- a/backend/lib/access/certificates-list.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "anyOf": [ - { - "$ref": "roles#/definitions/admin" - }, - { - "type": "object", - "required": ["permission_certificates", "roles"], - "properties": { - "permission_certificates": { - "$ref": "perms#/definitions/view" - }, - "roles": { - "type": "array", - "items": { - "type": "string", - "enum": ["user"] - } - } - } - } - ] -} diff --git a/backend/lib/access/certificates-update.json b/backend/lib/access/certificates-update.json deleted file mode 100644 index bcdf6674..00000000 --- a/backend/lib/access/certificates-update.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "anyOf": [ - { - "$ref": "roles#/definitions/admin" - }, - { - "type": "object", - "required": ["permission_certificates", "roles"], - "properties": { - "permission_certificates": { - "$ref": "perms#/definitions/manage" - }, - "roles": { - "type": "array", - "items": { - "type": "string", - "enum": ["user"] - } - } - } - } - ] -} diff --git a/backend/lib/access/dead_hosts-create.json b/backend/lib/access/dead_hosts-create.json deleted file mode 100644 index a276c681..00000000 --- a/backend/lib/access/dead_hosts-create.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "anyOf": [ - { - "$ref": "roles#/definitions/admin" - }, - { - "type": "object", - "required": ["permission_dead_hosts", "roles"], - "properties": { - "permission_dead_hosts": { - "$ref": "perms#/definitions/manage" - }, - "roles": { - "type": "array", - "items": { - "type": "string", - "enum": ["user"] - } - } - } - } - ] -} diff --git a/backend/lib/access/dead_hosts-delete.json b/backend/lib/access/dead_hosts-delete.json deleted file mode 100644 index a276c681..00000000 --- a/backend/lib/access/dead_hosts-delete.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "anyOf": [ - { - "$ref": "roles#/definitions/admin" - }, - { - "type": "object", - "required": ["permission_dead_hosts", "roles"], - "properties": { - "permission_dead_hosts": { - "$ref": "perms#/definitions/manage" - }, - "roles": { - "type": "array", - "items": { - "type": "string", - "enum": ["user"] - } - } - } - } - ] -} diff --git a/backend/lib/access/dead_hosts-get.json b/backend/lib/access/dead_hosts-get.json deleted file mode 100644 index 87aa12e7..00000000 --- a/backend/lib/access/dead_hosts-get.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "anyOf": [ - { - "$ref": "roles#/definitions/admin" - }, - { - "type": "object", - "required": ["permission_dead_hosts", "roles"], - "properties": { - "permission_dead_hosts": { - "$ref": "perms#/definitions/view" - }, - "roles": { - "type": "array", - "items": { - "type": "string", - "enum": ["user"] - } - } - } - } - ] -} diff --git a/backend/lib/access/dead_hosts-list.json b/backend/lib/access/dead_hosts-list.json deleted file mode 100644 index 87aa12e7..00000000 --- a/backend/lib/access/dead_hosts-list.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "anyOf": [ - { - "$ref": "roles#/definitions/admin" - }, - { - "type": "object", - "required": ["permission_dead_hosts", "roles"], - "properties": { - "permission_dead_hosts": { - "$ref": "perms#/definitions/view" - }, - "roles": { - "type": "array", - "items": { - "type": "string", - "enum": ["user"] - } - } - } - } - ] -} diff --git a/backend/lib/access/dead_hosts-update.json b/backend/lib/access/dead_hosts-update.json deleted file mode 100644 index a276c681..00000000 --- a/backend/lib/access/dead_hosts-update.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "anyOf": [ - { - "$ref": "roles#/definitions/admin" - }, - { - "type": "object", - "required": ["permission_dead_hosts", "roles"], - "properties": { - "permission_dead_hosts": { - "$ref": "perms#/definitions/manage" - }, - "roles": { - "type": "array", - "items": { - "type": "string", - "enum": ["user"] - } - } - } - } - ] -} diff --git a/backend/lib/access/permissions.json b/backend/lib/access/permissions.json deleted file mode 100644 index 8480f9a1..00000000 --- a/backend/lib/access/permissions.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "perms", - "definitions": { - "view": { - "type": "string", - "pattern": "^(view|manage)$" - }, - "manage": { - "type": "string", - "pattern": "^(manage)$" - } - } -} diff --git a/backend/lib/access/proxy_hosts-create.json b/backend/lib/access/proxy_hosts-create.json deleted file mode 100644 index 166527a3..00000000 --- a/backend/lib/access/proxy_hosts-create.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "anyOf": [ - { - "$ref": "roles#/definitions/admin" - }, - { - "type": "object", - "required": ["permission_proxy_hosts", "roles"], - "properties": { - "permission_proxy_hosts": { - "$ref": "perms#/definitions/manage" - }, - "roles": { - "type": "array", - "items": { - "type": "string", - "enum": ["user"] - } - } - } - } - ] -} diff --git a/backend/lib/access/proxy_hosts-delete.json b/backend/lib/access/proxy_hosts-delete.json deleted file mode 100644 index 166527a3..00000000 --- a/backend/lib/access/proxy_hosts-delete.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "anyOf": [ - { - "$ref": "roles#/definitions/admin" - }, - { - "type": "object", - "required": ["permission_proxy_hosts", "roles"], - "properties": { - "permission_proxy_hosts": { - "$ref": "perms#/definitions/manage" - }, - "roles": { - "type": "array", - "items": { - "type": "string", - "enum": ["user"] - } - } - } - } - ] -} diff --git a/backend/lib/access/proxy_hosts-get.json b/backend/lib/access/proxy_hosts-get.json deleted file mode 100644 index d88e4cff..00000000 --- a/backend/lib/access/proxy_hosts-get.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "anyOf": [ - { - "$ref": "roles#/definitions/admin" - }, - { - "type": "object", - "required": ["permission_proxy_hosts", "roles"], - "properties": { - "permission_proxy_hosts": { - "$ref": "perms#/definitions/view" - }, - "roles": { - "type": "array", - "items": { - "type": "string", - "enum": ["user"] - } - } - } - } - ] -} diff --git a/backend/lib/access/proxy_hosts-list.json b/backend/lib/access/proxy_hosts-list.json deleted file mode 100644 index d88e4cff..00000000 --- a/backend/lib/access/proxy_hosts-list.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "anyOf": [ - { - "$ref": "roles#/definitions/admin" - }, - { - "type": "object", - "required": ["permission_proxy_hosts", "roles"], - "properties": { - "permission_proxy_hosts": { - "$ref": "perms#/definitions/view" - }, - "roles": { - "type": "array", - "items": { - "type": "string", - "enum": ["user"] - } - } - } - } - ] -} diff --git a/backend/lib/access/proxy_hosts-update.json b/backend/lib/access/proxy_hosts-update.json deleted file mode 100644 index 166527a3..00000000 --- a/backend/lib/access/proxy_hosts-update.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "anyOf": [ - { - "$ref": "roles#/definitions/admin" - }, - { - "type": "object", - "required": ["permission_proxy_hosts", "roles"], - "properties": { - "permission_proxy_hosts": { - "$ref": "perms#/definitions/manage" - }, - "roles": { - "type": "array", - "items": { - "type": "string", - "enum": ["user"] - } - } - } - } - ] -} diff --git a/backend/lib/access/redirection_hosts-create.json b/backend/lib/access/redirection_hosts-create.json deleted file mode 100644 index 342babc8..00000000 --- a/backend/lib/access/redirection_hosts-create.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "anyOf": [ - { - "$ref": "roles#/definitions/admin" - }, - { - "type": "object", - "required": ["permission_redirection_hosts", "roles"], - "properties": { - "permission_redirection_hosts": { - "$ref": "perms#/definitions/manage" - }, - "roles": { - "type": "array", - "items": { - "type": "string", - "enum": ["user"] - } - } - } - } - ] -} diff --git a/backend/lib/access/redirection_hosts-delete.json b/backend/lib/access/redirection_hosts-delete.json deleted file mode 100644 index 342babc8..00000000 --- a/backend/lib/access/redirection_hosts-delete.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "anyOf": [ - { - "$ref": "roles#/definitions/admin" - }, - { - "type": "object", - "required": ["permission_redirection_hosts", "roles"], - "properties": { - "permission_redirection_hosts": { - "$ref": "perms#/definitions/manage" - }, - "roles": { - "type": "array", - "items": { - "type": "string", - "enum": ["user"] - } - } - } - } - ] -} diff --git a/backend/lib/access/redirection_hosts-get.json b/backend/lib/access/redirection_hosts-get.json deleted file mode 100644 index ba229206..00000000 --- a/backend/lib/access/redirection_hosts-get.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "anyOf": [ - { - "$ref": "roles#/definitions/admin" - }, - { - "type": "object", - "required": ["permission_redirection_hosts", "roles"], - "properties": { - "permission_redirection_hosts": { - "$ref": "perms#/definitions/view" - }, - "roles": { - "type": "array", - "items": { - "type": "string", - "enum": ["user"] - } - } - } - } - ] -} diff --git a/backend/lib/access/redirection_hosts-list.json b/backend/lib/access/redirection_hosts-list.json deleted file mode 100644 index ba229206..00000000 --- a/backend/lib/access/redirection_hosts-list.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "anyOf": [ - { - "$ref": "roles#/definitions/admin" - }, - { - "type": "object", - "required": ["permission_redirection_hosts", "roles"], - "properties": { - "permission_redirection_hosts": { - "$ref": "perms#/definitions/view" - }, - "roles": { - "type": "array", - "items": { - "type": "string", - "enum": ["user"] - } - } - } - } - ] -} diff --git a/backend/lib/access/redirection_hosts-update.json b/backend/lib/access/redirection_hosts-update.json deleted file mode 100644 index 342babc8..00000000 --- a/backend/lib/access/redirection_hosts-update.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "anyOf": [ - { - "$ref": "roles#/definitions/admin" - }, - { - "type": "object", - "required": ["permission_redirection_hosts", "roles"], - "properties": { - "permission_redirection_hosts": { - "$ref": "perms#/definitions/manage" - }, - "roles": { - "type": "array", - "items": { - "type": "string", - "enum": ["user"] - } - } - } - } - ] -} diff --git a/backend/lib/access/reports-hosts.json b/backend/lib/access/reports-hosts.json deleted file mode 100644 index dbc9e0c0..00000000 --- a/backend/lib/access/reports-hosts.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "anyOf": [ - { - "$ref": "roles#/definitions/user" - } - ] -} diff --git a/backend/lib/access/roles.json b/backend/lib/access/roles.json deleted file mode 100644 index 16b33b55..00000000 --- a/backend/lib/access/roles.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "roles", - "definitions": { - "admin": { - "type": "object", - "required": ["scope", "roles"], - "properties": { - "scope": { - "type": "array", - "contains": { - "type": "string", - "pattern": "^user$" - } - }, - "roles": { - "type": "array", - "contains": { - "type": "string", - "pattern": "^admin$" - } - } - } - }, - "user": { - "type": "object", - "required": ["scope"], - "properties": { - "scope": { - "type": "array", - "contains": { - "type": "string", - "pattern": "^user$" - } - } - } - } - } -} diff --git a/backend/lib/access/settings-get.json b/backend/lib/access/settings-get.json deleted file mode 100644 index aeadc94b..00000000 --- a/backend/lib/access/settings-get.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "anyOf": [ - { - "$ref": "roles#/definitions/admin" - } - ] -} diff --git a/backend/lib/access/settings-list.json b/backend/lib/access/settings-list.json deleted file mode 100644 index aeadc94b..00000000 --- a/backend/lib/access/settings-list.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "anyOf": [ - { - "$ref": "roles#/definitions/admin" - } - ] -} diff --git a/backend/lib/access/settings-update.json b/backend/lib/access/settings-update.json deleted file mode 100644 index aeadc94b..00000000 --- a/backend/lib/access/settings-update.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "anyOf": [ - { - "$ref": "roles#/definitions/admin" - } - ] -} diff --git a/backend/lib/access/streams-create.json b/backend/lib/access/streams-create.json deleted file mode 100644 index fbeb1cc9..00000000 --- a/backend/lib/access/streams-create.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "anyOf": [ - { - "$ref": "roles#/definitions/admin" - }, - { - "type": "object", - "required": ["permission_streams", "roles"], - "properties": { - "permission_streams": { - "$ref": "perms#/definitions/manage" - }, - "roles": { - "type": "array", - "items": { - "type": "string", - "enum": ["user"] - } - } - } - } - ] -} diff --git a/backend/lib/access/streams-delete.json b/backend/lib/access/streams-delete.json deleted file mode 100644 index fbeb1cc9..00000000 --- a/backend/lib/access/streams-delete.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "anyOf": [ - { - "$ref": "roles#/definitions/admin" - }, - { - "type": "object", - "required": ["permission_streams", "roles"], - "properties": { - "permission_streams": { - "$ref": "perms#/definitions/manage" - }, - "roles": { - "type": "array", - "items": { - "type": "string", - "enum": ["user"] - } - } - } - } - ] -} diff --git a/backend/lib/access/streams-get.json b/backend/lib/access/streams-get.json deleted file mode 100644 index 7e996287..00000000 --- a/backend/lib/access/streams-get.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "anyOf": [ - { - "$ref": "roles#/definitions/admin" - }, - { - "type": "object", - "required": ["permission_streams", "roles"], - "properties": { - "permission_streams": { - "$ref": "perms#/definitions/view" - }, - "roles": { - "type": "array", - "items": { - "type": "string", - "enum": ["user"] - } - } - } - } - ] -} diff --git a/backend/lib/access/streams-list.json b/backend/lib/access/streams-list.json deleted file mode 100644 index 7e996287..00000000 --- a/backend/lib/access/streams-list.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "anyOf": [ - { - "$ref": "roles#/definitions/admin" - }, - { - "type": "object", - "required": ["permission_streams", "roles"], - "properties": { - "permission_streams": { - "$ref": "perms#/definitions/view" - }, - "roles": { - "type": "array", - "items": { - "type": "string", - "enum": ["user"] - } - } - } - } - ] -} diff --git a/backend/lib/access/streams-update.json b/backend/lib/access/streams-update.json deleted file mode 100644 index fbeb1cc9..00000000 --- a/backend/lib/access/streams-update.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "anyOf": [ - { - "$ref": "roles#/definitions/admin" - }, - { - "type": "object", - "required": ["permission_streams", "roles"], - "properties": { - "permission_streams": { - "$ref": "perms#/definitions/manage" - }, - "roles": { - "type": "array", - "items": { - "type": "string", - "enum": ["user"] - } - } - } - } - ] -} diff --git a/backend/lib/access/users-create.json b/backend/lib/access/users-create.json deleted file mode 100644 index aeadc94b..00000000 --- a/backend/lib/access/users-create.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "anyOf": [ - { - "$ref": "roles#/definitions/admin" - } - ] -} diff --git a/backend/lib/access/users-delete.json b/backend/lib/access/users-delete.json deleted file mode 100644 index aeadc94b..00000000 --- a/backend/lib/access/users-delete.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "anyOf": [ - { - "$ref": "roles#/definitions/admin" - } - ] -} diff --git a/backend/lib/access/users-get.json b/backend/lib/access/users-get.json deleted file mode 100644 index 2a2f0423..00000000 --- a/backend/lib/access/users-get.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "anyOf": [ - { - "$ref": "roles#/definitions/admin" - }, - { - "type": "object", - "required": ["data", "scope"], - "properties": { - "data": { - "$ref": "objects#/properties/users" - }, - "scope": { - "type": "array", - "contains": { - "type": "string", - "pattern": "^user$" - } - } - } - } - ] -} diff --git a/backend/lib/access/users-list.json b/backend/lib/access/users-list.json deleted file mode 100644 index aeadc94b..00000000 --- a/backend/lib/access/users-list.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "anyOf": [ - { - "$ref": "roles#/definitions/admin" - } - ] -} diff --git a/backend/lib/access/users-loginas.json b/backend/lib/access/users-loginas.json deleted file mode 100644 index aeadc94b..00000000 --- a/backend/lib/access/users-loginas.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "anyOf": [ - { - "$ref": "roles#/definitions/admin" - } - ] -} diff --git a/backend/lib/access/users-password.json b/backend/lib/access/users-password.json deleted file mode 100644 index 2a2f0423..00000000 --- a/backend/lib/access/users-password.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "anyOf": [ - { - "$ref": "roles#/definitions/admin" - }, - { - "type": "object", - "required": ["data", "scope"], - "properties": { - "data": { - "$ref": "objects#/properties/users" - }, - "scope": { - "type": "array", - "contains": { - "type": "string", - "pattern": "^user$" - } - } - } - } - ] -} diff --git a/backend/lib/access/users-permissions.json b/backend/lib/access/users-permissions.json deleted file mode 100644 index aeadc94b..00000000 --- a/backend/lib/access/users-permissions.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "anyOf": [ - { - "$ref": "roles#/definitions/admin" - } - ] -} diff --git a/backend/lib/access/users-update.json b/backend/lib/access/users-update.json deleted file mode 100644 index 2a2f0423..00000000 --- a/backend/lib/access/users-update.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "anyOf": [ - { - "$ref": "roles#/definitions/admin" - }, - { - "type": "object", - "required": ["data", "scope"], - "properties": { - "data": { - "$ref": "objects#/properties/users" - }, - "scope": { - "type": "array", - "contains": { - "type": "string", - "pattern": "^user$" - } - } - } - } - ] -} diff --git a/backend/lib/error.js b/backend/lib/error.js deleted file mode 100644 index 9e456f05..00000000 --- a/backend/lib/error.js +++ /dev/null @@ -1,90 +0,0 @@ -const _ = require('lodash'); -const util = require('util'); - -module.exports = { - - PermissionError: function (message, previous) { - Error.captureStackTrace(this, this.constructor); - this.name = this.constructor.name; - this.previous = previous; - this.message = 'Permission Denied'; - this.public = true; - this.status = 403; - }, - - ItemNotFoundError: function (id, previous) { - Error.captureStackTrace(this, this.constructor); - this.name = this.constructor.name; - this.previous = previous; - this.message = 'Item Not Found - ' + id; - this.public = true; - this.status = 404; - }, - - AuthError: function (message, previous) { - Error.captureStackTrace(this, this.constructor); - this.name = this.constructor.name; - this.previous = previous; - this.message = message; - this.public = true; - this.status = 401; - }, - - InternalError: function (message, previous) { - Error.captureStackTrace(this, this.constructor); - this.name = this.constructor.name; - this.previous = previous; - this.message = message; - this.status = 500; - this.public = false; - }, - - InternalValidationError: function (message, previous) { - Error.captureStackTrace(this, this.constructor); - this.name = this.constructor.name; - this.previous = previous; - this.message = message; - this.status = 400; - this.public = false; - }, - - ConfigurationError: function (message, previous) { - Error.captureStackTrace(this, this.constructor); - this.name = this.constructor.name; - this.previous = previous; - this.message = message; - this.status = 400; - this.public = true; - }, - - CacheError: function (message, previous) { - Error.captureStackTrace(this, this.constructor); - this.name = this.constructor.name; - this.message = message; - this.previous = previous; - this.status = 500; - this.public = false; - }, - - ValidationError: function (message, previous) { - Error.captureStackTrace(this, this.constructor); - this.name = this.constructor.name; - this.previous = previous; - this.message = message; - this.public = true; - this.status = 400; - }, - - AssertionFailedError: function (message, previous) { - Error.captureStackTrace(this, this.constructor); - this.name = this.constructor.name; - this.previous = previous; - this.message = message; - this.public = false; - this.status = 400; - } -}; - -_.forEach(module.exports, function (error) { - util.inherits(error, Error); -}); diff --git a/backend/lib/express/cors.js b/backend/lib/express/cors.js deleted file mode 100644 index c9befeec..00000000 --- a/backend/lib/express/cors.js +++ /dev/null @@ -1,40 +0,0 @@ -const validator = require('../validator'); - -module.exports = function (req, res, next) { - - if (req.headers.origin) { - - const originSchema = { - oneOf: [ - { - type: 'string', - pattern: '^[a-z\\-]+:\\/\\/(?:[\\w\\-\\.]+(:[0-9]+)?/?)?$' - }, - { - type: 'string', - pattern: '^[a-z\\-]+:\\/\\/(?:\\[([a-z0-9]{0,4}\\:?)+\\])?/?(:[0-9]+)?$' - } - ] - }; - - // very relaxed validation.... - validator(originSchema, req.headers.origin) - .then(function () { - res.set({ - 'Access-Control-Allow-Origin': req.headers.origin, - 'Access-Control-Allow-Credentials': true, - 'Access-Control-Allow-Methods': 'OPTIONS, GET, POST', - 'Access-Control-Allow-Headers': 'Content-Type, Cache-Control, Pragma, Expires, Authorization, X-Dataset-Total, X-Dataset-Offset, X-Dataset-Limit', - 'Access-Control-Max-Age': 5 * 60, - 'Access-Control-Expose-Headers': 'X-Dataset-Total, X-Dataset-Offset, X-Dataset-Limit' - }); - next(); - }) - .catch(next); - - } else { - // No origin - next(); - } - -}; diff --git a/backend/lib/express/jwt-decode.js b/backend/lib/express/jwt-decode.js deleted file mode 100644 index 17edccec..00000000 --- a/backend/lib/express/jwt-decode.js +++ /dev/null @@ -1,15 +0,0 @@ -const Access = require('../access'); - -module.exports = () => { - return function (req, res, next) { - res.locals.access = null; - let access = new Access(res.locals.token || null); - access.load() - .then(() => { - res.locals.access = access; - next(); - }) - .catch(next); - }; -}; - diff --git a/backend/lib/express/jwt.js b/backend/lib/express/jwt.js deleted file mode 100644 index 44aa3693..00000000 --- a/backend/lib/express/jwt.js +++ /dev/null @@ -1,13 +0,0 @@ -module.exports = function () { - return function (req, res, next) { - if (req.headers.authorization) { - let parts = req.headers.authorization.split(' '); - - if (parts && parts[0] === 'Bearer' && parts[1]) { - res.locals.token = parts[1]; - } - } - - next(); - }; -}; diff --git a/backend/lib/express/pagination.js b/backend/lib/express/pagination.js deleted file mode 100644 index 24ffa58d..00000000 --- a/backend/lib/express/pagination.js +++ /dev/null @@ -1,55 +0,0 @@ -let _ = require('lodash'); - -module.exports = function (default_sort, default_offset, default_limit, max_limit) { - - /** - * This will setup the req query params with filtered data and defaults - * - * sort will be an array of fields and their direction - * offset will be an int, defaulting to zero if no other default supplied - * limit will be an int, defaulting to 50 if no other default supplied, and limited to the max if that was supplied - * - */ - - return function (req, res, next) { - - req.query.offset = typeof req.query.limit === 'undefined' ? default_offset || 0 : parseInt(req.query.offset, 10); - req.query.limit = typeof req.query.limit === 'undefined' ? default_limit || 50 : parseInt(req.query.limit, 10); - - if (max_limit && req.query.limit > max_limit) { - req.query.limit = max_limit; - } - - // Sorting - let sort = typeof req.query.sort === 'undefined' ? default_sort : req.query.sort; - let myRegexp = /.*\.(asc|desc)$/ig; - let sort_array = []; - - sort = sort.split(','); - _.map(sort, function (val) { - let matches = myRegexp.exec(val); - - if (matches !== null) { - let dir = matches[1]; - sort_array.push({ - field: val.substr(0, val.length - (dir.length + 1)), - dir: dir.toLowerCase() - }); - } else { - sort_array.push({ - field: val, - dir: 'asc' - }); - } - }); - - // Sort will now be in this format: - // [ - // { field: 'field1', dir: 'asc' }, - // { field: 'field2', dir: 'desc' } - // ] - - req.query.sort = sort_array; - next(); - }; -}; diff --git a/backend/lib/express/user-id-from-me.js b/backend/lib/express/user-id-from-me.js deleted file mode 100644 index 4a37a406..00000000 --- a/backend/lib/express/user-id-from-me.js +++ /dev/null @@ -1,9 +0,0 @@ -module.exports = (req, res, next) => { - if (req.params.user_id === 'me' && res.locals.access) { - req.params.user_id = res.locals.access.token.get('attrs').id; - } else { - req.params.user_id = parseInt(req.params.user_id, 10); - } - - next(); -}; diff --git a/backend/lib/helpers.js b/backend/lib/helpers.js deleted file mode 100644 index e38be991..00000000 --- a/backend/lib/helpers.js +++ /dev/null @@ -1,32 +0,0 @@ -const moment = require('moment'); - -module.exports = { - - /** - * Takes an expression such as 30d and returns a moment object of that date in future - * - * Key Shorthand - * ================== - * years y - * quarters Q - * months M - * weeks w - * days d - * hours h - * minutes m - * seconds s - * milliseconds ms - * - * @param {String} expression - * @returns {Object} - */ - parseDatePeriod: function (expression) { - let matches = expression.match(/^([0-9]+)(y|Q|M|w|d|h|m|s|ms)$/m); - if (matches) { - return moment().add(matches[1], matches[2]); - } - - return null; - } - -}; diff --git a/backend/lib/migrate_template.js b/backend/lib/migrate_template.js deleted file mode 100644 index f75f77ef..00000000 --- a/backend/lib/migrate_template.js +++ /dev/null @@ -1,55 +0,0 @@ -const migrate_name = 'identifier_for_migrate'; -const logger = require('../logger').migrate; - -/** - * Migrate - * - * @see http://knexjs.org/#Schema - * - * @param {Object} knex - * @param {Promise} Promise - * @returns {Promise} - */ -exports.up = function (knex, Promise) { - - logger.info('[' + migrate_name + '] Migrating Up...'); - - // Create Table example: - - /*return knex.schema.createTable('notification', (table) => { - table.increments().primary(); - table.string('name').notNull(); - table.string('type').notNull(); - table.integer('created_on').notNull(); - table.integer('modified_on').notNull(); - }) - .then(function () { - logger.info('[' + migrate_name + '] Notification Table created'); - });*/ - - logger.info('[' + migrate_name + '] Migrating Up Complete'); - - return Promise.resolve(true); -}; - -/** - * Undo Migrate - * - * @param {Object} knex - * @param {Promise} Promise - * @returns {Promise} - */ -exports.down = function (knex, Promise) { - logger.info('[' + migrate_name + '] Migrating Down...'); - - // Drop table example: - - /*return knex.schema.dropTable('notification') - .then(() => { - logger.info('[' + migrate_name + '] Notification Table dropped'); - });*/ - - logger.info('[' + migrate_name + '] Migrating Down Complete'); - - return Promise.resolve(true); -}; diff --git a/backend/lib/utils.js b/backend/lib/utils.js deleted file mode 100644 index 4c8b62a8..00000000 --- a/backend/lib/utils.js +++ /dev/null @@ -1,20 +0,0 @@ -const exec = require('child_process').exec; - -module.exports = { - - /** - * @param {String} cmd - * @returns {Promise} - */ - exec: function (cmd) { - return new Promise((resolve, reject) => { - exec(cmd, function (err, stdout, /*stderr*/) { - if (err && typeof err === 'object') { - reject(err); - } else { - resolve(stdout.trim()); - } - }); - }); - } -}; diff --git a/backend/lib/validator/api.js b/backend/lib/validator/api.js deleted file mode 100644 index 3f51b596..00000000 --- a/backend/lib/validator/api.js +++ /dev/null @@ -1,45 +0,0 @@ -const error = require('../error'); -const path = require('path'); -const parser = require('json-schema-ref-parser'); - -const ajv = require('ajv')({ - verbose: true, - validateSchema: true, - allErrors: false, - format: 'full', - coerceTypes: true -}); - -/** - * @param {Object} schema - * @param {Object} payload - * @returns {Promise} - */ -function apiValidator (schema, payload/*, description*/) { - return new Promise(function Promise_apiValidator (resolve, reject) { - if (typeof payload === 'undefined') { - reject(new error.ValidationError('Payload is undefined')); - } - - let validate = ajv.compile(schema); - let valid = validate(payload); - - if (valid && !validate.errors) { - resolve(payload); - } else { - let message = ajv.errorsText(validate.errors); - let err = new error.ValidationError(message); - err.debug = [validate.errors, payload]; - reject(err); - } - }); -} - -apiValidator.loadSchemas = parser - .dereference(path.resolve('schema/index.json')) - .then((schema) => { - ajv.addSchema(schema); - return schema; - }); - -module.exports = apiValidator; diff --git a/backend/lib/validator/index.js b/backend/lib/validator/index.js deleted file mode 100644 index fca6f4bf..00000000 --- a/backend/lib/validator/index.js +++ /dev/null @@ -1,49 +0,0 @@ -const _ = require('lodash'); -const error = require('../error'); -const definitions = require('../../schema/definitions.json'); - -RegExp.prototype.toJSON = RegExp.prototype.toString; - -const ajv = require('ajv')({ - verbose: true, //process.env.NODE_ENV === 'development', - allErrors: true, - format: 'full', // strict regexes for format checks - coerceTypes: true, - schemas: [ - definitions - ] -}); - -/** - * - * @param {Object} schema - * @param {Object} payload - * @returns {Promise} - */ -function validator (schema, payload) { - return new Promise(function (resolve, reject) { - if (!payload) { - reject(new error.InternalValidationError('Payload is falsy')); - } else { - try { - let validate = ajv.compile(schema); - - let valid = validate(payload); - if (valid && !validate.errors) { - resolve(_.cloneDeep(payload)); - } else { - let message = ajv.errorsText(validate.errors); - reject(new error.InternalValidationError(message)); - } - - } catch (err) { - reject(err); - } - - } - - }); - -} - -module.exports = validator; diff --git a/backend/logger.js b/backend/logger.js deleted file mode 100644 index 680af6d5..00000000 --- a/backend/logger.js +++ /dev/null @@ -1,13 +0,0 @@ -const {Signale} = require('signale'); - -module.exports = { - global: new Signale({scope: 'Global '}), - migrate: new Signale({scope: 'Migrate '}), - express: new Signale({scope: 'Express '}), - access: new Signale({scope: 'Access '}), - nginx: new Signale({scope: 'Nginx '}), - ssl: new Signale({scope: 'SSL '}), - import: new Signale({scope: 'Importer '}), - setup: new Signale({scope: 'Setup '}), - ip_ranges: new Signale({scope: 'IP Ranges'}) -}; diff --git a/backend/migrate.js b/backend/migrate.js deleted file mode 100644 index 263c8702..00000000 --- a/backend/migrate.js +++ /dev/null @@ -1,15 +0,0 @@ -const db = require('./db'); -const logger = require('./logger').migrate; - -module.exports = { - latest: function () { - return db.migrate.currentVersion() - .then((version) => { - logger.info('Current database version:', version); - return db.migrate.latest({ - tableName: 'migrations', - directory: 'migrations' - }); - }); - } -}; diff --git a/backend/migrations/20180618015850_initial.js b/backend/migrations/20180618015850_initial.js deleted file mode 100644 index a112e826..00000000 --- a/backend/migrations/20180618015850_initial.js +++ /dev/null @@ -1,205 +0,0 @@ -const migrate_name = 'initial-schema'; -const logger = require('../logger').migrate; - -/** - * Migrate - * - * @see http://knexjs.org/#Schema - * - * @param {Object} knex - * @param {Promise} Promise - * @returns {Promise} - */ -exports.up = function (knex/*, Promise*/) { - logger.info('[' + migrate_name + '] Migrating Up...'); - - return knex.schema.createTable('auth', (table) => { - table.increments().primary(); - table.dateTime('created_on').notNull(); - table.dateTime('modified_on').notNull(); - table.integer('user_id').notNull().unsigned(); - table.string('type', 30).notNull(); - table.string('secret').notNull(); - table.json('meta').notNull(); - table.integer('is_deleted').notNull().unsigned().defaultTo(0); - }) - .then(() => { - logger.info('[' + migrate_name + '] auth Table created'); - - return knex.schema.createTable('user', (table) => { - table.increments().primary(); - table.dateTime('created_on').notNull(); - table.dateTime('modified_on').notNull(); - table.integer('is_deleted').notNull().unsigned().defaultTo(0); - table.integer('is_disabled').notNull().unsigned().defaultTo(0); - table.string('email').notNull(); - table.string('name').notNull(); - table.string('nickname').notNull(); - table.string('avatar').notNull(); - table.json('roles').notNull(); - }); - }) - .then(() => { - logger.info('[' + migrate_name + '] user Table created'); - - return knex.schema.createTable('user_permission', (table) => { - table.increments().primary(); - table.dateTime('created_on').notNull(); - table.dateTime('modified_on').notNull(); - table.integer('user_id').notNull().unsigned(); - table.string('visibility').notNull(); - table.string('proxy_hosts').notNull(); - table.string('redirection_hosts').notNull(); - table.string('dead_hosts').notNull(); - table.string('streams').notNull(); - table.string('access_lists').notNull(); - table.string('certificates').notNull(); - table.unique('user_id'); - }); - }) - .then(() => { - logger.info('[' + migrate_name + '] user_permission Table created'); - - return knex.schema.createTable('proxy_host', (table) => { - table.increments().primary(); - table.dateTime('created_on').notNull(); - table.dateTime('modified_on').notNull(); - table.integer('owner_user_id').notNull().unsigned(); - table.integer('is_deleted').notNull().unsigned().defaultTo(0); - table.json('domain_names').notNull(); - table.string('forward_ip').notNull(); - table.integer('forward_port').notNull().unsigned(); - table.integer('access_list_id').notNull().unsigned().defaultTo(0); - table.integer('certificate_id').notNull().unsigned().defaultTo(0); - table.integer('ssl_forced').notNull().unsigned().defaultTo(0); - table.integer('caching_enabled').notNull().unsigned().defaultTo(0); - table.integer('block_exploits').notNull().unsigned().defaultTo(0); - table.text('advanced_config').notNull().defaultTo(''); - table.json('meta').notNull(); - }); - }) - .then(() => { - logger.info('[' + migrate_name + '] proxy_host Table created'); - - return knex.schema.createTable('redirection_host', (table) => { - table.increments().primary(); - table.dateTime('created_on').notNull(); - table.dateTime('modified_on').notNull(); - table.integer('owner_user_id').notNull().unsigned(); - table.integer('is_deleted').notNull().unsigned().defaultTo(0); - table.json('domain_names').notNull(); - table.string('forward_domain_name').notNull(); - table.integer('preserve_path').notNull().unsigned().defaultTo(0); - table.integer('certificate_id').notNull().unsigned().defaultTo(0); - table.integer('ssl_forced').notNull().unsigned().defaultTo(0); - table.integer('block_exploits').notNull().unsigned().defaultTo(0); - table.text('advanced_config').notNull().defaultTo(''); - table.json('meta').notNull(); - }); - }) - .then(() => { - logger.info('[' + migrate_name + '] redirection_host Table created'); - - return knex.schema.createTable('dead_host', (table) => { - table.increments().primary(); - table.dateTime('created_on').notNull(); - table.dateTime('modified_on').notNull(); - table.integer('owner_user_id').notNull().unsigned(); - table.integer('is_deleted').notNull().unsigned().defaultTo(0); - table.json('domain_names').notNull(); - table.integer('certificate_id').notNull().unsigned().defaultTo(0); - table.integer('ssl_forced').notNull().unsigned().defaultTo(0); - table.text('advanced_config').notNull().defaultTo(''); - table.json('meta').notNull(); - }); - }) - .then(() => { - logger.info('[' + migrate_name + '] dead_host Table created'); - - return knex.schema.createTable('stream', (table) => { - table.increments().primary(); - table.dateTime('created_on').notNull(); - table.dateTime('modified_on').notNull(); - table.integer('owner_user_id').notNull().unsigned(); - table.integer('is_deleted').notNull().unsigned().defaultTo(0); - table.integer('incoming_port').notNull().unsigned(); - table.string('forward_ip').notNull(); - table.integer('forwarding_port').notNull().unsigned(); - table.integer('tcp_forwarding').notNull().unsigned().defaultTo(0); - table.integer('udp_forwarding').notNull().unsigned().defaultTo(0); - table.json('meta').notNull(); - }); - }) - .then(() => { - logger.info('[' + migrate_name + '] stream Table created'); - - return knex.schema.createTable('access_list', (table) => { - table.increments().primary(); - table.dateTime('created_on').notNull(); - table.dateTime('modified_on').notNull(); - table.integer('owner_user_id').notNull().unsigned(); - table.integer('is_deleted').notNull().unsigned().defaultTo(0); - table.string('name').notNull(); - table.json('meta').notNull(); - }); - }) - .then(() => { - logger.info('[' + migrate_name + '] access_list Table created'); - - return knex.schema.createTable('certificate', (table) => { - table.increments().primary(); - table.dateTime('created_on').notNull(); - table.dateTime('modified_on').notNull(); - table.integer('owner_user_id').notNull().unsigned(); - table.integer('is_deleted').notNull().unsigned().defaultTo(0); - table.string('provider').notNull(); - table.string('nice_name').notNull().defaultTo(''); - table.json('domain_names').notNull(); - table.dateTime('expires_on').notNull(); - table.json('meta').notNull(); - }); - }) - .then(() => { - logger.info('[' + migrate_name + '] certificate Table created'); - - return knex.schema.createTable('access_list_auth', (table) => { - table.increments().primary(); - table.dateTime('created_on').notNull(); - table.dateTime('modified_on').notNull(); - table.integer('access_list_id').notNull().unsigned(); - table.string('username').notNull(); - table.string('password').notNull(); - table.json('meta').notNull(); - }); - }) - .then(() => { - logger.info('[' + migrate_name + '] access_list_auth Table created'); - - return knex.schema.createTable('audit_log', (table) => { - table.increments().primary(); - table.dateTime('created_on').notNull(); - table.dateTime('modified_on').notNull(); - table.integer('user_id').notNull().unsigned(); - table.string('object_type').notNull().defaultTo(''); - table.integer('object_id').notNull().unsigned().defaultTo(0); - table.string('action').notNull(); - table.json('meta').notNull(); - }); - }) - .then(() => { - logger.info('[' + migrate_name + '] audit_log Table created'); - }); - -}; - -/** - * Undo Migrate - * - * @param {Object} knex - * @param {Promise} Promise - * @returns {Promise} - */ -exports.down = function (knex, Promise) { - logger.warn('[' + migrate_name + '] You can\'t migrate down the initial data.'); - return Promise.resolve(true); -}; diff --git a/backend/migrations/20180929054513_websockets.js b/backend/migrations/20180929054513_websockets.js deleted file mode 100644 index 06054850..00000000 --- a/backend/migrations/20180929054513_websockets.js +++ /dev/null @@ -1,35 +0,0 @@ -const migrate_name = 'websockets'; -const logger = require('../logger').migrate; - -/** - * Migrate - * - * @see http://knexjs.org/#Schema - * - * @param {Object} knex - * @param {Promise} Promise - * @returns {Promise} - */ -exports.up = function (knex/*, Promise*/) { - logger.info('[' + migrate_name + '] Migrating Up...'); - - return knex.schema.table('proxy_host', function (proxy_host) { - proxy_host.integer('allow_websocket_upgrade').notNull().unsigned().defaultTo(0); - }) - .then(() => { - logger.info('[' + migrate_name + '] proxy_host Table altered'); - }); - -}; - -/** - * Undo Migrate - * - * @param {Object} knex - * @param {Promise} Promise - * @returns {Promise} - */ -exports.down = function (knex, Promise) { - logger.warn('[' + migrate_name + '] You can\'t migrate down this one.'); - return Promise.resolve(true); -}; \ No newline at end of file diff --git a/backend/migrations/20181019052346_forward_host.js b/backend/migrations/20181019052346_forward_host.js deleted file mode 100644 index 05c27739..00000000 --- a/backend/migrations/20181019052346_forward_host.js +++ /dev/null @@ -1,34 +0,0 @@ -const migrate_name = 'forward_host'; -const logger = require('../logger').migrate; - -/** - * Migrate - * - * @see http://knexjs.org/#Schema - * - * @param {Object} knex - * @param {Promise} Promise - * @returns {Promise} - */ -exports.up = function (knex/*, Promise*/) { - logger.info('[' + migrate_name + '] Migrating Up...'); - - return knex.schema.table('proxy_host', function (proxy_host) { - proxy_host.renameColumn('forward_ip', 'forward_host'); - }) - .then(() => { - logger.info('[' + migrate_name + '] proxy_host Table altered'); - }); -}; - -/** - * Undo Migrate - * - * @param {Object} knex - * @param {Promise} Promise - * @returns {Promise} - */ -exports.down = function (knex, Promise) { - logger.warn('[' + migrate_name + '] You can\'t migrate down this one.'); - return Promise.resolve(true); -}; \ No newline at end of file diff --git a/backend/migrations/20181113041458_http2_support.js b/backend/migrations/20181113041458_http2_support.js deleted file mode 100644 index 9f6b4336..00000000 --- a/backend/migrations/20181113041458_http2_support.js +++ /dev/null @@ -1,49 +0,0 @@ -const migrate_name = 'http2_support'; -const logger = require('../logger').migrate; - -/** - * Migrate - * - * @see http://knexjs.org/#Schema - * - * @param {Object} knex - * @param {Promise} Promise - * @returns {Promise} - */ -exports.up = function (knex/*, Promise*/) { - logger.info('[' + migrate_name + '] Migrating Up...'); - - return knex.schema.table('proxy_host', function (proxy_host) { - proxy_host.integer('http2_support').notNull().unsigned().defaultTo(0); - }) - .then(() => { - logger.info('[' + migrate_name + '] proxy_host Table altered'); - - return knex.schema.table('redirection_host', function (redirection_host) { - redirection_host.integer('http2_support').notNull().unsigned().defaultTo(0); - }); - }) - .then(() => { - logger.info('[' + migrate_name + '] redirection_host Table altered'); - - return knex.schema.table('dead_host', function (dead_host) { - dead_host.integer('http2_support').notNull().unsigned().defaultTo(0); - }); - }) - .then(() => { - logger.info('[' + migrate_name + '] dead_host Table altered'); - }); -}; - -/** - * Undo Migrate - * - * @param {Object} knex - * @param {Promise} Promise - * @returns {Promise} - */ -exports.down = function (knex, Promise) { - logger.warn('[' + migrate_name + '] You can\'t migrate down this one.'); - return Promise.resolve(true); -}; - diff --git a/backend/migrations/20181213013211_forward_scheme.js b/backend/migrations/20181213013211_forward_scheme.js deleted file mode 100644 index 22ae619e..00000000 --- a/backend/migrations/20181213013211_forward_scheme.js +++ /dev/null @@ -1,34 +0,0 @@ -const migrate_name = 'forward_scheme'; -const logger = require('../logger').migrate; - -/** - * Migrate - * - * @see http://knexjs.org/#Schema - * - * @param {Object} knex - * @param {Promise} Promise - * @returns {Promise} - */ -exports.up = function (knex/*, Promise*/) { - logger.info('[' + migrate_name + '] Migrating Up...'); - - return knex.schema.table('proxy_host', function (proxy_host) { - proxy_host.string('forward_scheme').notNull().defaultTo('http'); - }) - .then(() => { - logger.info('[' + migrate_name + '] proxy_host Table altered'); - }); -}; - -/** - * Undo Migrate - * - * @param {Object} knex - * @param {Promise} Promise - * @returns {Promise} - */ -exports.down = function (knex, Promise) { - logger.warn('[' + migrate_name + '] You can\'t migrate down this one.'); - return Promise.resolve(true); -}; diff --git a/backend/migrations/20190104035154_disabled.js b/backend/migrations/20190104035154_disabled.js deleted file mode 100644 index 2780c4df..00000000 --- a/backend/migrations/20190104035154_disabled.js +++ /dev/null @@ -1,55 +0,0 @@ -const migrate_name = 'disabled'; -const logger = require('../logger').migrate; - -/** - * Migrate - * - * @see http://knexjs.org/#Schema - * - * @param {Object} knex - * @param {Promise} Promise - * @returns {Promise} - */ -exports.up = function (knex/*, Promise*/) { - logger.info('[' + migrate_name + '] Migrating Up...'); - - return knex.schema.table('proxy_host', function (proxy_host) { - proxy_host.integer('enabled').notNull().unsigned().defaultTo(1); - }) - .then(() => { - logger.info('[' + migrate_name + '] proxy_host Table altered'); - - return knex.schema.table('redirection_host', function (redirection_host) { - redirection_host.integer('enabled').notNull().unsigned().defaultTo(1); - }); - }) - .then(() => { - logger.info('[' + migrate_name + '] redirection_host Table altered'); - - return knex.schema.table('dead_host', function (dead_host) { - dead_host.integer('enabled').notNull().unsigned().defaultTo(1); - }); - }) - .then(() => { - logger.info('[' + migrate_name + '] dead_host Table altered'); - - return knex.schema.table('stream', function (stream) { - stream.integer('enabled').notNull().unsigned().defaultTo(1); - }); - }) - .then(() => { - logger.info('[' + migrate_name + '] stream Table altered'); - }); -}; - -/** - * Undo Migrate - * - * @param {Object} knex - * @param {Promise} Promise - * @returns {Promise} - */ -exports.down = function (knex, Promise) { - logger.warn('[' + migrate_name + '] You can\'t migrate down this one.'); - return Promise.resolve(true); -}; diff --git a/backend/migrations/20190215115310_customlocations.js b/backend/migrations/20190215115310_customlocations.js deleted file mode 100644 index 4bcfd51a..00000000 --- a/backend/migrations/20190215115310_customlocations.js +++ /dev/null @@ -1,35 +0,0 @@ -const migrate_name = 'custom_locations'; -const logger = require('../logger').migrate; - -/** - * Migrate - * Extends proxy_host table with locations field - * - * @see http://knexjs.org/#Schema - * - * @param {Object} knex - * @param {Promise} Promise - * @returns {Promise} - */ -exports.up = function (knex/*, Promise*/) { - logger.info('[' + migrate_name + '] Migrating Up...'); - - return knex.schema.table('proxy_host', function (proxy_host) { - proxy_host.json('locations'); - }) - .then(() => { - logger.info('[' + migrate_name + '] proxy_host Table altered'); - }); -}; - -/** - * Undo Migrate - * - * @param {Object} knex - * @param {Promise} Promise - * @returns {Promise} - */ -exports.down = function (knex, Promise) { - logger.warn('[' + migrate_name + '] You can\'t migrate down this one.'); - return Promise.resolve(true); -}; diff --git a/backend/migrations/20190218060101_hsts.js b/backend/migrations/20190218060101_hsts.js deleted file mode 100644 index 648b162a..00000000 --- a/backend/migrations/20190218060101_hsts.js +++ /dev/null @@ -1,51 +0,0 @@ -const migrate_name = 'hsts'; -const logger = require('../logger').migrate; - -/** - * Migrate - * - * @see http://knexjs.org/#Schema - * - * @param {Object} knex - * @param {Promise} Promise - * @returns {Promise} - */ -exports.up = function (knex/*, Promise*/) { - logger.info('[' + migrate_name + '] Migrating Up...'); - - return knex.schema.table('proxy_host', function (proxy_host) { - proxy_host.integer('hsts_enabled').notNull().unsigned().defaultTo(0); - proxy_host.integer('hsts_subdomains').notNull().unsigned().defaultTo(0); - }) - .then(() => { - logger.info('[' + migrate_name + '] proxy_host Table altered'); - - return knex.schema.table('redirection_host', function (redirection_host) { - redirection_host.integer('hsts_enabled').notNull().unsigned().defaultTo(0); - redirection_host.integer('hsts_subdomains').notNull().unsigned().defaultTo(0); - }); - }) - .then(() => { - logger.info('[' + migrate_name + '] redirection_host Table altered'); - - return knex.schema.table('dead_host', function (dead_host) { - dead_host.integer('hsts_enabled').notNull().unsigned().defaultTo(0); - dead_host.integer('hsts_subdomains').notNull().unsigned().defaultTo(0); - }); - }) - .then(() => { - logger.info('[' + migrate_name + '] dead_host Table altered'); - }); -}; - -/** - * Undo Migrate - * - * @param {Object} knex - * @param {Promise} Promise - * @returns {Promise} - */ -exports.down = function (knex, Promise) { - logger.warn('[' + migrate_name + '] You can\'t migrate down this one.'); - return Promise.resolve(true); -}; diff --git a/backend/migrations/20190227065017_settings.js b/backend/migrations/20190227065017_settings.js deleted file mode 100644 index 7dc9c192..00000000 --- a/backend/migrations/20190227065017_settings.js +++ /dev/null @@ -1,38 +0,0 @@ -const migrate_name = 'settings'; -const logger = require('../logger').migrate; - -/** - * Migrate - * - * @see http://knexjs.org/#Schema - * - * @param {Object} knex - * @param {Promise} Promise - * @returns {Promise} - */ -exports.up = function (knex/*, Promise*/) { - logger.info('[' + migrate_name + '] Migrating Up...'); - - return knex.schema.createTable('setting', (table) => { - table.string('id').notNull().primary(); - table.string('name', 100).notNull(); - table.string('description', 255).notNull(); - table.string('value', 255).notNull(); - table.json('meta').notNull(); - }) - .then(() => { - logger.info('[' + migrate_name + '] setting Table created'); - }); -}; - -/** - * Undo Migrate - * - * @param {Object} knex - * @param {Promise} Promise - * @returns {Promise} - */ -exports.down = function (knex, Promise) { - logger.warn('[' + migrate_name + '] You can\'t migrate down the initial data.'); - return Promise.resolve(true); -}; diff --git a/backend/migrations/20200410143839_access_list_client.js b/backend/migrations/20200410143839_access_list_client.js deleted file mode 100644 index 3511e35b..00000000 --- a/backend/migrations/20200410143839_access_list_client.js +++ /dev/null @@ -1,53 +0,0 @@ -const migrate_name = 'access_list_client'; -const logger = require('../logger').migrate; - -/** - * Migrate - * - * @see http://knexjs.org/#Schema - * - * @param {Object} knex - * @param {Promise} Promise - * @returns {Promise} - */ -exports.up = function (knex/*, Promise*/) { - - logger.info('[' + migrate_name + '] Migrating Up...'); - - return knex.schema.createTable('access_list_client', (table) => { - table.increments().primary(); - table.dateTime('created_on').notNull(); - table.dateTime('modified_on').notNull(); - table.integer('access_list_id').notNull().unsigned(); - table.string('address').notNull(); - table.string('directive').notNull(); - table.json('meta').notNull(); - - }) - .then(function () { - logger.info('[' + migrate_name + '] access_list_client Table created'); - - return knex.schema.table('access_list', function (access_list) { - access_list.integer('satify_any').notNull().defaultTo(0); - }); - }) - .then(() => { - logger.info('[' + migrate_name + '] access_list Table altered'); - }); -}; - -/** - * Undo Migrate - * - * @param {Object} knex - * @param {Promise} Promise - * @returns {Promise} - */ -exports.down = function (knex/*, Promise*/) { - logger.info('[' + migrate_name + '] Migrating Down...'); - - return knex.schema.dropTable('access_list_client') - .then(() => { - logger.info('[' + migrate_name + '] access_list_client Table dropped'); - }); -}; diff --git a/backend/migrations/20200410143840_access_list_client_fix.js b/backend/migrations/20200410143840_access_list_client_fix.js deleted file mode 100644 index ee0f0906..00000000 --- a/backend/migrations/20200410143840_access_list_client_fix.js +++ /dev/null @@ -1,34 +0,0 @@ -const migrate_name = 'access_list_client_fix'; -const logger = require('../logger').migrate; - -/** - * Migrate - * - * @see http://knexjs.org/#Schema - * - * @param {Object} knex - * @param {Promise} Promise - * @returns {Promise} - */ -exports.up = function (knex/*, Promise*/) { - logger.info('[' + migrate_name + '] Migrating Up...'); - - return knex.schema.table('access_list', function (access_list) { - access_list.renameColumn('satify_any', 'satisfy_any'); - }) - .then(() => { - logger.info('[' + migrate_name + '] access_list Table altered'); - }); -}; - -/** - * Undo Migrate - * - * @param {Object} knex - * @param {Promise} Promise - * @returns {Promise} - */ -exports.down = function (knex, Promise) { - logger.warn('[' + migrate_name + '] You can\'t migrate down this one.'); - return Promise.resolve(true); -}; diff --git a/backend/migrations/20201013035318_initial_schema.sql b/backend/migrations/20201013035318_initial_schema.sql new file mode 100644 index 00000000..aa65b2ab --- /dev/null +++ b/backend/migrations/20201013035318_initial_schema.sql @@ -0,0 +1,172 @@ +-- migrate:up + +CREATE TABLE IF NOT EXISTS `user` +( + id INTEGER PRIMARY KEY AUTOINCREMENT, + created_on INTEGER NOT NULL DEFAULT 0, + modified_on INTEGER NOT NULL DEFAULT 0, + name TEXT NOT NULL, + nickname TEXT NOT NULL, + email TEXT NOT NULL, + roles TEXT NOT NULL, + is_disabled INTEGER NOT NULL DEFAULT 0, + is_deleted INTEGER NOT NULL DEFAULT 0 +); + +CREATE TABLE IF NOT EXISTS `auth` +( + id INTEGER PRIMARY KEY AUTOINCREMENT, + created_on INTEGER NOT NULL DEFAULT 0, + modified_on INTEGER NOT NULL DEFAULT 0, + user_id INTEGER NOT NULL, + type TEXT NOT NULL, + secret TEXT NOT NULL, + is_deleted INTEGER NOT NULL DEFAULT 0, + FOREIGN KEY (user_id) REFERENCES user (id), + UNIQUE (user_id, type) +); + +CREATE TABLE IF NOT EXISTS `setting` +( + id INTEGER PRIMARY KEY AUTOINCREMENT, + created_on INTEGER NOT NULL DEFAULT 0, + modified_on INTEGER NOT NULL DEFAULT 0, + name TEXT NOT NULL, + value TEXT NOT NULL, + UNIQUE (name) +); + +CREATE TABLE IF NOT EXISTS `audit_log` +( + id INTEGER PRIMARY KEY AUTOINCREMENT, + created_on INTEGER NOT NULL DEFAULT 0, + modified_on INTEGER NOT NULL DEFAULT 0, + user_id INTEGER NOT NULL, + object_type TEXT NOT NULL, + object_id INTEGER NOT NULL, + action TEXT NOT NULL, + meta TEXT NOT NULL, + FOREIGN KEY (user_id) REFERENCES user (id) +); + +CREATE TABLE IF NOT EXISTS `certificate_authority` +( + id INTEGER PRIMARY KEY AUTOINCREMENT, + created_on INTEGER NOT NULL DEFAULT 0, + modified_on INTEGER NOT NULL DEFAULT 0, + name TEXT NOT NULL, + acme2_url TEXT NOT NULL, + is_deleted INTEGER NOT NULL DEFAULT 0 +); + +CREATE TABLE IF NOT EXISTS `dns_provider` +( + id INTEGER PRIMARY KEY AUTOINCREMENT, + created_on INTEGER NOT NULL DEFAULT 0, + modified_on INTEGER NOT NULL DEFAULT 0, + user_id INTEGER NOT NULL, + provider_key TEXT NOT NULL, + name TEXT NOT NULL, + meta TEXT NOT NULL, + is_deleted INTEGER NOT NULL DEFAULT 0, + FOREIGN KEY (user_id) REFERENCES user (id) +); + +CREATE TABLE IF NOT EXISTS `certificate` +( + id INTEGER PRIMARY KEY AUTOINCREMENT, + created_on INTEGER NOT NULL DEFAULT 0, + modified_on INTEGER NOT NULL DEFAULT 0, + type TEXT NOT NULL, -- custom,dns,http + user_id INTEGER NOT NULL, + certificate_authority_id INTEGER, -- 0 for a custom cert + dns_provider_id INTEGER, -- 0, for a http or custom cert + name TEXT NOT NULL, + domain_names TEXT NOT NULL, + expires_on INTEGER DEFAULT 0, + status TEXT NOT NULL, -- ready,requesting,failed,provided + error_message text NOT NULL DEFAULT "", + meta TEXT NOT NULL, + is_deleted INTEGER NOT NULL DEFAULT 0, + FOREIGN KEY (user_id) REFERENCES user (id), + FOREIGN KEY (certificate_authority_id) REFERENCES certificate_authority (id), + FOREIGN KEY (dns_provider_id) REFERENCES dns_provider (id) +); + +CREATE TABLE IF NOT EXISTS `stream` +( + id INTEGER PRIMARY KEY AUTOINCREMENT, + created_on INTEGER NOT NULL DEFAULT 0, + modified_on INTEGER NOT NULL DEFAULT 0, + user_id INTEGER NOT NULL, + listen_interface TEXT NOT NULL, + incoming_port INTEGER NOT NULL, + upstream_options TEXT NOT NULL, + tcp_forwarding INTEGER NOT NULL DEFAULT 0, + udp_forwarding INTEGER NOT NULL DEFAULT 0, + advanced_config TEXT NOT NULL, + is_disabled INTEGER NOT NULL DEFAULT 0, + is_deleted INTEGER NOT NULL DEFAULT 0, + FOREIGN KEY (user_id) REFERENCES user (id) +); + +CREATE TABLE IF NOT EXISTS `upstream` +( + id INTEGER PRIMARY KEY AUTOINCREMENT, + created_on INTEGER NOT NULL DEFAULT 0, + modified_on INTEGER NOT NULL DEFAULT 0, + user_id INTEGER NOT NULL, + hosts TEXT NOT NULL, + balance_method TEXT NOT NULL, + max_fails INTEGER NOT NULL DEFAULT 1, + fail_timeout INTEGER NOT NULL DEFAULT 10, + advanced_config TEXT NOT NULL, + is_deleted INTEGER NOT NULL DEFAULT 0, + FOREIGN KEY (user_id) REFERENCES user (id) +); + +CREATE TABLE IF NOT EXISTS `access_list` +( + id INTEGER PRIMARY KEY AUTOINCREMENT, + created_on INTEGER NOT NULL DEFAULT 0, + modified_on INTEGER NOT NULL DEFAULT 0, + user_id INTEGER NOT NULL, + name TEXT NOT NULL, + meta TEXT NOT NULL, + is_deleted INTEGER NOT NULL DEFAULT 0, + FOREIGN KEY (user_id) REFERENCES user (id) +); + +CREATE TABLE IF NOT EXISTS `host` +( + id INTEGER PRIMARY KEY AUTOINCREMENT, + created_on INTEGER NOT NULL DEFAULT 0, + modified_on INTEGER NOT NULL DEFAULT 0, + user_id INTEGER NOT NULL, + type TEXT NOT NULL, + listen_interface TEXT NOT NULL, + domain_names TEXT NOT NULL, + upstream_id INTEGER NOT NULL, + certificate_id INTEGER, + access_list_id INTEGER, + ssl_forced INTEGER NOT NULL DEFAULT 0, + caching_enabled INTEGER NOT NULL DEFAULT 0, + block_exploits INTEGER NOT NULL DEFAULT 0, + allow_websocket_upgrade INTEGER NOT NULL DEFAULT 0, + http2_support INTEGER NOT NULL DEFAULT 0, + hsts_enabled INTEGER NOT NULL DEFAULT 0, + hsts_subdomains INTEGER NOT NULL DEFAULT 0, + paths TEXT NOT NULL, + upstream_options TEXT NOT NULL DEFAULT "", + advanced_config TEXT NOT NULL DEFAULT "", + is_disabled INTEGER NOT NULL DEFAULT 0, + is_deleted INTEGER NOT NULL DEFAULT 0, + FOREIGN KEY (user_id) REFERENCES user (id), + FOREIGN KEY (upstream_id) REFERENCES upstream (id), + FOREIGN KEY (certificate_id) REFERENCES certificate (id), + FOREIGN KEY (access_list_id) REFERENCES access_list (id) +); + +-- migrate:down + +-- Not allowed to go down from initial diff --git a/backend/migrations/20201013035839_initial_data.sql b/backend/migrations/20201013035839_initial_data.sql new file mode 100644 index 00000000..dad2ac8d --- /dev/null +++ b/backend/migrations/20201013035839_initial_data.sql @@ -0,0 +1,38 @@ +-- migrate:up + +-- Default error reporting setting +INSERT INTO `setting` ( + created_on, + modified_on, + name, + value +) VALUES ( + strftime('%s', 'now'), + strftime('%s', 'now'), + "error-reporting", + "true" +); + +-- Default Certificate Authorities + +INSERT INTO `certificate_authority` ( + created_on, + modified_on, + name, + acme2_url +) VALUES ( + strftime('%s', 'now'), + strftime('%s', 'now'), + "Let's Encrypt", + "https://acme-v02.api.letsencrypt.org/directory" +), ( + strftime('%s', 'now'), + strftime('%s', 'now'), + "Let's Encrypt (Staging)", + "https://acme-staging-v02.api.letsencrypt.org/directory" +); + + +-- migrate:down + +-- Not allowed to go down from initial diff --git a/backend/migrations/20201014143841_pass_auth.js b/backend/migrations/20201014143841_pass_auth.js deleted file mode 100644 index a7767eb1..00000000 --- a/backend/migrations/20201014143841_pass_auth.js +++ /dev/null @@ -1,41 +0,0 @@ -const migrate_name = 'pass_auth'; -const logger = require('../logger').migrate; - -/** - * Migrate - * - * @see http://knexjs.org/#Schema - * - * @param {Object} knex - * @param {Promise} Promise - * @returns {Promise} - */ -exports.up = function (knex/*, Promise*/) { - - logger.info('[' + migrate_name + '] Migrating Up...'); - - return knex.schema.table('access_list', function (access_list) { - access_list.integer('pass_auth').notNull().defaultTo(1); - }) - .then(() => { - logger.info('[' + migrate_name + '] access_list Table altered'); - }); -}; - -/** - * Undo Migrate - * - * @param {Object} knex - * @param {Promise} Promise - * @returns {Promise} - */ -exports.down = function (knex/*, Promise*/) { - logger.info('[' + migrate_name + '] Migrating Down...'); - - return knex.schema.table('access_list', function (access_list) { - access_list.dropColumn('pass_auth'); - }) - .then(() => { - logger.info('[' + migrate_name + '] access_list pass_auth Column dropped'); - }); -}; diff --git a/backend/migrations/20210210154702_redirection_scheme.js b/backend/migrations/20210210154702_redirection_scheme.js deleted file mode 100644 index 0dad4876..00000000 --- a/backend/migrations/20210210154702_redirection_scheme.js +++ /dev/null @@ -1,41 +0,0 @@ -const migrate_name = 'redirection_scheme'; -const logger = require('../logger').migrate; - -/** - * Migrate - * - * @see http://knexjs.org/#Schema - * - * @param {Object} knex - * @param {Promise} Promise - * @returns {Promise} - */ -exports.up = function (knex/*, Promise*/) { - - logger.info('[' + migrate_name + '] Migrating Up...'); - - return knex.schema.table('redirection_host', (table) => { - table.string('forward_scheme').notNull().defaultTo('$scheme'); - }) - .then(function () { - logger.info('[' + migrate_name + '] redirection_host Table altered'); - }); -}; - -/** - * Undo Migrate - * - * @param {Object} knex - * @param {Promise} Promise - * @returns {Promise} - */ -exports.down = function (knex/*, Promise*/) { - logger.info('[' + migrate_name + '] Migrating Down...'); - - return knex.schema.table('redirection_host', (table) => { - table.dropColumn('forward_scheme'); - }) - .then(function () { - logger.info('[' + migrate_name + '] redirection_host Table altered'); - }); -}; diff --git a/backend/migrations/20210210154703_redirection_status_code.js b/backend/migrations/20210210154703_redirection_status_code.js deleted file mode 100644 index b9bea0b9..00000000 --- a/backend/migrations/20210210154703_redirection_status_code.js +++ /dev/null @@ -1,41 +0,0 @@ -const migrate_name = 'redirection_status_code'; -const logger = require('../logger').migrate; - -/** - * Migrate - * - * @see http://knexjs.org/#Schema - * - * @param {Object} knex - * @param {Promise} Promise - * @returns {Promise} - */ -exports.up = function (knex/*, Promise*/) { - - logger.info('[' + migrate_name + '] Migrating Up...'); - - return knex.schema.table('redirection_host', (table) => { - table.integer('forward_http_code').notNull().unsigned().defaultTo(302); - }) - .then(function () { - logger.info('[' + migrate_name + '] redirection_host Table altered'); - }); -}; - -/** - * Undo Migrate - * - * @param {Object} knex - * @param {Promise} Promise - * @returns {Promise} - */ -exports.down = function (knex/*, Promise*/) { - logger.info('[' + migrate_name + '] Migrating Down...'); - - return knex.schema.table('redirection_host', (table) => { - table.dropColumn('forward_http_code'); - }) - .then(function () { - logger.info('[' + migrate_name + '] redirection_host Table altered'); - }); -}; diff --git a/backend/models/access_list.js b/backend/models/access_list.js deleted file mode 100644 index 01974e86..00000000 --- a/backend/models/access_list.js +++ /dev/null @@ -1,102 +0,0 @@ -// Objection Docs: -// http://vincit.github.io/objection.js/ - -const db = require('../db'); -const Model = require('objection').Model; -const User = require('./user'); -const AccessListAuth = require('./access_list_auth'); -const AccessListClient = require('./access_list_client'); -const now = require('./now_helper'); - -Model.knex(db); - -class AccessList extends Model { - $beforeInsert () { - this.created_on = now(); - this.modified_on = now(); - - // Default for meta - if (typeof this.meta === 'undefined') { - this.meta = {}; - } - } - - $beforeUpdate () { - this.modified_on = now(); - } - - static get name () { - return 'AccessList'; - } - - static get tableName () { - return 'access_list'; - } - - static get jsonAttributes () { - return ['meta']; - } - - static get relationMappings () { - const ProxyHost = require('./proxy_host'); - - return { - owner: { - relation: Model.HasOneRelation, - modelClass: User, - join: { - from: 'access_list.owner_user_id', - to: 'user.id' - }, - modify: function (qb) { - qb.where('user.is_deleted', 0); - qb.omit(['id', 'created_on', 'modified_on', 'is_deleted', 'email', 'roles']); - } - }, - items: { - relation: Model.HasManyRelation, - modelClass: AccessListAuth, - join: { - from: 'access_list.id', - to: 'access_list_auth.access_list_id' - }, - modify: function (qb) { - qb.omit(['id', 'created_on', 'modified_on', 'access_list_id', 'meta']); - } - }, - clients: { - relation: Model.HasManyRelation, - modelClass: AccessListClient, - join: { - from: 'access_list.id', - to: 'access_list_client.access_list_id' - }, - modify: function (qb) { - qb.omit(['id', 'created_on', 'modified_on', 'access_list_id', 'meta']); - } - }, - proxy_hosts: { - relation: Model.HasManyRelation, - modelClass: ProxyHost, - join: { - from: 'access_list.id', - to: 'proxy_host.access_list_id' - }, - modify: function (qb) { - qb.where('proxy_host.is_deleted', 0); - qb.omit(['is_deleted', 'meta']); - } - } - }; - } - - get satisfy() { - return this.satisfy_any ? 'satisfy any' : 'satisfy all'; - } - - get passauth() { - return this.pass_auth ? '' : 'proxy_set_header Authorization "";'; - } -} - -module.exports = AccessList; diff --git a/backend/models/access_list_auth.js b/backend/models/access_list_auth.js deleted file mode 100644 index 932371f3..00000000 --- a/backend/models/access_list_auth.js +++ /dev/null @@ -1,55 +0,0 @@ -// Objection Docs: -// http://vincit.github.io/objection.js/ - -const db = require('../db'); -const Model = require('objection').Model; -const now = require('./now_helper'); - -Model.knex(db); - -class AccessListAuth extends Model { - $beforeInsert () { - this.created_on = now(); - this.modified_on = now(); - - // Default for meta - if (typeof this.meta === 'undefined') { - this.meta = {}; - } - } - - $beforeUpdate () { - this.modified_on = now(); - } - - static get name () { - return 'AccessListAuth'; - } - - static get tableName () { - return 'access_list_auth'; - } - - static get jsonAttributes () { - return ['meta']; - } - - static get relationMappings () { - return { - access_list: { - relation: Model.HasOneRelation, - modelClass: require('./access_list'), - join: { - from: 'access_list_auth.access_list_id', - to: 'access_list.id' - }, - modify: function (qb) { - qb.where('access_list.is_deleted', 0); - qb.omit(['created_on', 'modified_on', 'is_deleted', 'access_list_id']); - } - } - }; - } -} - -module.exports = AccessListAuth; diff --git a/backend/models/access_list_client.js b/backend/models/access_list_client.js deleted file mode 100644 index e257213a..00000000 --- a/backend/models/access_list_client.js +++ /dev/null @@ -1,59 +0,0 @@ -// Objection Docs: -// http://vincit.github.io/objection.js/ - -const db = require('../db'); -const Model = require('objection').Model; -const now = require('./now_helper'); - -Model.knex(db); - -class AccessListClient extends Model { - $beforeInsert () { - this.created_on = now(); - this.modified_on = now(); - - // Default for meta - if (typeof this.meta === 'undefined') { - this.meta = {}; - } - } - - $beforeUpdate () { - this.modified_on = now(); - } - - static get name () { - return 'AccessListClient'; - } - - static get tableName () { - return 'access_list_client'; - } - - static get jsonAttributes () { - return ['meta']; - } - - static get relationMappings () { - return { - access_list: { - relation: Model.HasOneRelation, - modelClass: require('./access_list'), - join: { - from: 'access_list_client.access_list_id', - to: 'access_list.id' - }, - modify: function (qb) { - qb.where('access_list.is_deleted', 0); - qb.omit(['created_on', 'modified_on', 'is_deleted', 'access_list_id']); - } - } - }; - } - - get rule() { - return `${this.directive} ${this.address}`; - } -} - -module.exports = AccessListClient; diff --git a/backend/models/audit-log.js b/backend/models/audit-log.js deleted file mode 100644 index a3a318c8..00000000 --- a/backend/models/audit-log.js +++ /dev/null @@ -1,55 +0,0 @@ -// Objection Docs: -// http://vincit.github.io/objection.js/ - -const db = require('../db'); -const Model = require('objection').Model; -const User = require('./user'); -const now = require('./now_helper'); - -Model.knex(db); - -class AuditLog extends Model { - $beforeInsert () { - this.created_on = now(); - this.modified_on = now(); - - // Default for meta - if (typeof this.meta === 'undefined') { - this.meta = {}; - } - } - - $beforeUpdate () { - this.modified_on = now(); - } - - static get name () { - return 'AuditLog'; - } - - static get tableName () { - return 'audit_log'; - } - - static get jsonAttributes () { - return ['meta']; - } - - static get relationMappings () { - return { - user: { - relation: Model.HasOneRelation, - modelClass: User, - join: { - from: 'audit_log.user_id', - to: 'user.id' - }, - modify: function (qb) { - qb.omit(['id', 'created_on', 'modified_on', 'roles']); - } - } - }; - } -} - -module.exports = AuditLog; diff --git a/backend/models/auth.js b/backend/models/auth.js deleted file mode 100644 index 5ba5f380..00000000 --- a/backend/models/auth.js +++ /dev/null @@ -1,86 +0,0 @@ -// Objection Docs: -// http://vincit.github.io/objection.js/ - -const bcrypt = require('bcrypt'); -const db = require('../db'); -const Model = require('objection').Model; -const User = require('./user'); -const now = require('./now_helper'); - -Model.knex(db); - -function encryptPassword () { - /* jshint -W040 */ - let _this = this; - - if (_this.type === 'password' && _this.secret) { - return bcrypt.hash(_this.secret, 13) - .then(function (hash) { - _this.secret = hash; - }); - } - - return null; -} - -class Auth extends Model { - $beforeInsert (queryContext) { - this.created_on = now(); - this.modified_on = now(); - - // Default for meta - if (typeof this.meta === 'undefined') { - this.meta = {}; - } - - return encryptPassword.apply(this, queryContext); - } - - $beforeUpdate (queryContext) { - this.modified_on = now(); - return encryptPassword.apply(this, queryContext); - } - - /** - * Verify a plain password against the encrypted password - * - * @param {String} password - * @returns {Promise} - */ - verifyPassword (password) { - return bcrypt.compare(password, this.secret); - } - - static get name () { - return 'Auth'; - } - - static get tableName () { - return 'auth'; - } - - static get jsonAttributes () { - return ['meta']; - } - - static get relationMappings () { - return { - user: { - relation: Model.HasOneRelation, - modelClass: User, - join: { - from: 'auth.user_id', - to: 'user.id' - }, - filter: { - is_deleted: 0 - }, - modify: function (qb) { - qb.omit(['is_deleted']); - } - } - }; - } -} - -module.exports = Auth; diff --git a/backend/models/certificate.js b/backend/models/certificate.js deleted file mode 100644 index 6084a995..00000000 --- a/backend/models/certificate.js +++ /dev/null @@ -1,73 +0,0 @@ -// Objection Docs: -// http://vincit.github.io/objection.js/ - -const db = require('../db'); -const Model = require('objection').Model; -const User = require('./user'); -const now = require('./now_helper'); - -Model.knex(db); - -class Certificate extends Model { - $beforeInsert () { - this.created_on = now(); - this.modified_on = now(); - - // Default for expires_on - if (typeof this.expires_on === 'undefined') { - this.expires_on = now(); - } - - // Default for domain_names - if (typeof this.domain_names === 'undefined') { - this.domain_names = []; - } - - // Default for meta - if (typeof this.meta === 'undefined') { - this.meta = {}; - } - - this.domain_names.sort(); - } - - $beforeUpdate () { - this.modified_on = now(); - - // Sort domain_names - if (typeof this.domain_names !== 'undefined') { - this.domain_names.sort(); - } - } - - static get name () { - return 'Certificate'; - } - - static get tableName () { - return 'certificate'; - } - - static get jsonAttributes () { - return ['domain_names', 'meta']; - } - - static get relationMappings () { - return { - owner: { - relation: Model.HasOneRelation, - modelClass: User, - join: { - from: 'certificate.owner_user_id', - to: 'user.id' - }, - modify: function (qb) { - qb.where('user.is_deleted', 0); - qb.omit(['id', 'created_on', 'modified_on', 'is_deleted', 'email', 'roles']); - } - } - }; - } -} - -module.exports = Certificate; diff --git a/backend/models/dead_host.js b/backend/models/dead_host.js deleted file mode 100644 index 6de42a33..00000000 --- a/backend/models/dead_host.js +++ /dev/null @@ -1,81 +0,0 @@ -// Objection Docs: -// http://vincit.github.io/objection.js/ - -const db = require('../db'); -const Model = require('objection').Model; -const User = require('./user'); -const Certificate = require('./certificate'); -const now = require('./now_helper'); - -Model.knex(db); - -class DeadHost extends Model { - $beforeInsert () { - this.created_on = now(); - this.modified_on = now(); - - // Default for domain_names - if (typeof this.domain_names === 'undefined') { - this.domain_names = []; - } - - // Default for meta - if (typeof this.meta === 'undefined') { - this.meta = {}; - } - - this.domain_names.sort(); - } - - $beforeUpdate () { - this.modified_on = now(); - - // Sort domain_names - if (typeof this.domain_names !== 'undefined') { - this.domain_names.sort(); - } - } - - static get name () { - return 'DeadHost'; - } - - static get tableName () { - return 'dead_host'; - } - - static get jsonAttributes () { - return ['domain_names', 'meta']; - } - - static get relationMappings () { - return { - owner: { - relation: Model.HasOneRelation, - modelClass: User, - join: { - from: 'dead_host.owner_user_id', - to: 'user.id' - }, - modify: function (qb) { - qb.where('user.is_deleted', 0); - qb.omit(['id', 'created_on', 'modified_on', 'is_deleted', 'email', 'roles']); - } - }, - certificate: { - relation: Model.HasOneRelation, - modelClass: Certificate, - join: { - from: 'dead_host.certificate_id', - to: 'certificate.id' - }, - modify: function (qb) { - qb.where('certificate.is_deleted', 0); - qb.omit(['id', 'created_on', 'modified_on', 'is_deleted']); - } - } - }; - } -} - -module.exports = DeadHost; diff --git a/backend/models/now_helper.js b/backend/models/now_helper.js deleted file mode 100644 index def16d08..00000000 --- a/backend/models/now_helper.js +++ /dev/null @@ -1,13 +0,0 @@ -const db = require('../db'); -const config = require('config'); -const Model = require('objection').Model; - -Model.knex(db); - -module.exports = function () { - if (config.database.knex && config.database.knex.client === 'sqlite3') { - return Model.raw('datetime(\'now\',\'localtime\')'); - } else { - return Model.raw('NOW()'); - } -}; diff --git a/backend/models/proxy_host.js b/backend/models/proxy_host.js deleted file mode 100644 index a7583088..00000000 --- a/backend/models/proxy_host.js +++ /dev/null @@ -1,94 +0,0 @@ -// Objection Docs: -// http://vincit.github.io/objection.js/ - -const db = require('../db'); -const Model = require('objection').Model; -const User = require('./user'); -const AccessList = require('./access_list'); -const Certificate = require('./certificate'); -const now = require('./now_helper'); - -Model.knex(db); - -class ProxyHost extends Model { - $beforeInsert () { - this.created_on = now(); - this.modified_on = now(); - - // Default for domain_names - if (typeof this.domain_names === 'undefined') { - this.domain_names = []; - } - - // Default for meta - if (typeof this.meta === 'undefined') { - this.meta = {}; - } - - this.domain_names.sort(); - } - - $beforeUpdate () { - this.modified_on = now(); - - // Sort domain_names - if (typeof this.domain_names !== 'undefined') { - this.domain_names.sort(); - } - } - - static get name () { - return 'ProxyHost'; - } - - static get tableName () { - return 'proxy_host'; - } - - static get jsonAttributes () { - return ['domain_names', 'meta', 'locations']; - } - - static get relationMappings () { - return { - owner: { - relation: Model.HasOneRelation, - modelClass: User, - join: { - from: 'proxy_host.owner_user_id', - to: 'user.id' - }, - modify: function (qb) { - qb.where('user.is_deleted', 0); - qb.omit(['id', 'created_on', 'modified_on', 'is_deleted', 'email', 'roles']); - } - }, - access_list: { - relation: Model.HasOneRelation, - modelClass: AccessList, - join: { - from: 'proxy_host.access_list_id', - to: 'access_list.id' - }, - modify: function (qb) { - qb.where('access_list.is_deleted', 0); - qb.omit(['id', 'created_on', 'modified_on', 'is_deleted']); - } - }, - certificate: { - relation: Model.HasOneRelation, - modelClass: Certificate, - join: { - from: 'proxy_host.certificate_id', - to: 'certificate.id' - }, - modify: function (qb) { - qb.where('certificate.is_deleted', 0); - qb.omit(['id', 'created_on', 'modified_on', 'is_deleted']); - } - } - }; - } -} - -module.exports = ProxyHost; diff --git a/backend/models/redirection_host.js b/backend/models/redirection_host.js deleted file mode 100644 index dd149b76..00000000 --- a/backend/models/redirection_host.js +++ /dev/null @@ -1,81 +0,0 @@ -// Objection Docs: -// http://vincit.github.io/objection.js/ - -const db = require('../db'); -const Model = require('objection').Model; -const User = require('./user'); -const Certificate = require('./certificate'); -const now = require('./now_helper'); - -Model.knex(db); - -class RedirectionHost extends Model { - $beforeInsert () { - this.created_on = now(); - this.modified_on = now(); - - // Default for domain_names - if (typeof this.domain_names === 'undefined') { - this.domain_names = []; - } - - // Default for meta - if (typeof this.meta === 'undefined') { - this.meta = {}; - } - - this.domain_names.sort(); - } - - $beforeUpdate () { - this.modified_on = now(); - - // Sort domain_names - if (typeof this.domain_names !== 'undefined') { - this.domain_names.sort(); - } - } - - static get name () { - return 'RedirectionHost'; - } - - static get tableName () { - return 'redirection_host'; - } - - static get jsonAttributes () { - return ['domain_names', 'meta']; - } - - static get relationMappings () { - return { - owner: { - relation: Model.HasOneRelation, - modelClass: User, - join: { - from: 'redirection_host.owner_user_id', - to: 'user.id' - }, - modify: function (qb) { - qb.where('user.is_deleted', 0); - qb.omit(['id', 'created_on', 'modified_on', 'is_deleted', 'email', 'roles']); - } - }, - certificate: { - relation: Model.HasOneRelation, - modelClass: Certificate, - join: { - from: 'redirection_host.certificate_id', - to: 'certificate.id' - }, - modify: function (qb) { - qb.where('certificate.is_deleted', 0); - qb.omit(['id', 'created_on', 'modified_on', 'is_deleted']); - } - } - }; - } -} - -module.exports = RedirectionHost; diff --git a/backend/models/setting.js b/backend/models/setting.js deleted file mode 100644 index 75aa9007..00000000 --- a/backend/models/setting.js +++ /dev/null @@ -1,30 +0,0 @@ -// Objection Docs: -// http://vincit.github.io/objection.js/ - -const db = require('../db'); -const Model = require('objection').Model; - -Model.knex(db); - -class Setting extends Model { - $beforeInsert () { - // Default for meta - if (typeof this.meta === 'undefined') { - this.meta = {}; - } - } - - static get name () { - return 'Setting'; - } - - static get tableName () { - return 'setting'; - } - - static get jsonAttributes () { - return ['meta']; - } -} - -module.exports = Setting; diff --git a/backend/models/stream.js b/backend/models/stream.js deleted file mode 100644 index ed65de0f..00000000 --- a/backend/models/stream.js +++ /dev/null @@ -1,56 +0,0 @@ -// Objection Docs: -// http://vincit.github.io/objection.js/ - -const db = require('../db'); -const Model = require('objection').Model; -const User = require('./user'); -const now = require('./now_helper'); - -Model.knex(db); - -class Stream extends Model { - $beforeInsert () { - this.created_on = now(); - this.modified_on = now(); - - // Default for meta - if (typeof this.meta === 'undefined') { - this.meta = {}; - } - } - - $beforeUpdate () { - this.modified_on = now(); - } - - static get name () { - return 'Stream'; - } - - static get tableName () { - return 'stream'; - } - - static get jsonAttributes () { - return ['meta']; - } - - static get relationMappings () { - return { - owner: { - relation: Model.HasOneRelation, - modelClass: User, - join: { - from: 'stream.owner_user_id', - to: 'user.id' - }, - modify: function (qb) { - qb.where('user.is_deleted', 0); - qb.omit(['id', 'created_on', 'modified_on', 'is_deleted', 'email', 'roles']); - } - } - }; - } -} - -module.exports = Stream; diff --git a/backend/models/token.js b/backend/models/token.js deleted file mode 100644 index 4e1b1826..00000000 --- a/backend/models/token.js +++ /dev/null @@ -1,147 +0,0 @@ -/** - NOTE: This is not a database table, this is a model of a Token object that can be created/loaded - and then has abilities after that. - */ - -const _ = require('lodash'); -const jwt = require('jsonwebtoken'); -const crypto = require('crypto'); -const error = require('../lib/error'); -const ALGO = 'RS256'; - -let public_key = null; -let private_key = null; - -function checkJWTKeyPair() { - if (!public_key || !private_key) { - let config = require('config'); - public_key = config.get('jwt.pub'); - private_key = config.get('jwt.key'); - } -} - -module.exports = function () { - - let token_data = {}; - - let self = { - /** - * @param {Object} payload - * @returns {Promise} - */ - create: (payload) => { - // sign with RSA SHA256 - let options = { - algorithm: ALGO, - expiresIn: payload.expiresIn || '1d' - }; - - payload.jti = crypto.randomBytes(12) - .toString('base64') - .substr(-8); - - checkJWTKeyPair(); - - return new Promise((resolve, reject) => { - jwt.sign(payload, private_key, options, (err, token) => { - if (err) { - reject(err); - } else { - token_data = payload; - resolve({ - token: token, - payload: payload - }); - } - }); - }); - }, - - /** - * @param {String} token - * @returns {Promise} - */ - load: function (token) { - return new Promise((resolve, reject) => { - checkJWTKeyPair(); - try { - if (!token || token === null || token === 'null') { - reject(new error.AuthError('Empty token')); - } else { - jwt.verify(token, public_key, {ignoreExpiration: false, algorithms: [ALGO]}, (err, result) => { - if (err) { - - if (err.name === 'TokenExpiredError') { - reject(new error.AuthError('Token has expired', err)); - } else { - reject(err); - } - - } else { - token_data = result; - - // Hack: some tokens out in the wild have a scope of 'all' instead of 'user'. - // For 30 days at least, we need to replace 'all' with user. - if ((typeof token_data.scope !== 'undefined' && _.indexOf(token_data.scope, 'all') !== -1)) { - //console.log('Warning! Replacing "all" scope with "user"'); - - token_data.scope = ['user']; - } - - resolve(token_data); - } - }); - } - } catch (err) { - reject(err); - } - }); - - }, - - /** - * Does the token have the specified scope? - * - * @param {String} scope - * @returns {Boolean} - */ - hasScope: function (scope) { - return typeof token_data.scope !== 'undefined' && _.indexOf(token_data.scope, scope) !== -1; - }, - - /** - * @param {String} key - * @return {*} - */ - get: function (key) { - if (typeof token_data[key] !== 'undefined') { - return token_data[key]; - } - - return null; - }, - - /** - * @param {String} key - * @param {*} value - */ - set: function (key, value) { - token_data[key] = value; - }, - - /** - * @param [default_value] - * @returns {Integer} - */ - getUserId: (default_value) => { - let attrs = self.get('attrs'); - if (attrs && typeof attrs.id !== 'undefined' && attrs.id) { - return attrs.id; - } - - return default_value || 0; - } - }; - - return self; -}; diff --git a/backend/models/user.js b/backend/models/user.js deleted file mode 100644 index c76f7dbf..00000000 --- a/backend/models/user.js +++ /dev/null @@ -1,56 +0,0 @@ -// Objection Docs: -// http://vincit.github.io/objection.js/ - -const db = require('../db'); -const Model = require('objection').Model; -const UserPermission = require('./user_permission'); -const now = require('./now_helper'); - -Model.knex(db); - -class User extends Model { - $beforeInsert () { - this.created_on = now(); - this.modified_on = now(); - - // Default for roles - if (typeof this.roles === 'undefined') { - this.roles = []; - } - } - - $beforeUpdate () { - this.modified_on = now(); - } - - static get name () { - return 'User'; - } - - static get tableName () { - return 'user'; - } - - static get jsonAttributes () { - return ['roles']; - } - - static get relationMappings () { - return { - permissions: { - relation: Model.HasOneRelation, - modelClass: UserPermission, - join: { - from: 'user.id', - to: 'user_permission.user_id' - }, - modify: function (qb) { - qb.omit(['id', 'created_on', 'modified_on', 'user_id']); - } - } - }; - } - -} - -module.exports = User; diff --git a/backend/models/user_permission.js b/backend/models/user_permission.js deleted file mode 100644 index bb87d5dc..00000000 --- a/backend/models/user_permission.js +++ /dev/null @@ -1,29 +0,0 @@ -// Objection Docs: -// http://vincit.github.io/objection.js/ - -const db = require('../db'); -const Model = require('objection').Model; -const now = require('./now_helper'); - -Model.knex(db); - -class UserPermission extends Model { - $beforeInsert () { - this.created_on = now(); - this.modified_on = now(); - } - - $beforeUpdate () { - this.modified_on = now(); - } - - static get name () { - return 'UserPermission'; - } - - static get tableName () { - return 'user_permission'; - } -} - -module.exports = UserPermission; diff --git a/backend/nodemon.json b/backend/nodemon.json deleted file mode 100644 index 3d6d1342..00000000 --- a/backend/nodemon.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "verbose": false, - "ignore": [ - "data" - ], - "ext": "js json ejs" -} diff --git a/backend/package.json b/backend/package.json deleted file mode 100644 index 2130c7b8..00000000 --- a/backend/package.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "name": "nginx-proxy-manager", - "version": "0.0.0", - "description": "A beautiful interface for creating Nginx endpoints", - "main": "js/index.js", - "dependencies": { - "ajv": "^6.12.0", - "batchflow": "^0.4.0", - "bcrypt": "^5.0.0", - "body-parser": "^1.19.0", - "compression": "^1.7.4", - "config": "^3.3.1", - "diskdb": "^0.1.17", - "express": "^4.17.1", - "express-fileupload": "^1.1.9", - "gravatar": "^1.8.0", - "html-entities": "^1.2.1", - "json-schema-ref-parser": "^8.0.0", - "jsonwebtoken": "^8.5.1", - "knex": "^0.20.13", - "liquidjs": "^9.11.10", - "lodash": "^4.17.21", - "moment": "^2.24.0", - "mysql": "^2.18.1", - "node-rsa": "^1.0.8", - "nodemon": "^2.0.2", - "objection": "^2.1.3", - "path": "^0.12.7", - "pg": "^7.12.1", - "restler": "^3.4.0", - "signale": "^1.4.0", - "sqlite3": "^4.1.1", - "temp-write": "^4.0.0", - "unix-timestamp": "^0.2.0" - }, - "signale": { - "displayDate": true, - "displayTimestamp": true - }, - "author": "Jamie Curnow ", - "license": "MIT", - "devDependencies": { - "eslint": "^6.8.0", - "eslint-plugin-align-assignments": "^1.1.2", - "prettier": "^2.0.4" - } -} diff --git a/backend/routes/api/audit-log.js b/backend/routes/api/audit-log.js deleted file mode 100644 index 8a2490c3..00000000 --- a/backend/routes/api/audit-log.js +++ /dev/null @@ -1,52 +0,0 @@ -const express = require('express'); -const validator = require('../../lib/validator'); -const jwtdecode = require('../../lib/express/jwt-decode'); -const internalAuditLog = require('../../internal/audit-log'); - -let router = express.Router({ - caseSensitive: true, - strict: true, - mergeParams: true -}); - -/** - * /api/audit-log - */ -router - .route('/') - .options((req, res) => { - res.sendStatus(204); - }) - .all(jwtdecode()) - - /** - * GET /api/audit-log - * - * Retrieve all logs - */ - .get((req, res, next) => { - validator({ - additionalProperties: false, - properties: { - expand: { - $ref: 'definitions#/definitions/expand' - }, - query: { - $ref: 'definitions#/definitions/query' - } - } - }, { - expand: (typeof req.query.expand === 'string' ? req.query.expand.split(',') : null), - query: (typeof req.query.query === 'string' ? req.query.query : null) - }) - .then((data) => { - return internalAuditLog.getAll(res.locals.access, data.expand, data.query); - }) - .then((rows) => { - res.status(200) - .send(rows); - }) - .catch(next); - }); - -module.exports = router; diff --git a/backend/routes/api/main.js b/backend/routes/api/main.js deleted file mode 100644 index 33cbbc21..00000000 --- a/backend/routes/api/main.js +++ /dev/null @@ -1,51 +0,0 @@ -const express = require('express'); -const pjson = require('../../package.json'); -const error = require('../../lib/error'); - -let router = express.Router({ - caseSensitive: true, - strict: true, - mergeParams: true -}); - -/** - * Health Check - * GET /api - */ -router.get('/', (req, res/*, next*/) => { - let version = pjson.version.split('-').shift().split('.'); - - res.status(200).send({ - status: 'OK', - version: { - major: parseInt(version.shift(), 10), - minor: parseInt(version.shift(), 10), - revision: parseInt(version.shift(), 10) - } - }); -}); - -router.use('/schema', require('./schema')); -router.use('/tokens', require('./tokens')); -router.use('/users', require('./users')); -router.use('/audit-log', require('./audit-log')); -router.use('/reports', require('./reports')); -router.use('/settings', require('./settings')); -router.use('/nginx/proxy-hosts', require('./nginx/proxy_hosts')); -router.use('/nginx/redirection-hosts', require('./nginx/redirection_hosts')); -router.use('/nginx/dead-hosts', require('./nginx/dead_hosts')); -router.use('/nginx/streams', require('./nginx/streams')); -router.use('/nginx/access-lists', require('./nginx/access_lists')); -router.use('/nginx/certificates', require('./nginx/certificates')); - -/** - * API 404 for all other routes - * - * ALL /api/* - */ -router.all(/(.+)/, function (req, res, next) { - req.params.page = req.params['0']; - next(new error.ItemNotFoundError(req.params.page)); -}); - -module.exports = router; diff --git a/backend/routes/api/nginx/access_lists.js b/backend/routes/api/nginx/access_lists.js deleted file mode 100644 index d55c3ae1..00000000 --- a/backend/routes/api/nginx/access_lists.js +++ /dev/null @@ -1,148 +0,0 @@ -const express = require('express'); -const validator = require('../../../lib/validator'); -const jwtdecode = require('../../../lib/express/jwt-decode'); -const internalAccessList = require('../../../internal/access-list'); -const apiValidator = require('../../../lib/validator/api'); - -let router = express.Router({ - caseSensitive: true, - strict: true, - mergeParams: true -}); - -/** - * /api/nginx/access-lists - */ -router - .route('/') - .options((req, res) => { - res.sendStatus(204); - }) - .all(jwtdecode()) - - /** - * GET /api/nginx/access-lists - * - * Retrieve all access-lists - */ - .get((req, res, next) => { - validator({ - additionalProperties: false, - properties: { - expand: { - $ref: 'definitions#/definitions/expand' - }, - query: { - $ref: 'definitions#/definitions/query' - } - } - }, { - expand: (typeof req.query.expand === 'string' ? req.query.expand.split(',') : null), - query: (typeof req.query.query === 'string' ? req.query.query : null) - }) - .then((data) => { - return internalAccessList.getAll(res.locals.access, data.expand, data.query); - }) - .then((rows) => { - res.status(200) - .send(rows); - }) - .catch(next); - }) - - /** - * POST /api/nginx/access-lists - * - * Create a new access-list - */ - .post((req, res, next) => { - apiValidator({$ref: 'endpoints/access-lists#/links/1/schema'}, req.body) - .then((payload) => { - return internalAccessList.create(res.locals.access, payload); - }) - .then((result) => { - res.status(201) - .send(result); - }) - .catch(next); - }); - -/** - * Specific access-list - * - * /api/nginx/access-lists/123 - */ -router - .route('/:list_id') - .options((req, res) => { - res.sendStatus(204); - }) - .all(jwtdecode()) - - /** - * GET /api/nginx/access-lists/123 - * - * Retrieve a specific access-list - */ - .get((req, res, next) => { - validator({ - required: ['list_id'], - additionalProperties: false, - properties: { - list_id: { - $ref: 'definitions#/definitions/id' - }, - expand: { - $ref: 'definitions#/definitions/expand' - } - } - }, { - list_id: req.params.list_id, - expand: (typeof req.query.expand === 'string' ? req.query.expand.split(',') : null) - }) - .then((data) => { - return internalAccessList.get(res.locals.access, { - id: parseInt(data.list_id, 10), - expand: data.expand - }); - }) - .then((row) => { - res.status(200) - .send(row); - }) - .catch(next); - }) - - /** - * PUT /api/nginx/access-lists/123 - * - * Update and existing access-list - */ - .put((req, res, next) => { - apiValidator({$ref: 'endpoints/access-lists#/links/2/schema'}, req.body) - .then((payload) => { - payload.id = parseInt(req.params.list_id, 10); - return internalAccessList.update(res.locals.access, payload); - }) - .then((result) => { - res.status(200) - .send(result); - }) - .catch(next); - }) - - /** - * DELETE /api/nginx/access-lists/123 - * - * Delete and existing access-list - */ - .delete((req, res, next) => { - internalAccessList.delete(res.locals.access, {id: parseInt(req.params.list_id, 10)}) - .then((result) => { - res.status(200) - .send(result); - }) - .catch(next); - }); - -module.exports = router; diff --git a/backend/routes/api/nginx/certificates.js b/backend/routes/api/nginx/certificates.js deleted file mode 100644 index 553a0bba..00000000 --- a/backend/routes/api/nginx/certificates.js +++ /dev/null @@ -1,245 +0,0 @@ -const express = require('express'); -const validator = require('../../../lib/validator'); -const jwtdecode = require('../../../lib/express/jwt-decode'); -const internalCertificate = require('../../../internal/certificate'); -const apiValidator = require('../../../lib/validator/api'); - -let router = express.Router({ - caseSensitive: true, - strict: true, - mergeParams: true -}); - -/** - * /api/nginx/certificates - */ -router - .route('/') - .options((req, res) => { - res.sendStatus(204); - }) - .all(jwtdecode()) - - /** - * GET /api/nginx/certificates - * - * Retrieve all certificates - */ - .get((req, res, next) => { - validator({ - additionalProperties: false, - properties: { - expand: { - $ref: 'definitions#/definitions/expand' - }, - query: { - $ref: 'definitions#/definitions/query' - } - } - }, { - expand: (typeof req.query.expand === 'string' ? req.query.expand.split(',') : null), - query: (typeof req.query.query === 'string' ? req.query.query : null) - }) - .then((data) => { - return internalCertificate.getAll(res.locals.access, data.expand, data.query); - }) - .then((rows) => { - res.status(200) - .send(rows); - }) - .catch(next); - }) - - /** - * POST /api/nginx/certificates - * - * Create a new certificate - */ - .post((req, res, next) => { - apiValidator({$ref: 'endpoints/certificates#/links/1/schema'}, req.body) - .then((payload) => { - req.setTimeout(900000); // 15 minutes timeout - return internalCertificate.create(res.locals.access, payload); - }) - .then((result) => { - res.status(201) - .send(result); - }) - .catch(next); - }); - -/** - * Specific certificate - * - * /api/nginx/certificates/123 - */ -router - .route('/:certificate_id') - .options((req, res) => { - res.sendStatus(204); - }) - .all(jwtdecode()) - - /** - * GET /api/nginx/certificates/123 - * - * Retrieve a specific certificate - */ - .get((req, res, next) => { - validator({ - required: ['certificate_id'], - additionalProperties: false, - properties: { - certificate_id: { - $ref: 'definitions#/definitions/id' - }, - expand: { - $ref: 'definitions#/definitions/expand' - } - } - }, { - certificate_id: req.params.certificate_id, - expand: (typeof req.query.expand === 'string' ? req.query.expand.split(',') : null) - }) - .then((data) => { - return internalCertificate.get(res.locals.access, { - id: parseInt(data.certificate_id, 10), - expand: data.expand - }); - }) - .then((row) => { - res.status(200) - .send(row); - }) - .catch(next); - }) - - /** - * PUT /api/nginx/certificates/123 - * - * Update and existing certificate - */ - .put((req, res, next) => { - apiValidator({$ref: 'endpoints/certificates#/links/2/schema'}, req.body) - .then((payload) => { - payload.id = parseInt(req.params.certificate_id, 10); - return internalCertificate.update(res.locals.access, payload); - }) - .then((result) => { - res.status(200) - .send(result); - }) - .catch(next); - }) - - /** - * DELETE /api/nginx/certificates/123 - * - * Update and existing certificate - */ - .delete((req, res, next) => { - internalCertificate.delete(res.locals.access, {id: parseInt(req.params.certificate_id, 10)}) - .then((result) => { - res.status(200) - .send(result); - }) - .catch(next); - }); - -/** - * Upload Certs - * - * /api/nginx/certificates/123/upload - */ -router - .route('/:certificate_id/upload') - .options((req, res) => { - res.sendStatus(204); - }) - .all(jwtdecode()) - - /** - * POST /api/nginx/certificates/123/upload - * - * Upload certificates - */ - .post((req, res, next) => { - if (!req.files) { - res.status(400) - .send({error: 'No files were uploaded'}); - } else { - internalCertificate.upload(res.locals.access, { - id: parseInt(req.params.certificate_id, 10), - files: req.files - }) - .then((result) => { - res.status(200) - .send(result); - }) - .catch(next); - } - }); - -/** - * Renew LE Certs - * - * /api/nginx/certificates/123/renew - */ -router - .route('/:certificate_id/renew') - .options((req, res) => { - res.sendStatus(204); - }) - .all(jwtdecode()) - - /** - * POST /api/nginx/certificates/123/renew - * - * Renew certificate - */ - .post((req, res, next) => { - req.setTimeout(900000); // 15 minutes timeout - internalCertificate.renew(res.locals.access, { - id: parseInt(req.params.certificate_id, 10) - }) - .then((result) => { - res.status(200) - .send(result); - }) - .catch(next); - }); - -/** - * Validate Certs before saving - * - * /api/nginx/certificates/validate - */ -router - .route('/validate') - .options((req, res) => { - res.sendStatus(204); - }) - .all(jwtdecode()) - - /** - * POST /api/nginx/certificates/validate - * - * Validate certificates - */ - .post((req, res, next) => { - if (!req.files) { - res.status(400) - .send({error: 'No files were uploaded'}); - } else { - internalCertificate.validate({ - files: req.files - }) - .then((result) => { - res.status(200) - .send(result); - }) - .catch(next); - } - }); - -module.exports = router; diff --git a/backend/routes/api/nginx/dead_hosts.js b/backend/routes/api/nginx/dead_hosts.js deleted file mode 100644 index 08b58f2d..00000000 --- a/backend/routes/api/nginx/dead_hosts.js +++ /dev/null @@ -1,196 +0,0 @@ -const express = require('express'); -const validator = require('../../../lib/validator'); -const jwtdecode = require('../../../lib/express/jwt-decode'); -const internalDeadHost = require('../../../internal/dead-host'); -const apiValidator = require('../../../lib/validator/api'); - -let router = express.Router({ - caseSensitive: true, - strict: true, - mergeParams: true -}); - -/** - * /api/nginx/dead-hosts - */ -router - .route('/') - .options((req, res) => { - res.sendStatus(204); - }) - .all(jwtdecode()) - - /** - * GET /api/nginx/dead-hosts - * - * Retrieve all dead-hosts - */ - .get((req, res, next) => { - validator({ - additionalProperties: false, - properties: { - expand: { - $ref: 'definitions#/definitions/expand' - }, - query: { - $ref: 'definitions#/definitions/query' - } - } - }, { - expand: (typeof req.query.expand === 'string' ? req.query.expand.split(',') : null), - query: (typeof req.query.query === 'string' ? req.query.query : null) - }) - .then((data) => { - return internalDeadHost.getAll(res.locals.access, data.expand, data.query); - }) - .then((rows) => { - res.status(200) - .send(rows); - }) - .catch(next); - }) - - /** - * POST /api/nginx/dead-hosts - * - * Create a new dead-host - */ - .post((req, res, next) => { - apiValidator({$ref: 'endpoints/dead-hosts#/links/1/schema'}, req.body) - .then((payload) => { - return internalDeadHost.create(res.locals.access, payload); - }) - .then((result) => { - res.status(201) - .send(result); - }) - .catch(next); - }); - -/** - * Specific dead-host - * - * /api/nginx/dead-hosts/123 - */ -router - .route('/:host_id') - .options((req, res) => { - res.sendStatus(204); - }) - .all(jwtdecode()) - - /** - * GET /api/nginx/dead-hosts/123 - * - * Retrieve a specific dead-host - */ - .get((req, res, next) => { - validator({ - required: ['host_id'], - additionalProperties: false, - properties: { - host_id: { - $ref: 'definitions#/definitions/id' - }, - expand: { - $ref: 'definitions#/definitions/expand' - } - } - }, { - host_id: req.params.host_id, - expand: (typeof req.query.expand === 'string' ? req.query.expand.split(',') : null) - }) - .then((data) => { - return internalDeadHost.get(res.locals.access, { - id: parseInt(data.host_id, 10), - expand: data.expand - }); - }) - .then((row) => { - res.status(200) - .send(row); - }) - .catch(next); - }) - - /** - * PUT /api/nginx/dead-hosts/123 - * - * Update and existing dead-host - */ - .put((req, res, next) => { - apiValidator({$ref: 'endpoints/dead-hosts#/links/2/schema'}, req.body) - .then((payload) => { - payload.id = parseInt(req.params.host_id, 10); - return internalDeadHost.update(res.locals.access, payload); - }) - .then((result) => { - res.status(200) - .send(result); - }) - .catch(next); - }) - - /** - * DELETE /api/nginx/dead-hosts/123 - * - * Update and existing dead-host - */ - .delete((req, res, next) => { - internalDeadHost.delete(res.locals.access, {id: parseInt(req.params.host_id, 10)}) - .then((result) => { - res.status(200) - .send(result); - }) - .catch(next); - }); - -/** - * Enable dead-host - * - * /api/nginx/dead-hosts/123/enable - */ -router - .route('/:host_id/enable') - .options((req, res) => { - res.sendStatus(204); - }) - .all(jwtdecode()) - - /** - * POST /api/nginx/dead-hosts/123/enable - */ - .post((req, res, next) => { - internalDeadHost.enable(res.locals.access, {id: parseInt(req.params.host_id, 10)}) - .then((result) => { - res.status(200) - .send(result); - }) - .catch(next); - }); - -/** - * Disable dead-host - * - * /api/nginx/dead-hosts/123/disable - */ -router - .route('/:host_id/disable') - .options((req, res) => { - res.sendStatus(204); - }) - .all(jwtdecode()) - - /** - * POST /api/nginx/dead-hosts/123/disable - */ - .post((req, res, next) => { - internalDeadHost.disable(res.locals.access, {id: parseInt(req.params.host_id, 10)}) - .then((result) => { - res.status(200) - .send(result); - }) - .catch(next); - }); - -module.exports = router; diff --git a/backend/routes/api/nginx/proxy_hosts.js b/backend/routes/api/nginx/proxy_hosts.js deleted file mode 100644 index 6f933c3d..00000000 --- a/backend/routes/api/nginx/proxy_hosts.js +++ /dev/null @@ -1,196 +0,0 @@ -const express = require('express'); -const validator = require('../../../lib/validator'); -const jwtdecode = require('../../../lib/express/jwt-decode'); -const internalProxyHost = require('../../../internal/proxy-host'); -const apiValidator = require('../../../lib/validator/api'); - -let router = express.Router({ - caseSensitive: true, - strict: true, - mergeParams: true -}); - -/** - * /api/nginx/proxy-hosts - */ -router - .route('/') - .options((req, res) => { - res.sendStatus(204); - }) - .all(jwtdecode()) - - /** - * GET /api/nginx/proxy-hosts - * - * Retrieve all proxy-hosts - */ - .get((req, res, next) => { - validator({ - additionalProperties: false, - properties: { - expand: { - $ref: 'definitions#/definitions/expand' - }, - query: { - $ref: 'definitions#/definitions/query' - } - } - }, { - expand: (typeof req.query.expand === 'string' ? req.query.expand.split(',') : null), - query: (typeof req.query.query === 'string' ? req.query.query : null) - }) - .then((data) => { - return internalProxyHost.getAll(res.locals.access, data.expand, data.query); - }) - .then((rows) => { - res.status(200) - .send(rows); - }) - .catch(next); - }) - - /** - * POST /api/nginx/proxy-hosts - * - * Create a new proxy-host - */ - .post((req, res, next) => { - apiValidator({$ref: 'endpoints/proxy-hosts#/links/1/schema'}, req.body) - .then((payload) => { - return internalProxyHost.create(res.locals.access, payload); - }) - .then((result) => { - res.status(201) - .send(result); - }) - .catch(next); - }); - -/** - * Specific proxy-host - * - * /api/nginx/proxy-hosts/123 - */ -router - .route('/:host_id') - .options((req, res) => { - res.sendStatus(204); - }) - .all(jwtdecode()) - - /** - * GET /api/nginx/proxy-hosts/123 - * - * Retrieve a specific proxy-host - */ - .get((req, res, next) => { - validator({ - required: ['host_id'], - additionalProperties: false, - properties: { - host_id: { - $ref: 'definitions#/definitions/id' - }, - expand: { - $ref: 'definitions#/definitions/expand' - } - } - }, { - host_id: req.params.host_id, - expand: (typeof req.query.expand === 'string' ? req.query.expand.split(',') : null) - }) - .then((data) => { - return internalProxyHost.get(res.locals.access, { - id: parseInt(data.host_id, 10), - expand: data.expand - }); - }) - .then((row) => { - res.status(200) - .send(row); - }) - .catch(next); - }) - - /** - * PUT /api/nginx/proxy-hosts/123 - * - * Update and existing proxy-host - */ - .put((req, res, next) => { - apiValidator({$ref: 'endpoints/proxy-hosts#/links/2/schema'}, req.body) - .then((payload) => { - payload.id = parseInt(req.params.host_id, 10); - return internalProxyHost.update(res.locals.access, payload); - }) - .then((result) => { - res.status(200) - .send(result); - }) - .catch(next); - }) - - /** - * DELETE /api/nginx/proxy-hosts/123 - * - * Update and existing proxy-host - */ - .delete((req, res, next) => { - internalProxyHost.delete(res.locals.access, {id: parseInt(req.params.host_id, 10)}) - .then((result) => { - res.status(200) - .send(result); - }) - .catch(next); - }); - -/** - * Enable proxy-host - * - * /api/nginx/proxy-hosts/123/enable - */ -router - .route('/:host_id/enable') - .options((req, res) => { - res.sendStatus(204); - }) - .all(jwtdecode()) - - /** - * POST /api/nginx/proxy-hosts/123/enable - */ - .post((req, res, next) => { - internalProxyHost.enable(res.locals.access, {id: parseInt(req.params.host_id, 10)}) - .then((result) => { - res.status(200) - .send(result); - }) - .catch(next); - }); - -/** - * Disable proxy-host - * - * /api/nginx/proxy-hosts/123/disable - */ -router - .route('/:host_id/disable') - .options((req, res) => { - res.sendStatus(204); - }) - .all(jwtdecode()) - - /** - * POST /api/nginx/proxy-hosts/123/disable - */ - .post((req, res, next) => { - internalProxyHost.disable(res.locals.access, {id: parseInt(req.params.host_id, 10)}) - .then((result) => { - res.status(200) - .send(result); - }) - .catch(next); - }); - -module.exports = router; diff --git a/backend/routes/api/nginx/redirection_hosts.js b/backend/routes/api/nginx/redirection_hosts.js deleted file mode 100644 index 4d44c112..00000000 --- a/backend/routes/api/nginx/redirection_hosts.js +++ /dev/null @@ -1,196 +0,0 @@ -const express = require('express'); -const validator = require('../../../lib/validator'); -const jwtdecode = require('../../../lib/express/jwt-decode'); -const internalRedirectionHost = require('../../../internal/redirection-host'); -const apiValidator = require('../../../lib/validator/api'); - -let router = express.Router({ - caseSensitive: true, - strict: true, - mergeParams: true -}); - -/** - * /api/nginx/redirection-hosts - */ -router - .route('/') - .options((req, res) => { - res.sendStatus(204); - }) - .all(jwtdecode()) - - /** - * GET /api/nginx/redirection-hosts - * - * Retrieve all redirection-hosts - */ - .get((req, res, next) => { - validator({ - additionalProperties: false, - properties: { - expand: { - $ref: 'definitions#/definitions/expand' - }, - query: { - $ref: 'definitions#/definitions/query' - } - } - }, { - expand: (typeof req.query.expand === 'string' ? req.query.expand.split(',') : null), - query: (typeof req.query.query === 'string' ? req.query.query : null) - }) - .then((data) => { - return internalRedirectionHost.getAll(res.locals.access, data.expand, data.query); - }) - .then((rows) => { - res.status(200) - .send(rows); - }) - .catch(next); - }) - - /** - * POST /api/nginx/redirection-hosts - * - * Create a new redirection-host - */ - .post((req, res, next) => { - apiValidator({$ref: 'endpoints/redirection-hosts#/links/1/schema'}, req.body) - .then((payload) => { - return internalRedirectionHost.create(res.locals.access, payload); - }) - .then((result) => { - res.status(201) - .send(result); - }) - .catch(next); - }); - -/** - * Specific redirection-host - * - * /api/nginx/redirection-hosts/123 - */ -router - .route('/:host_id') - .options((req, res) => { - res.sendStatus(204); - }) - .all(jwtdecode()) - - /** - * GET /api/nginx/redirection-hosts/123 - * - * Retrieve a specific redirection-host - */ - .get((req, res, next) => { - validator({ - required: ['host_id'], - additionalProperties: false, - properties: { - host_id: { - $ref: 'definitions#/definitions/id' - }, - expand: { - $ref: 'definitions#/definitions/expand' - } - } - }, { - host_id: req.params.host_id, - expand: (typeof req.query.expand === 'string' ? req.query.expand.split(',') : null) - }) - .then((data) => { - return internalRedirectionHost.get(res.locals.access, { - id: parseInt(data.host_id, 10), - expand: data.expand - }); - }) - .then((row) => { - res.status(200) - .send(row); - }) - .catch(next); - }) - - /** - * PUT /api/nginx/redirection-hosts/123 - * - * Update and existing redirection-host - */ - .put((req, res, next) => { - apiValidator({$ref: 'endpoints/redirection-hosts#/links/2/schema'}, req.body) - .then((payload) => { - payload.id = parseInt(req.params.host_id, 10); - return internalRedirectionHost.update(res.locals.access, payload); - }) - .then((result) => { - res.status(200) - .send(result); - }) - .catch(next); - }) - - /** - * DELETE /api/nginx/redirection-hosts/123 - * - * Update and existing redirection-host - */ - .delete((req, res, next) => { - internalRedirectionHost.delete(res.locals.access, {id: parseInt(req.params.host_id, 10)}) - .then((result) => { - res.status(200) - .send(result); - }) - .catch(next); - }); - -/** - * Enable redirection-host - * - * /api/nginx/redirection-hosts/123/enable - */ -router - .route('/:host_id/enable') - .options((req, res) => { - res.sendStatus(204); - }) - .all(jwtdecode()) - - /** - * POST /api/nginx/redirection-hosts/123/enable - */ - .post((req, res, next) => { - internalRedirectionHost.enable(res.locals.access, {id: parseInt(req.params.host_id, 10)}) - .then((result) => { - res.status(200) - .send(result); - }) - .catch(next); - }); - -/** - * Disable redirection-host - * - * /api/nginx/redirection-hosts/123/disable - */ -router - .route('/:host_id/disable') - .options((req, res) => { - res.sendStatus(204); - }) - .all(jwtdecode()) - - /** - * POST /api/nginx/redirection-hosts/123/disable - */ - .post((req, res, next) => { - internalRedirectionHost.disable(res.locals.access, {id: parseInt(req.params.host_id, 10)}) - .then((result) => { - res.status(200) - .send(result); - }) - .catch(next); - }); - -module.exports = router; diff --git a/backend/routes/api/nginx/streams.js b/backend/routes/api/nginx/streams.js deleted file mode 100644 index 5e3fc28f..00000000 --- a/backend/routes/api/nginx/streams.js +++ /dev/null @@ -1,196 +0,0 @@ -const express = require('express'); -const validator = require('../../../lib/validator'); -const jwtdecode = require('../../../lib/express/jwt-decode'); -const internalStream = require('../../../internal/stream'); -const apiValidator = require('../../../lib/validator/api'); - -let router = express.Router({ - caseSensitive: true, - strict: true, - mergeParams: true -}); - -/** - * /api/nginx/streams - */ -router - .route('/') - .options((req, res) => { - res.sendStatus(204); - }) - .all(jwtdecode()) // preferred so it doesn't apply to nonexistent routes - - /** - * GET /api/nginx/streams - * - * Retrieve all streams - */ - .get((req, res, next) => { - validator({ - additionalProperties: false, - properties: { - expand: { - $ref: 'definitions#/definitions/expand' - }, - query: { - $ref: 'definitions#/definitions/query' - } - } - }, { - expand: (typeof req.query.expand === 'string' ? req.query.expand.split(',') : null), - query: (typeof req.query.query === 'string' ? req.query.query : null) - }) - .then((data) => { - return internalStream.getAll(res.locals.access, data.expand, data.query); - }) - .then((rows) => { - res.status(200) - .send(rows); - }) - .catch(next); - }) - - /** - * POST /api/nginx/streams - * - * Create a new stream - */ - .post((req, res, next) => { - apiValidator({$ref: 'endpoints/streams#/links/1/schema'}, req.body) - .then((payload) => { - return internalStream.create(res.locals.access, payload); - }) - .then((result) => { - res.status(201) - .send(result); - }) - .catch(next); - }); - -/** - * Specific stream - * - * /api/nginx/streams/123 - */ -router - .route('/:stream_id') - .options((req, res) => { - res.sendStatus(204); - }) - .all(jwtdecode()) // preferred so it doesn't apply to nonexistent routes - - /** - * GET /api/nginx/streams/123 - * - * Retrieve a specific stream - */ - .get((req, res, next) => { - validator({ - required: ['stream_id'], - additionalProperties: false, - properties: { - stream_id: { - $ref: 'definitions#/definitions/id' - }, - expand: { - $ref: 'definitions#/definitions/expand' - } - } - }, { - stream_id: req.params.stream_id, - expand: (typeof req.query.expand === 'string' ? req.query.expand.split(',') : null) - }) - .then((data) => { - return internalStream.get(res.locals.access, { - id: parseInt(data.stream_id, 10), - expand: data.expand - }); - }) - .then((row) => { - res.status(200) - .send(row); - }) - .catch(next); - }) - - /** - * PUT /api/nginx/streams/123 - * - * Update and existing stream - */ - .put((req, res, next) => { - apiValidator({$ref: 'endpoints/streams#/links/2/schema'}, req.body) - .then((payload) => { - payload.id = parseInt(req.params.stream_id, 10); - return internalStream.update(res.locals.access, payload); - }) - .then((result) => { - res.status(200) - .send(result); - }) - .catch(next); - }) - - /** - * DELETE /api/nginx/streams/123 - * - * Update and existing stream - */ - .delete((req, res, next) => { - internalStream.delete(res.locals.access, {id: parseInt(req.params.stream_id, 10)}) - .then((result) => { - res.status(200) - .send(result); - }) - .catch(next); - }); - -/** - * Enable stream - * - * /api/nginx/streams/123/enable - */ -router - .route('/:host_id/enable') - .options((req, res) => { - res.sendStatus(204); - }) - .all(jwtdecode()) - - /** - * POST /api/nginx/streams/123/enable - */ - .post((req, res, next) => { - internalStream.enable(res.locals.access, {id: parseInt(req.params.host_id, 10)}) - .then((result) => { - res.status(200) - .send(result); - }) - .catch(next); - }); - -/** - * Disable stream - * - * /api/nginx/streams/123/disable - */ -router - .route('/:host_id/disable') - .options((req, res) => { - res.sendStatus(204); - }) - .all(jwtdecode()) - - /** - * POST /api/nginx/streams/123/disable - */ - .post((req, res, next) => { - internalStream.disable(res.locals.access, {id: parseInt(req.params.host_id, 10)}) - .then((result) => { - res.status(200) - .send(result); - }) - .catch(next); - }); - -module.exports = router; diff --git a/backend/routes/api/reports.js b/backend/routes/api/reports.js deleted file mode 100644 index 9e2c98c8..00000000 --- a/backend/routes/api/reports.js +++ /dev/null @@ -1,29 +0,0 @@ -const express = require('express'); -const jwtdecode = require('../../lib/express/jwt-decode'); -const internalReport = require('../../internal/report'); - -let router = express.Router({ - caseSensitive: true, - strict: true, - mergeParams: true -}); - -router - .route('/hosts') - .options((req, res) => { - res.sendStatus(204); - }) - - /** - * GET /reports/hosts - */ - .get(jwtdecode(), (req, res, next) => { - internalReport.getHostsReport(res.locals.access) - .then((data) => { - res.status(200) - .send(data); - }) - .catch(next); - }); - -module.exports = router; diff --git a/backend/routes/api/schema.js b/backend/routes/api/schema.js deleted file mode 100644 index fc6bd5bd..00000000 --- a/backend/routes/api/schema.js +++ /dev/null @@ -1,36 +0,0 @@ -const express = require('express'); -const swaggerJSON = require('../../doc/api.swagger.json'); -const PACKAGE = require('../../package.json'); - -let router = express.Router({ - caseSensitive: true, - strict: true, - mergeParams: true -}); - -router - .route('/') - .options((req, res) => { - res.sendStatus(204); - }) - - /** - * GET /schema - */ - .get((req, res/*, next*/) => { - let proto = req.protocol; - if (typeof req.headers['x-forwarded-proto'] !== 'undefined' && req.headers['x-forwarded-proto']) { - proto = req.headers['x-forwarded-proto']; - } - - let origin = proto + '://' + req.hostname; - if (typeof req.headers.origin !== 'undefined' && req.headers.origin) { - origin = req.headers.origin; - } - - swaggerJSON.info.version = PACKAGE.version; - swaggerJSON.servers[0].url = origin + '/api'; - res.status(200).send(swaggerJSON); - }); - -module.exports = router; diff --git a/backend/routes/api/settings.js b/backend/routes/api/settings.js deleted file mode 100644 index d08b2bf5..00000000 --- a/backend/routes/api/settings.js +++ /dev/null @@ -1,96 +0,0 @@ -const express = require('express'); -const validator = require('../../lib/validator'); -const jwtdecode = require('../../lib/express/jwt-decode'); -const internalSetting = require('../../internal/setting'); -const apiValidator = require('../../lib/validator/api'); - -let router = express.Router({ - caseSensitive: true, - strict: true, - mergeParams: true -}); - -/** - * /api/settings - */ -router - .route('/') - .options((req, res) => { - res.sendStatus(204); - }) - .all(jwtdecode()) - - /** - * GET /api/settings - * - * Retrieve all settings - */ - .get((req, res, next) => { - internalSetting.getAll(res.locals.access) - .then((rows) => { - res.status(200) - .send(rows); - }) - .catch(next); - }); - -/** - * Specific setting - * - * /api/settings/something - */ -router - .route('/:setting_id') - .options((req, res) => { - res.sendStatus(204); - }) - .all(jwtdecode()) - - /** - * GET /settings/something - * - * Retrieve a specific setting - */ - .get((req, res, next) => { - validator({ - required: ['setting_id'], - additionalProperties: false, - properties: { - setting_id: { - $ref: 'definitions#/definitions/setting_id' - } - } - }, { - setting_id: req.params.setting_id - }) - .then((data) => { - return internalSetting.get(res.locals.access, { - id: data.setting_id - }); - }) - .then((row) => { - res.status(200) - .send(row); - }) - .catch(next); - }) - - /** - * PUT /api/settings/something - * - * Update and existing setting - */ - .put((req, res, next) => { - apiValidator({$ref: 'endpoints/settings#/links/1/schema'}, req.body) - .then((payload) => { - payload.id = req.params.setting_id; - return internalSetting.update(res.locals.access, payload); - }) - .then((result) => { - res.status(200) - .send(result); - }) - .catch(next); - }); - -module.exports = router; diff --git a/backend/routes/api/tokens.js b/backend/routes/api/tokens.js deleted file mode 100644 index a21f998a..00000000 --- a/backend/routes/api/tokens.js +++ /dev/null @@ -1,54 +0,0 @@ -const express = require('express'); -const jwtdecode = require('../../lib/express/jwt-decode'); -const internalToken = require('../../internal/token'); -const apiValidator = require('../../lib/validator/api'); - -let router = express.Router({ - caseSensitive: true, - strict: true, - mergeParams: true -}); - -router - .route('/') - .options((req, res) => { - res.sendStatus(204); - }) - - /** - * GET /tokens - * - * Get a new Token, given they already have a token they want to refresh - * We also piggy back on to this method, allowing admins to get tokens - * for services like Job board and Worker. - */ - .get(jwtdecode(), (req, res, next) => { - internalToken.getFreshToken(res.locals.access, { - expiry: (typeof req.query.expiry !== 'undefined' ? req.query.expiry : null), - scope: (typeof req.query.scope !== 'undefined' ? req.query.scope : null) - }) - .then((data) => { - res.status(200) - .send(data); - }) - .catch(next); - }) - - /** - * POST /tokens - * - * Create a new Token - */ - .post((req, res, next) => { - apiValidator({$ref: 'endpoints/tokens#/links/0/schema'}, req.body) - .then((payload) => { - return internalToken.getTokenFromEmail(payload); - }) - .then((data) => { - res.status(200) - .send(data); - }) - .catch(next); - }); - -module.exports = router; diff --git a/backend/routes/api/users.js b/backend/routes/api/users.js deleted file mode 100644 index 1c6bd0ad..00000000 --- a/backend/routes/api/users.js +++ /dev/null @@ -1,239 +0,0 @@ -const express = require('express'); -const validator = require('../../lib/validator'); -const jwtdecode = require('../../lib/express/jwt-decode'); -const userIdFromMe = require('../../lib/express/user-id-from-me'); -const internalUser = require('../../internal/user'); -const apiValidator = require('../../lib/validator/api'); - -let router = express.Router({ - caseSensitive: true, - strict: true, - mergeParams: true -}); - -/** - * /api/users - */ -router - .route('/') - .options((req, res) => { - res.sendStatus(204); - }) - .all(jwtdecode()) - - /** - * GET /api/users - * - * Retrieve all users - */ - .get((req, res, next) => { - validator({ - additionalProperties: false, - properties: { - expand: { - $ref: 'definitions#/definitions/expand' - }, - query: { - $ref: 'definitions#/definitions/query' - } - } - }, { - expand: (typeof req.query.expand === 'string' ? req.query.expand.split(',') : null), - query: (typeof req.query.query === 'string' ? req.query.query : null) - }) - .then((data) => { - return internalUser.getAll(res.locals.access, data.expand, data.query); - }) - .then((users) => { - res.status(200) - .send(users); - }) - .catch(next); - }) - - /** - * POST /api/users - * - * Create a new User - */ - .post((req, res, next) => { - apiValidator({$ref: 'endpoints/users#/links/1/schema'}, req.body) - .then((payload) => { - return internalUser.create(res.locals.access, payload); - }) - .then((result) => { - res.status(201) - .send(result); - }) - .catch(next); - }); - -/** - * Specific user - * - * /api/users/123 - */ -router - .route('/:user_id') - .options((req, res) => { - res.sendStatus(204); - }) - .all(jwtdecode()) - .all(userIdFromMe) - - /** - * GET /users/123 or /users/me - * - * Retrieve a specific user - */ - .get((req, res, next) => { - validator({ - required: ['user_id'], - additionalProperties: false, - properties: { - user_id: { - $ref: 'definitions#/definitions/id' - }, - expand: { - $ref: 'definitions#/definitions/expand' - } - } - }, { - user_id: req.params.user_id, - expand: (typeof req.query.expand === 'string' ? req.query.expand.split(',') : null) - }) - .then((data) => { - return internalUser.get(res.locals.access, { - id: data.user_id, - expand: data.expand, - omit: internalUser.getUserOmisionsByAccess(res.locals.access, data.user_id) - }); - }) - .then((user) => { - res.status(200) - .send(user); - }) - .catch(next); - }) - - /** - * PUT /api/users/123 - * - * Update and existing user - */ - .put((req, res, next) => { - apiValidator({$ref: 'endpoints/users#/links/2/schema'}, req.body) - .then((payload) => { - payload.id = req.params.user_id; - return internalUser.update(res.locals.access, payload); - }) - .then((result) => { - res.status(200) - .send(result); - }) - .catch(next); - }) - - /** - * DELETE /api/users/123 - * - * Update and existing user - */ - .delete((req, res, next) => { - internalUser.delete(res.locals.access, {id: req.params.user_id}) - .then((result) => { - res.status(200) - .send(result); - }) - .catch(next); - }); - -/** - * Specific user auth - * - * /api/users/123/auth - */ -router - .route('/:user_id/auth') - .options((req, res) => { - res.sendStatus(204); - }) - .all(jwtdecode()) - .all(userIdFromMe) - - /** - * PUT /api/users/123/auth - * - * Update password for a user - */ - .put((req, res, next) => { - apiValidator({$ref: 'endpoints/users#/links/4/schema'}, req.body) - .then((payload) => { - payload.id = req.params.user_id; - return internalUser.setPassword(res.locals.access, payload); - }) - .then((result) => { - res.status(201) - .send(result); - }) - .catch(next); - }); - -/** - * Specific user permissions - * - * /api/users/123/permissions - */ -router - .route('/:user_id/permissions') - .options((req, res) => { - res.sendStatus(204); - }) - .all(jwtdecode()) - .all(userIdFromMe) - - /** - * PUT /api/users/123/permissions - * - * Set some or all permissions for a user - */ - .put((req, res, next) => { - apiValidator({$ref: 'endpoints/users#/links/5/schema'}, req.body) - .then((payload) => { - payload.id = req.params.user_id; - return internalUser.setPermissions(res.locals.access, payload); - }) - .then((result) => { - res.status(201) - .send(result); - }) - .catch(next); - }); - -/** - * Specific user login as - * - * /api/users/123/login - */ -router - .route('/:user_id/login') - .options((req, res) => { - res.sendStatus(204); - }) - .all(jwtdecode()) - - /** - * POST /api/users/123/login - * - * Log in as a user - */ - .post((req, res, next) => { - internalUser.loginAs(res.locals.access, {id: parseInt(req.params.user_id, 10)}) - .then((result) => { - res.status(201) - .send(result); - }) - .catch(next); - }); - -module.exports = router; diff --git a/backend/schema/definitions.json b/backend/schema/definitions.json deleted file mode 100644 index 9895b87e..00000000 --- a/backend/schema/definitions.json +++ /dev/null @@ -1,240 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "definitions", - "definitions": { - "id": { - "description": "Unique identifier", - "example": 123456, - "readOnly": true, - "type": "integer", - "minimum": 1 - }, - "setting_id": { - "description": "Unique identifier for a Setting", - "example": "default-site", - "readOnly": true, - "type": "string", - "minLength": 2 - }, - "token": { - "type": "string", - "minLength": 10 - }, - "expand": { - "anyOf": [ - { - "type": "null" - }, - { - "type": "array", - "minItems": 1, - "items": { - "type": "string" - } - } - ] - }, - "sort": { - "type": "array", - "minItems": 1, - "items": { - "type": "object", - "required": [ - "field", - "dir" - ], - "additionalProperties": false, - "properties": { - "field": { - "type": "string" - }, - "dir": { - "type": "string", - "pattern": "^(asc|desc)$" - } - } - } - }, - "query": { - "anyOf": [ - { - "type": "null" - }, - { - "type": "string", - "minLength": 1, - "maxLength": 255 - } - ] - }, - "criteria": { - "anyOf": [ - { - "type": "null" - }, - { - "type": "object" - } - ] - }, - "fields": { - "anyOf": [ - { - "type": "null" - }, - { - "type": "array", - "minItems": 1, - "items": { - "type": "string" - } - } - ] - }, - "omit": { - "anyOf": [ - { - "type": "null" - }, - { - "type": "array", - "minItems": 1, - "items": { - "type": "string" - } - } - ] - }, - "created_on": { - "description": "Date and time of creation", - "format": "date-time", - "readOnly": true, - "type": "string" - }, - "modified_on": { - "description": "Date and time of last update", - "format": "date-time", - "readOnly": true, - "type": "string" - }, - "user_id": { - "description": "User ID", - "example": 1234, - "type": "integer", - "minimum": 1 - }, - "certificate_id": { - "description": "Certificate ID", - "example": 1234, - "anyOf": [ - { - "type": "integer", - "minimum": 0 - }, - { - "type": "string", - "pattern": "^new$" - } - ] - }, - "access_list_id": { - "description": "Access List ID", - "example": 1234, - "type": "integer", - "minimum": 0 - }, - "name": { - "type": "string", - "minLength": 1, - "maxLength": 255 - }, - "email": { - "description": "Email Address", - "example": "john@example.com", - "format": "email", - "type": "string", - "minLength": 8, - "maxLength": 100 - }, - "password": { - "description": "Password", - "type": "string", - "minLength": 8, - "maxLength": 255 - }, - "domain_name": { - "description": "Domain Name", - "example": "jc21.com", - "type": "string", - "pattern": "^(?:[^.*]+\\.?)+[^.]$" - }, - "domain_names": { - "description": "Domain Names separated by a comma", - "example": "*.jc21.com,blog.jc21.com", - "type": "array", - "maxItems": 15, - "uniqueItems": true, - "items": { - "type": "string", - "pattern": "^(?:\\*\\.)?(?:[^.*]+\\.?)+[^.]$" - } - }, - "http_code": { - "description": "Redirect HTTP Status Code", - "example": 302, - "type": "integer", - "minimum": 300, - "maximum": 308 - }, - "scheme": { - "description": "RFC Protocol", - "example": "HTTPS or $scheme", - "type": "string", - "minLength": 4 - }, - "enabled": { - "description": "Is Enabled", - "example": true, - "type": "boolean" - }, - "ssl_enabled": { - "description": "Is SSL Enabled", - "example": true, - "type": "boolean" - }, - "ssl_forced": { - "description": "Is SSL Forced", - "example": false, - "type": "boolean" - }, - "hsts_enabled": { - "description": "Is HSTS Enabled", - "example": false, - "type": "boolean" - }, - "hsts_subdomains": { - "description": "Is HSTS applicable to all subdomains", - "example": false, - "type": "boolean" - }, - "ssl_provider": { - "type": "string", - "pattern": "^(letsencrypt|other)$" - }, - "http2_support": { - "description": "HTTP2 Protocol Support", - "example": false, - "type": "boolean" - }, - "block_exploits": { - "description": "Should we block common exploits", - "example": true, - "type": "boolean" - }, - "caching_enabled": { - "description": "Should we cache assets", - "example": true, - "type": "boolean" - } - } -} diff --git a/backend/schema/endpoints/access-lists.json b/backend/schema/endpoints/access-lists.json deleted file mode 100644 index 404e3237..00000000 --- a/backend/schema/endpoints/access-lists.json +++ /dev/null @@ -1,236 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "endpoints/access-lists", - "title": "Access Lists", - "description": "Endpoints relating to Access Lists", - "stability": "stable", - "type": "object", - "definitions": { - "id": { - "$ref": "../definitions.json#/definitions/id" - }, - "created_on": { - "$ref": "../definitions.json#/definitions/created_on" - }, - "modified_on": { - "$ref": "../definitions.json#/definitions/modified_on" - }, - "name": { - "type": "string", - "description": "Name of the Access List" - }, - "directive": { - "type": "string", - "enum": ["allow", "deny"] - }, - "address": { - "oneOf": [ - { - "type": "string", - "pattern": "^([0-9]{1,3}\\.){3}[0-9]{1,3}(/([0-9]|[1-2][0-9]|3[0-2]))?$" - }, - { - "type": "string", - "pattern": "^s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*(/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8]))?$" - }, - { - "type": "string", - "pattern": "^all$" - } - ] - }, - "satisfy_any": { - "type": "boolean" - }, - "pass_auth": { - "type": "boolean" - }, - "meta": { - "type": "object" - } - }, - "properties": { - "id": { - "$ref": "#/definitions/id" - }, - "created_on": { - "$ref": "#/definitions/created_on" - }, - "modified_on": { - "$ref": "#/definitions/modified_on" - }, - "name": { - "$ref": "#/definitions/name" - }, - "meta": { - "$ref": "#/definitions/meta" - } - }, - "links": [ - { - "title": "List", - "description": "Returns a list of Access Lists", - "href": "/nginx/access-lists", - "access": "private", - "method": "GET", - "rel": "self", - "http_header": { - "$ref": "../examples.json#/definitions/auth_header" - }, - "targetSchema": { - "type": "array", - "items": { - "$ref": "#/properties" - } - } - }, - { - "title": "Create", - "description": "Creates a new Access List", - "href": "/nginx/access-list", - "access": "private", - "method": "POST", - "rel": "create", - "http_header": { - "$ref": "../examples.json#/definitions/auth_header" - }, - "schema": { - "type": "object", - "additionalProperties": false, - "required": ["name"], - "properties": { - "name": { - "$ref": "#/definitions/name" - }, - "satisfy_any": { - "$ref": "#/definitions/satisfy_any" - }, - "pass_auth": { - "$ref": "#/definitions/pass_auth" - }, - "items": { - "type": "array", - "minItems": 0, - "items": { - "type": "object", - "additionalProperties": false, - "properties": { - "username": { - "type": "string", - "minLength": 1 - }, - "password": { - "type": "string", - "minLength": 1 - } - } - } - }, - "clients": { - "type": "array", - "minItems": 0, - "items": { - "type": "object", - "additionalProperties": false, - "properties": { - "address": { - "$ref": "#/definitions/address" - }, - "directive": { - "$ref": "#/definitions/directive" - } - } - } - }, - "meta": { - "$ref": "#/definitions/meta" - } - } - }, - "targetSchema": { - "properties": { - "$ref": "#/properties" - } - } - }, - { - "title": "Update", - "description": "Updates a existing Access List", - "href": "/nginx/access-list/{definitions.identity.example}", - "access": "private", - "method": "PUT", - "rel": "update", - "http_header": { - "$ref": "../examples.json#/definitions/auth_header" - }, - "schema": { - "type": "object", - "additionalProperties": false, - "properties": { - "name": { - "$ref": "#/definitions/name" - }, - "satisfy_any": { - "$ref": "#/definitions/satisfy_any" - }, - "pass_auth": { - "$ref": "#/definitions/pass_auth" - }, - "items": { - "type": "array", - "minItems": 0, - "items": { - "type": "object", - "additionalProperties": false, - "properties": { - "username": { - "type": "string", - "minLength": 1 - }, - "password": { - "type": "string", - "minLength": 0 - } - } - } - }, - "clients": { - "type": "array", - "minItems": 0, - "items": { - "type": "object", - "additionalProperties": false, - "properties": { - "address": { - "$ref": "#/definitions/address" - }, - "directive": { - "$ref": "#/definitions/directive" - } - } - } - } - } - }, - "targetSchema": { - "properties": { - "$ref": "#/properties" - } - } - }, - { - "title": "Delete", - "description": "Deletes a existing Access List", - "href": "/nginx/access-list/{definitions.identity.example}", - "access": "private", - "method": "DELETE", - "rel": "delete", - "http_header": { - "$ref": "../examples.json#/definitions/auth_header" - }, - "targetSchema": { - "type": "boolean" - } - } - ] -} diff --git a/backend/schema/endpoints/certificates.json b/backend/schema/endpoints/certificates.json deleted file mode 100644 index 49fd6a7d..00000000 --- a/backend/schema/endpoints/certificates.json +++ /dev/null @@ -1,162 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "endpoints/certificates", - "title": "Certificates", - "description": "Endpoints relating to Certificates", - "stability": "stable", - "type": "object", - "definitions": { - "id": { - "$ref": "../definitions.json#/definitions/id" - }, - "created_on": { - "$ref": "../definitions.json#/definitions/created_on" - }, - "modified_on": { - "$ref": "../definitions.json#/definitions/modified_on" - }, - "provider": { - "$ref": "../definitions.json#/definitions/ssl_provider" - }, - "nice_name": { - "type": "string", - "description": "Nice Name for the custom certificate" - }, - "domain_names": { - "$ref": "../definitions.json#/definitions/domain_names" - }, - "expires_on": { - "description": "Date and time of expiration", - "format": "date-time", - "readOnly": true, - "type": "string" - }, - "meta": { - "type": "object", - "additionalProperties": false, - "properties": { - "letsencrypt_email": { - "type": "string", - "format": "email" - }, - "letsencrypt_agree": { - "type": "boolean" - }, - "dns_challenge": { - "type": "boolean" - }, - "dns_provider": { - "type": "string" - }, - "dns_provider_credentials": { - "type": "string" - }, - "propagation_seconds": { - "anyOf": [ - { - "type": "integer", - "minimum": 0 - } - ] - - } - } - } - }, - "properties": { - "id": { - "$ref": "#/definitions/id" - }, - "created_on": { - "$ref": "#/definitions/created_on" - }, - "modified_on": { - "$ref": "#/definitions/modified_on" - }, - "provider": { - "$ref": "#/definitions/provider" - }, - "nice_name": { - "$ref": "#/definitions/nice_name" - }, - "domain_names": { - "$ref": "#/definitions/domain_names" - }, - "expires_on": { - "$ref": "#/definitions/expires_on" - }, - "meta": { - "$ref": "#/definitions/meta" - } - }, - "links": [ - { - "title": "List", - "description": "Returns a list of Certificates", - "href": "/nginx/certificates", - "access": "private", - "method": "GET", - "rel": "self", - "http_header": { - "$ref": "../examples.json#/definitions/auth_header" - }, - "targetSchema": { - "type": "array", - "items": { - "$ref": "#/properties" - } - } - }, - { - "title": "Create", - "description": "Creates a new Certificate", - "href": "/nginx/certificates", - "access": "private", - "method": "POST", - "rel": "create", - "http_header": { - "$ref": "../examples.json#/definitions/auth_header" - }, - "schema": { - "type": "object", - "additionalProperties": false, - "required": [ - "provider" - ], - "properties": { - "provider": { - "$ref": "#/definitions/provider" - }, - "nice_name": { - "$ref": "#/definitions/nice_name" - }, - "domain_names": { - "$ref": "#/definitions/domain_names" - }, - "meta": { - "$ref": "#/definitions/meta" - } - } - }, - "targetSchema": { - "properties": { - "$ref": "#/properties" - } - } - }, - { - "title": "Delete", - "description": "Deletes a existing Certificate", - "href": "/nginx/certificates/{definitions.identity.example}", - "access": "private", - "method": "DELETE", - "rel": "delete", - "http_header": { - "$ref": "../examples.json#/definitions/auth_header" - }, - "targetSchema": { - "type": "boolean" - } - } - ] -} diff --git a/backend/schema/endpoints/dead-hosts.json b/backend/schema/endpoints/dead-hosts.json deleted file mode 100644 index 0c73c3be..00000000 --- a/backend/schema/endpoints/dead-hosts.json +++ /dev/null @@ -1,240 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "endpoints/dead-hosts", - "title": "404 Hosts", - "description": "Endpoints relating to 404 Hosts", - "stability": "stable", - "type": "object", - "definitions": { - "id": { - "$ref": "../definitions.json#/definitions/id" - }, - "created_on": { - "$ref": "../definitions.json#/definitions/created_on" - }, - "modified_on": { - "$ref": "../definitions.json#/definitions/modified_on" - }, - "domain_names": { - "$ref": "../definitions.json#/definitions/domain_names" - }, - "certificate_id": { - "$ref": "../definitions.json#/definitions/certificate_id" - }, - "ssl_forced": { - "$ref": "../definitions.json#/definitions/ssl_forced" - }, - "hsts_enabled": { - "$ref": "../definitions.json#/definitions/hsts_enabled" - }, - "hsts_subdomains": { - "$ref": "../definitions.json#/definitions/hsts_subdomains" - }, - "http2_support": { - "$ref": "../definitions.json#/definitions/http2_support" - }, - "advanced_config": { - "type": "string" - }, - "enabled": { - "$ref": "../definitions.json#/definitions/enabled" - }, - "meta": { - "type": "object" - } - }, - "properties": { - "id": { - "$ref": "#/definitions/id" - }, - "created_on": { - "$ref": "#/definitions/created_on" - }, - "modified_on": { - "$ref": "#/definitions/modified_on" - }, - "domain_names": { - "$ref": "#/definitions/domain_names" - }, - "certificate_id": { - "$ref": "#/definitions/certificate_id" - }, - "ssl_forced": { - "$ref": "#/definitions/ssl_forced" - }, - "hsts_enabled": { - "$ref": "#/definitions/hsts_enabled" - }, - "hsts_subdomains": { - "$ref": "#/definitions/hsts_subdomains" - }, - "http2_support": { - "$ref": "#/definitions/http2_support" - }, - "advanced_config": { - "$ref": "#/definitions/advanced_config" - }, - "enabled": { - "$ref": "#/definitions/enabled" - }, - "meta": { - "$ref": "#/definitions/meta" - } - }, - "links": [ - { - "title": "List", - "description": "Returns a list of 404 Hosts", - "href": "/nginx/dead-hosts", - "access": "private", - "method": "GET", - "rel": "self", - "http_header": { - "$ref": "../examples.json#/definitions/auth_header" - }, - "targetSchema": { - "type": "array", - "items": { - "$ref": "#/properties" - } - } - }, - { - "title": "Create", - "description": "Creates a new 404 Host", - "href": "/nginx/dead-hosts", - "access": "private", - "method": "POST", - "rel": "create", - "http_header": { - "$ref": "../examples.json#/definitions/auth_header" - }, - "schema": { - "type": "object", - "additionalProperties": false, - "required": [ - "domain_names" - ], - "properties": { - "domain_names": { - "$ref": "#/definitions/domain_names" - }, - "certificate_id": { - "$ref": "#/definitions/certificate_id" - }, - "ssl_forced": { - "$ref": "#/definitions/ssl_forced" - }, - "hsts_enabled": { - "$ref": "#/definitions/hsts_enabled" - }, - "hsts_subdomains": { - "$ref": "#/definitions/hsts_enabled" - }, - "http2_support": { - "$ref": "#/definitions/http2_support" - }, - "advanced_config": { - "$ref": "#/definitions/advanced_config" - }, - "meta": { - "$ref": "#/definitions/meta" - } - } - }, - "targetSchema": { - "properties": { - "$ref": "#/properties" - } - } - }, - { - "title": "Update", - "description": "Updates a existing 404 Host", - "href": "/nginx/dead-hosts/{definitions.identity.example}", - "access": "private", - "method": "PUT", - "rel": "update", - "http_header": { - "$ref": "../examples.json#/definitions/auth_header" - }, - "schema": { - "type": "object", - "additionalProperties": false, - "properties": { - "domain_names": { - "$ref": "#/definitions/domain_names" - }, - "certificate_id": { - "$ref": "#/definitions/certificate_id" - }, - "ssl_forced": { - "$ref": "#/definitions/ssl_forced" - }, - "hsts_enabled": { - "$ref": "#/definitions/hsts_enabled" - }, - "hsts_subdomains": { - "$ref": "#/definitions/hsts_enabled" - }, - "http2_support": { - "$ref": "#/definitions/http2_support" - }, - "advanced_config": { - "$ref": "#/definitions/advanced_config" - }, - "meta": { - "$ref": "#/definitions/meta" - } - } - }, - "targetSchema": { - "properties": { - "$ref": "#/properties" - } - } - }, - { - "title": "Delete", - "description": "Deletes a existing 404 Host", - "href": "/nginx/dead-hosts/{definitions.identity.example}", - "access": "private", - "method": "DELETE", - "rel": "delete", - "http_header": { - "$ref": "../examples.json#/definitions/auth_header" - }, - "targetSchema": { - "type": "boolean" - } - }, - { - "title": "Enable", - "description": "Enables a existing 404 Host", - "href": "/nginx/dead-hosts/{definitions.identity.example}/enable", - "access": "private", - "method": "POST", - "rel": "update", - "http_header": { - "$ref": "../examples.json#/definitions/auth_header" - }, - "targetSchema": { - "type": "boolean" - } - }, - { - "title": "Disable", - "description": "Disables a existing 404 Host", - "href": "/nginx/dead-hosts/{definitions.identity.example}/disable", - "access": "private", - "method": "POST", - "rel": "update", - "http_header": { - "$ref": "../examples.json#/definitions/auth_header" - }, - "targetSchema": { - "type": "boolean" - } - } - ] -} diff --git a/backend/schema/endpoints/proxy-hosts.json b/backend/schema/endpoints/proxy-hosts.json deleted file mode 100644 index 9a3fff2f..00000000 --- a/backend/schema/endpoints/proxy-hosts.json +++ /dev/null @@ -1,387 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "endpoints/proxy-hosts", - "title": "Proxy Hosts", - "description": "Endpoints relating to Proxy Hosts", - "stability": "stable", - "type": "object", - "definitions": { - "id": { - "$ref": "../definitions.json#/definitions/id" - }, - "created_on": { - "$ref": "../definitions.json#/definitions/created_on" - }, - "modified_on": { - "$ref": "../definitions.json#/definitions/modified_on" - }, - "domain_names": { - "$ref": "../definitions.json#/definitions/domain_names" - }, - "forward_scheme": { - "type": "string", - "enum": ["http", "https"] - }, - "forward_host": { - "type": "string", - "minLength": 1, - "maxLength": 255 - }, - "forward_port": { - "type": "integer", - "minimum": 1, - "maximum": 65535 - }, - "certificate_id": { - "$ref": "../definitions.json#/definitions/certificate_id" - }, - "ssl_forced": { - "$ref": "../definitions.json#/definitions/ssl_forced" - }, - "hsts_enabled": { - "$ref": "../definitions.json#/definitions/hsts_enabled" - }, - "hsts_subdomains": { - "$ref": "../definitions.json#/definitions/hsts_subdomains" - }, - "http2_support": { - "$ref": "../definitions.json#/definitions/http2_support" - }, - "block_exploits": { - "$ref": "../definitions.json#/definitions/block_exploits" - }, - "caching_enabled": { - "$ref": "../definitions.json#/definitions/caching_enabled" - }, - "allow_websocket_upgrade": { - "description": "Allow Websocket Upgrade for all paths", - "example": true, - "type": "boolean" - }, - "access_list_id": { - "$ref": "../definitions.json#/definitions/access_list_id" - }, - "advanced_config": { - "type": "string" - }, - "enabled": { - "$ref": "../definitions.json#/definitions/enabled" - }, - "meta": { - "type": "object" - }, - "locations": { - "type": "array", - "minItems": 0, - "items": { - "type": "object", - "required": [ - "forward_scheme", - "forward_host", - "forward_port", - "path" - ], - "additionalProperties": false, - "properties": { - "id": { - "type": ["integer", "null"] - }, - "path": { - "type": "string", - "minLength": 1 - }, - "forward_scheme": { - "$ref": "#/definitions/forward_scheme" - }, - "forward_host": { - "$ref": "#/definitions/forward_host" - }, - "forward_port": { - "$ref": "#/definitions/forward_port" - }, - "forward_path": { - "type": "string" - }, - "advanced_config": { - "type": "string" - } - } - } - } - }, - "properties": { - "id": { - "$ref": "#/definitions/id" - }, - "created_on": { - "$ref": "#/definitions/created_on" - }, - "modified_on": { - "$ref": "#/definitions/modified_on" - }, - "domain_names": { - "$ref": "#/definitions/domain_names" - }, - "forward_scheme": { - "$ref": "#/definitions/forward_scheme" - }, - "forward_host": { - "$ref": "#/definitions/forward_host" - }, - "forward_port": { - "$ref": "#/definitions/forward_port" - }, - "certificate_id": { - "$ref": "#/definitions/certificate_id" - }, - "ssl_forced": { - "$ref": "#/definitions/ssl_forced" - }, - "hsts_enabled": { - "$ref": "#/definitions/hsts_enabled" - }, - "hsts_subdomains": { - "$ref": "#/definitions/hsts_subdomains" - }, - "http2_support": { - "$ref": "#/definitions/http2_support" - }, - "block_exploits": { - "$ref": "#/definitions/block_exploits" - }, - "caching_enabled": { - "$ref": "#/definitions/caching_enabled" - }, - "allow_websocket_upgrade": { - "$ref": "#/definitions/allow_websocket_upgrade" - }, - "access_list_id": { - "$ref": "#/definitions/access_list_id" - }, - "advanced_config": { - "$ref": "#/definitions/advanced_config" - }, - "enabled": { - "$ref": "#/definitions/enabled" - }, - "meta": { - "$ref": "#/definitions/meta" - }, - "locations": { - "$ref": "#/definitions/locations" - } - }, - "links": [ - { - "title": "List", - "description": "Returns a list of Proxy Hosts", - "href": "/nginx/proxy-hosts", - "access": "private", - "method": "GET", - "rel": "self", - "http_header": { - "$ref": "../examples.json#/definitions/auth_header" - }, - "targetSchema": { - "type": "array", - "items": { - "$ref": "#/properties" - } - } - }, - { - "title": "Create", - "description": "Creates a new Proxy Host", - "href": "/nginx/proxy-hosts", - "access": "private", - "method": "POST", - "rel": "create", - "http_header": { - "$ref": "../examples.json#/definitions/auth_header" - }, - "schema": { - "type": "object", - "additionalProperties": false, - "required": [ - "domain_names", - "forward_scheme", - "forward_host", - "forward_port" - ], - "properties": { - "domain_names": { - "$ref": "#/definitions/domain_names" - }, - "forward_scheme": { - "$ref": "#/definitions/forward_scheme" - }, - "forward_host": { - "$ref": "#/definitions/forward_host" - }, - "forward_port": { - "$ref": "#/definitions/forward_port" - }, - "certificate_id": { - "$ref": "#/definitions/certificate_id" - }, - "ssl_forced": { - "$ref": "#/definitions/ssl_forced" - }, - "hsts_enabled": { - "$ref": "#/definitions/hsts_enabled" - }, - "hsts_subdomains": { - "$ref": "#/definitions/hsts_enabled" - }, - "http2_support": { - "$ref": "#/definitions/http2_support" - }, - "block_exploits": { - "$ref": "#/definitions/block_exploits" - }, - "caching_enabled": { - "$ref": "#/definitions/caching_enabled" - }, - "allow_websocket_upgrade": { - "$ref": "#/definitions/allow_websocket_upgrade" - }, - "access_list_id": { - "$ref": "#/definitions/access_list_id" - }, - "advanced_config": { - "$ref": "#/definitions/advanced_config" - }, - "enabled": { - "$ref": "#/definitions/enabled" - }, - "meta": { - "$ref": "#/definitions/meta" - }, - "locations": { - "$ref": "#/definitions/locations" - } - } - }, - "targetSchema": { - "properties": { - "$ref": "#/properties" - } - } - }, - { - "title": "Update", - "description": "Updates a existing Proxy Host", - "href": "/nginx/proxy-hosts/{definitions.identity.example}", - "access": "private", - "method": "PUT", - "rel": "update", - "http_header": { - "$ref": "../examples.json#/definitions/auth_header" - }, - "schema": { - "type": "object", - "additionalProperties": false, - "properties": { - "domain_names": { - "$ref": "#/definitions/domain_names" - }, - "forward_scheme": { - "$ref": "#/definitions/forward_scheme" - }, - "forward_host": { - "$ref": "#/definitions/forward_host" - }, - "forward_port": { - "$ref": "#/definitions/forward_port" - }, - "certificate_id": { - "$ref": "#/definitions/certificate_id" - }, - "ssl_forced": { - "$ref": "#/definitions/ssl_forced" - }, - "hsts_enabled": { - "$ref": "#/definitions/hsts_enabled" - }, - "hsts_subdomains": { - "$ref": "#/definitions/hsts_enabled" - }, - "http2_support": { - "$ref": "#/definitions/http2_support" - }, - "block_exploits": { - "$ref": "#/definitions/block_exploits" - }, - "caching_enabled": { - "$ref": "#/definitions/caching_enabled" - }, - "allow_websocket_upgrade": { - "$ref": "#/definitions/allow_websocket_upgrade" - }, - "access_list_id": { - "$ref": "#/definitions/access_list_id" - }, - "advanced_config": { - "$ref": "#/definitions/advanced_config" - }, - "enabled": { - "$ref": "#/definitions/enabled" - }, - "meta": { - "$ref": "#/definitions/meta" - }, - "locations": { - "$ref": "#/definitions/locations" - } - } - }, - "targetSchema": { - "properties": { - "$ref": "#/properties" - } - } - }, - { - "title": "Delete", - "description": "Deletes a existing Proxy Host", - "href": "/nginx/proxy-hosts/{definitions.identity.example}", - "access": "private", - "method": "DELETE", - "rel": "delete", - "http_header": { - "$ref": "../examples.json#/definitions/auth_header" - }, - "targetSchema": { - "type": "boolean" - } - }, - { - "title": "Enable", - "description": "Enables a existing Proxy Host", - "href": "/nginx/proxy-hosts/{definitions.identity.example}/enable", - "access": "private", - "method": "POST", - "rel": "update", - "http_header": { - "$ref": "../examples.json#/definitions/auth_header" - }, - "targetSchema": { - "type": "boolean" - } - }, - { - "title": "Disable", - "description": "Disables a existing Proxy Host", - "href": "/nginx/proxy-hosts/{definitions.identity.example}/disable", - "access": "private", - "method": "POST", - "rel": "update", - "http_header": { - "$ref": "../examples.json#/definitions/auth_header" - }, - "targetSchema": { - "type": "boolean" - } - } - ] -} diff --git a/backend/schema/endpoints/redirection-hosts.json b/backend/schema/endpoints/redirection-hosts.json deleted file mode 100644 index 14a46998..00000000 --- a/backend/schema/endpoints/redirection-hosts.json +++ /dev/null @@ -1,305 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "endpoints/redirection-hosts", - "title": "Redirection Hosts", - "description": "Endpoints relating to Redirection Hosts", - "stability": "stable", - "type": "object", - "definitions": { - "id": { - "$ref": "../definitions.json#/definitions/id" - }, - "created_on": { - "$ref": "../definitions.json#/definitions/created_on" - }, - "modified_on": { - "$ref": "../definitions.json#/definitions/modified_on" - }, - "domain_names": { - "$ref": "../definitions.json#/definitions/domain_names" - }, - "forward_http_code": { - "$ref": "../definitions.json#/definitions/http_code" - }, - "forward_scheme": { - "$ref": "../definitions.json#/definitions/scheme" - }, - "forward_domain_name": { - "$ref": "../definitions.json#/definitions/domain_name" - }, - "preserve_path": { - "description": "Should the path be preserved", - "example": true, - "type": "boolean" - }, - "certificate_id": { - "$ref": "../definitions.json#/definitions/certificate_id" - }, - "ssl_forced": { - "$ref": "../definitions.json#/definitions/ssl_forced" - }, - "hsts_enabled": { - "$ref": "../definitions.json#/definitions/hsts_enabled" - }, - "hsts_subdomains": { - "$ref": "../definitions.json#/definitions/hsts_subdomains" - }, - "http2_support": { - "$ref": "../definitions.json#/definitions/http2_support" - }, - "block_exploits": { - "$ref": "../definitions.json#/definitions/block_exploits" - }, - "advanced_config": { - "type": "string" - }, - "enabled": { - "$ref": "../definitions.json#/definitions/enabled" - }, - "meta": { - "type": "object" - } - }, - "properties": { - "id": { - "$ref": "#/definitions/id" - }, - "created_on": { - "$ref": "#/definitions/created_on" - }, - "modified_on": { - "$ref": "#/definitions/modified_on" - }, - "domain_names": { - "$ref": "#/definitions/domain_names" - }, - "forward_http_code": { - "$ref": "#/definitions/forward_http_code" - }, - "forward_scheme": { - "$ref": "#/definitions/forward_scheme" - }, - "forward_domain_name": { - "$ref": "#/definitions/forward_domain_name" - }, - "preserve_path": { - "$ref": "#/definitions/preserve_path" - }, - "certificate_id": { - "$ref": "#/definitions/certificate_id" - }, - "ssl_forced": { - "$ref": "#/definitions/ssl_forced" - }, - "hsts_enabled": { - "$ref": "#/definitions/hsts_enabled" - }, - "hsts_subdomains": { - "$ref": "#/definitions/hsts_subdomains" - }, - "http2_support": { - "$ref": "#/definitions/http2_support" - }, - "block_exploits": { - "$ref": "#/definitions/block_exploits" - }, - "advanced_config": { - "$ref": "#/definitions/advanced_config" - }, - "enabled": { - "$ref": "#/definitions/enabled" - }, - "meta": { - "$ref": "#/definitions/meta" - } - }, - "links": [ - { - "title": "List", - "description": "Returns a list of Redirection Hosts", - "href": "/nginx/redirection-hosts", - "access": "private", - "method": "GET", - "rel": "self", - "http_header": { - "$ref": "../examples.json#/definitions/auth_header" - }, - "targetSchema": { - "type": "array", - "items": { - "$ref": "#/properties" - } - } - }, - { - "title": "Create", - "description": "Creates a new Redirection Host", - "href": "/nginx/redirection-hosts", - "access": "private", - "method": "POST", - "rel": "create", - "http_header": { - "$ref": "../examples.json#/definitions/auth_header" - }, - "schema": { - "type": "object", - "additionalProperties": false, - "required": [ - "domain_names", - "forward_scheme", - "forward_http_code", - "forward_domain_name" - ], - "properties": { - "domain_names": { - "$ref": "#/definitions/domain_names" - }, - "forward_http_code": { - "$ref": "#/definitions/forward_http_code" - }, - "forward_scheme": { - "$ref": "#/definitions/forward_scheme" - }, - "forward_domain_name": { - "$ref": "#/definitions/forward_domain_name" - }, - "preserve_path": { - "$ref": "#/definitions/preserve_path" - }, - "certificate_id": { - "$ref": "#/definitions/certificate_id" - }, - "ssl_forced": { - "$ref": "#/definitions/ssl_forced" - }, - "hsts_enabled": { - "$ref": "#/definitions/hsts_enabled" - }, - "hsts_subdomains": { - "$ref": "#/definitions/hsts_enabled" - }, - "http2_support": { - "$ref": "#/definitions/http2_support" - }, - "block_exploits": { - "$ref": "#/definitions/block_exploits" - }, - "advanced_config": { - "$ref": "#/definitions/advanced_config" - }, - "meta": { - "$ref": "#/definitions/meta" - } - } - }, - "targetSchema": { - "properties": { - "$ref": "#/properties" - } - } - }, - { - "title": "Update", - "description": "Updates a existing Redirection Host", - "href": "/nginx/redirection-hosts/{definitions.identity.example}", - "access": "private", - "method": "PUT", - "rel": "update", - "http_header": { - "$ref": "../examples.json#/definitions/auth_header" - }, - "schema": { - "type": "object", - "additionalProperties": false, - "properties": { - "domain_names": { - "$ref": "#/definitions/domain_names" - }, - "forward_http_code": { - "$ref": "#/definitions/forward_http_code" - }, - "forward_scheme": { - "$ref": "#/definitions/forward_scheme" - }, - "forward_domain_name": { - "$ref": "#/definitions/forward_domain_name" - }, - "preserve_path": { - "$ref": "#/definitions/preserve_path" - }, - "certificate_id": { - "$ref": "#/definitions/certificate_id" - }, - "ssl_forced": { - "$ref": "#/definitions/ssl_forced" - }, - "hsts_enabled": { - "$ref": "#/definitions/hsts_enabled" - }, - "hsts_subdomains": { - "$ref": "#/definitions/hsts_enabled" - }, - "http2_support": { - "$ref": "#/definitions/http2_support" - }, - "block_exploits": { - "$ref": "#/definitions/block_exploits" - }, - "advanced_config": { - "$ref": "#/definitions/advanced_config" - }, - "meta": { - "$ref": "#/definitions/meta" - } - } - }, - "targetSchema": { - "properties": { - "$ref": "#/properties" - } - } - }, - { - "title": "Delete", - "description": "Deletes a existing Redirection Host", - "href": "/nginx/redirection-hosts/{definitions.identity.example}", - "access": "private", - "method": "DELETE", - "rel": "delete", - "http_header": { - "$ref": "../examples.json#/definitions/auth_header" - }, - "targetSchema": { - "type": "boolean" - } - }, - { - "title": "Enable", - "description": "Enables a existing Redirection Host", - "href": "/nginx/redirection-hosts/{definitions.identity.example}/enable", - "access": "private", - "method": "POST", - "rel": "update", - "http_header": { - "$ref": "../examples.json#/definitions/auth_header" - }, - "targetSchema": { - "type": "boolean" - } - }, - { - "title": "Disable", - "description": "Disables a existing Redirection Host", - "href": "/nginx/redirection-hosts/{definitions.identity.example}/disable", - "access": "private", - "method": "POST", - "rel": "update", - "http_header": { - "$ref": "../examples.json#/definitions/auth_header" - }, - "targetSchema": { - "type": "boolean" - } - } - ] -} diff --git a/backend/schema/endpoints/settings.json b/backend/schema/endpoints/settings.json deleted file mode 100644 index 29e2865a..00000000 --- a/backend/schema/endpoints/settings.json +++ /dev/null @@ -1,99 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "endpoints/settings", - "title": "Settings", - "description": "Endpoints relating to Settings", - "stability": "stable", - "type": "object", - "definitions": { - "id": { - "$ref": "../definitions.json#/definitions/setting_id" - }, - "name": { - "description": "Name", - "example": "Default Site", - "type": "string", - "minLength": 2, - "maxLength": 100 - }, - "description": { - "description": "Description", - "example": "Default Site", - "type": "string", - "minLength": 2, - "maxLength": 255 - }, - "value": { - "description": "Value", - "example": "404", - "type": "string", - "maxLength": 255 - }, - "meta": { - "type": "object" - } - }, - "links": [ - { - "title": "List", - "description": "Returns a list of Settings", - "href": "/settings", - "access": "private", - "method": "GET", - "rel": "self", - "http_header": { - "$ref": "../examples.json#/definitions/auth_header" - }, - "targetSchema": { - "type": "array", - "items": { - "$ref": "#/properties" - } - } - }, - { - "title": "Update", - "description": "Updates a existing Setting", - "href": "/settings/{definitions.identity.example}", - "access": "private", - "method": "PUT", - "rel": "update", - "http_header": { - "$ref": "../examples.json#/definitions/auth_header" - }, - "schema": { - "type": "object", - "properties": { - "value": { - "$ref": "#/definitions/value" - }, - "meta": { - "$ref": "#/definitions/meta" - } - } - }, - "targetSchema": { - "properties": { - "$ref": "#/properties" - } - } - } - ], - "properties": { - "id": { - "$ref": "#/definitions/id" - }, - "name": { - "$ref": "#/definitions/description" - }, - "description": { - "$ref": "#/definitions/description" - }, - "value": { - "$ref": "#/definitions/value" - }, - "meta": { - "$ref": "#/definitions/meta" - } - } -} diff --git a/backend/schema/endpoints/streams.json b/backend/schema/endpoints/streams.json deleted file mode 100644 index e93e1ff3..00000000 --- a/backend/schema/endpoints/streams.json +++ /dev/null @@ -1,223 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "endpoints/streams", - "title": "Streams", - "description": "Endpoints relating to Streams", - "stability": "stable", - "type": "object", - "definitions": { - "id": { - "$ref": "../definitions.json#/definitions/id" - }, - "created_on": { - "$ref": "../definitions.json#/definitions/created_on" - }, - "modified_on": { - "$ref": "../definitions.json#/definitions/modified_on" - }, - "incoming_port": { - "type": "integer", - "minimum": 1, - "maximum": 65535 - }, - "forward_ip": { - "type": "string", - "format": "ipv4" - }, - "forwarding_port": { - "type": "integer", - "minimum": 1, - "maximum": 65535 - }, - "tcp_forwarding": { - "type": "boolean" - }, - "udp_forwarding": { - "type": "boolean" - }, - "enabled": { - "$ref": "../definitions.json#/definitions/enabled" - }, - "meta": { - "type": "object" - } - }, - "properties": { - "id": { - "$ref": "#/definitions/id" - }, - "created_on": { - "$ref": "#/definitions/created_on" - }, - "modified_on": { - "$ref": "#/definitions/modified_on" - }, - "incoming_port": { - "$ref": "#/definitions/incoming_port" - }, - "forward_ip": { - "$ref": "#/definitions/forward_ip" - }, - "forwarding_port": { - "$ref": "#/definitions/forwarding_port" - }, - "tcp_forwarding": { - "$ref": "#/definitions/tcp_forwarding" - }, - "udp_forwarding": { - "$ref": "#/definitions/udp_forwarding" - }, - "enabled": { - "$ref": "#/definitions/enabled" - }, - "meta": { - "$ref": "#/definitions/meta" - } - }, - "links": [ - { - "title": "List", - "description": "Returns a list of Steams", - "href": "/nginx/streams", - "access": "private", - "method": "GET", - "rel": "self", - "http_header": { - "$ref": "../examples.json#/definitions/auth_header" - }, - "targetSchema": { - "type": "array", - "items": { - "$ref": "#/properties" - } - } - }, - { - "title": "Create", - "description": "Creates a new Stream", - "href": "/nginx/streams", - "access": "private", - "method": "POST", - "rel": "create", - "http_header": { - "$ref": "../examples.json#/definitions/auth_header" - }, - "schema": { - "type": "object", - "additionalProperties": false, - "required": [ - "incoming_port", - "forward_ip", - "forwarding_port" - ], - "properties": { - "incoming_port": { - "$ref": "#/definitions/incoming_port" - }, - "forward_ip": { - "$ref": "#/definitions/forward_ip" - }, - "forwarding_port": { - "$ref": "#/definitions/forwarding_port" - }, - "tcp_forwarding": { - "$ref": "#/definitions/tcp_forwarding" - }, - "udp_forwarding": { - "$ref": "#/definitions/udp_forwarding" - }, - "meta": { - "$ref": "#/definitions/meta" - } - } - }, - "targetSchema": { - "properties": { - "$ref": "#/properties" - } - } - }, - { - "title": "Update", - "description": "Updates a existing Stream", - "href": "/nginx/streams/{definitions.identity.example}", - "access": "private", - "method": "PUT", - "rel": "update", - "http_header": { - "$ref": "../examples.json#/definitions/auth_header" - }, - "schema": { - "type": "object", - "additionalProperties": false, - "properties": { - "incoming_port": { - "$ref": "#/definitions/incoming_port" - }, - "forward_ip": { - "$ref": "#/definitions/forward_ip" - }, - "forwarding_port": { - "$ref": "#/definitions/forwarding_port" - }, - "tcp_forwarding": { - "$ref": "#/definitions/tcp_forwarding" - }, - "udp_forwarding": { - "$ref": "#/definitions/udp_forwarding" - }, - "meta": { - "$ref": "#/definitions/meta" - } - } - }, - "targetSchema": { - "properties": { - "$ref": "#/properties" - } - } - }, - { - "title": "Delete", - "description": "Deletes a existing Stream", - "href": "/nginx/streams/{definitions.identity.example}", - "access": "private", - "method": "DELETE", - "rel": "delete", - "http_header": { - "$ref": "../examples.json#/definitions/auth_header" - }, - "targetSchema": { - "type": "boolean" - } - }, - { - "title": "Enable", - "description": "Enables a existing Stream", - "href": "/nginx/streams/{definitions.identity.example}/enable", - "access": "private", - "method": "POST", - "rel": "update", - "http_header": { - "$ref": "../examples.json#/definitions/auth_header" - }, - "targetSchema": { - "type": "boolean" - } - }, - { - "title": "Disable", - "description": "Disables a existing Stream", - "href": "/nginx/streams/{definitions.identity.example}/disable", - "access": "private", - "method": "POST", - "rel": "update", - "http_header": { - "$ref": "../examples.json#/definitions/auth_header" - }, - "targetSchema": { - "type": "boolean" - } - } - ] -} diff --git a/backend/schema/endpoints/tokens.json b/backend/schema/endpoints/tokens.json deleted file mode 100644 index 920af63f..00000000 --- a/backend/schema/endpoints/tokens.json +++ /dev/null @@ -1,100 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "endpoints/tokens", - "title": "Token", - "description": "Tokens are required to authenticate against the API", - "stability": "stable", - "type": "object", - "definitions": { - "identity": { - "description": "Email Address or other 3rd party providers identifier", - "example": "john@example.com", - "type": "string" - }, - "secret": { - "description": "A password or key", - "example": "correct horse battery staple", - "type": "string" - }, - "token": { - "description": "JWT", - "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.O_frfYM8RzmRsUNigHtu0_jZ_utSejyr1axMGa8rlsk", - "type": "string" - }, - "expires": { - "description": "Token expiry time", - "format": "date-time", - "type": "string" - }, - "scope": { - "description": "Scope of the Token, defaults to 'user'", - "example": "user", - "type": "string" - } - }, - "links": [ - { - "title": "Create", - "description": "Creates a new token.", - "href": "/tokens", - "access": "public", - "method": "POST", - "rel": "create", - "schema": { - "type": "object", - "required": [ - "identity", - "secret" - ], - "properties": { - "identity": { - "$ref": "#/definitions/identity" - }, - "secret": { - "$ref": "#/definitions/secret" - }, - "scope": { - "$ref": "#/definitions/scope" - } - } - }, - "targetSchema": { - "type": "object", - "properties": { - "token": { - "$ref": "#/definitions/token" - }, - "expires": { - "$ref": "#/definitions/expires" - } - } - } - }, - { - "title": "Refresh", - "description": "Returns a new token.", - "href": "/tokens", - "access": "private", - "method": "GET", - "rel": "self", - "http_header": { - "$ref": "../examples.json#/definitions/auth_header" - }, - "schema": {}, - "targetSchema": { - "type": "object", - "properties": { - "token": { - "$ref": "#/definitions/token" - }, - "expires": { - "$ref": "#/definitions/expires" - }, - "scope": { - "$ref": "#/definitions/scope" - } - } - } - } - ] -} diff --git a/backend/schema/endpoints/users.json b/backend/schema/endpoints/users.json deleted file mode 100644 index 42f44eac..00000000 --- a/backend/schema/endpoints/users.json +++ /dev/null @@ -1,287 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "endpoints/users", - "title": "Users", - "description": "Endpoints relating to Users", - "stability": "stable", - "type": "object", - "definitions": { - "id": { - "$ref": "../definitions.json#/definitions/id" - }, - "created_on": { - "$ref": "../definitions.json#/definitions/created_on" - }, - "modified_on": { - "$ref": "../definitions.json#/definitions/modified_on" - }, - "name": { - "description": "Name", - "example": "Jamie Curnow", - "type": "string", - "minLength": 2, - "maxLength": 100 - }, - "nickname": { - "description": "Nickname", - "example": "Jamie", - "type": "string", - "minLength": 2, - "maxLength": 50 - }, - "email": { - "$ref": "../definitions.json#/definitions/email" - }, - "avatar": { - "description": "Avatar", - "example": "http://somewhere.jpg", - "type": "string", - "minLength": 2, - "maxLength": 150, - "readOnly": true - }, - "roles": { - "description": "Roles", - "example": [ - "admin" - ], - "type": "array" - }, - "is_disabled": { - "description": "Is Disabled", - "example": false, - "type": "boolean" - } - }, - "links": [ - { - "title": "List", - "description": "Returns a list of Users", - "href": "/users", - "access": "private", - "method": "GET", - "rel": "self", - "http_header": { - "$ref": "../examples.json#/definitions/auth_header" - }, - "targetSchema": { - "type": "array", - "items": { - "$ref": "#/properties" - } - } - }, - { - "title": "Create", - "description": "Creates a new User", - "href": "/users", - "access": "private", - "method": "POST", - "rel": "create", - "http_header": { - "$ref": "../examples.json#/definitions/auth_header" - }, - "schema": { - "type": "object", - "required": [ - "name", - "nickname", - "email" - ], - "properties": { - "name": { - "$ref": "#/definitions/name" - }, - "nickname": { - "$ref": "#/definitions/nickname" - }, - "email": { - "$ref": "#/definitions/email" - }, - "roles": { - "$ref": "#/definitions/roles" - }, - "is_disabled": { - "$ref": "#/definitions/is_disabled" - }, - "auth": { - "type": "object", - "description": "Auth Credentials", - "example": { - "type": "password", - "secret": "bigredhorsebanana" - } - } - } - }, - "targetSchema": { - "properties": { - "$ref": "#/properties" - } - } - }, - { - "title": "Update", - "description": "Updates a existing User", - "href": "/users/{definitions.identity.example}", - "access": "private", - "method": "PUT", - "rel": "update", - "http_header": { - "$ref": "../examples.json#/definitions/auth_header" - }, - "schema": { - "type": "object", - "properties": { - "name": { - "$ref": "#/definitions/name" - }, - "nickname": { - "$ref": "#/definitions/nickname" - }, - "email": { - "$ref": "#/definitions/email" - }, - "roles": { - "$ref": "#/definitions/roles" - }, - "is_disabled": { - "$ref": "#/definitions/is_disabled" - } - } - }, - "targetSchema": { - "properties": { - "$ref": "#/properties" - } - } - }, - { - "title": "Delete", - "description": "Deletes a existing User", - "href": "/users/{definitions.identity.example}", - "access": "private", - "method": "DELETE", - "rel": "delete", - "http_header": { - "$ref": "../examples.json#/definitions/auth_header" - }, - "targetSchema": { - "type": "boolean" - } - }, - { - "title": "Set Password", - "description": "Sets a password for an existing User", - "href": "/users/{definitions.identity.example}/auth", - "access": "private", - "method": "PUT", - "rel": "update", - "http_header": { - "$ref": "../examples.json#/definitions/auth_header" - }, - "schema": { - "type": "object", - "required": [ - "type", - "secret" - ], - "properties": { - "type": { - "type": "string", - "pattern": "^password$" - }, - "current": { - "type": "string", - "minLength": 1, - "maxLength": 64 - }, - "secret": { - "type": "string", - "minLength": 8, - "maxLength": 64 - } - } - }, - "targetSchema": { - "type": "boolean" - } - }, - { - "title": "Set Permissions", - "description": "Sets Permissions for a User", - "href": "/users/{definitions.identity.example}/permissions", - "access": "private", - "method": "PUT", - "rel": "update", - "http_header": { - "$ref": "../examples.json#/definitions/auth_header" - }, - "schema": { - "type": "object", - "properties": { - "visibility": { - "type": "string", - "pattern": "^(all|user)$" - }, - "access_lists": { - "type": "string", - "pattern": "^(hidden|view|manage)$" - }, - "dead_hosts": { - "type": "string", - "pattern": "^(hidden|view|manage)$" - }, - "proxy_hosts": { - "type": "string", - "pattern": "^(hidden|view|manage)$" - }, - "redirection_hosts": { - "type": "string", - "pattern": "^(hidden|view|manage)$" - }, - "streams": { - "type": "string", - "pattern": "^(hidden|view|manage)$" - }, - "certificates": { - "type": "string", - "pattern": "^(hidden|view|manage)$" - } - } - }, - "targetSchema": { - "type": "boolean" - } - } - ], - "properties": { - "id": { - "$ref": "#/definitions/id" - }, - "created_on": { - "$ref": "#/definitions/created_on" - }, - "modified_on": { - "$ref": "#/definitions/modified_on" - }, - "name": { - "$ref": "#/definitions/name" - }, - "nickname": { - "$ref": "#/definitions/nickname" - }, - "email": { - "$ref": "#/definitions/email" - }, - "avatar": { - "$ref": "#/definitions/avatar" - }, - "roles": { - "$ref": "#/definitions/roles" - }, - "is_disabled": { - "$ref": "#/definitions/is_disabled" - } - } -} diff --git a/backend/schema/examples.json b/backend/schema/examples.json deleted file mode 100644 index 37bc6c4d..00000000 --- a/backend/schema/examples.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "examples", - "type": "object", - "definitions": { - "name": { - "description": "Name", - "example": "John Smith", - "type": "string", - "minLength": 1, - "maxLength": 255 - }, - "auth_header": { - "Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.O_frfYM8RzmRsUNigHtu0_jZ_utSejyr1axMGa8rlsk", - "X-API-Version": "next" - }, - "token": { - "type": "string", - "description": "JWT", - "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.O_frfYM8RzmRsUNigHtu0_jZ_utSejyr1axMGa8rlsk" - } - } -} diff --git a/backend/schema/index.json b/backend/schema/index.json deleted file mode 100644 index 6e7d1c8a..00000000 --- a/backend/schema/index.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "root", - "title": "Nginx Proxy Manager REST API", - "description": "This is the Nginx Proxy Manager REST API", - "version": "2.0.0", - "links": [ - { - "href": "http://npm.example.com/api", - "rel": "self" - } - ], - "properties": { - "tokens": { - "$ref": "endpoints/tokens.json" - }, - "users": { - "$ref": "endpoints/users.json" - }, - "proxy-hosts": { - "$ref": "endpoints/proxy-hosts.json" - }, - "redirection-hosts": { - "$ref": "endpoints/redirection-hosts.json" - }, - "dead-hosts": { - "$ref": "endpoints/dead-hosts.json" - }, - "streams": { - "$ref": "endpoints/streams.json" - }, - "certificates": { - "$ref": "endpoints/certificates.json" - }, - "access-lists": { - "$ref": "endpoints/access-lists.json" - }, - "settings": { - "$ref": "endpoints/settings.json" - } - } -} diff --git a/backend/scripts/lint.sh b/backend/scripts/lint.sh new file mode 100755 index 00000000..bf6bf4c8 --- /dev/null +++ b/backend/scripts/lint.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +BLUE='\E[1;34m' +YELLOW='\E[1;33m' +RESET='\E[0m' +RESULT=0 + +# go files: incomplete comment check +INCOMPLETE_COMMENTS=$(find . -iname "*.go*" | grep -v " " | xargs grep --colour -H -n -E "^\s*\/\/\s*[A-Z]\w+ \.{3}" 2>/dev/null) +if [[ -n "$INCOMPLETE_COMMENTS" ]]; then + echo -e "${BLUE}❯ ${YELLOW}WARN: Please fix incomplete exported comments:${RESET}" + echo -e "${RED}${INCOMPLETE_COMMENTS}${RESET}" + echo + # RESULT=1 +fi + +if ! golangci-lint run -E goimports -E maligned ./...; then + exit 1 +fi + +exit "$RESULT" diff --git a/backend/scripts/test.sh b/backend/scripts/test.sh new file mode 100755 index 00000000..92a0a02f --- /dev/null +++ b/backend/scripts/test.sh @@ -0,0 +1,5 @@ +#!/bin/bash -e + +export RICHGO_FORCE_COLOR=1 + +richgo test -bench=. -cover -v ./internal/... diff --git a/backend/setup.js b/backend/setup.js deleted file mode 100644 index b25ffc00..00000000 --- a/backend/setup.js +++ /dev/null @@ -1,209 +0,0 @@ -const fs = require('fs'); -const NodeRSA = require('node-rsa'); -const config = require('config'); -const logger = require('./logger').setup; -const certificateModel = require('./models/certificate'); -const userModel = require('./models/user'); -const userPermissionModel = require('./models/user_permission'); -const utils = require('./lib/utils'); -const authModel = require('./models/auth'); -const settingModel = require('./models/setting'); -const dns_plugins = require('./global/certbot-dns-plugins'); -const debug_mode = process.env.NODE_ENV !== 'production' || !!process.env.DEBUG; - -/** - * Creates a new JWT RSA Keypair if not alread set on the config - * - * @returns {Promise} - */ -const setupJwt = () => { - return new Promise((resolve, reject) => { - // Now go and check if the jwt gpg keys have been created and if not, create them - if (!config.has('jwt') || !config.has('jwt.key') || !config.has('jwt.pub')) { - logger.info('Creating a new JWT key pair...'); - - // jwt keys are not configured properly - const filename = config.util.getEnv('NODE_CONFIG_DIR') + '/' + (config.util.getEnv('NODE_ENV') || 'default') + '.json'; - let config_data = {}; - - try { - config_data = require(filename); - } catch (err) { - // do nothing - if (debug_mode) { - logger.debug(filename + ' config file could not be required'); - } - } - - // Now create the keys and save them in the config. - let key = new NodeRSA({ b: 2048 }); - key.generateKeyPair(); - - config_data.jwt = { - key: key.exportKey('private').toString(), - pub: key.exportKey('public').toString(), - }; - - // Write config - fs.writeFile(filename, JSON.stringify(config_data, null, 2), (err) => { - if (err) { - logger.error('Could not write JWT key pair to config file: ' + filename); - reject(err); - } else { - logger.info('Wrote JWT key pair to config file: ' + filename); - delete require.cache[require.resolve('config')]; - resolve(); - } - }); - } else { - // JWT key pair exists - if (debug_mode) { - logger.debug('JWT Keypair already exists'); - } - - resolve(); - } - }); -}; - -/** - * Creates a default admin users if one doesn't already exist in the database - * - * @returns {Promise} - */ -const setupDefaultUser = () => { - return userModel - .query() - .select(userModel.raw('COUNT(`id`) as `count`')) - .where('is_deleted', 0) - .first() - .then((row) => { - if (!row.count) { - // Create a new user and set password - logger.info('Creating a new user: admin@example.com with password: changeme'); - - let data = { - is_deleted: 0, - email: 'admin@example.com', - name: 'Administrator', - nickname: 'Admin', - avatar: '', - roles: ['admin'], - }; - - return userModel - .query() - .insertAndFetch(data) - .then((user) => { - return authModel - .query() - .insert({ - user_id: user.id, - type: 'password', - secret: 'changeme', - meta: {}, - }) - .then(() => { - return userPermissionModel.query().insert({ - user_id: user.id, - visibility: 'all', - proxy_hosts: 'manage', - redirection_hosts: 'manage', - dead_hosts: 'manage', - streams: 'manage', - access_lists: 'manage', - certificates: 'manage', - }); - }); - }) - .then(() => { - logger.info('Initial admin setup completed'); - }); - } else if (debug_mode) { - logger.debug('Admin user setup not required'); - } - }); -}; - -/** - * Creates default settings if they don't already exist in the database - * - * @returns {Promise} - */ -const setupDefaultSettings = () => { - return settingModel - .query() - .select(settingModel.raw('COUNT(`id`) as `count`')) - .where({id: 'default-site'}) - .first() - .then((row) => { - if (!row.count) { - settingModel - .query() - .insert({ - id: 'default-site', - name: 'Default Site', - description: 'What to show when Nginx is hit with an unknown Host', - value: 'congratulations', - meta: {}, - }) - .then(() => { - logger.info('Default settings added'); - }); - } - if (debug_mode) { - logger.debug('Default setting setup not required'); - } - }); -}; - -/** - * Installs all Certbot plugins which are required for an installed certificate - * - * @returns {Promise} - */ -const setupCertbotPlugins = () => { - return certificateModel - .query() - .where('is_deleted', 0) - .andWhere('provider', 'letsencrypt') - .then((certificates) => { - if (certificates && certificates.length) { - let plugins = []; - let promises = []; - - certificates.map(function (certificate) { - if (certificate.meta && certificate.meta.dns_challenge === true) { - const dns_plugin = dns_plugins[certificate.meta.dns_provider]; - const packages_to_install = `${dns_plugin.package_name}==${dns_plugin.package_version} ${dns_plugin.dependencies}`; - - if (plugins.indexOf(packages_to_install) === -1) plugins.push(packages_to_install); - - // Make sure credentials file exists - const credentials_loc = '/etc/letsencrypt/credentials/credentials-' + certificate.id; - const credentials_cmd = '[ -f \'' + credentials_loc + '\' ] || { mkdir -p /etc/letsencrypt/credentials 2> /dev/null; echo \'' + certificate.meta.dns_provider_credentials.replace('\'', '\\\'') + '\' > \'' + credentials_loc + '\' && chmod 600 \'' + credentials_loc + '\'; }'; - promises.push(utils.exec(credentials_cmd)); - } - }); - - if (plugins.length) { - const install_cmd = 'pip install ' + plugins.join(' '); - promises.push(utils.exec(install_cmd)); - } - - if (promises.length) { - return Promise.all(promises) - .then(() => { - logger.info('Added Certbot plugins ' + plugins.join(', ')); - }); - } - } - }); -}; - -module.exports = function () { - return setupJwt() - .then(setupDefaultUser) - .then(setupDefaultSettings) - .then(setupCertbotPlugins); -}; diff --git a/backend/templates/_assets.conf b/backend/templates/_assets.conf deleted file mode 100644 index dcb183c5..00000000 --- a/backend/templates/_assets.conf +++ /dev/null @@ -1,4 +0,0 @@ -{% if caching_enabled == 1 or caching_enabled == true -%} - # Asset Caching - include conf.d/include/assets.conf; -{% endif %} \ No newline at end of file diff --git a/backend/templates/_certificates.conf b/backend/templates/_certificates.conf deleted file mode 100644 index 06ca7bb8..00000000 --- a/backend/templates/_certificates.conf +++ /dev/null @@ -1,14 +0,0 @@ -{% if certificate and certificate_id > 0 -%} -{% if certificate.provider == "letsencrypt" %} - # Let's Encrypt SSL - include conf.d/include/letsencrypt-acme-challenge.conf; - include conf.d/include/ssl-ciphers.conf; - ssl_certificate /etc/letsencrypt/live/npm-{{ certificate_id }}/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/npm-{{ certificate_id }}/privkey.pem; -{% else %} - # Custom SSL - ssl_certificate /data/custom_ssl/npm-{{ certificate_id }}/fullchain.pem; - ssl_certificate_key /data/custom_ssl/npm-{{ certificate_id }}/privkey.pem; -{% endif %} -{% endif %} - diff --git a/backend/templates/_exploits.conf b/backend/templates/_exploits.conf deleted file mode 100644 index 002970d5..00000000 --- a/backend/templates/_exploits.conf +++ /dev/null @@ -1,4 +0,0 @@ -{% if block_exploits == 1 or block_exploits == true %} - # Block Exploits - include conf.d/include/block-exploits.conf; -{% endif %} \ No newline at end of file diff --git a/backend/templates/_forced_ssl.conf b/backend/templates/_forced_ssl.conf deleted file mode 100644 index 7fade20c..00000000 --- a/backend/templates/_forced_ssl.conf +++ /dev/null @@ -1,6 +0,0 @@ -{% if certificate and certificate_id > 0 -%} -{% if ssl_forced == 1 or ssl_forced == true %} - # Force SSL - include conf.d/include/force-ssl.conf; -{% endif %} -{% endif %} \ No newline at end of file diff --git a/backend/templates/_header_comment.conf b/backend/templates/_header_comment.conf deleted file mode 100644 index 8f996d34..00000000 --- a/backend/templates/_header_comment.conf +++ /dev/null @@ -1,3 +0,0 @@ -# ------------------------------------------------------------ -# {{ domain_names | join: ", " }} -# ------------------------------------------------------------ \ No newline at end of file diff --git a/backend/templates/_hsts.conf b/backend/templates/_hsts.conf deleted file mode 100644 index 11aecf24..00000000 --- a/backend/templates/_hsts.conf +++ /dev/null @@ -1,8 +0,0 @@ -{% if certificate and certificate_id > 0 -%} -{% if ssl_forced == 1 or ssl_forced == true %} -{% if hsts_enabled == 1 or hsts_enabled == true %} - # HSTS (ngx_http_headers_module is required) (63072000 seconds = 2 years) - add_header Strict-Transport-Security "max-age=63072000;{% if hsts_subdomains == 1 or hsts_subdomains == true -%} includeSubDomains;{% endif %} preload" always; -{% endif %} -{% endif %} -{% endif %} diff --git a/backend/templates/_listen.conf b/backend/templates/_listen.conf deleted file mode 100644 index 8f40bea2..00000000 --- a/backend/templates/_listen.conf +++ /dev/null @@ -1,15 +0,0 @@ - listen 80; -{% if ipv6 -%} - listen [::]:80; -{% else -%} - #listen [::]:80; -{% endif %} -{% if certificate -%} - listen 443 ssl{% if http2_support %} http2{% endif %}; -{% if ipv6 -%} - listen [::]:443; -{% else -%} - #listen [::]:443; -{% endif %} -{% endif %} - server_name {{ domain_names | join: " " }}; diff --git a/backend/templates/_location.conf b/backend/templates/_location.conf deleted file mode 100644 index 5a7a6abe..00000000 --- a/backend/templates/_location.conf +++ /dev/null @@ -1,45 +0,0 @@ - location {{ path }} { - proxy_set_header Host $host; - proxy_set_header X-Forwarded-Scheme $scheme; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-Real-IP $remote_addr; - proxy_pass {{ forward_scheme }}://{{ forward_host }}:{{ forward_port }}{{ forward_path }}; - - {% if access_list_id > 0 %} - {% if access_list.items.length > 0 %} - # Authorization - auth_basic "Authorization required"; - auth_basic_user_file /data/access/{{ access_list_id }}; - - {{ access_list.passauth }} - {% endif %} - - # Access Rules - {% for client in access_list.clients %} - {{- client.rule -}}; - {% endfor %}deny all; - - # Access checks must... - {% if access_list.satisfy %} - {{ access_list.satisfy }}; - {% endif %} - - {% endif %} - - {% include "_assets.conf" %} - {% include "_exploits.conf" %} - - {% include "_forced_ssl.conf" %} - {% include "_hsts.conf" %} - - {% if allow_websocket_upgrade == 1 or allow_websocket_upgrade == true %} - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection $http_connection; - proxy_http_version 1.1; - {% endif %} - - - {{ advanced_config }} - } - diff --git a/backend/templates/dead_host.conf b/backend/templates/dead_host.conf deleted file mode 100644 index be53f6df..00000000 --- a/backend/templates/dead_host.conf +++ /dev/null @@ -1,22 +0,0 @@ -{% include "_header_comment.conf" %} - -{% if enabled %} -server { -{% include "_listen.conf" %} -{% include "_certificates.conf" %} -{% include "_hsts.conf" %} -{% include "_forced_ssl.conf" %} - - access_log /data/logs/dead_host-{{ id }}.log standard; - -{{ advanced_config }} - -{% if use_default_location %} - location / { -{% include "_hsts.conf" %} - return 404; - } -{% endif %} - -} -{% endif %} diff --git a/backend/templates/default.conf b/backend/templates/default.conf deleted file mode 100644 index 56b67090..00000000 --- a/backend/templates/default.conf +++ /dev/null @@ -1,37 +0,0 @@ -# ------------------------------------------------------------ -# Default Site -# ------------------------------------------------------------ -{% if value == "congratulations" %} -# Skipping output, congratulations page configration is baked in. -{%- else %} -server { - listen 80 default; -{% if ipv6 -%} - listen [::]:80; -{% else -%} - #listen [::]:80; -{% endif %} - server_name default-host.localhost; - access_log /data/logs/default_host.log combined; -{% include "_exploits.conf" %} - -{%- if value == "404" %} - location / { - return 404; - } -{% endif %} - -{%- if value == "redirect" %} - location / { - return 301 {{ meta.redirect }}; - } -{%- endif %} - -{%- if value == "html" %} - root /data/nginx/default_www; - location / { - try_files $uri /index.html; - } -{%- endif %} -} -{% endif %} diff --git a/backend/templates/ip_ranges.conf b/backend/templates/ip_ranges.conf deleted file mode 100644 index 8ede2bd9..00000000 --- a/backend/templates/ip_ranges.conf +++ /dev/null @@ -1,3 +0,0 @@ -{% for range in ip_ranges %} -set_real_ip_from {{ range }}; -{% endfor %} \ No newline at end of file diff --git a/backend/templates/letsencrypt-request.conf b/backend/templates/letsencrypt-request.conf deleted file mode 100644 index cda2f892..00000000 --- a/backend/templates/letsencrypt-request.conf +++ /dev/null @@ -1,18 +0,0 @@ -{% include "_header_comment.conf" %} - -server { - listen 80; -{% if ipv6 -%} - listen [::]:80; -{% endif %} - - server_name {{ domain_names | join: " " }}; - - access_log /data/logs/letsencrypt-requests.log standard; - - include conf.d/include/letsencrypt-acme-challenge.conf; - - location / { - return 404; - } -} diff --git a/backend/templates/proxy_host.conf b/backend/templates/proxy_host.conf deleted file mode 100644 index 538b85e5..00000000 --- a/backend/templates/proxy_host.conf +++ /dev/null @@ -1,70 +0,0 @@ -{% include "_header_comment.conf" %} - -{% if enabled %} -server { - set $forward_scheme {{ forward_scheme }}; - set $server "{{ forward_host }}"; - set $port {{ forward_port }}; - -{% include "_listen.conf" %} -{% include "_certificates.conf" %} -{% include "_assets.conf" %} -{% include "_exploits.conf" %} -{% include "_hsts.conf" %} -{% include "_forced_ssl.conf" %} - -{% if allow_websocket_upgrade == 1 or allow_websocket_upgrade == true %} -proxy_set_header Upgrade $http_upgrade; -proxy_set_header Connection $http_connection; -proxy_http_version 1.1; -{% endif %} - - - access_log /data/logs/proxy_host-{{ id }}.log proxy; - -{{ advanced_config }} - -{{ locations }} - -{% if use_default_location %} - - location / { - - {% if access_list_id > 0 %} - {% if access_list.items.length > 0 %} - # Authorization - auth_basic "Authorization required"; - auth_basic_user_file /data/access/{{ access_list_id }}; - - {{ access_list.passauth }} - {% endif %} - - # Access Rules - {% for client in access_list.clients %} - {{- client.rule -}}; - {% endfor %}deny all; - - # Access checks must... - {% if access_list.satisfy %} - {{ access_list.satisfy }}; - {% endif %} - - {% endif %} - -{% include "_hsts.conf" %} - - {% if allow_websocket_upgrade == 1 or allow_websocket_upgrade == true %} - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection $http_connection; - proxy_http_version 1.1; - {% endif %} - - # Proxy! - include conf.d/include/proxy.conf; - } -{% endif %} - - # Custom - include /data/nginx/custom/server_proxy[.]conf; -} -{% endif %} diff --git a/backend/templates/redirection_host.conf b/backend/templates/redirection_host.conf deleted file mode 100644 index f42e146b..00000000 --- a/backend/templates/redirection_host.conf +++ /dev/null @@ -1,31 +0,0 @@ -{% include "_header_comment.conf" %} - -{% if enabled %} -server { -{% include "_listen.conf" %} -{% include "_certificates.conf" %} -{% include "_assets.conf" %} -{% include "_exploits.conf" %} -{% include "_hsts.conf" %} -{% include "_forced_ssl.conf" %} - - access_log /data/logs/redirection_host-{{ id }}.log standard; - -{{ advanced_config }} - -{% if use_default_location %} - location / { -{% include "_hsts.conf" %} - - {% if preserve_path == 1 or preserve_path == true %} - return {{ forward_http_code }} {{ forward_scheme }}://{{ forward_domain_name }}$request_uri; - {% else %} - return {{ forward_http_code }} {{ forward_scheme }}://{{ forward_domain_name }}; - {% endif %} - } -{% endif %} - - # Custom - include /data/nginx/custom/server_redirect[.]conf; -} -{% endif %} diff --git a/backend/templates/stream.conf b/backend/templates/stream.conf deleted file mode 100644 index 05f68772..00000000 --- a/backend/templates/stream.conf +++ /dev/null @@ -1,37 +0,0 @@ -# ------------------------------------------------------------ -# {{ incoming_port }} TCP: {{ tcp_forwarding }} UDP: {{ udp_forwarding }} -# ------------------------------------------------------------ - -{% if enabled %} -{% if tcp_forwarding == 1 or tcp_forwarding == true -%} -server { - listen {{ incoming_port }}; -{% if ipv6 -%} - listen [::]:{{ incoming_port }}; -{% else -%} - #listen [::]:{{ incoming_port }}; -{% endif %} - - proxy_pass {{ forward_ip }}:{{ forwarding_port }}; - - # Custom - include /data/nginx/custom/server_stream[.]conf; - include /data/nginx/custom/server_stream_tcp[.]conf; -} -{% endif %} -{% if udp_forwarding == 1 or udp_forwarding == true %} -server { - listen {{ incoming_port }} udp; -{% if ipv6 -%} - listen [::]:{{ incoming_port }} udp; -{% else -%} - #listen [::]:{{ incoming_port }} udp; -{% endif %} - proxy_pass {{ forward_ip }}:{{ forwarding_port }}; - - # Custom - include /data/nginx/custom/server_stream[.]conf; - include /data/nginx/custom/server_stream_udp[.]conf; -} -{% endif %} -{% endif %} \ No newline at end of file diff --git a/backend/yarn.lock b/backend/yarn.lock deleted file mode 100644 index 84180c26..00000000 --- a/backend/yarn.lock +++ /dev/null @@ -1,3757 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@apidevtools/json-schema-ref-parser@8.0.0": - version "8.0.0" - resolved "https://registry.yarnpkg.com/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-8.0.0.tgz#9eb749499b3f8d919e90bb141e4b6f67aee4692d" - integrity sha512-n4YBtwQhdpLto1BaUCyAeflizmIbaloGShsPyRtFf5qdFJxfssj+GgLavczgKJFa3Bq+3St2CKcpRJdjtB4EBw== - dependencies: - "@jsdevtools/ono" "^7.1.0" - call-me-maybe "^1.0.1" - js-yaml "^3.13.1" - -"@babel/code-frame@^7.0.0": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a" - integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg== - dependencies: - "@babel/highlight" "^7.10.4" - -"@babel/helper-validator-identifier@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" - integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== - -"@babel/highlight@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143" - integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA== - dependencies: - "@babel/helper-validator-identifier" "^7.10.4" - chalk "^2.0.0" - js-tokens "^4.0.0" - -"@jsdevtools/ono@^7.1.0": - version "7.1.3" - resolved "https://registry.yarnpkg.com/@jsdevtools/ono/-/ono-7.1.3.tgz#9df03bbd7c696a5c58885c34aa06da41c8543796" - integrity sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg== - -"@sindresorhus/is@^0.14.0": - version "0.14.0" - resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" - integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== - -"@szmarczak/http-timer@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" - integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== - dependencies: - defer-to-connect "^1.0.1" - -"@types/color-name@^1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" - integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== - -abbrev@1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" - integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== - -accepts@~1.3.5, accepts@~1.3.7: - version "1.3.7" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" - integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== - dependencies: - mime-types "~2.1.24" - negotiator "0.6.2" - -acorn-jsx@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.2.0.tgz#4c66069173d6fdd68ed85239fc256226182b2ebe" - integrity sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ== - -acorn@^7.1.1: - version "7.4.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.0.tgz#e1ad486e6c54501634c6c397c5c121daa383607c" - integrity sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w== - -ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.0: - version "6.12.3" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.3.tgz#18c5af38a111ddeb4f2697bd78d68abc1cabd706" - integrity sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA== - dependencies: - fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - -ansi-align@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.0.tgz#b536b371cf687caaef236c18d3e21fe3797467cb" - integrity sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw== - dependencies: - string-width "^3.0.0" - -ansi-escapes@^4.2.1: - version "4.3.1" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61" - integrity sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA== - dependencies: - type-fest "^0.11.0" - -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= - -ansi-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" - integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= - -ansi-regex@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" - integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== - -ansi-regex@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" - integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== - -ansi-styles@^3.2.0, ansi-styles@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" - integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== - dependencies: - color-convert "^1.9.0" - -ansi-styles@^4.0.0, ansi-styles@^4.1.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359" - integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA== - dependencies: - "@types/color-name" "^1.1.1" - color-convert "^2.0.1" - -ansi-styles@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-1.0.0.tgz#cb102df1c56f5123eab8b67cd7b98027a0279178" - integrity sha1-yxAt8cVvUSPquLZ817mAJ6AnkXg= - -anymatch@~3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" - integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== - dependencies: - normalize-path "^3.0.0" - picomatch "^2.0.4" - -aproba@^1.0.3: - version "1.2.0" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" - integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== - -are-we-there-yet@~1.1.2: - version "1.1.5" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" - integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== - dependencies: - delegates "^1.0.0" - readable-stream "^2.0.6" - -argparse@^1.0.7: - version "1.0.10" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" - integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== - dependencies: - sprintf-js "~1.0.2" - -arr-diff@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" - integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= - -arr-flatten@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" - integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== - -arr-union@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" - integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= - -array-each@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/array-each/-/array-each-1.0.1.tgz#a794af0c05ab1752846ee753a1f211a05ba0c44f" - integrity sha1-p5SvDAWrF1KEbudTofIRoFugxE8= - -array-flatten@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" - integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= - -array-slice@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/array-slice/-/array-slice-1.1.0.tgz#e368ea15f89bc7069f7ffb89aec3a6c7d4ac22d4" - integrity sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w== - -array-unique@^0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" - integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= - -asn1@^0.2.4: - version "0.2.4" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" - integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== - dependencies: - safer-buffer "~2.1.0" - -assign-symbols@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" - integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= - -astral-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" - integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== - -atob@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" - integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== - -balanced-match@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" - integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= - -base@^0.11.1: - version "0.11.2" - resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" - integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== - dependencies: - cache-base "^1.0.1" - class-utils "^0.3.5" - component-emitter "^1.2.1" - define-property "^1.0.0" - isobject "^3.0.1" - mixin-deep "^1.2.0" - pascalcase "^0.1.1" - -batchflow@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/batchflow/-/batchflow-0.4.0.tgz#7d419df79b6b7587b06f9ea34f96ccef6f74e5b5" - integrity sha1-fUGd95trdYewb56jT5bM72905bU= - -bcrypt@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/bcrypt/-/bcrypt-5.0.0.tgz#051407c7cd5ffbfb773d541ca3760ea0754e37e2" - integrity sha512-jB0yCBl4W/kVHM2whjfyqnxTmOHkCX4kHEa5nYKSoGeYe8YrjTYTc87/6bwt1g8cmV0QrbhKriETg9jWtcREhg== - dependencies: - node-addon-api "^3.0.0" - node-pre-gyp "0.15.0" - -bignumber.js@9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.0.tgz#805880f84a329b5eac6e7cb6f8274b6d82bdf075" - integrity sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A== - -binary-extensions@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.1.0.tgz#30fa40c9e7fe07dbc895678cd287024dea241dd9" - integrity sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ== - -blueimp-md5@^2.16.0: - version "2.17.0" - resolved "https://registry.yarnpkg.com/blueimp-md5/-/blueimp-md5-2.17.0.tgz#f4fcac088b115f7b4045f19f5da59e9d01b1bb96" - integrity sha512-x5PKJHY5rHQYaADj6NwPUR2QRCUVSggPzrUKkeENpj871o9l9IefJbO2jkT5UvYykeOK9dx0VmkIo6dZ+vThYw== - -body-parser@1.19.0, body-parser@^1.19.0: - version "1.19.0" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" - integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== - dependencies: - bytes "3.1.0" - content-type "~1.0.4" - debug "2.6.9" - depd "~1.1.2" - http-errors "1.7.2" - iconv-lite "0.4.24" - on-finished "~2.3.0" - qs "6.7.0" - raw-body "2.4.0" - type-is "~1.6.17" - -boxen@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/boxen/-/boxen-4.2.0.tgz#e411b62357d6d6d36587c8ac3d5d974daa070e64" - integrity sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ== - dependencies: - ansi-align "^3.0.0" - camelcase "^5.3.1" - chalk "^3.0.0" - cli-boxes "^2.2.0" - string-width "^4.1.0" - term-size "^2.1.0" - type-fest "^0.8.1" - widest-line "^3.1.0" - -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -braces@^2.3.1: - version "2.3.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" - integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== - dependencies: - arr-flatten "^1.1.0" - array-unique "^0.3.2" - extend-shallow "^2.0.1" - fill-range "^4.0.0" - isobject "^3.0.1" - repeat-element "^1.1.2" - snapdragon "^0.8.1" - snapdragon-node "^2.0.1" - split-string "^3.0.2" - to-regex "^3.0.1" - -braces@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== - dependencies: - fill-range "^7.0.1" - -buffer-equal-constant-time@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" - integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk= - -buffer-writer@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/buffer-writer/-/buffer-writer-2.0.0.tgz#ce7eb81a38f7829db09c873f2fbb792c0c98ec04" - integrity sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw== - -busboy@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/busboy/-/busboy-0.3.1.tgz#170899274c5bf38aae27d5c62b71268cd585fd1b" - integrity sha512-y7tTxhGKXcyBxRKAni+awqx8uqaJKrSFSNFSeRG5CsWNdmy2BIK+6VGWEW7TZnIO/533mtMEA4rOevQV815YJw== - dependencies: - dicer "0.3.0" - -bytes@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" - integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= - -bytes@3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" - integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== - -cache-base@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" - integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== - dependencies: - collection-visit "^1.0.0" - component-emitter "^1.2.1" - get-value "^2.0.6" - has-value "^1.0.0" - isobject "^3.0.1" - set-value "^2.0.0" - to-object-path "^0.3.0" - union-value "^1.0.0" - unset-value "^1.0.0" - -cacheable-request@^6.0.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" - integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== - dependencies: - clone-response "^1.0.2" - get-stream "^5.1.0" - http-cache-semantics "^4.0.0" - keyv "^3.0.0" - lowercase-keys "^2.0.0" - normalize-url "^4.1.0" - responselike "^1.0.2" - -call-me-maybe@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b" - integrity sha1-JtII6onje1y95gJQoV8DHBak1ms= - -callsites@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" - integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== - -camelcase@^5.0.0, camelcase@^5.3.1: - version "5.3.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" - integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== - -chalk@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-0.4.0.tgz#5199a3ddcd0c1efe23bc08c1b027b06176e0c64f" - integrity sha1-UZmj3c0MHv4jvAjBsCewYXbgxk8= - dependencies: - ansi-styles "~1.0.0" - has-color "~0.1.0" - strip-ansi "~0.1.0" - -chalk@^2.0.0, chalk@^2.1.0, chalk@^2.3.2: - version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - -chalk@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" - integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -chalk@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" - integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -chardet@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" - integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== - -chokidar@^3.2.2: - version "3.4.1" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.1.tgz#e905bdecf10eaa0a0b1db0c664481cc4cbc22ba1" - integrity sha512-TQTJyr2stihpC4Sya9hs2Xh+O2wf+igjL36Y75xx2WdHuiICcn/XJza46Jwt0eT5hVpQOzo3FpY3cj3RVYLX0g== - dependencies: - anymatch "~3.1.1" - braces "~3.0.2" - glob-parent "~5.1.0" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.4.0" - optionalDependencies: - fsevents "~2.1.2" - -chownr@^1.1.1: - version "1.1.4" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" - integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== - -ci-info@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" - integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== - -class-utils@^0.3.5: - version "0.3.6" - resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" - integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== - dependencies: - arr-union "^3.1.0" - define-property "^0.2.5" - isobject "^3.0.0" - static-extend "^0.1.1" - -cli-boxes@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.0.tgz#538ecae8f9c6ca508e3c3c95b453fe93cb4c168d" - integrity sha512-gpaBrMAizVEANOpfZp/EEUixTXDyGt7DFzdK5hU+UbWt/J0lB0w20ncZj59Z9a93xHb9u12zF5BS6i9RKbtg4w== - -cli-cursor@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" - integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== - dependencies: - restore-cursor "^3.1.0" - -cli-width@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" - integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== - -cliui@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" - integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.0" - wrap-ansi "^6.2.0" - -clone-response@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" - integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= - dependencies: - mimic-response "^1.0.0" - -code-point-at@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" - integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= - -collection-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" - integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= - dependencies: - map-visit "^1.0.0" - object-visit "^1.0.0" - -color-convert@^1.9.0: - version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== - dependencies: - color-name "1.1.3" - -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= - -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -colorette@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.1.0.tgz#1f943e5a357fac10b4e0f5aaef3b14cdc1af6ec7" - integrity sha512-6S062WDQUXi6hOfkO/sBPVwE5ASXY4G2+b4atvhJfSsuUUhIaUKlkjLe9692Ipyt5/a+IPF5aVTu3V5gvXq5cg== - -commander@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" - integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== - -component-emitter@^1.2.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" - integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== - -compressible@~2.0.16: - version "2.0.18" - resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" - integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== - dependencies: - mime-db ">= 1.43.0 < 2" - -compression@^1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" - integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== - dependencies: - accepts "~1.3.5" - bytes "3.0.0" - compressible "~2.0.16" - debug "2.6.9" - on-headers "~1.0.2" - safe-buffer "5.1.2" - vary "~1.1.2" - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= - -config@^3.3.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/config/-/config-3.3.1.tgz#b6a70e2908a43b98ed20be7e367edf0cc8ed5a19" - integrity sha512-+2/KaaaAzdwUBE3jgZON11L1ggLLhpf2FsGrfqYFHZW22ySGv/HqYIXrBwKKvn+XZh1UBUjHwAcrfsSkSygT+Q== - dependencies: - json5 "^2.1.1" - -configstore@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96" - integrity sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA== - dependencies: - dot-prop "^5.2.0" - graceful-fs "^4.1.2" - make-dir "^3.0.0" - unique-string "^2.0.0" - write-file-atomic "^3.0.0" - xdg-basedir "^4.0.0" - -console-control-strings@^1.0.0, console-control-strings@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" - integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= - -content-disposition@0.5.3: - version "0.5.3" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" - integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== - dependencies: - safe-buffer "5.1.2" - -content-type@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" - integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== - -cookie-signature@1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" - integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= - -cookie@0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" - integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== - -copy-descriptor@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" - integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= - -core-util-is@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= - -cross-spawn@^6.0.5: - version "6.0.5" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" - integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== - dependencies: - nice-try "^1.0.4" - path-key "^2.0.1" - semver "^5.5.0" - shebang-command "^1.2.0" - which "^1.2.9" - -crypto-random-string@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" - integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== - -db-errors@^0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/db-errors/-/db-errors-0.2.3.tgz#a6a38952e00b20e790f2695a6446b3c65497ffa2" - integrity sha512-OOgqgDuCavHXjYSJoV2yGhv6SeG8nk42aoCSoyXLZUH7VwFG27rxbavU1z+VrZbZjphw5UkDQwUlD21MwZpUng== - -debug@2.6.9, debug@^2.2.0, debug@^2.3.3: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -debug@4.1.1, debug@^4.0.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" - integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== - dependencies: - ms "^2.1.1" - -debug@^3.2.6: - version "3.2.6" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" - integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== - dependencies: - ms "^2.1.1" - -decamelize@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" - integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= - -decode-uri-component@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" - integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= - -decompress-response@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" - integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= - dependencies: - mimic-response "^1.0.0" - -deep-extend@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" - integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== - -deep-is@~0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" - integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= - -defer-to-connect@^1.0.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" - integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== - -define-property@^0.2.5: - version "0.2.5" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" - integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= - dependencies: - is-descriptor "^0.1.0" - -define-property@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" - integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= - dependencies: - is-descriptor "^1.0.0" - -define-property@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" - integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== - dependencies: - is-descriptor "^1.0.2" - isobject "^3.0.1" - -delegates@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" - integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= - -depd@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" - integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= - -destroy@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" - integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= - -detect-file@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7" - integrity sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc= - -detect-libc@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" - integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= - -dicer@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/dicer/-/dicer-0.3.0.tgz#eacd98b3bfbf92e8ab5c2fdb71aaac44bb06b872" - integrity sha512-MdceRRWqltEG2dZqO769g27N/3PXfcKl04VhYnBlo2YhH7zPi88VebsjTKclaOyiuMaGU72hTfw3VkUitGcVCA== - dependencies: - streamsearch "0.1.2" - -diskdb@^0.1.17: - version "0.1.17" - resolved "https://registry.yarnpkg.com/diskdb/-/diskdb-0.1.17.tgz#8abd095196b33b406791f1494b6b13b4422240c4" - integrity sha1-ir0JUZazO0BnkfFJS2sTtEIiQMQ= - dependencies: - chalk "^0.4.0" - merge "^1.1.3" - node-uuid "^1.4.1" - -doctrine@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" - integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== - dependencies: - esutils "^2.0.2" - -dot-prop@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.2.0.tgz#c34ecc29556dc45f1f4c22697b6f4904e0cc4fcb" - integrity sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A== - dependencies: - is-obj "^2.0.0" - -duplexer3@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" - integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= - -ecdsa-sig-formatter@1.0.11: - version "1.0.11" - resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" - integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== - dependencies: - safe-buffer "^5.0.1" - -ee-first@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" - integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= - -email-validator@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/email-validator/-/email-validator-2.0.4.tgz#b8dfaa5d0dae28f1b03c95881d904d4e40bfe7ed" - integrity sha512-gYCwo7kh5S3IDyZPLZf6hSS0MnZT8QmJFqYvbqlDZSbwdZlY6QZWxJ4i/6UhITOJ4XzyI647Bm2MXKCLqnJ4nQ== - -emoji-regex@^7.0.1: - version "7.0.3" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" - integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== - -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== - -encodeurl@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" - integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= - -end-of-stream@^1.1.0: - version "1.4.4" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" - integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== - dependencies: - once "^1.4.0" - -error-ex@^1.3.1: - version "1.3.2" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" - integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== - dependencies: - is-arrayish "^0.2.1" - -escape-goat@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675" - integrity sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q== - -escape-html@~1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" - integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= - -escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= - -eslint-plugin-align-assignments@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-align-assignments/-/eslint-plugin-align-assignments-1.1.2.tgz#83e1a8a826d4adf29e82b52d0bb39c88b301b576" - integrity sha512-I1ZJgk9EjHfGVU9M2Ex8UkVkkjLL5Y9BS6VNnQHq79eHj2H4/Cgxf36lQSUTLgm2ntB03A2NtF+zg9fyi5vChg== - -eslint-scope@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.0.tgz#d0f971dfe59c69e0cada684b23d49dbf82600ce5" - integrity sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w== - dependencies: - esrecurse "^4.1.0" - estraverse "^4.1.1" - -eslint-utils@^1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" - integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== - dependencies: - eslint-visitor-keys "^1.1.0" - -eslint-visitor-keys@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" - integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== - -eslint@^6.8.0: - version "6.8.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.8.0.tgz#62262d6729739f9275723824302fb227c8c93ffb" - integrity sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig== - dependencies: - "@babel/code-frame" "^7.0.0" - ajv "^6.10.0" - chalk "^2.1.0" - cross-spawn "^6.0.5" - debug "^4.0.1" - doctrine "^3.0.0" - eslint-scope "^5.0.0" - eslint-utils "^1.4.3" - eslint-visitor-keys "^1.1.0" - espree "^6.1.2" - esquery "^1.0.1" - esutils "^2.0.2" - file-entry-cache "^5.0.1" - functional-red-black-tree "^1.0.1" - glob-parent "^5.0.0" - globals "^12.1.0" - ignore "^4.0.6" - import-fresh "^3.0.0" - imurmurhash "^0.1.4" - inquirer "^7.0.0" - is-glob "^4.0.0" - js-yaml "^3.13.1" - json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.3.0" - lodash "^4.17.14" - minimatch "^3.0.4" - mkdirp "^0.5.1" - natural-compare "^1.4.0" - optionator "^0.8.3" - progress "^2.0.0" - regexpp "^2.0.1" - semver "^6.1.2" - strip-ansi "^5.2.0" - strip-json-comments "^3.0.1" - table "^5.2.3" - text-table "^0.2.0" - v8-compile-cache "^2.0.3" - -esm@^3.2.25: - version "3.2.25" - resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10" - integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA== - -espree@^6.1.2: - version "6.2.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-6.2.1.tgz#77fc72e1fd744a2052c20f38a5b575832e82734a" - integrity sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw== - dependencies: - acorn "^7.1.1" - acorn-jsx "^5.2.0" - eslint-visitor-keys "^1.1.0" - -esprima@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" - integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== - -esquery@^1.0.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.3.1.tgz#b78b5828aa8e214e29fb74c4d5b752e1c033da57" - integrity sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ== - dependencies: - estraverse "^5.1.0" - -esrecurse@^4.1.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" - integrity sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ== - dependencies: - estraverse "^4.1.0" - -estraverse@^4.1.0, estraverse@^4.1.1: - version "4.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" - integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== - -estraverse@^5.1.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" - integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== - -esutils@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" - integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== - -etag@~1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" - integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= - -expand-brackets@^2.1.4: - version "2.1.4" - resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" - integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= - dependencies: - debug "^2.3.3" - define-property "^0.2.5" - extend-shallow "^2.0.1" - posix-character-classes "^0.1.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -expand-tilde@^2.0.0, expand-tilde@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502" - integrity sha1-l+gBqgUt8CRU3kawK/YhZCzchQI= - dependencies: - homedir-polyfill "^1.0.1" - -express-fileupload@^1.1.9: - version "1.1.9" - resolved "https://registry.yarnpkg.com/express-fileupload/-/express-fileupload-1.1.9.tgz#e798e9318394ed5083e56217ad6cda576da465d2" - integrity sha512-f2w0aoe7lj3NeD8a4MXmYQsqir3Z66I08l9AKq04QbFUAjeZNmPwTlR5Lx2NGwSu/PslsAjGC38MWzo5tTjoBg== - dependencies: - busboy "^0.3.1" - -express@^4.17.1: - version "4.17.1" - resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" - integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== - dependencies: - accepts "~1.3.7" - array-flatten "1.1.1" - body-parser "1.19.0" - content-disposition "0.5.3" - content-type "~1.0.4" - cookie "0.4.0" - cookie-signature "1.0.6" - debug "2.6.9" - depd "~1.1.2" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - finalhandler "~1.1.2" - fresh "0.5.2" - merge-descriptors "1.0.1" - methods "~1.1.2" - on-finished "~2.3.0" - parseurl "~1.3.3" - path-to-regexp "0.1.7" - proxy-addr "~2.0.5" - qs "6.7.0" - range-parser "~1.2.1" - safe-buffer "5.1.2" - send "0.17.1" - serve-static "1.14.1" - setprototypeof "1.1.1" - statuses "~1.5.0" - type-is "~1.6.18" - utils-merge "1.0.1" - vary "~1.1.2" - -extend-shallow@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" - integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= - dependencies: - is-extendable "^0.1.0" - -extend-shallow@^3.0.0, extend-shallow@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" - integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= - dependencies: - assign-symbols "^1.0.0" - is-extendable "^1.0.1" - -extend@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== - -external-editor@^3.0.3: - version "3.1.0" - resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" - integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== - dependencies: - chardet "^0.7.0" - iconv-lite "^0.4.24" - tmp "^0.0.33" - -extglob@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" - integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== - dependencies: - array-unique "^0.3.2" - define-property "^1.0.0" - expand-brackets "^2.1.4" - extend-shallow "^2.0.1" - fragment-cache "^0.2.1" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -fast-deep-equal@^3.1.1: - version "3.1.3" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" - integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== - -fast-json-stable-stringify@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" - integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== - -fast-levenshtein@~2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" - integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= - -figures@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" - integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI= - dependencies: - escape-string-regexp "^1.0.5" - -figures@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" - integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== - dependencies: - escape-string-regexp "^1.0.5" - -file-entry-cache@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" - integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== - dependencies: - flat-cache "^2.0.1" - -fill-range@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" - integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= - dependencies: - extend-shallow "^2.0.1" - is-number "^3.0.0" - repeat-string "^1.6.1" - to-regex-range "^2.1.0" - -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== - dependencies: - to-regex-range "^5.0.1" - -finalhandler@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" - integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== - dependencies: - debug "2.6.9" - encodeurl "~1.0.2" - escape-html "~1.0.3" - on-finished "~2.3.0" - parseurl "~1.3.3" - statuses "~1.5.0" - unpipe "~1.0.0" - -find-up@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" - integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= - dependencies: - locate-path "^2.0.0" - -find-up@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" - integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== - dependencies: - locate-path "^5.0.0" - path-exists "^4.0.0" - -findup-sync@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-3.0.0.tgz#17b108f9ee512dfb7a5c7f3c8b27ea9e1a9c08d1" - integrity sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg== - dependencies: - detect-file "^1.0.0" - is-glob "^4.0.0" - micromatch "^3.0.4" - resolve-dir "^1.0.1" - -fined@^1.0.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/fined/-/fined-1.2.0.tgz#d00beccf1aa2b475d16d423b0238b713a2c4a37b" - integrity sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng== - dependencies: - expand-tilde "^2.0.2" - is-plain-object "^2.0.3" - object.defaults "^1.1.0" - object.pick "^1.2.0" - parse-filepath "^1.0.1" - -flagged-respawn@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/flagged-respawn/-/flagged-respawn-1.0.1.tgz#e7de6f1279ddd9ca9aac8a5971d618606b3aab41" - integrity sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q== - -flat-cache@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" - integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== - dependencies: - flatted "^2.0.0" - rimraf "2.6.3" - write "1.0.3" - -flatted@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" - integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== - -for-in@^1.0.1, for-in@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" - integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= - -for-own@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/for-own/-/for-own-1.0.0.tgz#c63332f415cedc4b04dbfe70cf836494c53cb44b" - integrity sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs= - dependencies: - for-in "^1.0.1" - -forwarded@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" - integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= - -fragment-cache@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" - integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= - dependencies: - map-cache "^0.2.2" - -fresh@0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" - integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= - -fs-minipass@^1.2.5: - version "1.2.7" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" - integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== - dependencies: - minipass "^2.6.0" - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= - -fsevents@~2.1.2: - version "2.1.3" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" - integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== - -functional-red-black-tree@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" - integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= - -gauge@~2.7.3: - version "2.7.4" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" - integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= - dependencies: - aproba "^1.0.3" - console-control-strings "^1.0.0" - has-unicode "^2.0.0" - object-assign "^4.1.0" - signal-exit "^3.0.0" - string-width "^1.0.1" - strip-ansi "^3.0.1" - wide-align "^1.1.0" - -get-caller-file@^2.0.1: - version "2.0.5" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" - integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== - -get-stream@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" - integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== - dependencies: - pump "^3.0.0" - -get-stream@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.1.0.tgz#01203cdc92597f9b909067c3e656cc1f4d3c4dc9" - integrity sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw== - dependencies: - pump "^3.0.0" - -get-value@^2.0.3, get-value@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" - integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= - -getopts@2.2.5: - version "2.2.5" - resolved "https://registry.yarnpkg.com/getopts/-/getopts-2.2.5.tgz#67a0fe471cacb9c687d817cab6450b96dde8313b" - integrity sha512-9jb7AW5p3in+IiJWhQiZmmwkpLaR/ccTWdWQCtZM66HJcHHLegowh4q4tSD7gouUyeNvFWRavfK9GXosQHDpFA== - -glob-parent@^5.0.0, glob-parent@~5.1.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" - integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== - dependencies: - is-glob "^4.0.1" - -glob@^7.1.3: - version "7.1.6" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" - integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -global-dirs@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-2.0.1.tgz#acdf3bb6685bcd55cb35e8a052266569e9469201" - integrity sha512-5HqUqdhkEovj2Of/ms3IeS/EekcO54ytHRLV4PEY2rhRwrHXLQjeVEES0Lhka0xwNDtGYn58wyC4s5+MHsOO6A== - dependencies: - ini "^1.3.5" - -global-modules@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-1.0.0.tgz#6d770f0eb523ac78164d72b5e71a8877265cc3ea" - integrity sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg== - dependencies: - global-prefix "^1.0.1" - is-windows "^1.0.1" - resolve-dir "^1.0.0" - -global-prefix@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-1.0.2.tgz#dbf743c6c14992593c655568cb66ed32c0122ebe" - integrity sha1-2/dDxsFJklk8ZVVoy2btMsASLr4= - dependencies: - expand-tilde "^2.0.2" - homedir-polyfill "^1.0.1" - ini "^1.3.4" - is-windows "^1.0.1" - which "^1.2.14" - -globals@^12.1.0: - version "12.4.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-12.4.0.tgz#a18813576a41b00a24a97e7f815918c2e19925f8" - integrity sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg== - dependencies: - type-fest "^0.8.1" - -got@^9.6.0: - version "9.6.0" - resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" - integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== - dependencies: - "@sindresorhus/is" "^0.14.0" - "@szmarczak/http-timer" "^1.1.2" - cacheable-request "^6.0.0" - decompress-response "^3.3.0" - duplexer3 "^0.1.4" - get-stream "^4.1.0" - lowercase-keys "^1.0.1" - mimic-response "^1.0.1" - p-cancelable "^1.0.0" - to-readable-stream "^1.0.0" - url-parse-lax "^3.0.0" - -graceful-fs@^4.1.15, graceful-fs@^4.1.2: - version "4.2.4" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" - integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== - -gravatar@^1.8.0: - version "1.8.1" - resolved "https://registry.yarnpkg.com/gravatar/-/gravatar-1.8.1.tgz#743bbdf3185c3433172e00e0e6ff5f6b30c58997" - integrity sha512-18frnfVp4kRYkM/eQW32Mfwlsh/KMbwd3S6nkescBZHioobflFEFHsvM71qZAkUSLNifyi2uoI+TuGxJAnQIOA== - dependencies: - blueimp-md5 "^2.16.0" - email-validator "^2.0.4" - querystring "0.2.0" - yargs "^15.4.1" - -has-color@~0.1.0: - version "0.1.7" - resolved "https://registry.yarnpkg.com/has-color/-/has-color-0.1.7.tgz#67144a5260c34fc3cca677d041daf52fe7b78b2f" - integrity sha1-ZxRKUmDDT8PMpnfQQdr1L+e3iy8= - -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= - -has-flag@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== - -has-unicode@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" - integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= - -has-value@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" - integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= - dependencies: - get-value "^2.0.3" - has-values "^0.1.4" - isobject "^2.0.0" - -has-value@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" - integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= - dependencies: - get-value "^2.0.6" - has-values "^1.0.0" - isobject "^3.0.0" - -has-values@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" - integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= - -has-values@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" - integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= - dependencies: - is-number "^3.0.0" - kind-of "^4.0.0" - -has-yarn@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77" - integrity sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw== - -homedir-polyfill@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8" - integrity sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA== - dependencies: - parse-passwd "^1.0.0" - -html-entities@^1.2.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.3.1.tgz#fb9a1a4b5b14c5daba82d3e34c6ae4fe701a0e44" - integrity sha512-rhE/4Z3hIhzHAUKbW8jVcCyuT5oJCXXqhN/6mXXVCpzTmvJnoH2HL/bt3EZ6p55jbFJBeAe1ZNpL5BugLujxNA== - -http-cache-semantics@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" - integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== - -http-errors@1.7.2: - version "1.7.2" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" - integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.1" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.0" - -http-errors@~1.7.2: - version "1.7.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" - integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== - dependencies: - depd "~1.1.2" - inherits "2.0.4" - setprototypeof "1.1.1" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.0" - -iconv-lite@0.2.11: - version "0.2.11" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.2.11.tgz#1ce60a3a57864a292d1321ff4609ca4bb965adc8" - integrity sha1-HOYKOleGSiktEyH/RgnKS7llrcg= - -iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4: - version "0.4.24" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" - integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== - dependencies: - safer-buffer ">= 2.1.2 < 3" - -ignore-by-default@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" - integrity sha1-SMptcvbGo68Aqa1K5odr44ieKwk= - -ignore-walk@^3.0.1: - version "3.0.3" - resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.3.tgz#017e2447184bfeade7c238e4aefdd1e8f95b1e37" - integrity sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw== - dependencies: - minimatch "^3.0.4" - -ignore@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" - integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== - -import-fresh@^3.0.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.1.tgz#633ff618506e793af5ac91bf48b72677e15cbe66" - integrity sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ== - dependencies: - parent-module "^1.0.0" - resolve-from "^4.0.0" - -import-lazy@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" - integrity sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM= - -imurmurhash@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" - integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@2.0.4, inherits@~2.0.3, inherits@~2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -inherits@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= - -ini@^1.3.4, ini@^1.3.5, ini@~1.3.0: - version "1.3.8" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" - integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== - -inquirer@^7.0.0: - version "7.3.3" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" - integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== - dependencies: - ansi-escapes "^4.2.1" - chalk "^4.1.0" - cli-cursor "^3.1.0" - cli-width "^3.0.0" - external-editor "^3.0.3" - figures "^3.0.0" - lodash "^4.17.19" - mute-stream "0.0.8" - run-async "^2.4.0" - rxjs "^6.6.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - through "^2.3.6" - -interpret@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9" - integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw== - -ipaddr.js@1.9.1: - version "1.9.1" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" - integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== - -is-absolute@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-1.0.0.tgz#395e1ae84b11f26ad1795e73c17378e48a301576" - integrity sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA== - dependencies: - is-relative "^1.0.0" - is-windows "^1.0.1" - -is-accessor-descriptor@^0.1.6: - version "0.1.6" - resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" - integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= - dependencies: - kind-of "^3.0.2" - -is-accessor-descriptor@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" - integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== - dependencies: - kind-of "^6.0.0" - -is-arrayish@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" - integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= - -is-binary-path@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" - integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== - dependencies: - binary-extensions "^2.0.0" - -is-buffer@^1.1.5: - version "1.1.6" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" - integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== - -is-ci@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" - integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== - dependencies: - ci-info "^2.0.0" - -is-data-descriptor@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" - integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= - dependencies: - kind-of "^3.0.2" - -is-data-descriptor@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" - integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== - dependencies: - kind-of "^6.0.0" - -is-descriptor@^0.1.0: - version "0.1.6" - resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" - integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== - dependencies: - is-accessor-descriptor "^0.1.6" - is-data-descriptor "^0.1.4" - kind-of "^5.0.0" - -is-descriptor@^1.0.0, is-descriptor@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" - integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== - dependencies: - is-accessor-descriptor "^1.0.0" - is-data-descriptor "^1.0.0" - kind-of "^6.0.2" - -is-extendable@^0.1.0, is-extendable@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" - integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= - -is-extendable@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" - integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== - dependencies: - is-plain-object "^2.0.4" - -is-extglob@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= - -is-fullwidth-code-point@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" - integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= - dependencies: - number-is-nan "^1.0.0" - -is-fullwidth-code-point@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" - integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= - -is-fullwidth-code-point@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" - integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== - -is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" - integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== - dependencies: - is-extglob "^2.1.1" - -is-installed-globally@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.3.2.tgz#fd3efa79ee670d1187233182d5b0a1dd00313141" - integrity sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g== - dependencies: - global-dirs "^2.0.1" - is-path-inside "^3.0.1" - -is-npm@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-4.0.0.tgz#c90dd8380696df87a7a6d823c20d0b12bbe3c84d" - integrity sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig== - -is-number@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" - integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= - dependencies: - kind-of "^3.0.2" - -is-number@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" - integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== - -is-obj@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" - integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== - -is-path-inside@^3.0.1: - version "3.0.2" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.2.tgz#f5220fc82a3e233757291dddc9c5877f2a1f3017" - integrity sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg== - -is-plain-object@^2.0.3, is-plain-object@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" - integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== - dependencies: - isobject "^3.0.1" - -is-relative@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-1.0.0.tgz#a1bb6935ce8c5dba1e8b9754b9b2dcc020e2260d" - integrity sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA== - dependencies: - is-unc-path "^1.0.0" - -is-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" - integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== - -is-typedarray@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= - -is-unc-path@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-unc-path/-/is-unc-path-1.0.0.tgz#d731e8898ed090a12c352ad2eaed5095ad322c9d" - integrity sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ== - dependencies: - unc-path-regex "^0.1.2" - -is-windows@^1.0.1, is-windows@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" - integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== - -is-yarn-global@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232" - integrity sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw== - -isarray@1.0.0, isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= - -isobject@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" - integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= - dependencies: - isarray "1.0.0" - -isobject@^3.0.0, isobject@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" - integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= - -js-tokens@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - -js-yaml@^3.13.1: - version "3.14.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" - integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - -json-buffer@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" - integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= - -json-parse-better-errors@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" - integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== - -json-schema-ref-parser@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/json-schema-ref-parser/-/json-schema-ref-parser-8.0.0.tgz#7c758fac2cf822c05e837abd0a13f8fa2c15ffd4" - integrity sha512-2P4icmNkZLrBr6oa5gSZaDSol/oaBHYkoP/8dsw63E54NnHGRhhiFuy9yFoxPuSm+uHKmeGxAAWMDF16SCHhcQ== - dependencies: - "@apidevtools/json-schema-ref-parser" "8.0.0" - -json-schema-traverse@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" - integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== - -json-stable-stringify-without-jsonify@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" - integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= - -json5@^2.1.1: - version "2.1.3" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43" - integrity sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA== - dependencies: - minimist "^1.2.5" - -jsonwebtoken@^8.5.1: - version "8.5.1" - resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" - integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w== - dependencies: - jws "^3.2.2" - lodash.includes "^4.3.0" - lodash.isboolean "^3.0.3" - lodash.isinteger "^4.0.4" - lodash.isnumber "^3.0.3" - lodash.isplainobject "^4.0.6" - lodash.isstring "^4.0.1" - lodash.once "^4.0.0" - ms "^2.1.1" - semver "^5.6.0" - -jwa@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" - integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== - dependencies: - buffer-equal-constant-time "1.0.1" - ecdsa-sig-formatter "1.0.11" - safe-buffer "^5.0.1" - -jws@^3.2.2: - version "3.2.2" - resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" - integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== - dependencies: - jwa "^1.4.1" - safe-buffer "^5.0.1" - -keyv@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" - integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== - dependencies: - json-buffer "3.0.0" - -kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: - version "3.2.2" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" - integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= - dependencies: - is-buffer "^1.1.5" - -kind-of@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" - integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= - dependencies: - is-buffer "^1.1.5" - -kind-of@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" - integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== - -kind-of@^6.0.0, kind-of@^6.0.2: - version "6.0.3" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" - integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== - -knex@^0.20.13: - version "0.20.15" - resolved "https://registry.yarnpkg.com/knex/-/knex-0.20.15.tgz#b7e9e1efd9cf35d214440d9439ed21153574679d" - integrity sha512-WHmvgfQfxA5v8pyb9zbskxCS1L1WmYgUbwBhHojlkmdouUOazvroUWlCr6KIKMQ8anXZh1NXOOtIUMnxENZG5Q== - dependencies: - colorette "1.1.0" - commander "^4.1.1" - debug "4.1.1" - esm "^3.2.25" - getopts "2.2.5" - inherits "~2.0.4" - interpret "^2.0.0" - liftoff "3.1.0" - lodash "^4.17.15" - mkdirp "^0.5.1" - pg-connection-string "2.1.0" - tarn "^2.0.0" - tildify "2.0.0" - uuid "^7.0.1" - v8flags "^3.1.3" - -latest-version@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face" - integrity sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA== - dependencies: - package-json "^6.3.0" - -levn@^0.3.0, levn@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= - dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" - -liftoff@3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/liftoff/-/liftoff-3.1.0.tgz#c9ba6081f908670607ee79062d700df062c52ed3" - integrity sha512-DlIPlJUkCV0Ips2zf2pJP0unEoT1kwYhiiPUGF3s/jtxTCjziNLoiVVh+jqWOWeFi6mmwQ5fNxvAUyPad4Dfog== - dependencies: - extend "^3.0.0" - findup-sync "^3.0.0" - fined "^1.0.1" - flagged-respawn "^1.0.0" - is-plain-object "^2.0.4" - object.map "^1.0.0" - rechoir "^0.6.2" - resolve "^1.1.7" - -liquidjs@^9.11.10: - version "9.15.0" - resolved "https://registry.yarnpkg.com/liquidjs/-/liquidjs-9.15.0.tgz#03e8c13aeda89801a346c614b0802f320458d0ac" - integrity sha512-wRPNfMx6X3GGEDqTlBpw7VMo8ylKkzLYTcd7eeaDeYnZyR5BqUgF9tZy3FdPCHV2N/BassGKmlmlpJiRXGFOqg== - -load-json-file@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" - integrity sha1-L19Fq5HjMhYjT9U62rZo607AmTs= - dependencies: - graceful-fs "^4.1.2" - parse-json "^4.0.0" - pify "^3.0.0" - strip-bom "^3.0.0" - -locate-path@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" - integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= - dependencies: - p-locate "^2.0.0" - path-exists "^3.0.0" - -locate-path@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" - integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== - dependencies: - p-locate "^4.1.0" - -lodash.includes@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" - integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8= - -lodash.isboolean@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" - integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY= - -lodash.isinteger@^4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" - integrity sha1-YZwK89A/iwTDH1iChAt3sRzWg0M= - -lodash.isnumber@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" - integrity sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w= - -lodash.isplainobject@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" - integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= - -lodash.isstring@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" - integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= - -lodash.once@^4.0.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" - integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= - -lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== - -lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" - integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== - -lowercase-keys@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" - integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== - -make-dir@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" - integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== - dependencies: - semver "^6.0.0" - -make-iterator@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/make-iterator/-/make-iterator-1.0.1.tgz#29b33f312aa8f547c4a5e490f56afcec99133ad6" - integrity sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw== - dependencies: - kind-of "^6.0.2" - -map-cache@^0.2.0, map-cache@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" - integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= - -map-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" - integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= - dependencies: - object-visit "^1.0.0" - -media-typer@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" - integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= - -merge-descriptors@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" - integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= - -merge@^1.1.3: - version "1.2.1" - resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.1.tgz#38bebf80c3220a8a487b6fcfb3941bb11720c145" - integrity sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ== - -methods@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" - integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= - -micromatch@^3.0.4: - version "3.1.10" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" - integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== - dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - braces "^2.3.1" - define-property "^2.0.2" - extend-shallow "^3.0.2" - extglob "^2.0.4" - fragment-cache "^0.2.1" - kind-of "^6.0.2" - nanomatch "^1.2.9" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.2" - -mime-db@1.44.0, "mime-db@>= 1.43.0 < 2": - version "1.44.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" - integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== - -mime-types@~2.1.24: - version "2.1.27" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" - integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w== - dependencies: - mime-db "1.44.0" - -mime@1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" - integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== - -mimic-fn@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" - integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== - -mimic-response@^1.0.0, mimic-response@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" - integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== - -minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== - dependencies: - brace-expansion "^1.1.7" - -minimist@^1.2.0, minimist@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== - -minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: - version "2.9.0" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" - integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== - dependencies: - safe-buffer "^5.1.2" - yallist "^3.0.0" - -minizlib@^1.2.1: - version "1.3.3" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" - integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== - dependencies: - minipass "^2.9.0" - -mixin-deep@^1.2.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" - integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== - dependencies: - for-in "^1.0.2" - is-extendable "^1.0.1" - -mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3: - version "0.5.5" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" - integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== - dependencies: - minimist "^1.2.5" - -moment@^2.24.0: - version "2.27.0" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.27.0.tgz#8bff4e3e26a236220dfe3e36de756b6ebaa0105d" - integrity sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ== - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= - -ms@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" - integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== - -ms@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -mute-stream@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" - integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== - -mysql@^2.18.1: - version "2.18.1" - resolved "https://registry.yarnpkg.com/mysql/-/mysql-2.18.1.tgz#2254143855c5a8c73825e4522baf2ea021766717" - integrity sha512-Bca+gk2YWmqp2Uf6k5NFEurwY/0td0cpebAucFpY/3jhrwrVGuxU2uQFCHjU19SJfje0yQvi+rVWdq78hR5lig== - dependencies: - bignumber.js "9.0.0" - readable-stream "2.3.7" - safe-buffer "5.1.2" - sqlstring "2.3.1" - -nan@^2.12.1: - version "2.14.1" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01" - integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw== - -nanomatch@^1.2.9: - version "1.2.13" - resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" - integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== - dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - define-property "^2.0.2" - extend-shallow "^3.0.2" - fragment-cache "^0.2.1" - is-windows "^1.0.2" - kind-of "^6.0.2" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -natural-compare@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" - integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= - -needle@^2.2.1, needle@^2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/needle/-/needle-2.5.0.tgz#e6fc4b3cc6c25caed7554bd613a5cf0bac8c31c0" - integrity sha512-o/qITSDR0JCyCKEQ1/1bnUXMmznxabbwi/Y4WwJElf+evwJNFNwIDMCCt5IigFVxgeGBJESLohGtIS9gEzo1fA== - dependencies: - debug "^3.2.6" - iconv-lite "^0.4.4" - sax "^1.2.4" - -negotiator@0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" - integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== - -nice-try@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" - integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== - -node-addon-api@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.0.0.tgz#812446a1001a54f71663bed188314bba07e09247" - integrity sha512-sSHCgWfJ+Lui/u+0msF3oyCgvdkhxDbkCS6Q8uiJquzOimkJBvX6hl5aSSA7DR1XbMpdM8r7phjcF63sF4rkKg== - -node-pre-gyp@0.15.0: - version "0.15.0" - resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.15.0.tgz#c2fc383276b74c7ffa842925241553e8b40f1087" - integrity sha512-7QcZa8/fpaU/BKenjcaeFF9hLz2+7S9AqyXFhlH/rilsQ/hPZKK32RtR5EQHJElgu+q5RfbJ34KriI79UWaorA== - dependencies: - detect-libc "^1.0.2" - mkdirp "^0.5.3" - needle "^2.5.0" - nopt "^4.0.1" - npm-packlist "^1.1.6" - npmlog "^4.0.2" - rc "^1.2.7" - rimraf "^2.6.1" - semver "^5.3.0" - tar "^4.4.2" - -node-pre-gyp@^0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz#db1f33215272f692cd38f03238e3e9b47c5dd054" - integrity sha512-TwWAOZb0j7e9eGaf9esRx3ZcLaE5tQ2lvYy1pb5IAaG1a2e2Kv5Lms1Y4hpj+ciXJRofIxxlt5haeQ/2ANeE0Q== - dependencies: - detect-libc "^1.0.2" - mkdirp "^0.5.1" - needle "^2.2.1" - nopt "^4.0.1" - npm-packlist "^1.1.6" - npmlog "^4.0.2" - rc "^1.2.7" - rimraf "^2.6.1" - semver "^5.3.0" - tar "^4" - -node-rsa@^1.0.8: - version "1.1.1" - resolved "https://registry.yarnpkg.com/node-rsa/-/node-rsa-1.1.1.tgz#efd9ad382097782f506153398496f79e4464434d" - integrity sha512-Jd4cvbJMryN21r5HgxQOpMEqv+ooke/korixNNK3mGqfGJmy0M77WDDzo/05969+OkMy3XW1UuZsSmW9KQm7Fw== - dependencies: - asn1 "^0.2.4" - -node-uuid@^1.4.1: - version "1.4.8" - resolved "https://registry.yarnpkg.com/node-uuid/-/node-uuid-1.4.8.tgz#b040eb0923968afabf8d32fb1f17f1167fdab907" - integrity sha1-sEDrCSOWivq/jTL7HxfxFn/auQc= - -nodemon@^2.0.2: - version "2.0.4" - resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.4.tgz#55b09319eb488d6394aa9818148c0c2d1c04c416" - integrity sha512-Ltced+hIfTmaS28Zjv1BM552oQ3dbwPqI4+zI0SLgq+wpJhSyqgYude/aZa/3i31VCQWMfXJVxvu86abcam3uQ== - dependencies: - chokidar "^3.2.2" - debug "^3.2.6" - ignore-by-default "^1.0.1" - minimatch "^3.0.4" - pstree.remy "^1.1.7" - semver "^5.7.1" - supports-color "^5.5.0" - touch "^3.1.0" - undefsafe "^2.0.2" - update-notifier "^4.0.0" - -nopt@^4.0.1: - version "4.0.3" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48" - integrity sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg== - dependencies: - abbrev "1" - osenv "^0.1.4" - -nopt@~1.0.10: - version "1.0.10" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" - integrity sha1-bd0hvSoxQXuScn3Vhfim83YI6+4= - dependencies: - abbrev "1" - -normalize-path@^3.0.0, normalize-path@~3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" - integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== - -normalize-url@^4.1.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129" - integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ== - -npm-bundled@^1.0.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.1.tgz#1edd570865a94cdb1bc8220775e29466c9fb234b" - integrity sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA== - dependencies: - npm-normalize-package-bin "^1.0.1" - -npm-normalize-package-bin@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" - integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== - -npm-packlist@^1.1.6: - version "1.4.8" - resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.8.tgz#56ee6cc135b9f98ad3d51c1c95da22bbb9b2ef3e" - integrity sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A== - dependencies: - ignore-walk "^3.0.1" - npm-bundled "^1.0.1" - npm-normalize-package-bin "^1.0.1" - -npmlog@^4.0.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" - integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== - dependencies: - are-we-there-yet "~1.1.2" - console-control-strings "~1.1.0" - gauge "~2.7.3" - set-blocking "~2.0.0" - -number-is-nan@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" - integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= - -object-assign@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= - -object-copy@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" - integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= - dependencies: - copy-descriptor "^0.1.0" - define-property "^0.2.5" - kind-of "^3.0.3" - -object-visit@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" - integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= - dependencies: - isobject "^3.0.0" - -object.defaults@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/object.defaults/-/object.defaults-1.1.0.tgz#3a7f868334b407dea06da16d88d5cd29e435fecf" - integrity sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8= - dependencies: - array-each "^1.0.1" - array-slice "^1.0.0" - for-own "^1.0.0" - isobject "^3.0.0" - -object.map@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/object.map/-/object.map-1.0.1.tgz#cf83e59dc8fcc0ad5f4250e1f78b3b81bd801d37" - integrity sha1-z4Plncj8wK1fQlDh94s7gb2AHTc= - dependencies: - for-own "^1.0.0" - make-iterator "^1.0.0" - -object.pick@^1.2.0, object.pick@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" - integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= - dependencies: - isobject "^3.0.1" - -objection@^2.1.3: - version "2.2.2" - resolved "https://registry.yarnpkg.com/objection/-/objection-2.2.2.tgz#1a3c9010270e3677940d2bc91aeaeb3c0f103800" - integrity sha512-+1Ap7u9NQRochzDW5/BggUlKi94JfZGTJwQJuNXo8DwmAb8czEirvxcWBcX91/MmQq0BQUJjM4RPSiZhnkkWQw== - dependencies: - ajv "^6.12.0" - db-errors "^0.2.3" - -on-finished@~2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" - integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= - dependencies: - ee-first "1.1.1" - -on-headers@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" - integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== - -once@^1.3.0, once@^1.3.1, once@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= - dependencies: - wrappy "1" - -onetime@^5.1.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.1.tgz#5c8016847b0d67fcedb7eef254751cfcdc7e9418" - integrity sha512-ZpZpjcJeugQfWsfyQlshVoowIIQ1qBGSVll4rfDq6JJVO//fesjoX808hXWfBjY+ROZgpKDI5TRSRBSoJiZ8eg== - dependencies: - mimic-fn "^2.1.0" - -optionator@^0.8.3: - version "0.8.3" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" - integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== - dependencies: - deep-is "~0.1.3" - fast-levenshtein "~2.0.6" - levn "~0.3.0" - prelude-ls "~1.1.2" - type-check "~0.3.2" - word-wrap "~1.2.3" - -os-homedir@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" - integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= - -os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" - integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= - -osenv@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" - integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== - dependencies: - os-homedir "^1.0.0" - os-tmpdir "^1.0.0" - -p-cancelable@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" - integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== - -p-limit@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" - integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== - dependencies: - p-try "^1.0.0" - -p-limit@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" - integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== - dependencies: - p-try "^2.0.0" - -p-locate@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" - integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= - dependencies: - p-limit "^1.1.0" - -p-locate@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" - integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== - dependencies: - p-limit "^2.2.0" - -p-try@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" - integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= - -p-try@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" - integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== - -package-json@^6.3.0: - version "6.5.0" - resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0" - integrity sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ== - dependencies: - got "^9.6.0" - registry-auth-token "^4.0.0" - registry-url "^5.0.0" - semver "^6.2.0" - -packet-reader@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/packet-reader/-/packet-reader-1.0.0.tgz#9238e5480dedabacfe1fe3f2771063f164157d74" - integrity sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ== - -parent-module@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" - integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== - dependencies: - callsites "^3.0.0" - -parse-filepath@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/parse-filepath/-/parse-filepath-1.0.2.tgz#a632127f53aaf3d15876f5872f3ffac763d6c891" - integrity sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE= - dependencies: - is-absolute "^1.0.0" - map-cache "^0.2.0" - path-root "^0.1.1" - -parse-json@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" - integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= - dependencies: - error-ex "^1.3.1" - json-parse-better-errors "^1.0.1" - -parse-passwd@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" - integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY= - -parseurl@~1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" - integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== - -pascalcase@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" - integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= - -path-exists@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" - integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= - -path-exists@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= - -path-key@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" - integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= - -path-parse@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" - integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== - -path-root-regex@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/path-root-regex/-/path-root-regex-0.1.2.tgz#bfccdc8df5b12dc52c8b43ec38d18d72c04ba96d" - integrity sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0= - -path-root@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/path-root/-/path-root-0.1.1.tgz#9a4a6814cac1c0cd73360a95f32083c8ea4745b7" - integrity sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc= - dependencies: - path-root-regex "^0.1.0" - -path-to-regexp@0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" - integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= - -path@^0.12.7: - version "0.12.7" - resolved "https://registry.yarnpkg.com/path/-/path-0.12.7.tgz#d4dc2a506c4ce2197eb481ebfcd5b36c0140b10f" - integrity sha1-1NwqUGxM4hl+tIHr/NWzbAFAsQ8= - dependencies: - process "^0.11.1" - util "^0.10.3" - -pg-connection-string@0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-0.1.3.tgz#da1847b20940e42ee1492beaf65d49d91b245df7" - integrity sha1-2hhHsglA5C7hSSvq9l1J2RskXfc= - -pg-connection-string@2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.1.0.tgz#e07258f280476540b24818ebb5dca29e101ca502" - integrity sha512-bhlV7Eq09JrRIvo1eKngpwuqKtJnNhZdpdOlvrPrA4dxqXPjxSrbNrfnIDmTpwMyRszrcV4kU5ZA4mMsQUrjdg== - -pg-int8@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/pg-int8/-/pg-int8-1.0.1.tgz#943bd463bf5b71b4170115f80f8efc9a0c0eb78c" - integrity sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw== - -pg-packet-stream@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/pg-packet-stream/-/pg-packet-stream-1.1.0.tgz#e45c3ae678b901a2873af1e17b92d787962ef914" - integrity sha512-kRBH0tDIW/8lfnnOyTwKD23ygJ/kexQVXZs7gEyBljw4FYqimZFxnMMx50ndZ8In77QgfGuItS5LLclC2TtjYg== - -pg-pool@^2.0.10: - version "2.0.10" - resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-2.0.10.tgz#842ee23b04e86824ce9d786430f8365082d81c4a" - integrity sha512-qdwzY92bHf3nwzIUcj+zJ0Qo5lpG/YxchahxIN8+ZVmXqkahKXsnl2aiJPHLYN9o5mB/leG+Xh6XKxtP7e0sjg== - -pg-types@^2.1.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/pg-types/-/pg-types-2.2.0.tgz#2d0250d636454f7cfa3b6ae0382fdfa8063254a3" - integrity sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA== - dependencies: - pg-int8 "1.0.1" - postgres-array "~2.0.0" - postgres-bytea "~1.0.0" - postgres-date "~1.0.4" - postgres-interval "^1.1.0" - -pg@^7.12.1: - version "7.18.2" - resolved "https://registry.yarnpkg.com/pg/-/pg-7.18.2.tgz#4e219f05a00aff4db6aab1ba02f28ffa4513b0bb" - integrity sha512-Mvt0dGYMwvEADNKy5PMQGlzPudKcKKzJds/VbOeZJpb6f/pI3mmoXX0JksPgI3l3JPP/2Apq7F36O63J7mgveA== - dependencies: - buffer-writer "2.0.0" - packet-reader "1.0.0" - pg-connection-string "0.1.3" - pg-packet-stream "^1.1.0" - pg-pool "^2.0.10" - pg-types "^2.1.0" - pgpass "1.x" - semver "4.3.2" - -pgpass@1.x: - version "1.0.2" - resolved "https://registry.yarnpkg.com/pgpass/-/pgpass-1.0.2.tgz#2a7bb41b6065b67907e91da1b07c1847c877b306" - integrity sha1-Knu0G2BltnkH6R2hsHwYR8h3swY= - dependencies: - split "^1.0.0" - -picomatch@^2.0.4, picomatch@^2.2.1: - version "2.2.2" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" - integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== - -pify@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" - integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= - -pkg-conf@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/pkg-conf/-/pkg-conf-2.1.0.tgz#2126514ca6f2abfebd168596df18ba57867f0058" - integrity sha1-ISZRTKbyq/69FoWW3xi6V4Z/AFg= - dependencies: - find-up "^2.0.0" - load-json-file "^4.0.0" - -posix-character-classes@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" - integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= - -postgres-array@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/postgres-array/-/postgres-array-2.0.0.tgz#48f8fce054fbc69671999329b8834b772652d82e" - integrity sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA== - -postgres-bytea@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/postgres-bytea/-/postgres-bytea-1.0.0.tgz#027b533c0aa890e26d172d47cf9ccecc521acd35" - integrity sha1-AntTPAqokOJtFy1Hz5zOzFIazTU= - -postgres-date@~1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/postgres-date/-/postgres-date-1.0.6.tgz#4925e8085b30c2ba1a06ac91b9a3473954a2ce2d" - integrity sha512-o2a4gxeFcox+CgB3Ig/kNHBP23PiEXHCXx7pcIIsvzoNz4qv+lKTyiSkjOXIMNUl12MO/mOYl2K6wR9X5K6Plg== - -postgres-interval@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/postgres-interval/-/postgres-interval-1.2.0.tgz#b460c82cb1587507788819a06aa0fffdb3544695" - integrity sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ== - dependencies: - xtend "^4.0.0" - -prelude-ls@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" - integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= - -prepend-http@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" - integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= - -prettier@^2.0.4: - version "2.0.5" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.0.5.tgz#d6d56282455243f2f92cc1716692c08aa31522d4" - integrity sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg== - -process-nextick-args@~2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" - integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== - -process@^0.11.1: - version "0.11.10" - resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" - integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= - -progress@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" - integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== - -proxy-addr@~2.0.5: - version "2.0.6" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf" - integrity sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw== - dependencies: - forwarded "~0.1.2" - ipaddr.js "1.9.1" - -pstree.remy@^1.1.7: - version "1.1.8" - resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a" - integrity sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w== - -pump@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" - integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - -punycode@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== - -pupa@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.0.1.tgz#dbdc9ff48ffbea4a26a069b6f9f7abb051008726" - integrity sha512-hEJH0s8PXLY/cdXh66tNEQGndDrIKNqNC5xmrysZy3i5C3oEoLna7YAOad+7u125+zH1HNXUmGEkrhb3c2VriA== - dependencies: - escape-goat "^2.0.0" - -qs@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-1.2.0.tgz#ed079be28682147e6fd9a34cc2b0c1e0ec6453ee" - integrity sha1-7Qeb4oaCFH5v2aNMwrDB4OxkU+4= - -qs@6.7.0: - version "6.7.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" - integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== - -querystring@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" - integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= - -range-parser@~1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" - integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== - -raw-body@2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" - integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== - dependencies: - bytes "3.1.0" - http-errors "1.7.2" - iconv-lite "0.4.24" - unpipe "1.0.0" - -rc@^1.2.7, rc@^1.2.8: - version "1.2.8" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" - integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== - dependencies: - deep-extend "^0.6.0" - ini "~1.3.0" - minimist "^1.2.0" - strip-json-comments "~2.0.1" - -readable-stream@2.3.7, readable-stream@^2.0.6: - version "2.3.7" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" - integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - -readdirp@~3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.4.0.tgz#9fdccdf9e9155805449221ac645e8303ab5b9ada" - integrity sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ== - dependencies: - picomatch "^2.2.1" - -rechoir@^0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" - integrity sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q= - dependencies: - resolve "^1.1.6" - -regex-not@^1.0.0, regex-not@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" - integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== - dependencies: - extend-shallow "^3.0.2" - safe-regex "^1.1.0" - -regexpp@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" - integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== - -registry-auth-token@^4.0.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.2.0.tgz#1d37dffda72bbecd0f581e4715540213a65eb7da" - integrity sha512-P+lWzPrsgfN+UEpDS3U8AQKg/UjZX6mQSJueZj3EK+vNESoqBSpBUD3gmu4sF9lOsjXWjF11dQKUqemf3veq1w== - dependencies: - rc "^1.2.8" - -registry-url@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-5.1.0.tgz#e98334b50d5434b81136b44ec638d9c2009c5009" - integrity sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw== - dependencies: - rc "^1.2.8" - -repeat-element@^1.1.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" - integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== - -repeat-string@^1.6.1: - version "1.6.1" - resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" - integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= - -require-directory@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= - -require-main-filename@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" - integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== - -resolve-dir@^1.0.0, resolve-dir@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-1.0.1.tgz#79a40644c362be82f26effe739c9bb5382046f43" - integrity sha1-eaQGRMNivoLybv/nOcm7U4IEb0M= - dependencies: - expand-tilde "^2.0.0" - global-modules "^1.0.0" - -resolve-from@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" - integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== - -resolve-url@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" - integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= - -resolve@^1.1.6, resolve@^1.1.7: - version "1.17.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" - integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== - dependencies: - path-parse "^1.0.6" - -responselike@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" - integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= - dependencies: - lowercase-keys "^1.0.0" - -restler@^3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/restler/-/restler-3.4.0.tgz#741ec0b3d16b949feea2813d0c3c68529e888d9b" - integrity sha1-dB7As9FrlJ/uooE9DDxoUp6IjZs= - dependencies: - iconv-lite "0.2.11" - qs "1.2.0" - xml2js "0.4.0" - yaml "0.2.3" - -restore-cursor@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" - integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== - dependencies: - onetime "^5.1.0" - signal-exit "^3.0.2" - -ret@~0.1.10: - version "0.1.15" - resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" - integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== - -rimraf@2.6.3: - version "2.6.3" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" - integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== - dependencies: - glob "^7.1.3" - -rimraf@^2.6.1: - version "2.7.1" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" - integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== - dependencies: - glob "^7.1.3" - -run-async@^2.4.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" - integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== - -rxjs@^6.6.0: - version "6.6.2" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.2.tgz#8096a7ac03f2cc4fe5860ef6e572810d9e01c0d2" - integrity sha512-BHdBMVoWC2sL26w//BCu3YzKT4s2jip/WhwsGEDmeKYBhKDZeYezVUnHatYB7L85v5xs0BAQmg6BEYJEKxBabg== - dependencies: - tslib "^1.9.0" - -safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -safe-buffer@^5.0.1, safe-buffer@^5.1.2: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -safe-regex@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" - integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= - dependencies: - ret "~0.1.10" - -"safer-buffer@>= 2.1.2 < 3", safer-buffer@~2.1.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -sax@0.5.x: - version "0.5.8" - resolved "https://registry.yarnpkg.com/sax/-/sax-0.5.8.tgz#d472db228eb331c2506b0e8c15524adb939d12c1" - integrity sha1-1HLbIo6zMcJQaw6MFVJK25OdEsE= - -sax@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" - integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== - -semver-diff@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-3.1.1.tgz#05f77ce59f325e00e2706afd67bb506ddb1ca32b" - integrity sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg== - dependencies: - semver "^6.3.0" - -semver@4.3.2: - version "4.3.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.2.tgz#c7a07158a80bedd052355b770d82d6640f803be7" - integrity sha1-x6BxWKgL7dBSNVt3DYLWZA+AO+c= - -semver@^5.3.0, semver@^5.5.0, semver@^5.6.0, semver@^5.7.1: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== - -semver@^6.0.0, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== - -send@0.17.1: - version "0.17.1" - resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" - integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== - dependencies: - debug "2.6.9" - depd "~1.1.2" - destroy "~1.0.4" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - fresh "0.5.2" - http-errors "~1.7.2" - mime "1.6.0" - ms "2.1.1" - on-finished "~2.3.0" - range-parser "~1.2.1" - statuses "~1.5.0" - -serve-static@1.14.1: - version "1.14.1" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" - integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== - dependencies: - encodeurl "~1.0.2" - escape-html "~1.0.3" - parseurl "~1.3.3" - send "0.17.1" - -set-blocking@^2.0.0, set-blocking@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" - integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= - -set-value@^2.0.0, set-value@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" - integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== - dependencies: - extend-shallow "^2.0.1" - is-extendable "^0.1.1" - is-plain-object "^2.0.3" - split-string "^3.0.1" - -setprototypeof@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" - integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== - -shebang-command@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" - integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= - dependencies: - shebang-regex "^1.0.0" - -shebang-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" - integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= - -signal-exit@^3.0.0, signal-exit@^3.0.2: - version "3.0.3" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" - integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== - -signale@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/signale/-/signale-1.4.0.tgz#c4be58302fb0262ac00fc3d886a7c113759042f1" - integrity sha512-iuh+gPf28RkltuJC7W5MRi6XAjTDCAPC/prJUpQoG4vIP3MJZ+GTydVnodXA7pwvTKb2cA0m9OFZW/cdWy/I/w== - dependencies: - chalk "^2.3.2" - figures "^2.0.0" - pkg-conf "^2.1.0" - -slice-ansi@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" - integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== - dependencies: - ansi-styles "^3.2.0" - astral-regex "^1.0.0" - is-fullwidth-code-point "^2.0.0" - -snapdragon-node@^2.0.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" - integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== - dependencies: - define-property "^1.0.0" - isobject "^3.0.0" - snapdragon-util "^3.0.1" - -snapdragon-util@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" - integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== - dependencies: - kind-of "^3.2.0" - -snapdragon@^0.8.1: - version "0.8.2" - resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" - integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== - dependencies: - base "^0.11.1" - debug "^2.2.0" - define-property "^0.2.5" - extend-shallow "^2.0.1" - map-cache "^0.2.2" - source-map "^0.5.6" - source-map-resolve "^0.5.0" - use "^3.1.0" - -source-map-resolve@^0.5.0: - version "0.5.3" - resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" - integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== - dependencies: - atob "^2.1.2" - decode-uri-component "^0.2.0" - resolve-url "^0.2.1" - source-map-url "^0.4.0" - urix "^0.1.0" - -source-map-url@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" - integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= - -source-map@^0.5.6: - version "0.5.7" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" - integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= - -split-string@^3.0.1, split-string@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" - integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== - dependencies: - extend-shallow "^3.0.0" - -split@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9" - integrity sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg== - dependencies: - through "2" - -sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= - -sqlite3@^4.1.1: - version "4.2.0" - resolved "https://registry.yarnpkg.com/sqlite3/-/sqlite3-4.2.0.tgz#49026d665e9fc4f922e56fb9711ba5b4c85c4901" - integrity sha512-roEOz41hxui2Q7uYnWsjMOTry6TcNUNmp8audCx18gF10P2NknwdpF+E+HKvz/F2NvPKGGBF4NGc+ZPQ+AABwg== - dependencies: - nan "^2.12.1" - node-pre-gyp "^0.11.0" - -sqlstring@2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/sqlstring/-/sqlstring-2.3.1.tgz#475393ff9e91479aea62dcaf0ca3d14983a7fb40" - integrity sha1-R1OT/56RR5rqYtyvDKPRSYOn+0A= - -static-extend@^0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" - integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= - dependencies: - define-property "^0.2.5" - object-copy "^0.1.0" - -"statuses@>= 1.5.0 < 2", statuses@~1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" - integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= - -streamsearch@0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a" - integrity sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo= - -string-width@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" - integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - strip-ansi "^3.0.0" - -"string-width@^1.0.2 || 2": - version "2.1.1" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" - integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== - dependencies: - is-fullwidth-code-point "^2.0.0" - strip-ansi "^4.0.0" - -string-width@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" - integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== - dependencies: - emoji-regex "^7.0.1" - is-fullwidth-code-point "^2.0.0" - strip-ansi "^5.1.0" - -string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" - integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.0" - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - -strip-ansi@^3.0.0, strip-ansi@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= - dependencies: - ansi-regex "^2.0.0" - -strip-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" - integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= - dependencies: - ansi-regex "^3.0.0" - -strip-ansi@^5.1.0, strip-ansi@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" - integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== - dependencies: - ansi-regex "^4.1.0" - -strip-ansi@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" - integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== - dependencies: - ansi-regex "^5.0.0" - -strip-ansi@~0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-0.1.1.tgz#39e8a98d044d150660abe4a6808acf70bb7bc991" - integrity sha1-OeipjQRNFQZgq+SmgIrPcLt7yZE= - -strip-bom@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" - integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= - -strip-json-comments@^3.0.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" - integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== - -strip-json-comments@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" - integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= - -supports-color@^5.3.0, supports-color@^5.5.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.0.0" - -supports-color@^7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1" - integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g== - dependencies: - has-flag "^4.0.0" - -table@^5.2.3: - version "5.4.6" - resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" - integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== - dependencies: - ajv "^6.10.2" - lodash "^4.17.14" - slice-ansi "^2.1.0" - string-width "^3.0.0" - -tar@^4, tar@^4.4.2: - version "4.4.13" - resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" - integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA== - dependencies: - chownr "^1.1.1" - fs-minipass "^1.2.5" - minipass "^2.8.6" - minizlib "^1.2.1" - mkdirp "^0.5.0" - safe-buffer "^5.1.2" - yallist "^3.0.3" - -tarn@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/tarn/-/tarn-2.0.0.tgz#c68499f69881f99ae955b4317ca7d212d942fdee" - integrity sha512-7rNMCZd3s9bhQh47ksAQd92ADFcJUjjbyOvyFjNLwTPpGieFHMC84S+LOzw0fx1uh6hnDz/19r8CPMnIjJlMMA== - -temp-dir@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-1.0.0.tgz#0a7c0ea26d3a39afa7e0ebea9c1fc0bc4daa011d" - integrity sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0= - -temp-write@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/temp-write/-/temp-write-4.0.0.tgz#cd2e0825fc826ae72d201dc26eef3bf7e6fc9320" - integrity sha512-HIeWmj77uOOHb0QX7siN3OtwV3CTntquin6TNVg6SHOqCP3hYKmox90eeFOGaY1MqJ9WYDDjkyZrW6qS5AWpbw== - dependencies: - graceful-fs "^4.1.15" - is-stream "^2.0.0" - make-dir "^3.0.0" - temp-dir "^1.0.0" - uuid "^3.3.2" - -term-size@^2.1.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/term-size/-/term-size-2.2.0.tgz#1f16adedfe9bdc18800e1776821734086fcc6753" - integrity sha512-a6sumDlzyHVJWb8+YofY4TW112G6p2FCPEAFk+59gIYHv3XHRhm9ltVQ9kli4hNWeQBwSpe8cRN25x0ROunMOw== - -text-table@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= - -through@2, through@^2.3.6: - version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= - -tildify@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/tildify/-/tildify-2.0.0.tgz#f205f3674d677ce698b7067a99e949ce03b4754a" - integrity sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw== - -tmp@^0.0.33: - version "0.0.33" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" - integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== - dependencies: - os-tmpdir "~1.0.2" - -to-object-path@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" - integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= - dependencies: - kind-of "^3.0.2" - -to-readable-stream@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" - integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== - -to-regex-range@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" - integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= - dependencies: - is-number "^3.0.0" - repeat-string "^1.6.1" - -to-regex-range@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" - integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== - dependencies: - is-number "^7.0.0" - -to-regex@^3.0.1, to-regex@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" - integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== - dependencies: - define-property "^2.0.2" - extend-shallow "^3.0.2" - regex-not "^1.0.2" - safe-regex "^1.1.0" - -toidentifier@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" - integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== - -touch@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.0.tgz#fe365f5f75ec9ed4e56825e0bb76d24ab74af83b" - integrity sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA== - dependencies: - nopt "~1.0.10" - -tslib@^1.9.0: - version "1.13.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" - integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== - -type-check@~0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" - integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= - dependencies: - prelude-ls "~1.1.2" - -type-fest@^0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" - integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== - -type-fest@^0.8.1: - version "0.8.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" - integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== - -type-is@~1.6.17, type-is@~1.6.18: - version "1.6.18" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" - integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== - dependencies: - media-typer "0.3.0" - mime-types "~2.1.24" - -typedarray-to-buffer@^3.1.5: - version "3.1.5" - resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" - integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== - dependencies: - is-typedarray "^1.0.0" - -unc-path-regex@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" - integrity sha1-5z3T17DXxe2G+6xrCufYxqadUPo= - -undefsafe@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.3.tgz#6b166e7094ad46313b2202da7ecc2cd7cc6e7aae" - integrity sha512-nrXZwwXrD/T/JXeygJqdCO6NZZ1L66HrxM/Z7mIq2oPanoN0F1nLx3lwJMu6AwJY69hdixaFQOuoYsMjE5/C2A== - dependencies: - debug "^2.2.0" - -union-value@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" - integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== - dependencies: - arr-union "^3.1.0" - get-value "^2.0.6" - is-extendable "^0.1.1" - set-value "^2.0.1" - -unique-string@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" - integrity sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg== - dependencies: - crypto-random-string "^2.0.0" - -unix-timestamp@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/unix-timestamp/-/unix-timestamp-0.2.0.tgz#e1cdc2808df6327d27e635d9351e72815288733e" - integrity sha1-4c3CgI32Mn0n5jXZNR5ygVKIcz4= - -unpipe@1.0.0, unpipe@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" - integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= - -unset-value@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" - integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= - dependencies: - has-value "^0.3.1" - isobject "^3.0.0" - -update-notifier@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-4.1.0.tgz#4866b98c3bc5b5473c020b1250583628f9a328f3" - integrity sha512-w3doE1qtI0/ZmgeoDoARmI5fjDoT93IfKgEGqm26dGUOh8oNpaSTsGNdYRN/SjOuo10jcJGwkEL3mroKzktkew== - dependencies: - boxen "^4.2.0" - chalk "^3.0.0" - configstore "^5.0.1" - has-yarn "^2.1.0" - import-lazy "^2.1.0" - is-ci "^2.0.0" - is-installed-globally "^0.3.1" - is-npm "^4.0.0" - is-yarn-global "^0.3.0" - latest-version "^5.0.0" - pupa "^2.0.1" - semver-diff "^3.1.1" - xdg-basedir "^4.0.0" - -uri-js@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" - integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== - dependencies: - punycode "^2.1.0" - -urix@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" - integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= - -url-parse-lax@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" - integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= - dependencies: - prepend-http "^2.0.0" - -use@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" - integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== - -util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= - -util@^0.10.3: - version "0.10.4" - resolved "https://registry.yarnpkg.com/util/-/util-0.10.4.tgz#3aa0125bfe668a4672de58857d3ace27ecb76901" - integrity sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A== - dependencies: - inherits "2.0.3" - -utils-merge@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" - integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= - -uuid@^3.3.2: - version "3.4.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" - integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== - -uuid@^7.0.1: - version "7.0.3" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b" - integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg== - -v8-compile-cache@^2.0.3: - version "2.1.1" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz#54bc3cdd43317bca91e35dcaf305b1a7237de745" - integrity sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ== - -v8flags@^3.1.3: - version "3.2.0" - resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-3.2.0.tgz#b243e3b4dfd731fa774e7492128109a0fe66d656" - integrity sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg== - dependencies: - homedir-polyfill "^1.0.1" - -vary@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" - integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= - -which-module@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" - integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= - -which@^1.2.14, which@^1.2.9: - version "1.3.1" - resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" - integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== - dependencies: - isexe "^2.0.0" - -wide-align@^1.1.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" - integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== - dependencies: - string-width "^1.0.2 || 2" - -widest-line@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" - integrity sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg== - dependencies: - string-width "^4.0.0" - -word-wrap@~1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" - integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== - -wrap-ansi@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" - integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= - -write-file-atomic@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" - integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== - dependencies: - imurmurhash "^0.1.4" - is-typedarray "^1.0.0" - signal-exit "^3.0.2" - typedarray-to-buffer "^3.1.5" - -write@1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" - integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== - dependencies: - mkdirp "^0.5.1" - -xdg-basedir@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" - integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q== - -xml2js@0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.0.tgz#124fc4114b4129c810800ecb2ac86cf25462cb9a" - integrity sha1-Ek/EEUtBKcgQgA7LKshs8lRiy5o= - dependencies: - sax "0.5.x" - xmlbuilder ">=0.4.2" - -xmlbuilder@>=0.4.2: - version "15.1.1" - resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-15.1.1.tgz#9dcdce49eea66d8d10b42cae94a79c3c8d0c2ec5" - integrity sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg== - -xtend@^4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" - integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== - -y18n@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.1.tgz#8db2b83c31c5d75099bb890b23f3094891e247d4" - integrity sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ== - -yallist@^3.0.0, yallist@^3.0.3: - version "3.1.1" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" - integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== - -yaml@0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-0.2.3.tgz#b5450e92e76ef36b5dd24e3660091ebaeef3e5c7" - integrity sha1-tUUOkudu82td0k42YAkeuu7z5cc= - -yargs-parser@^18.1.2: - version "18.1.3" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" - integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" - -yargs@^15.4.1: - version "15.4.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" - integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== - dependencies: - cliui "^6.0.0" - decamelize "^1.2.0" - find-up "^4.1.0" - get-caller-file "^2.0.1" - require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^4.2.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^18.1.2" diff --git a/docker/Dockerfile b/docker/Dockerfile index d85782b6..5ccdc947 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -3,54 +3,105 @@ # This file assumes that the frontend has been built using ./scripts/frontend-build -FROM jc21/nginx-full:node +#=============== +# gobuild +#=============== + +FROM jc21/nginx-full:github-acme.sh-golang AS gobuild + +SHELL ["/bin/bash", "-o", "pipefail", "-c"] + +ARG GOPROXY +ARG GOPRIVATE + +ENV GOPROXY=$GOPROXY \ + GOPRIVATE=$GOPRIVATE \ + GO111MODULE=on \ + CGO_ENABLED=1 + +# Nancy +RUN go get github.com/sonatype-nexus-community/nancy +RUN mkdir -p /workspace +WORKDIR /workspace +COPY backend/go.mod backend/go.sum backend/.nancy-ignore ./ +RUN go mod download + +ARG NANCY_TOKEN +ARG NANCY_USER +RUN go list -json -m all | nancy sleuth --quiet --username "${NANCY_USER}" --token "${NANCY_TOKEN}" +RUN rm -rf /workspace + +# Code +WORKDIR /app +COPY . . +WORKDIR /app/backend + +# Build +RUN go mod download +RUN echo "Testing and compiling project" \ + && [ -z "$(go tool fix -diff ./internal)" ] + +# Disabled as CI has issues at the moment +#RUN if [ "$TARGETPLATFORM" == "" ] || [ "$TARGETPLATFORM" == "linux/amd64" ]; then golangci-lint -v run ./...; fi + +RUN richgo test -cover -v ./internal/... +RUN richgo test -bench=. ./internal/... -ARG TARGETPLATFORM ARG BUILD_VERSION ARG BUILD_COMMIT -ARG BUILD_DATE +ARG SENTRY_DSN +RUN go build \ + -ldflags "-w -s -X main.commit=${BUILD_COMMIT} -X main.version=${BUILD_VERSION} -X main.sentryDSN=${SENTRY_DSN:-}" \ + -o ../dist/bin/server \ + -v ./cmd/server -ENV SUPPRESS_NO_CONFIG_WARNING=1 \ - S6_FIX_ATTRS_HIDDEN=1 \ - S6_BEHAVIOUR_IF_STAGE2_FAILS=1 \ - NODE_ENV=production \ - NPM_BUILD_VERSION="${BUILD_VERSION}" \ - NPM_BUILD_COMMIT="${BUILD_COMMIT}" \ - NPM_BUILD_DATE="${BUILD_DATE}" +#=============== +# Final image +#=============== -RUN echo "fs.file-max = 65535" > /etc/sysctl.conf \ - && apt-get update \ - && apt-get install -y --no-install-recommends jq \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* +FROM jc21/nginx-full:github-acme.sh + +COPY --from=gobuild /app/dist /app +COPY --from=gobuild /app/backend/migrations /app/migrations +# COPY frontend/build /app/frontend + +ENV SUPPRESS_NO_CONFIG_WARNING=1 +ENV S6_FIX_ATTRS_HIDDEN=1 +RUN echo "fs.file-max = 65535" > /etc/sysctl.conf # s6 overlay -COPY scripts/install-s6 /tmp/install-s6 -RUN /tmp/install-s6 "${TARGETPLATFORM}" && rm -f /tmp/install-s6 +RUN curl -L -o /tmp/s6-overlay-amd64.tar.gz "https://github.com/just-containers/s6-overlay/releases/download/v1.22.1.0/s6-overlay-amd64.tar.gz" \ + && tar -xzf /tmp/s6-overlay-amd64.tar.gz -C / -EXPOSE 80 81 443 +EXPOSE 80/tcp 81/tcp 443/tcp -COPY backend /app -COPY frontend/dist /app/frontend -COPY global /app/global - -WORKDIR /app -RUN yarn install - -# add late to limit cache-busting by modifications COPY docker/rootfs / # Remove frontend service not required for prod, dev nginx config as well RUN rm -rf /etc/services.d/frontend /etc/nginx/conf.d/dev.conf -VOLUME [ "/data", "/etc/letsencrypt" ] -ENTRYPOINT [ "/init" ] -HEALTHCHECK --interval=5s --timeout=3s CMD /bin/check-health +VOLUME /data + +CMD [ "/init" ] +HEALTHCHECK --interval=15s --timeout=3s CMD curl -f http://127.0.0.1:81/api || exit 1 + +ARG NOW +ARG BUILD_VERSION +ARG BUILD_COMMIT +ARG BUILD_DATE +ENV NPM_BUILD_VERSION="${BUILD_VERSION}" NPM_BUILD_COMMIT="${BUILD_COMMIT}" NPM_BUILD_DATE="${BUILD_DATE}" +ENV DATABASE_URL="sqlite:////data/nginxproxymanager.db" \ + DBMATE_MIGRATIONS_DIR="/app/migrations" \ + DBMATE_SCHEMA_FILE="/data/schema.sql" \ + DBMATE_NO_DUMP_SCHEMA="1" LABEL org.label-schema.schema-version="1.0" \ org.label-schema.license="MIT" \ org.label-schema.name="nginx-proxy-manager" \ - org.label-schema.description="Docker container for managing Nginx proxy hosts with a simple, powerful interface " \ - org.label-schema.url="https://github.com/jc21/nginx-proxy-manager" \ + org.label-schema.description="Nginx Host Management and Proxy" \ + org.label-schema.build-date="$NOW" \ + org.label-schema.version="$BUILD_VERSION" \ + org.label-schema.url="https://nginxproxymanager.com" \ org.label-schema.vcs-url="https://github.com/jc21/nginx-proxy-manager.git" \ - org.label-schema.cmd="docker run --rm -ti jc21/nginx-proxy-manager:latest" + org.label-schema.vcs-ref="$BUILD_COMMIT" \ + org.label-schema.cmd="docker run --rm -ti jc21/nginx-proxy-manager:$BUILD_VERSION" diff --git a/docker/dev/Dockerfile b/docker/dev/Dockerfile index ae17e861..f868356a 100644 --- a/docker/dev/Dockerfile +++ b/docker/dev/Dockerfile @@ -1,15 +1,30 @@ -FROM jc21/nginx-full:node +FROM jc21/nginx-full:github-acme.sh-golang LABEL maintainer="Jamie Curnow " -ENV S6_LOGGING=0 \ - SUPPRESS_NO_CONFIG_WARNING=1 \ - S6_FIX_ATTRS_HIDDEN=1 +SHELL ["/bin/bash", "-o", "pipefail", "-c"] -RUN echo "fs.file-max = 65535" > /etc/sysctl.conf \ +ARG GOPROXY +ARG GOPRIVATE + +ENV GOPROXY=$GOPROXY \ + GOPRIVATE=$GOPRIVATE \ + S6_LOGGING=0 \ + SUPPRESS_NO_CONFIG_WARNING=1 \ + S6_FIX_ATTRS_HIDDEN=1 \ + DATABASE_URL="sqlite:////data/nginxproxymanager.db" \ + DBMATE_MIGRATIONS_DIR="/app/backend/migrations" \ + DBMATE_SCHEMA_FILE="/data/schema.sql" + +RUN echo "fs.file-max = 65535" > /etc/sysctl.conf + +# Sqlite client and litecli client, and node +RUN curl -fsSL https://deb.nodesource.com/setup_14.x | bash - \ && apt-get update \ - && apt-get install -y certbot jq python3-pip \ + && apt-get install -y --no-install-recommends nodejs \ && apt-get clean \ - && rm -rf /var/lib/apt/lists/* + && rm -rf /var/lib/apt/lists/* \ + && pip install -U litecli \ + && npm install -g yarn # Task RUN cd /usr \ @@ -23,6 +38,9 @@ RUN rm -f /etc/nginx/conf.d/production.conf RUN curl -L -o /tmp/s6-overlay-amd64.tar.gz "https://github.com/just-containers/s6-overlay/releases/download/v1.22.1.0/s6-overlay-amd64.tar.gz" \ && tar -xzf /tmp/s6-overlay-amd64.tar.gz -C / -EXPOSE 80 81 443 -ENTRYPOINT [ "/init" ] -HEALTHCHECK --interval=5s --timeout=3s CMD /bin/check-health +# Fix for golang dev: +RUN chown -R 1000:1000 /opt/go + +EXPOSE 80 +CMD [ "/init" ] +HEALTHCHECK --interval=15s --timeout=3s CMD curl -f http://127.0.0.1:81/api || exit 1 diff --git a/docker/docker-compose.ci.yml b/docker/docker-compose.ci.yml index 771b8299..9412f302 100644 --- a/docker/docker-compose.ci.yml +++ b/docker/docker-compose.ci.yml @@ -2,71 +2,25 @@ version: "3" services: - fullstack-mysql: + fullstack: image: ${IMAGE}:ci-${BUILD_NUMBER} environment: - NODE_ENV: "development" - FORCE_COLOR: 1 - DB_MYSQL_HOST: "db" - DB_MYSQL_PORT: 3306 - DB_MYSQL_USER: "npm" - DB_MYSQL_PASSWORD: "npm" - DB_MYSQL_NAME: "npm" + - LOG_LEVEL=debug volumes: - - npm_data:/data - expose: - - 81 - - 80 - - 443 - depends_on: - - db + - npm_data_ci:/data + - ../docs:/temp-docs - fullstack-sqlite: - image: ${IMAGE}:ci-${BUILD_NUMBER} - environment: - NODE_ENV: "development" - FORCE_COLOR: 1 - DB_SQLITE_FILE: "/data/database.sqlite" - volumes: - - npm_data:/data - expose: - - 81 - - 80 - - 443 - - db: - image: jc21/mariadb-aria - environment: - MYSQL_ROOT_PASSWORD: "npm" - MYSQL_DATABASE: "npm" - MYSQL_USER: "npm" - MYSQL_PASSWORD: "npm" - volumes: - - db_data:/var/lib/mysql - - cypress-mysql: + cypress: image: ${IMAGE}-cypress:ci-${BUILD_NUMBER} build: - context: ../test/ - dockerfile: cypress/Dockerfile + context: ../ + dockerfile: test/cypress/Dockerfile environment: - CYPRESS_baseUrl: "http://fullstack-mysql:81" - volumes: - - cypress-logs:/results - command: cypress run --browser chrome --config-file=${CYPRESS_CONFIG:-cypress/config/ci.json} - - cypress-sqlite: - image: ${IMAGE}-cypress:ci-${BUILD_NUMBER} - build: - context: ../test/ - dockerfile: cypress/Dockerfile - environment: - CYPRESS_baseUrl: "http://fullstack-sqlite:81" + CYPRESS_baseUrl: "http://fullstack:81" volumes: - cypress-logs:/results command: cypress run --browser chrome --config-file=${CYPRESS_CONFIG:-cypress/config/ci.json} volumes: cypress-logs: - npm_data: - db_data: + npm_data_ci: diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml index a0b4547b..25c60dd7 100644 --- a/docker/docker-compose.dev.yml +++ b/docker/docker-compose.dev.yml @@ -1,4 +1,4 @@ -# WARNING: This is a DEVELOPMENT docker-compose file, it should not be used for production. +# WARNING: This is a DEVELOPMENT docker-compose file used for development of the entire app, it should not be used for production. version: "3" services: @@ -11,57 +11,25 @@ services: - 3080:80 - 3081:81 - 3443:443 - networks: - - nginx_proxy_manager environment: - NODE_ENV: "development" - FORCE_COLOR: 1 - DEVELOPMENT: "true" - DB_MYSQL_HOST: "db" - DB_MYSQL_PORT: 3306 - DB_MYSQL_USER: "npm" - DB_MYSQL_PASSWORD: "npm" - DB_MYSQL_NAME: "npm" - # DB_SQLITE_FILE: "/data/database.sqlite" - # DISABLE_IPV6: "true" + - DEVELOPMENT=true + - GOPROXY=${GOPROXY:-} + - GOPRIVATE=${GOPRIVATE:-} + - LOG_LEVEL=debug + - PUID=1000 + - PGID=1000 volumes: - - npm_data:/data - - le_data:/etc/letsencrypt - - ../backend:/app - - ../frontend:/app/frontend - - ../global:/app/global - depends_on: - - db + - ../:/app + - ./rootfs/var/www/html:/var/www/html + - ../data:/data working_dir: /app - db: - image: jc21/mariadb-aria - networks: - - nginx_proxy_manager - environment: - MYSQL_ROOT_PASSWORD: "npm" - MYSQL_DATABASE: "npm" - MYSQL_USER: "npm" - MYSQL_PASSWORD: "npm" - volumes: - - db_data:/var/lib/mysql - swagger: image: 'swaggerapi/swagger-ui:latest' ports: - 3001:80 - networks: - - nginx_proxy_manager environment: - URL: "http://127.0.0.1:3081/api/schema" + URL: "http://${SWAGGER_PUBLIC_DOMAIN:-127.0.0.1:3081}/api/schema" PORT: '80' depends_on: - npm - -volumes: - npm_data: - le_data: - db_data: - -networks: - nginx_proxy_manager: diff --git a/docker/rootfs/bin/check-health b/docker/rootfs/bin/check-health deleted file mode 100755 index bcf5552b..00000000 --- a/docker/rootfs/bin/check-health +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -OK=$(curl --silent http://127.0.0.1:81/api/ | jq --raw-output '.status') - -if [ "$OK" == "OK" ]; then - echo "OK" - exit 0 -else - echo "NOT OK" - exit 1 -fi diff --git a/docker/rootfs/bin/handle-ipv6-setting b/docker/rootfs/bin/handle-ipv6-setting deleted file mode 100755 index 2aa0e41a..00000000 --- a/docker/rootfs/bin/handle-ipv6-setting +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/bash - -# This command reads the `DISABLE_IPV6` env var and will either enable -# or disable ipv6 in all nginx configs based on this setting. - -# Lowercase -DISABLE_IPV6=$(echo "${DISABLE_IPV6:-}" | tr '[:upper:]' '[:lower:]') - -CYAN='\E[1;36m' -BLUE='\E[1;34m' -YELLOW='\E[1;33m' -RED='\E[1;31m' -RESET='\E[0m' - -FOLDER=$1 -if [ "$FOLDER" == "" ]; then - echo -e "${RED}❯ $0 requires a absolute folder path as the first argument!${RESET}" - echo -e "${YELLOW} ie: $0 /data/nginx${RESET}" - exit 1 -fi - -FILES=$(find "$FOLDER" -type f -name "*.conf") -if [ "$DISABLE_IPV6" == "true" ] || [ "$DISABLE_IPV6" == "on" ] || [ "$DISABLE_IPV6" == "1" ] || [ "$DISABLE_IPV6" == "yes" ]; then - # IPV6 is disabled - echo "Disabling IPV6 in hosts" - echo -e "${BLUE}❯ ${CYAN}Disabling IPV6 in hosts: ${YELLOW}${FOLDER}${RESET}" - - # Iterate over configs and run the regex - for FILE in $FILES - do - echo -e " ${BLUE}❯ ${YELLOW}${FILE}${RESET}" - sed -E -i 's/^([^#]*)listen \[::\]/\1#listen [::]/g' "$FILE" - done - -else - # IPV6 is enabled - echo -e "${BLUE}❯ ${CYAN}Enabling IPV6 in hosts: ${YELLOW}${FOLDER}${RESET}" - - # Iterate over configs and run the regex - for FILE in $FILES - do - echo -e " ${BLUE}❯ ${YELLOW}${FILE}${RESET}" - sed -E -i 's/^(\s*)#listen \[::\]/\1listen [::]/g' "$FILE" - done - -fi diff --git a/docker/rootfs/etc/cont-init.d/.gitignore b/docker/rootfs/etc/cont-init.d/.gitignore deleted file mode 100644 index f04f0f6e..00000000 --- a/docker/rootfs/etc/cont-init.d/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -* -!.gitignore -!*.sh diff --git a/docker/rootfs/etc/cont-init.d/01_s6-secret-init.sh b/docker/rootfs/etc/cont-init.d/01_s6-secret-init.sh deleted file mode 100644 index f145807a..00000000 --- a/docker/rootfs/etc/cont-init.d/01_s6-secret-init.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/with-contenv bash -# ref: https://github.com/linuxserver/docker-baseimage-alpine/blob/master/root/etc/cont-init.d/01-envfile - -# in s6, environmental variables are written as text files for s6 to monitor -# seach through full-path filenames for files ending in "__FILE" -for FILENAME in $(find /var/run/s6/container_environment/ | grep "__FILE$"); do - echo "[secret-init] Evaluating ${FILENAME##*/} ..." - - # set SECRETFILE to the contents of the full-path textfile - SECRETFILE=$(cat ${FILENAME}) - # SECRETFILE=${FILENAME} - # echo "[secret-init] Set SECRETFILE to ${SECRETFILE}" # DEBUG - rm for prod! - - # if SECRETFILE exists / is not null - if [[ -f ${SECRETFILE} ]]; then - # strip the appended "__FILE" from environmental variable name ... - STRIPFILE=$(echo ${FILENAME} | sed "s/__FILE//g") - # echo "[secret-init] Set STRIPFILE to ${STRIPFILE}" # DEBUG - rm for prod! - - # ... and set value to contents of secretfile - # since s6 uses text files, this is effectively "export ..." - printf $(cat ${SECRETFILE}) > ${STRIPFILE} - # echo "[secret-init] Set ${STRIPFILE##*/} to $(cat ${STRIPFILE})" # DEBUG - rm for prod!" - echo "[secret-init] Success! ${STRIPFILE##*/} set from ${FILENAME##*/}" - - else - echo "[secret-init] cannot find secret in ${FILENAME}" - fi -done diff --git a/docker/rootfs/etc/cont-init.d/10-nginx b/docker/rootfs/etc/cont-init.d/10-nginx new file mode 100755 index 00000000..4a3b38be --- /dev/null +++ b/docker/rootfs/etc/cont-init.d/10-nginx @@ -0,0 +1,44 @@ +#!/usr/bin/with-contenv bash + +# Create required folders +mkdir -p /tmp/nginx/body \ + /run/nginx \ + /var/log/nginx \ + /data/nginx \ + /data/custom_ssl \ + /data/logs \ + /data/access \ + /data/nginx/default_host \ + /data/nginx/default_www \ + /data/nginx/proxy_host \ + /data/nginx/redirection_host \ + /data/nginx/stream \ + /data/nginx/dead_host \ + /data/nginx/temp \ + /var/lib/nginx/cache/public \ + /var/lib/nginx/cache/private \ + /var/cache/nginx/proxy_temp \ + /data/acme.sh + +touch /var/log/nginx/error.log && chmod 777 /var/log/nginx/error.log && chmod -R 777 /var/cache/nginx + +# Dynamically generate resolvers file +echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" {print $2}' /etc/resolv.conf)" ";" > /etc/nginx/conf.d/include/resolvers.conf + +# Generate dummy self-signed certificate. +if [ ! -f /data/nginx/dummycert.pem ] || [ ! -f /data/nginx/dummykey.pem ] +then + echo "Generating dummy SSL certificate..." + openssl req \ + -new \ + -newkey rsa:2048 \ + -days 3650 \ + -nodes \ + -x509 \ + -subj '/O=Nginx Proxy Manager/OU=Dummy Certificate/CN=localhost' \ + -keyout /data/nginx/dummykey.pem \ + -out /data/nginx/dummycert.pem + echo "Complete" +else + echo "Skipping generation of dummy SSL cert" +fi diff --git a/docker/rootfs/etc/cont-init.d/20-adduser b/docker/rootfs/etc/cont-init.d/20-adduser new file mode 100755 index 00000000..31340800 --- /dev/null +++ b/docker/rootfs/etc/cont-init.d/20-adduser @@ -0,0 +1,33 @@ +#!/usr/bin/with-contenv bash + +PUID=${PUID:-911} +PGID=${PGID:-911} + +groupmod -g 1000 users || exit 1 +useradd -u "${PUID}" -U -d /data -s /bin/false npmuser || exit 1 +usermod -G users npmuser || exit 1 +groupmod -o -g "$PGID" npmuser || exit 1 + +echo "------------------------------------- + _ _ ____ __ __ +| \ | | _ \| \/ | +| \| | |_) | |\/| | +| |\ | __/| | | | +|_| \_|_| |_| |_| +------------------------------------- +User UID: $(id -u npmuser) +User GID: $(id -g npmuser) +------------------------------------- +" + +chown -R npmuser:npmuser /data +chown -R npmuser:npmuser /run/nginx +chown -R npmuser:npmuser /etc/nginx +chown -R npmuser:npmuser /tmp/nginx +chown -R npmuser:npmuser /var/cache/nginx +chown -R npmuser:npmuser /var/lib/nginx +chown -R npmuser:npmuser /var/log/nginx + +# Home for npmuser +mkdir -p /tmp/npmuserhome +chown -R npmuser:npmuser /tmp/npmuserhome diff --git a/docker/rootfs/etc/cont-init.d/30-dbmate b/docker/rootfs/etc/cont-init.d/30-dbmate new file mode 100755 index 00000000..bf68358b --- /dev/null +++ b/docker/rootfs/etc/cont-init.d/30-dbmate @@ -0,0 +1,16 @@ +#!/usr/bin/with-contenv bash + +CYAN='\E[1;36m' +YELLOW='\E[1;33m' +MAGENTA='\E[1;35m' +RESET='\E[0m' + +if [ "$LOG_LEVEL" == "debug" ]; then + echo -e "${MAGENTA}[DEBUG] ${CYAN}DATABASE_URL=${YELLOW}${DATABASE_URL}${RESET}" +fi + +# Firstly create the sqlite database if it doesn't already exist +# and run any migrations required +echo -e "${YELLOW}Running dbmate migrations ...${RESET}" +s6-setuidgid npmuser /bin/dbmate up || exit 1 +echo -e "${GREEN}Completed dbmate migrations!${RESET}" diff --git a/docker/rootfs/etc/letsencrypt.ini b/docker/rootfs/etc/letsencrypt.ini deleted file mode 100644 index 3565d6e5..00000000 --- a/docker/rootfs/etc/letsencrypt.ini +++ /dev/null @@ -1,4 +0,0 @@ -text = True -non-interactive = True -authenticator = webroot -webroot-path = /data/letsencrypt-acme-challenge diff --git a/docker/rootfs/etc/nginx/conf.d/default.conf b/docker/rootfs/etc/nginx/conf.d/default.conf index d1684ea7..5d5eedf9 100644 --- a/docker/rootfs/etc/nginx/conf.d/default.conf +++ b/docker/rootfs/etc/nginx/conf.d/default.conf @@ -1,17 +1,9 @@ # "You are not configured" page, which is the default if another default doesn't exist server { listen 80; - listen [::]:80; - - set $forward_scheme "http"; - set $server "127.0.0.1"; - set $port "80"; - - server_name localhost-nginx-proxy-manager; - access_log /data/logs/default.log standard; - error_log /dev/null crit; - include conf.d/include/assets.conf; + server_name localhost; include conf.d/include/block-exploits.conf; + access_log /data/logs/default.log proxy; location / { index index.html; @@ -22,15 +14,10 @@ server { # First 443 Host, which is the default if another default doesn't exist server { listen 443 ssl; - listen [::]:443 ssl; - - set $forward_scheme "https"; - set $server "127.0.0.1"; - set $port "443"; - server_name localhost; - access_log /data/logs/default.log standard; - error_log /dev/null crit; + include conf.d/include/block-exploits.conf; + access_log /data/logs/default.log proxy; + ssl_certificate /data/nginx/dummycert.pem; ssl_certificate_key /data/nginx/dummykey.pem; include conf.d/include/ssl-ciphers.conf; diff --git a/docker/rootfs/etc/nginx/conf.d/dev.conf b/docker/rootfs/etc/nginx/conf.d/dev.conf index edbdec8a..ce8c1da7 100644 --- a/docker/rootfs/etc/nginx/conf.d/dev.conf +++ b/docker/rootfs/etc/nginx/conf.d/dev.conf @@ -1,10 +1,6 @@ server { listen 81 default; - listen [::]:81 default; - server_name nginxproxymanager-dev; - root /app/frontend/dist; - access_log /dev/null; location /api { return 302 /api/; @@ -16,14 +12,22 @@ server { proxy_set_header X-Forwarded-Scheme $scheme; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-For $remote_addr; - proxy_pass http://127.0.0.1:3000/; + proxy_pass http://127.0.0.1:3000/api/; + } - proxy_read_timeout 15m; - proxy_send_timeout 15m; + location ~ .html { + try_files $uri =404; } location / { - index index.html; - try_files $uri $uri.html $uri/ /index.html; + add_header X-Served-By $host; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_set_header X-Forwarded-Scheme $scheme; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_pass http://127.0.0.1:9000; } } diff --git a/docker/rootfs/etc/nginx/conf.d/include/.gitignore b/docker/rootfs/etc/nginx/conf.d/include/.gitignore deleted file mode 100644 index 5291fe15..00000000 --- a/docker/rootfs/etc/nginx/conf.d/include/.gitignore +++ /dev/null @@ -1 +0,0 @@ -resolvers.conf diff --git a/docker/rootfs/etc/nginx/conf.d/include/assets.conf b/docker/rootfs/etc/nginx/conf.d/include/assets.conf index e95c2e8b..7dd0f5ce 100644 --- a/docker/rootfs/etc/nginx/conf.d/include/assets.conf +++ b/docker/rootfs/etc/nginx/conf.d/include/assets.conf @@ -1,31 +1,31 @@ location ~* ^.*\.(css|js|jpe?g|gif|png|woff|eot|ttf|svg|ico|css\.map|js\.map)$ { - if_modified_since off; + if_modified_since off; - # use the public cache - proxy_cache public-cache; - proxy_cache_key $host$request_uri; + # use the public cache + proxy_cache public-cache; + proxy_cache_key $host$request_uri; - # ignore these headers for media - proxy_ignore_headers Set-Cookie Cache-Control Expires X-Accel-Expires; + # ignore these headers for media + proxy_ignore_headers Set-Cookie Cache-Control Expires X-Accel-Expires; - # cache 200s and also 404s (not ideal but there are a few 404 images for some reason) - proxy_cache_valid any 30m; - proxy_cache_valid 404 1m; + # cache 200s and also 404s (not ideal but there are a few 404 images for some reason) + proxy_cache_valid any 30m; + proxy_cache_valid 404 1m; - # strip this header to avoid If-Modified-Since requests - proxy_hide_header Last-Modified; - proxy_hide_header Cache-Control; - proxy_hide_header Vary; + # strip this header to avoid If-Modified-Since requests + proxy_hide_header Last-Modified; + proxy_hide_header Cache-Control; + proxy_hide_header Vary; - proxy_cache_bypass 0; - proxy_no_cache 0; + proxy_cache_bypass 0; + proxy_no_cache 0; - proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504 http_404; - proxy_connect_timeout 5s; - proxy_read_timeout 45s; + proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504 http_404; + proxy_connect_timeout 5s; + proxy_read_timeout 45s; - expires @30m; - access_log off; + expires @30m; + access_log off; - include conf.d/include/proxy.conf; + include conf.d/include/proxy.conf; } diff --git a/docker/rootfs/etc/nginx/conf.d/include/block-exploits.conf b/docker/rootfs/etc/nginx/conf.d/include/block-exploits.conf index 093bda23..22360fc1 100644 --- a/docker/rootfs/etc/nginx/conf.d/include/block-exploits.conf +++ b/docker/rootfs/etc/nginx/conf.d/include/block-exploits.conf @@ -2,92 +2,92 @@ set $block_sql_injections 0; if ($query_string ~ "union.*select.*\(") { - set $block_sql_injections 1; + set $block_sql_injections 1; } if ($query_string ~ "union.*all.*select.*") { - set $block_sql_injections 1; + set $block_sql_injections 1; } if ($query_string ~ "concat.*\(") { - set $block_sql_injections 1; + set $block_sql_injections 1; } if ($block_sql_injections = 1) { - return 403; + return 403; } ## Block file injections set $block_file_injections 0; if ($query_string ~ "[a-zA-Z0-9_]=http://") { - set $block_file_injections 1; + set $block_file_injections 1; } if ($query_string ~ "[a-zA-Z0-9_]=(\.\.//?)+") { - set $block_file_injections 1; + set $block_file_injections 1; } if ($query_string ~ "[a-zA-Z0-9_]=/([a-z0-9_.]//?)+") { - set $block_file_injections 1; + set $block_file_injections 1; } if ($block_file_injections = 1) { - return 403; + return 403; } ## Block common exploits set $block_common_exploits 0; if ($query_string ~ "(<|%3C).*script.*(>|%3E)") { - set $block_common_exploits 1; + set $block_common_exploits 1; } if ($query_string ~ "GLOBALS(=|\[|\%[0-9A-Z]{0,2})") { - set $block_common_exploits 1; + set $block_common_exploits 1; } if ($query_string ~ "_REQUEST(=|\[|\%[0-9A-Z]{0,2})") { - set $block_common_exploits 1; + set $block_common_exploits 1; } if ($query_string ~ "proc/self/environ") { - set $block_common_exploits 1; + set $block_common_exploits 1; } if ($query_string ~ "mosConfig_[a-zA-Z_]{1,21}(=|\%3D)") { - set $block_common_exploits 1; + set $block_common_exploits 1; } if ($query_string ~ "base64_(en|de)code\(.*\)") { - set $block_common_exploits 1; + set $block_common_exploits 1; } if ($block_common_exploits = 1) { - return 403; + return 403; } ## Block spam set $block_spam 0; if ($query_string ~ "\b(ultram|unicauca|valium|viagra|vicodin|xanax|ypxaieo)\b") { - set $block_spam 1; + set $block_spam 1; } if ($query_string ~ "\b(erections|hoodia|huronriveracres|impotence|levitra|libido)\b") { - set $block_spam 1; + set $block_spam 1; } if ($query_string ~ "\b(ambien|blue\spill|cialis|cocaine|ejaculation|erectile)\b") { - set $block_spam 1; + set $block_spam 1; } if ($query_string ~ "\b(lipitor|phentermin|pro[sz]ac|sandyauer|tramadol|troyhamby)\b") { - set $block_spam 1; + set $block_spam 1; } if ($block_spam = 1) { - return 403; + return 403; } ## Block user agents @@ -95,42 +95,42 @@ set $block_user_agents 0; # Disable Akeeba Remote Control 2.5 and earlier if ($http_user_agent ~ "Indy Library") { - set $block_user_agents 1; + set $block_user_agents 1; } # Common bandwidth hoggers and hacking tools. if ($http_user_agent ~ "libwww-perl") { - set $block_user_agents 1; + set $block_user_agents 1; } if ($http_user_agent ~ "GetRight") { - set $block_user_agents 1; + set $block_user_agents 1; } if ($http_user_agent ~ "GetWeb!") { - set $block_user_agents 1; + set $block_user_agents 1; } if ($http_user_agent ~ "Go!Zilla") { - set $block_user_agents 1; + set $block_user_agents 1; } if ($http_user_agent ~ "Download Demon") { - set $block_user_agents 1; + set $block_user_agents 1; } if ($http_user_agent ~ "Go-Ahead-Got-It") { - set $block_user_agents 1; + set $block_user_agents 1; } if ($http_user_agent ~ "TurnitinBot") { - set $block_user_agents 1; + set $block_user_agents 1; } if ($http_user_agent ~ "GrabNet") { - set $block_user_agents 1; + set $block_user_agents 1; } if ($block_user_agents = 1) { - return 403; + return 403; } diff --git a/docker/rootfs/etc/nginx/conf.d/include/force-ssl.conf b/docker/rootfs/etc/nginx/conf.d/include/force-ssl.conf index 15f0d285..5fd4810f 100644 --- a/docker/rootfs/etc/nginx/conf.d/include/force-ssl.conf +++ b/docker/rootfs/etc/nginx/conf.d/include/force-ssl.conf @@ -1,3 +1,3 @@ if ($scheme = "http") { - return 301 https://$host$request_uri; + return 301 https://$host$request_uri; } diff --git a/docker/rootfs/etc/nginx/conf.d/include/ip_ranges.conf b/docker/rootfs/etc/nginx/conf.d/include/ip_ranges.conf deleted file mode 100644 index 34249325..00000000 --- a/docker/rootfs/etc/nginx/conf.d/include/ip_ranges.conf +++ /dev/null @@ -1,2 +0,0 @@ -# This should be left blank is it is populated programatically -# by the application backend. diff --git a/docker/rootfs/etc/nginx/conf.d/include/letsencrypt-acme-challenge.conf b/docker/rootfs/etc/nginx/conf.d/include/letsencrypt-acme-challenge.conf deleted file mode 100644 index d04f7033..00000000 --- a/docker/rootfs/etc/nginx/conf.d/include/letsencrypt-acme-challenge.conf +++ /dev/null @@ -1,29 +0,0 @@ -# Rule for legitimate ACME Challenge requests (like /.well-known/acme-challenge/xxxxxxxxx) -# We use ^~ here, so that we don't check other regexes (for speed-up). We actually MUST cancel -# other regex checks, because in our other config files have regex rule that denies access to files with dotted names. -location ^~ /.well-known/acme-challenge/ { - # Since this is for letsencrypt authentication of a domain and they do not give IP ranges of their infrastructure - # we need to open up access by turning off auth and IP ACL for this location. - auth_basic off; - allow all; - - # Set correct content type. According to this: - # https://community.letsencrypt.org/t/using-the-webroot-domain-verification-method/1445/29 - # Current specification requires "text/plain" or no content header at all. - # It seems that "text/plain" is a safe option. - default_type "text/plain"; - - # This directory must be the same as in /etc/letsencrypt/cli.ini - # as "webroot-path" parameter. Also don't forget to set "authenticator" parameter - # there to "webroot". - # Do NOT use alias, use root! Target directory is located here: - # /var/www/common/letsencrypt/.well-known/acme-challenge/ - root /data/letsencrypt-acme-challenge; -} - -# Hide /acme-challenge subdirectory and return 404 on all requests. -# It is somewhat more secure than letting Nginx return 403. -# Ending slash is important! -location = /.well-known/acme-challenge/ { - return 404; -} diff --git a/docker/rootfs/etc/nginx/conf.d/include/proxy.conf b/docker/rootfs/etc/nginx/conf.d/include/proxy.conf index c0dce061..b84a4513 100644 --- a/docker/rootfs/etc/nginx/conf.d/include/proxy.conf +++ b/docker/rootfs/etc/nginx/conf.d/include/proxy.conf @@ -3,6 +3,4 @@ proxy_set_header Host $host; proxy_set_header X-Forwarded-Scheme $scheme; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-For $remote_addr; -proxy_set_header X-Real-IP $remote_addr; proxy_pass $forward_scheme://$server:$port; - diff --git a/docker/rootfs/etc/nginx/conf.d/include/resolvers.conf b/docker/rootfs/etc/nginx/conf.d/include/resolvers.conf new file mode 100644 index 00000000..ccd9dcef --- /dev/null +++ b/docker/rootfs/etc/nginx/conf.d/include/resolvers.conf @@ -0,0 +1 @@ +# Intentionally blank diff --git a/docker/rootfs/etc/nginx/conf.d/production.conf b/docker/rootfs/etc/nginx/conf.d/production.conf index 877e51dd..325cb8cc 100644 --- a/docker/rootfs/etc/nginx/conf.d/production.conf +++ b/docker/rootfs/etc/nginx/conf.d/production.conf @@ -1,33 +1,14 @@ # Admin Interface server { listen 81 default; - listen [::]:81 default; - server_name nginxproxymanager; - root /app/frontend; - access_log /dev/null; - location /api { - return 302 /api/; - } - - location /api/ { + location / { add_header X-Served-By $host; proxy_set_header Host $host; proxy_set_header X-Forwarded-Scheme $scheme; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-For $remote_addr; - proxy_pass http://127.0.0.1:3000/; - - proxy_read_timeout 15m; - proxy_send_timeout 15m; - } - - location / { - index index.html; - if ($request_uri ~ ^/(.*)\.html$) { - return 302 /$1; - } - try_files $uri $uri.html $uri/ /index.html; + proxy_pass http://localhost:3000/; } } diff --git a/docker/rootfs/etc/nginx/nginx.conf b/docker/rootfs/etc/nginx/nginx.conf index 40432968..aac80c3c 100644 --- a/docker/rootfs/etc/nginx/nginx.conf +++ b/docker/rootfs/etc/nginx/nginx.conf @@ -1,7 +1,6 @@ # run nginx in foreground daemon off; - -user root; +pid /run/nginx/nginx.pid; # Set number of worker processes automatically based on number of CPU cores. worker_processes auto; @@ -26,15 +25,12 @@ http { tcp_nopush on; tcp_nodelay on; client_body_temp_path /tmp/nginx/body 1 2; - keepalive_timeout 90s; - proxy_connect_timeout 90s; - proxy_send_timeout 90s; - proxy_read_timeout 90s; + keepalive_timeout 65; ssl_prefer_server_ciphers on; gzip on; proxy_ignore_client_abort off; - client_max_body_size 2000m; - server_names_hash_bucket_size 1024; + client_max_body_size 200m; + server_names_hash_bucket_size 64; proxy_http_version 1.1; proxy_set_header X-Forwarded-Scheme $scheme; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; @@ -43,54 +39,27 @@ http { proxy_cache_path /var/lib/nginx/cache/public levels=1:2 keys_zone=public-cache:30m max_size=192m; proxy_cache_path /var/lib/nginx/cache/private levels=1:2 keys_zone=private-cache:5m max_size=1024m; - 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"'; - 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"'; - - + 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] "$http_user_agent" "$http_referer"'; access_log /data/logs/default.log proxy; - # Dynamically generated resolvers file - include /etc/nginx/conf.d/include/resolvers.conf; - # Default upstream scheme map $host $forward_scheme { default http; } # Real IP Determination - - # Local subnets: - set_real_ip_from 10.0.0.0/8; - set_real_ip_from 172.16.0.0/12; # Includes Docker subnet - set_real_ip_from 192.168.0.0/16; + # Docker subnet: + set_real_ip_from 172.0.0.0/8; # NPM generated CDN ip ranges: - include conf.d/include/ip_ranges.conf; + #include conf.d/include/ip_ranges.conf; # always put the following 2 lines after ip subnets: - real_ip_header X-Real-IP; + real_ip_header X-Forwarded-For; real_ip_recursive on; - # Custom - include /data/nginx/custom/http_top[.]conf; - # Files generated by NPM include /etc/nginx/conf.d/*.conf; include /data/nginx/default_host/*.conf; include /data/nginx/proxy_host/*.conf; include /data/nginx/redirection_host/*.conf; include /data/nginx/dead_host/*.conf; - include /data/nginx/temp/*.conf; - - # Custom - include /data/nginx/custom/http[.]conf; } - -stream { - # Files generated by NPM - include /data/nginx/stream/*.conf; - - # Custom - include /data/nginx/custom/stream[.]conf; -} - -# Custom -include /data/nginx/custom/root[.]conf; diff --git a/docker/rootfs/etc/services.d/backend/finish b/docker/rootfs/etc/services.d/backend/finish new file mode 100755 index 00000000..2b661f61 --- /dev/null +++ b/docker/rootfs/etc/services.d/backend/finish @@ -0,0 +1,5 @@ +#!/usr/bin/execlineb -S1 +if { s6-test ${1} -ne 0 } +if { s6-test ${1} -ne 256 } + +s6-svscanctl -t /var/run/s6/services diff --git a/docker/rootfs/etc/services.d/backend/run b/docker/rootfs/etc/services.d/backend/run new file mode 100755 index 00000000..b63ad826 --- /dev/null +++ b/docker/rootfs/etc/services.d/backend/run @@ -0,0 +1,23 @@ +#!/usr/bin/with-contenv bash + +RESET='\E[0m' +YELLOW='\E[1;33m' + +echo -e "${YELLOW}Starting backend API ...${RESET}" + +if [ "$DEVELOPMENT" == "true" ]; then + HOME=/tmp/npmuserhome + GOPATH="$HOME/go" + mkdir -p "$GOPATH" + chown -R npmuser:npmuser "$GOPATH" + export HOME GOPATH + cd /app/backend || exit 1 + s6-setuidgid npmuser task -w +else + cd /app/bin || exit 1 + while : + do + s6-setuidgid npmuser /app/bin/server + sleep 1 + done +fi diff --git a/docker/rootfs/etc/services.d/frontend/finish b/docker/rootfs/etc/services.d/frontend/finish index bca9a35d..2b661f61 100755 --- a/docker/rootfs/etc/services.d/frontend/finish +++ b/docker/rootfs/etc/services.d/frontend/finish @@ -3,4 +3,3 @@ if { s6-test ${1} -ne 0 } if { s6-test ${1} -ne 256 } s6-svscanctl -t /var/run/s6/services - diff --git a/docker/rootfs/etc/services.d/frontend/run b/docker/rootfs/etc/services.d/frontend/run index 32558d98..87963721 100755 --- a/docker/rootfs/etc/services.d/frontend/run +++ b/docker/rootfs/etc/services.d/frontend/run @@ -3,9 +3,13 @@ # This service is DEVELOPMENT only. if [ "$DEVELOPMENT" == "true" ]; then + CI=true + HOME=/tmp/npmuserhome + export CI + export HOME cd /app/frontend || exit 1 - yarn install - yarn watch + s6-setuidgid npmuser yarn install + s6-setuidgid npmuser yarn start else exit 0 fi diff --git a/docker/rootfs/etc/services.d/manager/finish b/docker/rootfs/etc/services.d/manager/finish deleted file mode 100755 index 7d442d6a..00000000 --- a/docker/rootfs/etc/services.d/manager/finish +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/with-contenv bash - -s6-svscanctl -t /var/run/s6/services diff --git a/docker/rootfs/etc/services.d/manager/run b/docker/rootfs/etc/services.d/manager/run deleted file mode 100755 index ba0fb05e..00000000 --- a/docker/rootfs/etc/services.d/manager/run +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/with-contenv bash - -mkdir -p /data/letsencrypt-acme-challenge - -cd /app || echo - -if [ "$DEVELOPMENT" == "true" ]; then - cd /app || exit 1 - yarn install - node --max_old_space_size=250 --abort_on_uncaught_exception node_modules/nodemon/bin/nodemon.js -else - cd /app || exit 1 - while : - do - node --abort_on_uncaught_exception --max_old_space_size=250 index.js - sleep 1 - done -fi diff --git a/docker/rootfs/etc/services.d/nginx/finish b/docker/rootfs/etc/services.d/nginx/finish deleted file mode 120000 index 63b10de4..00000000 --- a/docker/rootfs/etc/services.d/nginx/finish +++ /dev/null @@ -1 +0,0 @@ -/bin/true \ No newline at end of file diff --git a/docker/rootfs/etc/services.d/nginx/finish b/docker/rootfs/etc/services.d/nginx/finish new file mode 100755 index 00000000..bca9a35d --- /dev/null +++ b/docker/rootfs/etc/services.d/nginx/finish @@ -0,0 +1,6 @@ +#!/usr/bin/execlineb -S1 +if { s6-test ${1} -ne 0 } +if { s6-test ${1} -ne 256 } + +s6-svscanctl -t /var/run/s6/services + diff --git a/docker/rootfs/etc/services.d/nginx/run b/docker/rootfs/etc/services.d/nginx/run index 2941db40..e04643e7 100755 --- a/docker/rootfs/etc/services.d/nginx/run +++ b/docker/rootfs/etc/services.d/nginx/run @@ -1,49 +1,3 @@ #!/usr/bin/with-contenv bash -# Create required folders -mkdir -p /tmp/nginx/body \ - /run/nginx \ - /var/log/nginx \ - /data/nginx \ - /data/custom_ssl \ - /data/logs \ - /data/access \ - /data/nginx/default_host \ - /data/nginx/default_www \ - /data/nginx/proxy_host \ - /data/nginx/redirection_host \ - /data/nginx/stream \ - /data/nginx/dead_host \ - /data/nginx/temp \ - /var/lib/nginx/cache/public \ - /var/lib/nginx/cache/private \ - /var/cache/nginx/proxy_temp - -touch /var/log/nginx/error.log && chmod 777 /var/log/nginx/error.log && chmod -R 777 /var/cache/nginx -chown root /tmp/nginx - -# Dynamically generate resolvers file, if resolver is IPv6, enclose in `[]` -# thanks @tfmm -echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" {print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf);" > /etc/nginx/conf.d/include/resolvers.conf - -# Generate dummy self-signed certificate. -if [ ! -f /data/nginx/dummycert.pem ] || [ ! -f /data/nginx/dummykey.pem ] -then - echo "Generating dummy SSL certificate..." - openssl req \ - -new \ - -newkey rsa:2048 \ - -days 3650 \ - -nodes \ - -x509 \ - -subj '/O=Nginx Proxy Manager/OU=Dummy Certificate/CN=localhost' \ - -keyout /data/nginx/dummykey.pem \ - -out /data/nginx/dummycert.pem - echo "Complete" -fi - -# Handle IPV6 settings -/bin/handle-ipv6-setting /etc/nginx/conf.d -/bin/handle-ipv6-setting /data/nginx - -exec nginx +exec s6-setuidgid npmuser nginx diff --git a/docker/rootfs/root/.bashrc b/docker/rootfs/root/.bashrc index 1deb975c..122da279 100644 --- a/docker/rootfs/root/.bashrc +++ b/docker/rootfs/root/.bashrc @@ -16,7 +16,5 @@ alias h='cd ~;clear;' echo -e -n '\E[1;34m' figlet -w 120 "NginxProxyManager" -echo -e "\E[1;36mVersion \E[1;32m${NPM_BUILD_VERSION:-2.0.0-dev} (${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" -echo -e -n '\E[1;34m' -cat /built-for-arch -echo -e '\E[0m' +echo -e "\E[1;36mVersion \E[1;32m${NPM_BUILD_VERSION:-3.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, Debian \E[1;32m${VERSION_ID:-unknown}\E[1;36m, Kernel \E[1;32m$(uname -r)\E[0m" +echo diff --git a/docker/rootfs/root/.config/litecli/config b/docker/rootfs/root/.config/litecli/config new file mode 100644 index 00000000..bfc67aa8 --- /dev/null +++ b/docker/rootfs/root/.config/litecli/config @@ -0,0 +1,13 @@ +[main] +multi_line = True +log_level = INFO +table_format = psql +syntax_style = monokai +wider_completion_menu = True +prompt = '\d> ' +prompt_continuation = '-> ' +less_chatty = True +auto_vertical_output = True + +[favorite_queries] +show_users = select * from user diff --git a/docker/rootfs/var/www/html/fonts/Aldrich-Regular.ttf b/docker/rootfs/var/www/html/fonts/Aldrich-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..5c15ab92a55dbca44cb6683ec9a6f4e0647edb59 GIT binary patch literal 56548 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 wbj-1Xy#x897{ z>ZkDhR`Gd6O}8PE_#C2fmmpI2JR)>gBPREv_{pdcovTKL`{Z?B*R)TpAJuA`N5#_Y z1L#NNjMKX?Bds%^KmNj{yR|cMqj%y#djVHQXMWjkqr7ZnU2%Q>ruw-ez!Pf-GxX`U02vWv_3O&dEWZgo*K6Em^ zMOG|i_@D^=3(Z=;6jix9SDmu%cr>iAz6%p|I9IS8Ju!K_SL@Zjqi@tdq~E8%YRoV; z8vkHyGj<#MYzbShZM!{TKhu7Xqs_6z@t@8UogZ+1#rZwwuV8Jz*WKbi(Y?o$_nhnb zq~}3zi}!5r-+4#9zk_!EF5hA?MgL&1jNslks1?+^Yc_y%mu{?OXc zMWGLdUJ0)b-w$i;eUW>k`RIG2+oC^!iy$BSWbB1_Z~W8oHxf${cP4(CY)h_8emHr5 z^2f>XhFuL0raqGTF8;llz9emCe$d$4xGo#WF3dIN?#O*F_e#^_O)um>lz*g*YTMTKWF=I2Z{?QC zeU%rWK7FkH^^SEN4|TS6?(Pb8&F|XXExJ$c-qn4mXKv3&XXrCNJX4?fk>0l6-F+YK zU)%qqf#HGC!Op>*gWnl^WmaO=x>?_yy?XY&a~kG+cy4U&-_N~wUi-Wc&HK;!Gv;rf z|HB2n3qH2s(88gGH!a+`@RdcY7j0kky`j?3yM}HVdSu94e97W{OFEa_bX>!6A3N^% zOV=)a{P-EiKeTLc*-gv#EPG{n_wwQ8XD(Bo8$@_DG&;bbx?E7CQc_VT@&pkO~re+kPeMVorXcXcFqZs8`9O#P{1_~Li5H0B6 zAJ8rs*gdfKzvgI3^9Oyq`^;@fzIAY}`5UckuvNQYj=9ag-Tb0WpV7W}AzWzg)(67<3-!Lj z0PcHpdR8kA;5n}1;aET~7UH<)TTwjK!iDhGN3~2%w6B_-&(B`ft_iRj-aKl)sV?}m zCPvL)x38L2cvf?of6~6CIke5A+UESJdJp9YkislOdRdEGM*cO z{OpzOza*T5@|W!^XXnlL80LfH`XQ~!_AP=bAJ$*PuQRnKGuCzWq+-KkZ=0@Ag z=C#@lhkrihca+GF8~FpqBJgD20X%gA1-I>in&FZ_QCT5y2J)oDh!4LE%BxWQ3Iqc} z%RjdVUuE*wkD(+a(V10YcO zu&-d>G4z8|-tm3&Df3HbzI)9(v<+I%*T4GR;SuvEYlauEGtM|NgfZoek2op$%v*RCE}y?W&I6IU7EHUF??_3Aa6cjd^XYgTg*V{Yi1 z+C9LZT}1aF(E%X&8;5ldXrO2e6yn;&A6=|@@ZzHf5AvI>=7_!%ig-$7_CPUo@|!)f zG-8LEqtP*ArafMqBpzG`III$V`p)e8_S~?1`3-yCm(4D_WOe1XPrUjF?~F(Id~e$c zmn<8veBw6LvkCQlT-HB$3wN4VoS3v&Jy4w|(L^ z7MVq@A6~xv!>IMu+dg3&Sa!(?ZF^bm_(E3u2;thKuQgrzvg%q8}Th+q=YA*e95`0=VsfTY#V_Bs@>n`^YZa zrVvB+>ck5=ayQ3s7}6qK3`0tUjR9@U?AvGdX=Ap_&A$D-*sqPTF7pZP39SQ_Bq^|1 zlq`D>d8@>$24TY?KXB`(KBaZw^_fVYk?qu;*Kg2w;XN<<9EOc+ZI5^quN~jgF0f+K$?Q z5Ia^(NFKQgas)4C`7*|D8mAq|pltCX_u_d9&m6c;;Th#s8hPYNv^*_2AUPwM#*CR| z$^9+j?UND#A|PyUBLAxPS=>k;eW5{)qM>qp0lx}l2@~nJ zj$iaqaX=Siha|@v8?$g`dlBCi#c9@e99l&*5GX;*<6t`txDQCS(@=+xou2`0gt5#~ zk_2=dC8Sn<>cS5i5rjPT9H<0BP6|P5$T+n)1R|it(^*@}frN^^*$DpJHu*c6-HGP+5jF>`Ou|;EbAk};R^GQE51{}l z>A`ZqP8Voovjc^N;Jg;YEe_OqaLnP%X<2gQe0bwyV4=5@k(%qYovRkFSYzJ2X818M z-Rr;p)px9U_nGD{bFKL$?cUYoq?eAY{I0R;#M8-OSFa_rOKlPvoG#Mn{r~o9xzYx z7m{O0=5gG+CHCWJN}6~ER^?BYP|e701MLJfDbf1kQ;{s$PN6pux7}%cq%<+P`QU)=DIPd(A5Sz?k{aSHG^tw50oHNYv{_MJw9ShO-sm zllze&2kT?IjVQr&04K+F0QVf%RI?NHd#cev+*2K7UPR9K${hFQx?HzQiN*0x`L0;4 zA7dkG4UD&FhEglU7#Wp%+g7rHvFhczypTNNa`{Nd${%ZZOxO7MlBRS$@H_O!F7$^3 z?I4KkeGM&{F|$tt&BnFg={Jl&ZyTx3v5k!_d~+8$2lO7>>-Z)J8)S~yvXQvjF_OOO_TIQ8DY^8Y0Huke={uw>oTMfR7K1c z<#3v$r3sm`Ks&~DKk3FqM)X2!geq>0$vb@_Jpkh3ULZF zLrGf@6=1Z1_9;2+qXIo@YPr60h5J<5mfG6!0mv$%2#c(dL6@VmtltPM*fC`^=a3 zQPdD;(6_9*?HDfP!1OuWW!-LV0RJ_+j<2p8zutB_wVS83tF>#MGz+ME6WUH0pNA*( z9ptDUiGpSbCZZxqj3LJy0nseSAGuf-6v$qoftp0#!f{7$DqjuDai?rCH>EU%aT%;& zQdq-a2~(j$UT&MNZ7B(cyzsJ05QY<&#AyWF-nCvp$7dti^+qN-61vU9L=t94z;v zC~C@d2ZL@67!}HEW^ZT z;#Je&@{0Mh*5msNhEW_?+G4(586IfTx0zkq(?)A$`CwD7@A%eljvv%tqBS9l+_C)f zliFK{2lLXR*P^U&xA$3MtZt4os5*0c?kJ7dR@;Ra9qldP%%h!0Y6S&6JC;^J?-Zp! zoopnqG&x}yb}V61T7aTSKafEcI&u^(0N$brvXk=P@JL}le#r31!8d-0_fsYRxELMf zGjel|Gr)2qVDX~nKC}XmWN^k%sezNKxeqP$;hC4?i4hsLFd(}U_DS8+Ef&JgDgG+s z)~(w~gw@}V=?AZPfHw96SB!1ns@+5K92aBu?W9U`gj7kshj!TT5apwA8FYj4qqsV1 zwbG(d+dX@FJ|X`oTjfTC|p6O7Z{@HU#DG^&qD z1?DDwgk60!LeMshRS!(if}(3h3(HlH)0s>IO~*;jO3jw82s`dwxTb|SVgD;@L2(NR z9kdxEl6C-2+Hm$8$Hu=uHf9WvPv{$~+wJf$>gOw5YMasbb(_5I_pxkkmZ5D1`rqU_ zf8w6{=Eb-Ui$xd~XipR;`GtjhwmFJ>@)1%oH7aTwK57OwDNpIVge0c^(>KAU2eyvs zOUJ0gR3D_StnEEy8#_E)d7H5{fz#e>f&ly-(2OQd4(Gr;kMH!qDg(4V+CacO?4bo zoj_49j*r;4AMQgj>O04Sxblf}R8Sy*Ci#H_1Gq5QJm7`2=Y}>;ElSy09@>32{LJ1| zP~a5_36L%!fit9jMPC!2!J|lM`rwfzP{xkx%VRI-&3f_q$Ez>fGKYVGW_{N-8!l8_ ztG3y9qit?+it^giBSKHShPKIWXTRZ^){_(0E}S;vSI3LQFat`urx zk-MH-Y(T!dgmxM(p0qA*krxQPsGoD{=UZUq*Wcp>8i`it&|*7EqxXS*O+HwtL&M)_ zJ<}sW>zVH~BPYc0C@C6<*X=_qc0d%k@e)5SKxMr8d80jt!4&MbDDI(BAN%-Cg3mJ&546NrDO2ePMRo zcVJMdL6<-q8IWO5kPaC~<9!eHg3h|h@2$5xfzsJ-4&f+1q-boX^)Stqc{FE+GD@}0 zmSEDQx-wQBRu}AqYCnG*(;wG2l3A-@7O&pw*r0hzIHB9xxc7@wG+7QUV4Jc)$$nrK zEp&ihq;&?=F0gGfD`O@$uz?7`jvPupl5d2?aesr{AB(yAlAuvmC|!j)?`B`;}%k<)uZMfZCUj<4zc%72Oh!qM~_F?+2xz62OiaPDj*^2 zvSLUD#Vl--7*ZRLn*77TQ-V7xXO;n)hfOwika&o(tY(tH2kOh4Fa#(Y0_&5=s|p z;qf(X_QpCq6mFKEFoS2*%sFSl510ZRu99mr;uND)@nKvyN=dQ@tUW8? z3LwX#4rojh{=fn7qlLy~kjXZ}_En#i&DQe+%i6Rv2~IVkm-q96KikaeS6Ywn%gR^J z(!Zek(CV8eam92SIfRxn)=eFqIHv|fU6j2-9o^E^Dfh{elgKI3DRp?CW8diL750rK zWvDz-(NFLNN*NIH6w_Eutw1+X-e?zS;}Y+tjc}pa=qS}m=T^_U?>-AI{R`IBefJS2 zMQ`kv1pFE2DLa{QKH}Zd$)?8osI@A3Qw$U<&>%@^Q8O|?I%{CxP}(=t;jzmI8IXY= zLlZPcW%jMqg)XR*)fZI%l&jAg=d#V(_BR(ykd*9@$+5z=gQjTXTWu%iId_J=K#HN9 zr_MrbbL=o@uMxR~kF8fdgPt(zTqFc-nmv*NrljSEQ5`=*<05yoRjP~hZ&w#hM}2jX zHm>ddwd|Hjy#MyMik2X`jE@8F5wXZ(m5L^abq87{ZBTltLb7(tHUb#<(OGY({1K6`^R?}=k9-oBNS0%;LaSejl4ad$pbGIpP|FX zhm#&29~wYP&Lbyx;k~5b1Y`*DWuMSq75*?)W|}|J;xiy#dL5;mPd-gG3Rze&nzLG$ zfpJUpRX?d;qDHL#vPBSC_y!+*^EFB2T7&~te}DBR;7aO$HqvxdymQjZAgAH@q?Sjh zXmZ(b?*w;p&nlkXdGKTN|-nonY5i$Y8xhWz<*G@N1}cE zEe8+2Y>(8D?6_ObXMlXV1~7OS+f?SdHLB2JrMuyQ1P`wQTT16rSB#QRwXUVxz&O{m z?c1k4t;MVR^z*bw%{lhH&s%90_VN`!=7f8Ix`3>w0)TQLL!i;voKKtG`7vP=x z`;MzY+s)!y3xO84DFU1EJ+KUHxX|~E&o)UeM%;6HTNCmUEiq~XB$GU#Q9J;(;zu$P zdM`;jw9!-Yb3O^+2^SmiiE5c7llVeI&hJXr^Ef;6cl*r+b&4@tmkky)y$Qm|lmt!U z;c2TpNHIiOx5N>DuxK1GM#a1TW*R33C+SDo2?o(i(PIdmIPDMS+lHHbWMVh9^_N{sqcJ7hnJ58^$L$e~s zOV>Gi0C`%reeXV(=TdW+Lx2;T7jWx+1M*- z`5lwAOg^og1B!2xL()4Gf^U!W=r{pInc-eyU(M-Ifi!R3yUaNq9%vlookX`BgGybh z4?L-{?w>N!*f6L@(jRNQiZ(UT^O46uDd21X21y~5d=$YnG3otaJK5EKOEr zGF7%sN?(gJQBsmw6N!GdhfepYn&Ym0N2rW8m>$NthYw8Xif9X%l5HjW@-Of)XiuAK z$F$?LrDNt=6t&k_aQJ|P2W2=;L>d1BA45j#!+)*l)wJu)FF!i|>&JdQ{;2uovDbcs zntoywc^*Ds1jb)So9Wv@3+lcF;)A{gKl7UT zQ}e~ww9lH?{zmg_K4__LRG&xH*Xae_Wp2|hU_Vu$2SR}bp6P#?^hHnlp=Zp@XvzWe z>htF5Phc(AtS728!vn@+)uR5BH-Q@EM}Xz}XZ;9UjmxW#>9fW+>9eXou~i1fpV~iQ z^eCD^Tl72ON0=|Zwg-!v7?fL_L9g}0I_bxRntn__?#D`|eympN2bK3@bx%LmC-q}L z#C|v}`>_V7AAA4x3;$`0?bxcX9}9%~g^n41b5L5pmpS_*9eHE4c5+ z^KP*0ne{KEuEdeL2k~At_as)gqGuvk)@FS!E{5=cHA7ZiQX`*-Ie^WMcntnoIW>pj zP0r}j@64XDJ!fS{cwe|<<(%~Jnvv4!pV~OM_x$slhF7gDT=bdtj?Y|JSh;Gr>3rF@@XKhM zfzP*%K{>=5BhGsyjk8l!r=c_4xJcow;GBbV1| z!x`PDW{|VzFn8RV?JWB&r}~oJ$Ij&Z{ef*4UWQIvJUqM@*<}}I(WjTpN2kSmR`j{u zeJgt6xS4+m`t;QcR6kw#nU40)T%`Kx{Jreh(#V?ODIL3F%}9|QOW)Wg?IqhHBMUiH z1Pqo3-Lk34K*><4%m&FHp{GH7^z~voOCO{&cs3`7J2z=vCtb2+=~-(|oSErpis=8W zb)GEmPr!XXtZOeVx%`wFEj=x<=FZgO%TKi~IWDmV0Ccnm`y7)S!@jUf^H^1?p>pXo zLjx>n&BnkRr)2Vk{SqxixEiJv=W&>@;7?ZTojv8t0Dvw@Kk^On6(Ke_qOL>2Fcj^ti}*SoTHm zInEHD+4OL8m5$EbBMf>cC9sa2WR}F-O{0%qr+w^XwIsXP$wrEcDZL>Xr_RbQ292K(xKRI?2aM1mnO3z7IQv!D= zfl5k{H6_TJ5@byYvZe%CQ-Z81LDrNYYf6wcCCHkRC2LBMH6=6)5(OnvGc2KWawA6a zyKzlvlj;F=TGE_h{xqnxQuj9{FzZulgsqtP+uYLXY{g0za--H+$%R^9Zz4G>iJ8=t zB1sJ#h{K!=i$mO&x})#?ixywmd&hNmVL`*pYXUWFHD$`4!e$Yx%>Xz^9Ow`-ew*Dq;pU9uiuF<&S3 zg?cO^A+93*DpeML_M+}_CY^c!X9&6U(pnlt5O7+3j7940}{7cini` z?|ygJjij?l*(f6kmM?}HC@l?g6o;}aXd^2dA__Z#@%J(9(eay&t88InQSv zX4yTk2TQVhq+pqhIQEEMIrQjENMJAmhaw@LaL_dZ1vzj$8i;Bba-`G#=|j!|Jhy&7 zA2~gi3y|~6#T@xFC*+u#4VcB)p?)xyDipcx%ocv%R$9=a^ zU#~Yg_HhL}FK5&j>`6Oia(Zn=!^Fs2$pa-D*eC;-)$O{xkd~_qY|5zT7(zK%pEo;i z^zjXSlU|s$qMZ@Z3AV6_Z)0f#d4{=(@A3hHoidoMZ8kRn*Nmh5-cHW$6!Vl)>;_h8 zP0%tUNwNRvh_VwqNAFlb3oln0r=B@@&^+_tLHnMASbSw*2k}=O75hD~cU!Sy{v|R_ z#iRQXTqIg&)#J$?>MTk>N`WJ#ERK}IWeP(&1&)*gM@oSsrNEI=;7BQOq!c((3LGf~ zj+6pNN|Aw3%PpeEir_p&aGoMKPZ6A_2+mUk=P82o6v26l;5KX$v*S-FWzbX_x?-tpWbkA$J(_!4&Lw&2X9-u z_O^o$MVcx}?eob>Q{-mzHtoEdd2gQ2dwp!$$1Fw*nyc5{@$3yZJbTAFD>p9T%lz#6 z^G&hw`eY>+iR3C=)`h;?s-0;BAX^zb!(X9*P5>%s#n}QpSaxR_R6_3#$6_mYEa|_y1^JHyzVNOTG=ZsAItfspQbK6t;b=B*)X(yYzjf1@1vOU!?H(%Xx zb5mbA*3dpDZ{E9Edtx_mpiA5TS>r39CqHOdi8)%11lI>XhI!sbG_I9Zwo&`mn{S$n zwQpz-Azz~XUb}zHe8YTWOuK*Kp1b(>M4t=aZ-_%n?Dy4uBFk z%7%_M%6cUS=Av0n^v1eXyhx3!o&E3^j5sakj1~>K?x;6zpO_Q#mkl=Uv5$Xt+ovDC zS=;@Pnbka9g+iBk=r0^*`o4dPo8Q*vnfr~z!=GsGEO()=V2jv(Z>+}nnjY)s+>%ZD zQ`H{(_Pc-^@@S)IIO;s~`)Ykr1?1NT@U2l;!L1OJPG|HZ)nViy04f&axM|6@;JdKPWg9C&ih;>kI1*&KLs4iuIH zPtJiS=fIP5;K@1g#ro}2?u&Y@^8J83DOQyRa_D-I*LR(=O7Db9ifA5 z>R)&DamT%XeP7@D_aArM)$982Om)pK6&G}-Qk@HmrTJYc+XZ~AUSt0LM#zrSC^y=f!c8!4HNw$S2ur^o%o? z^9nLR8QY;&=_qrBP8PbNvvQ92;9HkhV0Uo@PoYi>Y^ro<>&%~RjThVF+6jsF=6GwD z`DLx+mg@b+RWs%#cD`@J=7yF`Aed=Mes0G6_|9b;e*F|_O||nGw6mLbBe&0CGg_tZ zNSSpO#j)*{-cP=a) zxz9Xe+%P*knP1XQGeTp}nmaWf|b!4IHg0V{A~=bS0p14J=l z*Oljl@JpCsXlom`V&-(!qi^Jdt4Tda?OictE($eZYqkajejeqK4NP5~O&y10fyNbS zg02t7D@YqO5gKvLc4X-@<33b`+KOwXWkb;}39F0wC4c;y2&Kjv)7&E5|gK<{C3sE}Xms9fPsi_WE0?Eb5h0f`C@WSh?dDyO?B8 zbo(a?0X5VMQCD1*+NaUhM9<5G$I{3xhmCuT`@b?iG;8Z7+wkYkJaemgvvw6&AD0kF z+Nf?YD^sB4d$;%CxF*MUlZYNr@d}LTa+?o5?VA7%^{353OO%^QijcBUYWFm z$@3}_Q4JDd_z^FVolv+N$mx*cq%=Nn0q36r3mh45Sz zhiA=L5PPd&IPFym1{>E?IG&ZT2hh?b;%78=lwHp4?bz4cicR%-Ud)$WpyLU(JOwwc z1dfOxa_V|&l#o^;bzP87HuXOJJC?6!KCWqHWbhs5B=Mel;sD5C4(euIs)nT&rFigD zUmdgDCyvc(hR>IIgIwJQn;peafRPXTkUNnFL56cUr2$29#e!U}0ISxb7+AU7??la} zo+g>F_sNl>iINFClQd>rxy>T5!youWI{Q>Moz8yyn-o{y%%(s7$hRNK=8L6Ap25^> zta~{$h3@nl=VO1@?4$&1%uW?yk32&C5b{94pG=?j36oyXyqt_kV^xhs2k!F{SL9ig z4>5o&cU`d7l;JP80Y{3VaV=RA+z3P_r^(f|iwRDJ$>oJj*kPdsoFUvi*aFV{YdJUB zjMLYkf1EpH%v7s5@4k_1HzU_QGr6wJ*r|13B+2!J&OYc}oNZHpoRSNQ(7?$Rg^Vd^ z!Y}*8J-N0pipicwt}nb#&iy17Cs!G2dtU?zAGIzrawVG?yEPZ@lr)6gW?ob+=#~Ou`tFnm4`MN)9QQm0n3!OqSdLon#7@rTZH4xUY9`G6aG@ zSaU?1%xg+R>j!6_xMWdVXTPyUo4D)fH;(9z{R118wl=ji&2O8yVE@?aw9rwfg^0LS z`Fgpog>&UN-@?+g=*A>_7G(LVr~y$MQyM8X5cpQnsqAL_p+tG*L6rus zrr^i&s9JboH7uIah4K3-c;U1zI=qfV#MyQK*L1SjL2uY34GuuqrC`NY;mJm;dvKED zSt{r`Pbu-fZu(Z5V$*f4tgnEI<5e zbzBYI`WjR{seh*W9@Gv#Fj{?Gt{v_bR}-VkByEEqoZ>5rZeyI< zWFWwb6Tk3DKP|4A5Z3(2kFH{#u8W2Sz)yZ(z>AcHQi}rLx6e}3Q!Hha!i|=0eZKT` z{al}Sm$}PwFCV$M*!=krRQGt?Qr%yo4|;Ne)B_KU*Vh_vn5xmswQaUB$OSL(4!Sbx z{bHb@oF>SPtLkGn0LrrF-6-!gDUH2E-`QkOaBaZEN?or1)gP-1i+UG0MkMc^UN;J# zUN`8Okel#4FlH5(O{LNrbbeDO3eyJ%OigztWt1HtK7KO zk{T26N`WEQ-~-nk64z02sRc2~g>y#0WhK^CPHmQ=%!QneQ2s{SUy%-I&BBb*(5?1a zeWG4uAxd*UqTO|-I<4XQ)fP*&(AGh2s^f1_3hf!t2xAvnt0MFd)f+W`4SNp*amwy1 zqyG_Y(y|jOf_?j_#Q#O;glmpcj+{UYjAP%T`Yc=8%2WkV&S%+`-&6{;_IZ|5W5D%B z=P=si;VN=PjCJWk%bQcjxP+GCNL^VilfA{pJz@0`t&maGKluBtLf?S?A)H=u3Q??F z2yWn>ks3?tS@MI=>4#vjP{TDMHwq(D=cTa&9r)3j(Ps%XG~@dg!r2P#RwcXS-cWK) zZW!DH>jRomy0xO6%!7(l5d~7SWUUmOY)QO+&mBba&oclwgJ`F(aG%uv~$fJ`pf#)CwC9fmzrCwjRnlce>As% zeaLZV-|ajZ=qz()dkflcMK>&X=mDUf(Tb)`^XSllWA@!-rwaMD}gwix|LA?T^%Dx#i ztb7>goje*?z&X{2aWwyh<-1UR3oZdtD}xwARaYoG2S}B#!tz~ATM=Z52{PNTfpKO( z=4$3&$k8{kigr>GjB|Kw#yI+4goXQ$s%d>qldv@Y6|iU+>&J%0{Iui&#{fp(_~HaC z!?ItRIchj!N=NhyM-A2QseYwn_e*{fmcm~Fi=m8(V}mnO@t$LVqit5^#RN-Y;32FhE^m17~`#)$zpaRD|0vQ=ZRqdxYEVii5-$`?`)c?npyJ^>thGVZ59KHVdz z_(vWP)6?Jlb`aMk<*`9DA5&cX7~trqE4B_vv`J)akJ&CjlNsS;_@9JG)5;E?satot zwN|$_%dOcSGtbuUxXC=nJbR0FhfK8Fjpx<KXn*bf0~*V`Dvj zeDNfQC7tAP+5u<_01Y6gkwB3|wV)Zl(YB!WorJbmqQ0jzAa4CbzMGexQ#v;|hg5}@ z8BQjPo5UNe=ZRSuv-+Hvc)>OCa0k{oR5f+=&TBK>g($UB{rRizzOJ#mIr^LGZvC?H zv8=wj`Zuw`zeW}`xl}lZCgfVBG;8X|-<{_-i2kQotSe7$OIpia$!Ix5posde-rm ze7p83PIyEUjS1cBNE}*z6>FTcpHm#=8tNn4O4e<%C{!EXbFUNZweb;|c@6xGD;H4&PjfpbWk8!VyRcbs~dtJzJ z0N|;nb7e0_y|lB*5rDBc{o|bQ$5vWbxm3<>OOVQp&>wwLeTU43v2 zTk~M2lFzGMO7vY{|Ef7^-)p{zJy0KCu?E{&m~c1BJsNC}qHX=$t5!|yr07w0c2iwB z70A^(HmZ7EXqyvl;|#Jcq5?0B(D~tpm;PBD(`5g%tK|Rnn_TXH%*FCX2(jD$*WoY15O7Md#!&Y`2=Vewyy2k-=o-dnNihN7UeKF#sy z^p7AGs%U;v4Y?jl0?8sc1y@?W)7SLNDTC8YNK7qsIR6}euB_|2LzZE=ey}_bf2Xc1 z14HxJNQ+FUxx5G_GKt`PF8!+W5=OH*FR&tR<(Ae;5*F*`uoHO^<3TZU$x8sJ+D&8P z7d&Z#c4tvcohN|@(Rl74hqbWDzu81=9t(>!pjw_(Q^ec;92Tvqs(%h`+ErAbfBfTP zW9Vh7&~EjzJDDoHs>!(X4bD_54;5T1@S|wjS$-21Hx_DAv(AB4OQf1{( zBjmoBEP;M8=Ikv_EGr@B{S4%c%+6&`S$mlKL1*kKhtz|(oQwpfj0zot$c5h-A*I`< zLH-F5HFuzh!Z;aWCUUxH`!G5fxs76)SlL>I&On7+kUFskZT%0;C^p0m1+X6qwyN3b zF3p%<(00l7;6wVu)uA@1C%T%gBzLZX+M)Ke!5^BOS<;>|f3JVxx4)gA>n+Em=Cb9G%O+dn>IEn2GY z5F>F#SUGQl%J^JPU>9)i>_dNE6FQL2MQbOP zJnsMUomlwykUVmo1X(v0x`JGwrDP$ctsPp_t_+-#!8j2qrA`==D;fA;fIdE~e?7)7 zEi05pc9gwZv}=DlMHfrJ8S=jhUn5ag9|Q_gz?b=3;Dh3IZ1|pn#`U#t9&Ilg{ftTY z4#@s1u>Yod%BS`p#nxZfe-?aAll{ltH2&KDiz>zH*dV@#8r5rmI2w%FwMv8MV@Mv*vQv#3H&#E< zPW&_0bkbiQYhv)`uZuHXu^)e*iED-GfK9}isG~1%WTEr5aW@vrQlZz-T5UqB#f$Ez zqmqCB%7JT6nNu33<@Ij>2z9?9z5ZqBkEEqACNz_~9qCQ(*3=Tg)>-wHfl0ZQ$Pv4t zXuk}1XTP%WmezN4FW!2^Jqtg5;s)2g>Ti$O8l~gp;(3>z+B^{d*ueQ4-@ESgpI>Wj zG-B-$_B^~nGszu#0Ye{7!bd-0FW{gR&Kaj~v!fE-5}XN{s>k+o5-MwVCFMw+y8Dc> zG3ZvMV~t}5u_@_Fnb`)XHpoGJMB>BY1bXqkWe-`_Za$Lj#+WD zAn^UaHfD9~U2jhPruj9kWb9IkG>jHWv;P~gWT6Hr8TL2EtPIMoBWQMV2cKu@ zE%@d!V4?V$faQSfm;YhR%7TS4t9rluH8CqoP8}1RGgZv$81Pu4Y67O`B~0?~&sY(f zcImUl1SJd#jzH$=`XJ$MKlD0Vb^-?u*8|dappQiU?U~6K7T5m$Z^p3H;5#Pl)yqnY zttoeXHZ|prc6~=D?fPS(%V>b&L_4m8)`)1MN?WFyqr_!1Gz4_*Q-+S-A`|i8%zWeSq^Z6R^els5D zs#{X~SkC^{H0)!|NY3X9XK$UJwRfC3cRLvQsC!%V%SO|>@u~O`GjWX5Ml)@5OwLA} zlwI~*R!Q{%7g1(GM6ENm&Dpd_aBNn_vZ>)}iT@IbML(wV6L0f_XmM6w;_ac=5^u|x zK`hcW?(^Wr)QTKR$$X@#06ygCb5I^ljTtF{<95y>BEmfW(^%wNWLr!$AqCtxw>XzKR4 zZ&QM5IND^+WgIcwv!U$-?I~3U5x3WNOZy@y%bq3 zvi&v6S&@7C8<4rkn~nd%k5pGi7&E$E+Bs(I&%Iw=slOdn*L+dfj;?DWRo6k_bsF)S z?t?QOuM~gB#;a4;k`!L2elWa>5z?sYx~dF|9bMn)W*uPWzB=#?(n6^^l-d`Y3RMJI zq7PT&)Q@CLI$-yD!9B9USAJKmvR|4EAH@@>6yB*rn zmyGhAm-OiS0et8A4h%gt(s>@74GF&3)wI~$XKVy{9*V|X4$pHan0${OP&GP_X;K8u z4Y(;p>oG_ArS({rIn&|rXk${w7v|tco}Y8PX*r#pj_vc9Cqnq7IoI+vCG8&2S%ULY zM(%T>adKp_UbJR2(zju*g+i^`vAIPpX|+JrS}~u-RlF9`V z2Kju3oG%vsK(RFpYdh;Jp4b22b&3uoUZV))*v#FfWmO_JbCqths?4lYV19sG7(fd= zbm^$*gU9uO8XYjNU>m>zl=p<3f0{x&af4S!qDU#sNtDITDI`V>nqu2Fau|@T$g{jQ zt|hWwnsx%)74Qt|S8`B0cxa%Sg+s=L<}3DZ0s0oa9yil{{n`h1YSQBxJIVNPEAoO zk{PFW>LrZ-3PdoFs&js1iYMyCbc4Z-GRtP6?gfdPZ7;<#-t3r0**xV;jW?Uk@s*@$ z&@kzhvZdFVB^*SWQ-7pWvQ^6PCi;;c&{hHg8YBkNP$H2q@}X;oJ(jA?RL{wi2LqE< z)F^{&lJ+WYXM9u7W_Y|A4^m3gY5jy0A^wwxUO@@uH{3Wq3jQ&HexBXv5>gXq56+34 z7|0y>JqzrSk*zVDQ4b)jv!>Kk|)T9u7**gDQ_ESs&uUKSj7ql^N4aL&l5_9M}a&oO*lI6sv0 zk8(2yH*;_kgMl6)al!{#u#^c57whLJ-q?;k0eh!xU=|jJ>8IhHS)4Ky zB@FIo{a zdhI+i89Ga@LvEvpV0a$u$F&mIP7aSAPw!`J@SFqA25qP{S+}DPG~U&L3cM~||3^ts z+5R<0YyPM-8s&36u|uTQ5&AsQ64|)VV%#xW!@;uIi3>=L2`P&eqQ;+XD#blYd|dTX zTra?t+mSNYp9XGgJhg zT$;SY$(*_&{!+$Svq&T>wuDxxbZ%oj`16E8W(3hvJZW5((HxG;e4^ztT<7CTTW8xd zbP-+E8DkJM8I8V--t3ZXr@Ifc!T$Z#q2l6O*6I_q5|gdDd)MAlTpU`RSkE2G)7B(J z6*0jdHdIw@p#Cy}-PkLuL=S+SNiOgaPQlpUS7J=jc5j;M$y|+aBvDqUzJlj|9B#}W zX76&L#8Jj5BPE<5WeS*Pr$DPTjpYkC!c62Uicj>lW*DJixzr-LWFH&6VC+~{Vw^v2 zJS%;~(C(OFt!&%;OM@OK9@h+NyPTuqGgvsf5By247HV&FGCs7@X_>vVKuc4{k7%i# z@k2!9(q<|iKQNcRQKsdw<8%Ec$C;kVUoH*We!t;^JtYqo2oah!3VX{!8>WI=)9W`^ zg*Z9)_Q6T!i_1y59)hGgyz zoI3T}XtE6s?QIs?o56KidSsjNXphG7nY0J;!acTxTS@g9H{8QL>H&|BpAG&nBh zxhjjHRhv70pXC{y$=H2n9lqUt2H$XWAqB$+{A<^}c{SEXZlYNy`B z^c}kgJ#A(&&IfF`*$Fhg_x`YH`3Q*y5B}v*_s2=73e1?dK|@mQe&>oGbjRnr08*Wb3afJxuMkt3m+_)ocj&w zVd4gB9M@auV|vQPUf%+A%H{Ytp6?f+@|@x*IFp;Q-$|_#a3zOzG{Za=M~a>at{MQJ zGHZf)=yRJ31@MBoz>>1}%Z)dD+E4RrU4||4D6ja11{3e)9DkEE|DADs4jE+1&;h%_M zkJCGI(lXuNFUYV?f7@&kKq^j)(3U#OC~?|Avv4$n3Ejwj6HpccqYHJ7j@V{nDI;*( zcMMvR@!Ra7jeh6*75e={zdysFyN&jwUw3EB>EZja{vU)~KNT_tdzs5qBjNGtEY2u? zQ%yyd&sb3i$?bzfAp{z)o?Qsf67+99+h_ghYCU^y;`NihMQdvpCf*I`+>;mQUR;H* z*Ice=&vqvgPYhftGai|LGN8BFQIEK;f$uE%up7iOVIr_dk&pb9=Y|~T6}4ue?LKdo zzVDgG#_?e@*gS(IE0!_n+U%W*@m`9y=1LD4DsUE01vs;=jeqV1l{4Y98*WQnn|*6` z-nQxL!#mezJ>r{FdF8yhS60sRd8cmdgx}s9IycS<(rr?34#teM12KQz1O>x?lol3a zX3R9Nr;y1YcOI72-J7zmv(#DlN$XuNdH4c##*FQSKmctK92s~a(*Ngt^PQ!Kg;mDW z!YZ0!-aC0-d8y-wp|pJ7WY3EutTTh74KsoFp$is8?-}G39`G|eMgi9X9`H;Lyi<7K zox%g}6drh|@W4BT2i_?>@J``@cM1=@Q+VK=!sB?S@W49-B%99&HByg72^_06uP+~- zo{M-waC!9ekGgGiB<`g^T8Y@`{xk z3v%-c?wMSWUo>mgjuq=#uP6tbBh9?&Is&~WW8RWW8Cl;n!kqDO=u>vG#@4JJ9`n?% zoB?*fnW0d%m6zZx?(=TiO;Gp85y5$eES3|7Bj9;C+Oep^SzO19o%Ll%#NYfboY}pB zZT^o;^Ef}FU-vv^9Y^`ODBY++IW`zy9WffQGm6*aKA!V!9{V}(vES}bK6l);MxVnv z5)O(|44CQxs;%sPG!|}X6TIPI$*0%c_oXi-;H@k1GTjWB@>1`C#xDH~R&oyk+rL5$ zw$QDsj6}LdF9T{|`a~ap=kz(e(92lb<{fqo#?Fh!D@A8eF){#X5(^ zBwqPV24eKGB!V*QhjVsL{a6b}vSvM54EGkPFX5VSM(ILRBJ{AJIPtQBYM=qQcI^~gPvE(~|IPY1-R4})c)%Xk&FH^ewTiq5y^ltEfCUa? zFRsZr_LNG}=U9qxoMR<^pr|LXe^WJkl66iWO&l0V9MI7HC3X+|zSwOj$u=5VYeB_wv?L|j=Xv$KQ2nO3h<-IDc7 z)^VuwbYi!@|Fm^CYbty~?{sa1uA~r{;oOdQi^pBZdynzndU!>scgxn^&=hSLVrFnc5G695Vc>_h!f;O_0YR``!VB-HFc~Kuiu0 zL{+-q_!{F5r61CqAJUv3(wraCoFCGhAJUv3x&4sl{E+7Skmmf5=KPK{=Z7@sm;HsM zcq9F!-HEra1JTtO-)J>BRaJur#5Y{Zdc9*XMKJ^m4TM2ag@uHR|Dyd30gv%+g7G z)H{KX!tqVvv>8(i{1YcuuU zyUS}s&3kXDEn2*8;be_JZ^pwFGg^9%M6UDZ=jId?W_hmv?6Er!%ruX>^}(F_j=t#j zoLrwT&!6Xf$@b+VZZ_!5TW1}CF4^!ujw|uQ3%S&e(=A@NMn1=Nq=lZ4Gv~$!v-rxA z=UlZ|w~t`U^HruV`M3mhJnGq3 za3Amm^!cxCe^uDK<&FTEDfhUz*Oi44Oh!tbPul9*Ohl;}p7L#GOc9^>{bHZiyN8*%SC!rj4%WU zb8)tPt41AqYL3=;W`UY8j58=|qQE)B_anV%=taOTNp}nE5!frRPhh{m0eSKw z@-9(XiQRxzlCH+wv_!4I`=D>2 zuuovWzyW#kBJx(MMYvO`1^}z%omKMADm4c!t-?ETN?UD^UpEQdCUCpJdVxCx-YD=U zfnAcfTVRjCUV(iA`vrboo_s*yg90BC_^`m^0v{Fln83#co)CCa)uC2TO8O~*PYXOH z@EL*63VcrB^8#NGI3O)z+pE#u+W{vEoC9oE0~?zGH=y6E)kcAv1a1?!U0}Vy9Rj-~ zSGT|(fxQCz1ojL3x;*)Szy}3BB=BK@#|1tr@G*gp3p^q4Nr6uZd|KcsfzJqhR^W32 zpBMOozyWE+MYL-L+ISLhqQE%<7ok=w(8d=5HwnB};5LEV1=b7vguopFyCiS7z#f6U z0{aB^3;en~`GCL&1wJJ3VS&d5J}U4rfsYG3A@E6oPYHZl;3MH&t&(r{3bhJ)}SMMxSBD$;OJk%og9t!x+hMwf7{F5y~T!nL}Xlr*lB-v8^-8W@$<-^ldL>t{X-(Uvl+Ju71hYFS+_9SHI-ymt6glD<+f^6UvDR<-~+?VnR7Fp_~{-%Izv9 zloJ!mi78S}OeiNNloJ!mi3#PzgmPj+IWeJ}m{3kkC?_VA6BEjb3FX9ua$-U`F`=B8 zP)N z-Rv4W&G>^MJMF&GA2_i2^lq$j`@?dSIW zd^Jt)$2vp1X{{B}ciCyJ^7SPhhI^f!B-_KVcbML*XegC0I^PZ-Tz~v1K z1J{H)I-0@<0~^E9*7ml>#erqz6%}QH^(`#{c_MP9^(~E&=7y%a@Se_= zP^6-)ynNZJYu0b9UuFNW#Qw$p8nD0a2uGsL?QH?cAg>?i`Q~UK6zGVA8pExj$i6`P z?va%%8&TP&j*k7orArSUJXqEmYV0_)KU~((-df*;7j0^9>j-Rb-`#OA6bT1#qouha z+!hTt20Ggs!;t{m9N504HgMhkaGQC!);zE{kg6~6ATP7?@-?AbL(MIrT`l3j!RC%8 z)OXYR>jR;VV8E_Rv?0>Gzav@}ZEh)RkL+1`-KN^{e^FPdcF^S^6@j8)4|EwFDgc|X z2B;AN>KagQ2mYH>SRKUojkrctD{{1{Mpz^SV6adQiIe`d1Be0Gg8x9;o1*fKdBV7R zD_|p@xCk8di+|1z}-rLRYc^+;vrSTZ6{=C6SfzwVH-qi7v# z9Y`x7Rr}00yx;qq(VhTm8vte^xHh7MR;m3y+-rwk=lw8O_CA3?uCTSu2Z@bi@1G$LPFz%QqT5xT^_crN~M%?d|o@DQ^ zHSA~R-lA%8y-vy@&Qfo#&3FSTU@ZI^dPvnQvs|Y(Ay7mJ?_YM0^i-^lzJLk*Ke!S>Xdp$4PajWfx1Kex%!^^zWR=ys{Ta%yPl>?^>jT$ z&(yPYK+o25^jvP(c*@#3TUvabZO!H7>&v~Zp@v9%TmJrVq`AEjoW3I*2{*c%c7-C| zEgj7*jbU%IT&yiyt>(SCTN|1q4V|sKTf(2s-J441?Zf=h8VWV=4SD5TOn%nj;k=NJ^ OJVcwL^0**?ss9F;Z+D*n literal 0 HcmV?d00001 diff --git a/docker/rootfs/var/www/html/fonts/Poppins-Bold.ttf b/docker/rootfs/var/www/html/fonts/Poppins-Bold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..44313ca448a294de7a8e39b816203390edc52377 GIT binary patch literal 141260 zcmce92YggT*Y~}1wQsNkRw-36MYvCG_4q5fG4$AT^4DR6&Y> zfC$nBL9i<#f(=pZ$Rm6AJ2Ut0=B9zq`+nc2zhQ3Md*_rnbN*+}OemV7C>Lrr#ZaTO zdgkZC8BL+!Gf+F^W@YC%dOG@0^v}=1d0K8s_nvwWFW?ZpAo?a=lAYqSJ$G$Bl;TUE%l7;P($6K4s9_ z_i|=aRNy{}-dWHvuy%l(UQ3{ioU2yPC5B7$=fGY%c6#1w@V zA>7(-Tut|*^mH+Pi39$J{5M-_JHmB-Sx$HAMT+_dacvNdLgVBVwF=)uQRqBWa!N%1 zfzRQ;3tVCHs3cJ%m4q^^_ee2XA28+C2O@tco#A*9l;^Dv&=RhQ=EL()@Hx|sJEl{u z5BTCrQEVubxllGhISxh77cJDjbeeS#JX;ROL+Ett>&#hr&)ZOR@cv^^-h%QgoX0__ zf-(?FCS02jr3s1&$}+e%8|pJq`%-2!0FJ+)(ik(;TjAQLQ164foH8@r;P_7{?r^>m z>J3m=Qkm!g)Jbrj5A`W1)l>q0*4~~dIEL@rzlYxm??5M@Y=)u)nsFPTz4V*#EVLJC zp~OIuL21=ql*c_!e-7=fhvQvT8bVOl!n4PrZiLz&?!j;A*Weg`YZe?2hB}nWr17y8 zj`{W?9hHC-a4u*M`98dhqO71%z+Z1F8kJM=a4v)MMd%qSj2=LBLMBRys;N-)A{Bue zsR}fnilumiihz3p-aJ)Sf_4#EZ3KS*OIhJ|5ji&1Bdiako>0$(YtTQ$7ohk+c>pDrE0VEr50`OpJ_u?@s6ju(FT*kD znz#qN1N1{e!SO6AL7GM-h<&I8(OIZ}fO`pW?+>UaLn*R8aNGpvec{;(zTAfTKYUpM zH7-WJ;Cr}y2}K2M-UfAdC=GC24h3W?Spy{#u7yJl?G^!l#o2KDE7Un~{tZ{e&p-{f zL=5s0f&4_DLjjv3>IdZ>SHx1d5AqeAf;ycqzd^l{FTh7>CA`}L=kQx`7(BZd3h0LD z7br{N+Ayd;f_erNC09h}p@#N~Cqn50_kkZ`_>Fi96p#S|o?zKw-lJ7eUV>xzEv~^< zK0t-m-$h5E2L7Q};2wMzL7PNxK=}*`yhC&euJ3{ZvJ?lwIc@{+MJnfN$yK-p@(>qu z1py7B7p>omN}&c>Vmh#lMGjn%VEH7$@nyKa4C>)fFnzfFzEG~i^&3zYLWzOvTFNZ? z9?C-KW6(xim=~ZiDgup=m3-wI0jvm z-iLe8E^z_WjZi?wMCPNR-T~kJ5bpUyeGkfFxK<0L1`5bXbO)~O=j&j2E(P9);Cvld z#6TA*C2-vdo;?QV@LQ=0?*9X21Dt2V_2)rn1EI#(5~1B7J5g6Ca7_$y6#>sh-f&z9 zHP9u(^x$V`?p%U@i=V~(T+J1U2`E(4Cm0tMDSY% z`iQ7EoC95=Yf!Lme+LD(Ydl={fam{&V~~aDD<~J?S})2hX85ufWOES8`*3e6+yh+_ z9pek|8h?xE=@8%nZpUo62K3>+CU%DMD%?K^^-w51;J6!<2T2tN12gT%ZOyfxbNuVcP*TiPyq+Ft0#%L@yoTcd2mQ1m$xmK!X_c5!)K+W2iwc zQp~s0@I3sM=ss=>)<-Y+KFDAEEtQE2Q$V?kr&5`s{ZM{@YpHNP7HTgj`}q4~;28QA zE)4Ko0{scsA~6(SIEOw-^!#f$o(spoUtDm%6`kR);eO{1MG4n=eZuxZ415*W!Sy9j z-i2p@4$%Rq6>uHP3HT)fK8ur~eu*pMyHFp3Yvn+v9~}Py$InB(5$bs8D{n(Bhhm0y z&Vlo@P*%YCE*KM1EuXwit7}Vg2Syy2n3;Z9~=3@B_KgT`GVjsy?eBqcBcozFi zwo(qqq`>{LaQr&)l_>Zw@PHK1NAPFd@i+?i|KFCG)KK~*DuS5^{=*bX%cvz17C3yd2sxvc^ z5`m8) zo)P&FIbc}`3-ASeVpk#yEDu`&SrA#{8q!Pp*B<)yM;C&^vOy z0_q2p6J1C-VVPk4WV&*-{TyV3c|`h+pcVxKUhswXK_4P|yqd3V=eS>i&XN8ltf8+F zeYe$E#+c7ft8aoWf#-1lBz*+y1eS-bpF&?D{Z&xYrPi}pFNtnE%eROBF7+$)?cUHI zaKZgz66pP5&=naRM*~l`LOBn8<2>bU`)2FfE<<5kaGO?A24)6jfNwJ?u_ZTJBl?kT2(W{gz-ewu0Eq8*m>AGQw|Gf_<7sIm5Wb z@Wr3PF%Jq|f-CsQAFR)7P_Ln8gT3~IBWH*n%%r8XAFZNeXfvHh_oWBYGwGGI1^J=l z=sdcJZZdB>GLD@bogG~rJsd+Eqa6*7@s8P!`Hn@7C64_Z2Re>&9PKzZa9-evz~2KO z$(`jMa$k9%Tq)PeqvZ+mEctf%Zuy(?6AE{QQlV8uDUnj5bWyq~WlCRVpfX&Ur>s#9 z)LhbB{s&pD&?>H73hG7Li4LSgXcHYz7t;Oc258q~c#q;et+NNEu&@LZ) zy9SVU0Z6B4>z~#;XdEgyLE-didOPhvSt*wKh3Z04e-SGFvOIKo z2xkvF5t^un@qhgSXFd;o|9a`0?%(*_{pRlNuNv=MxO4W-nLF>?dHc@cJA3bJyY-TvbCwc7`8Z@RtyOVej(9Z778d-uQi zcQ`yVlEN`L{NG3@qp7*nd~(M9OAep#4}QbH`)?{Wg_;J@FgM_a3*|v|r&6hGh>cCB zWT1CmlsAlZzLX!Ogg99cr2;KfP&24bppX9GWh|ziqn0xFDGB{Ie9n=2NHiLM8Ef{_ z|6|Z<^gLRF)}j~CI7#_7tu>-3wjy7g0`Y<=vA~G?La%xF0>o%L9d~` zXdikV?MDaDL39Ycfexc1=qP#<9RrO!f!;zV(c9=0I*r~zXV6*D!Sm<>=;0;wF1n1a zp!d*KbPc_auA>|11N0&K2z`t`L7$?}(C6q2bQ67vZlSNxZS*y|gYKel(6{J2^ga3k z-9ta3`{*b1GkSo2LB9f#o{Sgc!}u{OCWMJ%42+(MWlW5bF*ET@B9p)*G099SlftAi z=}ab*&15k-OdgZV6f)hIBBq$R!yxnr`V&23zF{1g7=}XsFb^0B`VV7ayqPfOKMcdL zj1@g(q|9&3pUfZ3d(1!RG1D2o9mfu1r^# zu6|vEyBfMycAeJsaMyQSX;-6bh3j*!=iC^#ShrfY5pH|jzH;|<&u|~-KFj@vhqp(b zN0~>B$8e9y9?y8J_c-nGy-X%k$l_#avOHOdtWs7ZYmkkWO_t4;J?rV>Y4*(Y?B)4} z=UvZVy`*HfnZg-q2XJ3xf^8$3nbAW`x`h%?Mo_`k$~VVV{Rrg

_M{J9vBWof*kNhrb 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

  • +GqI=&YU%RSOJBBT9Fme-UgER~ABD5MCz>d9s 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 z%0D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 Lw^_2ZIeGsJyA}#n literal 0 HcmV?d00001 diff --git a/docker/rootfs/var/www/html/fonts/Poppins-Regular.ttf b/docker/rootfs/var/www/html/fonts/Poppins-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..246a861a77e28073ff254655f032b155c3b2158c GIT binary patch literal 145312 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 literal 0 HcmV?d00001 diff --git a/docker/rootfs/var/www/html/images/bg.jpg b/docker/rootfs/var/www/html/images/bg.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ccdca13680b96c296f18106afe21389166168a3c GIT binary patch literal 839380 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

    YoC8ZP3HfzOc20P70+25%u5=yHwNutVor_Jn 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

    5b&xj$TIGi_N2p&{I~IwJ1(zP#JE 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 *9 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ξ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%0D-ezks8lE%q?X8@2*1abET)(4l}>E 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

    |Jr$}KCsaL7DX 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

    - + + Site not found :: Nginx Proxy Manager + + + + + + + +
    +
    +
    + Logo +
    +
    + + +
    +
    +
    +
    +

    This site is

    +

    not yet configured

    +
    +
    +
    + diff --git a/docker/rootfs/var/www/html/main.css b/docker/rootfs/var/www/html/main.css new file mode 100644 index 00000000..e55b7ae0 --- /dev/null +++ b/docker/rootfs/var/www/html/main.css @@ -0,0 +1,399 @@ +*, *:before, *:after { + margin: 0px; + padding: 0px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +body, html { + font-family: Arial, sans-serif; + font-size: 15px; + color: #666666; + + height: 100%; + background-color: #fff; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +a:focus {outline: none;} +a:hover {text-decoration: none;} + +h1,h2,h3,h4,h5,h6,p {margin: 0px;} + +ul, li { + margin: 0px; + list-style-type: none; +} + +.logo-img { + border: 2px solid #3c99b9; + border-radius: 50%; +} + +.p-t-24 {padding-top: 24px;} +.p-t-34 {padding-top: 34px;} +.p-b-10 {padding-bottom: 10px;} +.p-b-45 {padding-bottom: 45px;} +.p-b-60 {padding-bottom: 60px;} +.p-b-175 {padding-bottom: 175px;} +.p-l-80 {padding-left: 80px;} +.p-r-74 {padding-right: 74px;} +.p-r-200 {padding-right: 200px;} +.m-t-10 {margin-top: 10px;} +.m-b-10 {margin-bottom: 10px;} +.m-r-6 {margin-right: 6px;} +.m-r-30 {margin-right: 30px;} + +.bo-cir {border-radius: 50%;} +.of-hidden {overflow: hidden;} +.visible-false {visibility: hidden;} +.visible-true {visibility: visible;} + +.trans-04 { + -webkit-transition: all 0.4s; + -o-transition: all 0.4s; + -moz-transition: all 0.4s; + transition: all 0.4s; +} + +.flex-w, +.flex-sa, +.flex-c-m, +.flex-sb-m, +.dis-flex { + display: -webkit-box; + display: -webkit-flex; + display: -moz-box; + display: -ms-flexbox; + display: flex; +} + +.flex-w { + -webkit-flex-wrap: wrap; + -moz-flex-wrap: wrap; + -ms-flex-wrap: wrap; + -o-flex-wrap: wrap; + flex-wrap: wrap; +} + +.flex-sa { + justify-content: space-around; +} + +.flex-c-m { + justify-content: center; + -ms-align-items: center; + align-items: center; +} + +.flex-sb-m { + justify-content: space-between; + -ms-align-items: center; + align-items: center; +} + +.flex-row { + -webkit-flex-direction: row; + -moz-flex-direction: row; + -ms-flex-direction: row; + -o-flex-direction: row; + flex-direction: row; +} + +@font-face { + font-family: Poppins-Regular; + src: url('fonts/Poppins-Regular.ttf'); +} + +@font-face { + font-family: Poppins-Bold; + src: url('fonts/Poppins-Bold.ttf'); +} + +@font-face { + font-family: Aldrich-Regular; + src: url('fonts/Aldrich-Regular.ttf'); +} + +.container {max-width: 1200px;} + +.cl0 {color: #fff;} +.s1-txt1 { + font-family: Poppins-Bold; + font-size: 15px; + color: #555; + line-height: 1.2; +} + +.s1-txt2 { + font-family: Poppins-Bold; + font-size: 15px; + color: #fff; + line-height: 1.2; +} + +.s1-txt3 { + font-family: Poppins-Regular; + font-size: 13px; + color: #999; + line-height: 1.5; +} + +.l1-txt1 { + font-family: Poppins-Regular; + font-size: 30px; + color: #fff; + line-height: 1.2; + text-transform: uppercase; +} + +.l1-txt2 { + font-family: Poppins-Bold; + font-size: 70px; + color: #fff; + line-height: 1.1; + text-transform: uppercase; +} + + .l1-txt3 { + font-family: Poppins-Bold; + font-size: 30px; + color: #333; + line-height: 1.2; + text-transform: uppercase; + } + + .size1 { + width: 100%; + min-height: 100vh; + } + + .size2 { + width: 100%; + height: 50px; + } + + .size3 { + width: 36px; + height: 36px; + } + + .wsize1 { + width: 390px; + max-width: 100%; + } + + .bg0 {background-color: #fff;} + + .bg-img1 { + background-position: center; + background-repeat: no-repeat; + background-size: cover; + } + + .bor1 { + border-radius: 10px; + } + + .overlay1 { + position: relative; + z-index: 1; + } + .overlay1::before { + content: ""; + display: block; + position: absolute; + z-index: -1; + width: 100%; + height: 100%; + top: 0; + left: 0; + background: #30bab6; + background: -webkit-linear-gradient(top, #00B4DB, #240b36); + background: -o-linear-gradient(top, #00B4DB, #240b36); + background: -moz-linear-gradient(top, #00B4DB, #240b36); + background: linear-gradient(top, #00B4DB, #240b36); + opacity: 0.8; + } + + .how-btn1 { + border-radius: 25px; + background-color: #240b36; + padding-right: 20px; + padding-left: 20px; + } + + .how-btn1:hover { + background-color: #333333; + } + + .wrappic1 { + display: block; + flex-grow: 1; + } + + .wrappic1 img { + max-width: 100%; + } + + .how-social { + color: #fff; + font-size: 22px; + + background-color: transparent; + border: 2px solid #fff; + border-radius: 2px; + } + + .how-social:hover { + background-color: #240b36; + color: #fff; + } + + .focus-in0:focus::-webkit-input-placeholder { color:transparent; } + .focus-in0:focus:-moz-placeholder { color:transparent; } + .focus-in0:focus::-moz-placeholder { color:transparent; } + .focus-in0:focus:-ms-input-placeholder { color:transparent; } + .hov-cl0:hover {color: #fff;} + .hov-bg0:hover {background-color: #fff;} + + @media (max-width: 1400px) { + .respon1 { + padding: 15px; + } + } + + @media (max-width: 1200px) { + .m-0-xl {margin: 0;} + .m-lr-0-xl {margin-left: 0; margin-right: 0;} + .m-lr-15-xl {margin-left: 15px; margin-right: 15px;} + .m-l-0-xl {margin-left: 0;} + .m-r-0-xl {margin-right: 0;} + .m-l-15-xl {margin-left: 15px;} + .m-r-15-xl {margin-right: 15px;} + + .p-0-xl {padding: 0;} + .p-lr-0-xl {padding-left: 0; padding-right: 0;} + .p-lr-15-xl {padding-left: 15px; padding-right: 15px;} + .p-l-0-xl {padding-left: 0;} + .p-r-0-xl {padding-right: 0;} + .p-l-15-xl {padding-left: 15px;} + .p-r-15-xl {padding-right: 15px;} + + .w-full-xl {width: 100%;} + + .respon1 { + flex-direction: column; + align-items: center; + } + + .respon2 { + text-align: center; + } + + .respon3 { + display: -webkit-box; + display: -webkit-flex; + display: -moz-box; + display: -ms-flexbox; + display: flex; + flex-direction: column; + align-items: center; + } + } + + @media (max-width: 992px) { + .m-0-lg {margin: 0;} + .m-lr-0-lg {margin-left: 0; margin-right: 0;} + .m-lr-15-lg {margin-left: 15px; margin-right: 15px;} + .m-l-0-lg {margin-left: 0;} + .m-r-0-lg {margin-right: 0;} + .m-l-15-lg {margin-left: 15px;} + .m-r-15-lg {margin-right: 15px;} + + .p-0-lg {padding: 0;} + .p-lr-0-lg {padding-left: 0; padding-right: 0;} + .p-lr-15-lg {padding-left: 15px; padding-right: 15px;} + .p-l-0-lg {padding-left: 0;} + .p-r-0-lg{padding-right: 0;} + .p-l-15-lg {padding-left: 15px;} + .p-r-15-lg {padding-right: 15px;} + + .w-full-lg {width: 100%;} + } + + @media (max-width: 768px) { + .m-0-md {margin: 0;} + .m-lr-0-md {margin-left: 0; margin-right: 0;} + .m-lr-15-md {margin-left: 15px; margin-right: 15px;} + .m-l-0-md {margin-left: 0;} + .m-r-0-md {margin-right: 0;} + .m-l-15-md {margin-left: 15px;} + .m-r-15-md {margin-right: 15px;} + + .p-0-md {padding: 0;} + .p-lr-0-md {padding-left: 0; padding-right: 0;} + .p-lr-15-md {padding-left: 15px; padding-right: 15px;} + .p-l-0-md {padding-left: 0;} + .p-r-0-md{padding-right: 0;} + .p-l-15-md {padding-left: 15px;} + .p-r-15-md {padding-right: 15px;} + + .w-full-md {width: 100%;} + } + + @media (max-width: 576px) { + .m-0-sm {margin: 0;} + .m-lr-0-sm {margin-left: 0; margin-right: 0;} + .m-lr-15-sm {margin-left: 15px; margin-right: 15px;} + .m-l-0-sm {margin-left: 0;} + .m-r-0-sm {margin-right: 0;} + .m-l-15-sm {margin-left: 15px;} + .m-r-15-sm {margin-right: 15px;} + + .p-0-sm {padding: 0;} + .p-lr-0-sm {padding-left: 0; padding-right: 0;} + .p-lr-15-sm {padding-left: 15px; padding-right: 15px;} + .p-l-0-sm {padding-left: 0;} + .p-r-0-sm{padding-right: 0;} + .p-l-15-sm {padding-left: 15px;} + .p-r-15-sm {padding-right: 15px;} + + .w-full-sm {width: 100%;} + + .respon4 { + font-size: 50px; + } + + .respon5 { + padding-left: 20px; + padding-right: 14px; + padding-bottom: 50px; + } + + } + + @media (max-width: 480px) { + .m-0-ssm {margin: 0;} + .m-lr-0-ssm {margin-left: 0; margin-right: 0;} + .m-lr-15-ssm {margin-left: 15px; margin-right: 15px;} + .m-l-0-ssm {margin-left: 0;} + .m-r-0-ssm {margin-right: 0;} + .m-l-15-ssm {margin-left: 15px;} + .m-r-15-ssm {margin-right: 15px;} + + .p-0-ssm {padding: 0;} + .p-lr-0-ssm {padding-left: 0; padding-right: 0;} + .p-lr-15-ssm {padding-left: 15px; padding-right: 15px;} + .p-l-0-ssm {padding-left: 0;} + .p-r-0-ssm{padding-right: 0;} + .p-l-15-ssm {padding-left: 15px;} + .p-r-15-ssm {padding-right: 15px;} + + .w-full-ssm {width: 100%;} + } + + diff --git a/docs/.gitignore b/docs/.gitignore index 38353fb5..eccfbb30 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -1,3 +1,5 @@ .vuepress/dist node_modules ts +api.md +api/ diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index f3b735b8..562ed723 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -39,7 +39,7 @@ module.exports = { // Custom text for edit link. Defaults to "Edit this page" editLinkText: "Edit this page on GitHub", // Custom navbar values - nav: [{ text: "Setup", link: "/setup/" }], + nav: [{ text: "Setup", link: "/setup/" }, { text: "API", link: "/api/index.html" }], // Custom sidebar values sidebar: [ "/", diff --git a/docs/README.md b/docs/README.md index 082bb05c..4356775b 100644 --- a/docs/README.md +++ b/docs/README.md @@ -3,7 +3,7 @@ home: true heroImage: /logo.png actionText: Get Started → actionLink: /guide/ -footer: MIT Licensed | Copyright © 2016-present jc21.com +footer: MIT Licensed | Copyright © 2016-2021 jc21.com ---
    @@ -37,3 +37,37 @@ footer: MIT Licensed | Copyright © 2016-present jc21.com

    Configure other users to either view or manage their own hosts. Full access permissions are available.

    + +### Quick Setup + +1. Install Docker and Docker-Compose + +- [Docker Install documentation](https://docs.docker.com/install/) +- [Docker-Compose Install documentation](https://docs.docker.com/compose/install/) + +2. Create a docker-compose.yml file similar to this: + +```yml +version: '3' +services: + app: + image: 'jc21/nginx-proxy-manager:3' + ports: + - '80:80' + - '81:81' + - '443:443' + volumes: + - ./data:/data +``` + +3. Bring up your stack + +```bash +docker-compose up -d +``` + +4. Log in to the Admin UI + +When your docker container is running, connect to it on port `81` for the admin interface. + +[http://127.0.0.1:81](http://127.0.0.1:81) diff --git a/docs/setup/README.md b/docs/setup/README.md index 99fbd7bb..56eb8618 100644 --- a/docs/setup/README.md +++ b/docs/setup/README.md @@ -1,22 +1,6 @@ # Full Setup Instructions -## MySQL Database - -If you opt for the MySQL configuration you will have to provide the database server yourself. You can also use MariaDB. Here are the minimum supported versions: - -- MySQL v5.7.8+ -- MariaDB v10.2.7+ - -It's easy to use another docker container for your database also and link it as part of the docker stack, so that's what the following examples -are going to use. - -::: warning - -When using a `mariadb` database, the NPM configuration file should still use the `mysql` engine! - -::: - -## Running the App +### Running the App Via `docker-compose`: @@ -24,8 +8,8 @@ Via `docker-compose`: version: "3" services: app: - image: 'jc21/nginx-proxy-manager:latest' - restart: unless-stopped + image: 'jc21/nginx-proxy-manager:3' + restart: always ports: # Public HTTP Port: - '80:80' @@ -33,46 +17,20 @@ services: - '443:443' # Admin Web Port: - '81:81' - # Add any other Stream port you want to expose - # - '21:21' # FTP environment: - # These are the settings to access your db - DB_MYSQL_HOST: "db" - DB_MYSQL_PORT: 3306 - DB_MYSQL_USER: "npm" - DB_MYSQL_PASSWORD: "npm" - DB_MYSQL_NAME: "npm" - # If you would rather use Sqlite uncomment this - # and remove all DB_MYSQL_* lines above - # DB_SQLITE_FILE: "/data/database.sqlite" # Uncomment this if IPv6 is not enabled on your host # DISABLE_IPV6: 'true' volumes: - ./data:/data - - ./letsencrypt:/etc/letsencrypt - depends_on: - - db - db: - image: 'jc21/mariadb-aria:latest' - restart: unless-stopped - environment: - MYSQL_ROOT_PASSWORD: 'npm' - MYSQL_DATABASE: 'npm' - MYSQL_USER: 'npm' - MYSQL_PASSWORD: 'npm' - volumes: - - ./data/mysql:/var/lib/mysql ``` -_Please note, that `DB_MYSQL_*` environment variables will take precedent over `DB_SQLITE_*` variables. So if you keep the MySQL variables, you will not be able to use Sqlite._ - Then: ```bash docker-compose up -d ``` -## Running on Raspberry PI / ARM devices +### Running on Raspberry PI / ARM devices The docker images support the following architectures: - amd64 @@ -89,121 +47,12 @@ for a list of supported architectures and if you want one that doesn't exist, Also, if you don't know how to already, follow [this guide to install docker and docker-compose](https://manre-universe.net/how-to-run-docker-and-docker-compose-on-raspbian/) on Raspbian. -Via `docker-compose`: -```yml -version: "3" -services: - app: - image: 'jc21/nginx-proxy-manager:latest' - restart: unless-stopped - ports: - # Public HTTP Port: - - '80:80' - # Public HTTPS Port: - - '443:443' - # Admin Web Port: - - '81:81' - environment: - # These are the settings to access your db - DB_MYSQL_HOST: "db" - DB_MYSQL_PORT: 3306 - DB_MYSQL_USER: "changeuser" - DB_MYSQL_PASSWORD: "changepass" - DB_MYSQL_NAME: "npm" - # If you would rather use Sqlite uncomment this - # and remove all DB_MYSQL_* lines above - # DB_SQLITE_FILE: "/data/database.sqlite" - # Uncomment this if IPv6 is not enabled on your host - # DISABLE_IPV6: 'true' - volumes: - - ./data/nginx-proxy-manager:/data - - ./letsencrypt:/etc/letsencrypt - depends_on: - - db - db: - image: yobasystems/alpine-mariadb:latest - restart: unless-stopped - environment: - MYSQL_ROOT_PASSWORD: "changeme" - MYSQL_DATABASE: "npm" - MYSQL_USER: "changeuser" - MYSQL_PASSWORD: "changepass" - volumes: - - ./data/mariadb:/var/lib/mysql -``` - -_Please note, that `DB_MYSQL_*` environment variables will take precedent over `DB_SQLITE_*` var> - -Then: - -```bash -docker-compose up -d -``` - -## Initial Run +### Initial Run After the app is running for the first time, the following will happen: 1. The database will initialize with table structures 2. GPG keys will be generated and saved in the configuration file -3. A default admin user will be created This process can take a couple of minutes depending on your machine. - - -## Default Administrator User - -``` -Email: admin@example.com -Password: changeme -``` - -Immediately after logging in with this default user you will be asked to modify your details and change your password. - -## Configuration File - -::: warning - -This section is meant for advanced users - -::: - -If you would like more control over the database settings you can define a custom config JSON file. - - -Here's an example for `sqlite` configuration as it is generated from the environment variables: - -```json -{ - "database": { - "engine": "knex-native", - "knex": { - "client": "sqlite3", - "connection": { - "filename": "/data/database.sqlite" - }, - "useNullAsDefault": true - } - } -} -``` - -You can modify the `knex` object with your custom configuration, but note that not all knex clients might be installed in the image. - -Once you've created your configuration file you can mount it to `/app/config/production.json` inside you container using: - -``` -[...] -services: - app: - image: 'jc21/nginx-proxy-manager:latest' - [...] - volumes: - - ./config.json:/app/config/production.json - [...] -[...] -``` - -**Note:** After the first run of the application, the config file will be altered to include generated encryption keys unique to your installation. -These keys affect the login and session management of the application. If these keys change for any reason, all users will be logged out. diff --git a/frontend/.babelrc b/frontend/.babelrc deleted file mode 100644 index 54071ecd..00000000 --- a/frontend/.babelrc +++ /dev/null @@ -1,17 +0,0 @@ -{ - "presets": [ - [ - "env", - { - "targets": { - "browsers": [ - "Chrome >= 65" - ] - }, - "debug": false, - "modules": false, - "useBuiltIns": "usage" - } - ] - ] -} \ No newline at end of file diff --git a/frontend/.env.development b/frontend/.env.development new file mode 100644 index 00000000..fe9267b2 --- /dev/null +++ b/frontend/.env.development @@ -0,0 +1,2 @@ +PORT=9000 +IMAGE_INLINE_SIZE_LIMIT=20000 diff --git a/frontend/.eslintrc b/frontend/.eslintrc new file mode 100644 index 00000000..ca27c839 --- /dev/null +++ b/frontend/.eslintrc @@ -0,0 +1,119 @@ +{ + "parser": "@typescript-eslint/parser", + "plugins": [ + "@typescript-eslint", + "prettier", + "import", + "react-hooks" + ], + "extends": [ + "react-app", + "eslint-config-prettier", + "plugin:@typescript-eslint/recommended", + "plugin:prettier/recommended" + ], + "env": { + "jest": true, + "browser": true, + "commonjs": true + }, + "rules": { + "prettier/prettier": [ + "error" + ], + "@typescript-eslint/ban-ts-comment": [ + "error", + { + "ts-ignore": "allow-with-description" + } + ], + "@typescript-eslint/consistent-type-definitions": [ + "error", + "interface" + ], + "@typescript-eslint/explicit-function-return-type": [ + "off" + ], + "@typescript-eslint/explicit-module-boundary-types": [ + "off" + ], + "@typescript-eslint/explicit-member-accessibility": [ + "off" + ], + "@typescript-eslint/no-empty-function": [ + "off" + ], + "@typescript-eslint/no-explicit-any": [ + "off" + ], + "@typescript-eslint/no-non-null-assertion": [ + "off" + ], + "@typescript-eslint/naming-convention": [ + "error", + { + "selector": "default", + "format": [ + "camelCase", + "PascalCase", + "UPPER_CASE" + ], + "leadingUnderscore": "allow", + "trailingUnderscore": "allow" + } + ], + "react-hooks/rules-of-hooks": [ + "error" + ], + "react-hooks/exhaustive-deps": [ + "warn", + { + "additionalHooks": "useAction|useReduxAction" + } + ], + "no-restricted-globals": [ + "off" + ], + "import/extensions": 0, // We let webpack handle resolving file extensions + "import/order": [ + "error", + { + "alphabetize": { + "order": "asc", + "caseInsensitive": true + }, + "newlines-between": "always", + "pathGroups": [ + { + "pattern": "@(react)", + "group": "external", + "position": "before" + }, + { + "pattern": "@/@(fixtures|jest)/**", + "group": "internal", + "position": "before" + }, + { + "pattern": "@/**", + "group": "internal" + } + ], + "pathGroupsExcludedImportTypes": [ + "builtin", + "internal" + ], + "groups": [ + "builtin", + "external", + "internal", + [ + "parent", + "sibling", + "index" + ] + ] + } + ] + } +} \ No newline at end of file diff --git a/frontend/.gitignore b/frontend/.gitignore index c8f4b4f9..aa97ba4b 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -1,4 +1,4 @@ -dist -node_modules -webpack_stats.html -yarn-error.log +.eslintcache +coverage +junit.xml +eslint.xml diff --git a/backend/.prettierrc b/frontend/.prettierrc similarity index 70% rename from backend/.prettierrc rename to frontend/.prettierrc index fefbcfa6..00b01a49 100644 --- a/backend/.prettierrc +++ b/frontend/.prettierrc @@ -1,9 +1,9 @@ { - "printWidth": 320, - "tabWidth": 4, + "printWidth": 80, + "tabWidth": 2, "useTabs": true, "semi": true, - "singleQuote": true, + "singleQuote": false, "bracketSpacing": true, "jsxBracketSameLine": true, "trailingComma": "all", diff --git a/frontend/README.md b/frontend/README.md new file mode 100644 index 00000000..2fa78e71 --- /dev/null +++ b/frontend/README.md @@ -0,0 +1,44 @@ +This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). + +## Available Scripts + +In the project directory, you can run: + +### `yarn start` + +Runs the app in the development mode.
    +Open [http://localhost:3000](http://localhost:3000) to view it in the browser. + +The page will reload if you make edits.
    +You will also see any lint errors in the console. + +### `yarn test` + +Launches the test runner in the interactive watch mode.
    +See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. + +### `yarn build` + +Builds the app for production to the `build` folder.
    +It correctly bundles React in production mode and optimizes the build for the best performance. + +The build is minified and the filenames include the hashes.
    +Your app is ready to be deployed! + +See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. + +### `yarn eject` + +**Note: this is a one-way operation. Once you `eject`, you can’t go back!** + +If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. + +Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. + +You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. + +## Learn More + +You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). + +To learn React, check out the [React documentation](https://reactjs.org/). diff --git a/frontend/fonts/feather b/frontend/fonts/feather deleted file mode 120000 index 440203ba..00000000 --- a/frontend/fonts/feather +++ /dev/null @@ -1 +0,0 @@ -../node_modules/tabler-ui/dist/assets/fonts/feather \ No newline at end of file diff --git a/frontend/fonts/source-sans-pro/source-sans-pro-v14-latin-ext_latin-700.woff b/frontend/fonts/source-sans-pro/source-sans-pro-v14-latin-ext_latin-700.woff deleted file mode 100644 index 96d8768ea9a10f004e2215a1a674287c8c7abfe5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 31740 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 diff --git a/frontend/fonts/source-sans-pro/source-sans-pro-v14-latin-ext_latin-700italic.woff b/frontend/fonts/source-sans-pro/source-sans-pro-v14-latin-ext_latin-700italic.woff deleted file mode 100644 index 0829caef1eb96f768bedf713c983e2a3904ad835..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28540 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 diff --git a/frontend/fonts/source-sans-pro/source-sans-pro-v14-latin-ext_latin-700italic.woff2 b/frontend/fonts/source-sans-pro/source-sans-pro-v14-latin-ext_latin-700italic.woff2 deleted file mode 100644 index 7c901cd8450cecf93767560eecf4a3dc6b0cefb3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22240 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 diff --git a/frontend/fonts/source-sans-pro/source-sans-pro-v14-latin-ext_latin-italic.woff b/frontend/fonts/source-sans-pro/source-sans-pro-v14-latin-ext_latin-italic.woff deleted file mode 100644 index 99652481a07f237145d733e06e6b2c0b926dfafc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28744 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 DnjJ_~ diff --git a/frontend/fonts/source-sans-pro/source-sans-pro-v14-latin-ext_latin-italic.woff2 b/frontend/fonts/source-sans-pro/source-sans-pro-v14-latin-ext_latin-italic.woff2 deleted file mode 100644 index 343e5ba8d3f924aef2e589f25657cb97ff423ef5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22436 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 diff --git a/frontend/fonts/source-sans-pro/source-sans-pro-v14-latin-ext_latin-regular.woff2 b/frontend/fonts/source-sans-pro/source-sans-pro-v14-latin-ext_latin-regular.woff2 deleted file mode 100644 index d552543be16ee93a7c66358c9cc80540a3b11860..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25656 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 jcp0p9&3Un?g { + process.env.TZ = "Australia/Queensland"; + process.env.IMAGE_INLINE_SIZE_LIMIT = "20000"; +}; diff --git a/frontend/html/index.ejs b/frontend/html/index.ejs deleted file mode 100644 index ae08b012..00000000 --- a/frontend/html/index.ejs +++ /dev/null @@ -1,9 +0,0 @@ -<% var title = 'Nginx Proxy Manager' %> -<%- include partials/header.ejs %> - -

    - -
    - - -<%- include partials/footer.ejs %> diff --git a/frontend/html/login.ejs b/frontend/html/login.ejs deleted file mode 100644 index bc4b9a27..00000000 --- a/frontend/html/login.ejs +++ /dev/null @@ -1,9 +0,0 @@ -<% var title = 'Login – Nginx Proxy Manager' %> -<%- include partials/header.ejs %> - -
    - -
    - - -<%- include partials/footer.ejs %> diff --git a/frontend/html/partials/footer.ejs b/frontend/html/partials/footer.ejs deleted file mode 100644 index 7fb2bd61..00000000 --- a/frontend/html/partials/footer.ejs +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/frontend/html/partials/header.ejs b/frontend/html/partials/header.ejs deleted file mode 100644 index b8d88331..00000000 --- a/frontend/html/partials/header.ejs +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - <%- title %> - - - - - - - - - - - - - - diff --git a/frontend/images b/frontend/images deleted file mode 120000 index 37c31854..00000000 --- a/frontend/images +++ /dev/null @@ -1 +0,0 @@ -./node_modules/tabler-ui/dist/assets/images \ No newline at end of file diff --git a/frontend/jest.eslint.js b/frontend/jest.eslint.js new file mode 100644 index 00000000..04ee4f2c --- /dev/null +++ b/frontend/jest.eslint.js @@ -0,0 +1,6 @@ +module.exports = { + displayName: "ESLint", + runner: "jest-runner-eslint", + testMatch: ["/src/**/*.(js|jsx|ts|tsx)"], + watchPlugins: ["jest-runner-eslint/watch-fix"], +}; diff --git a/frontend/js/app/api.js b/frontend/js/app/api.js deleted file mode 100644 index 9d11d268..00000000 --- a/frontend/js/app/api.js +++ /dev/null @@ -1,694 +0,0 @@ -const $ = require('jquery'); -const _ = require('underscore'); -const Tokens = require('./tokens'); - -/** - * @param {String} message - * @param {*} debug - * @param {Number} code - * @constructor - */ -const ApiError = function (message, debug, code) { - let temp = Error.call(this, message); - temp.name = this.name = 'ApiError'; - this.stack = temp.stack; - this.message = temp.message; - this.debug = debug; - this.code = code; -}; - -ApiError.prototype = Object.create(Error.prototype, { - constructor: { - value: ApiError, - writable: true, - configurable: true - } -}); - -/** - * - * @param {String} verb - * @param {String} path - * @param {Object} [data] - * @param {Object} [options] - * @returns {Promise} - */ -function fetch(verb, path, data, options) { - options = options || {}; - - return new Promise(function (resolve, reject) { - let api_url = '/api/'; - let url = api_url + path; - let token = Tokens.getTopToken(); - - if ((typeof options.contentType === 'undefined' || options.contentType.match(/json/im)) && typeof data === 'object') { - data = JSON.stringify(data); - } - - $.ajax({ - url: url, - data: typeof data === 'object' ? JSON.stringify(data) : data, - type: verb, - dataType: 'json', - contentType: options.contentType || 'application/json; charset=UTF-8', - processData: options.processData || true, - crossDomain: true, - timeout: options.timeout ? options.timeout : 180000, - xhrFields: { - withCredentials: true - }, - - beforeSend: function (xhr) { - xhr.setRequestHeader('Authorization', 'Bearer ' + (token ? token.t : null)); - }, - - success: function (data, textStatus, response) { - let total = response.getResponseHeader('X-Dataset-Total'); - if (total !== null) { - resolve({ - data: data, - pagination: { - total: parseInt(total, 10), - offset: parseInt(response.getResponseHeader('X-Dataset-Offset'), 10), - limit: parseInt(response.getResponseHeader('X-Dataset-Limit'), 10) - } - }); - } else { - resolve(response); - } - }, - - error: function (xhr, status, error_thrown) { - let code = 400; - - if (typeof xhr.responseJSON !== 'undefined' && typeof xhr.responseJSON.error !== 'undefined' && typeof xhr.responseJSON.error.message !== 'undefined') { - error_thrown = xhr.responseJSON.error.message; - code = xhr.responseJSON.error.code || 500; - } - - reject(new ApiError(error_thrown, xhr.responseText, code)); - } - }); - }); -} - -/** - * - * @param {Array} expand - * @returns {String} - */ -function makeExpansionString(expand) { - let items = []; - _.forEach(expand, function (exp) { - items.push(encodeURIComponent(exp)); - }); - - return items.join(','); -} - -/** - * @param {String} path - * @param {Array} [expand] - * @param {String} [query] - * @returns {Promise} - */ -function getAllObjects(path, expand, query) { - let params = []; - - if (typeof expand === 'object' && expand !== null && expand.length) { - params.push('expand=' + makeExpansionString(expand)); - } - - if (typeof query === 'string') { - params.push('query=' + query); - } - - return fetch('get', path + (params.length ? '?' + params.join('&') : '')); -} - -function FileUpload(path, fd) { - return new Promise((resolve, reject) => { - let xhr = new XMLHttpRequest(); - let token = Tokens.getTopToken(); - - xhr.open('POST', '/api/' + path); - xhr.overrideMimeType('text/plain'); - xhr.setRequestHeader('Authorization', 'Bearer ' + (token ? token.t : null)); - xhr.send(fd); - - xhr.onreadystatechange = function () { - if (this.readyState === XMLHttpRequest.DONE) { - if (xhr.status !== 200 && xhr.status !== 201) { - try { - reject(new Error('Upload failed: ' + JSON.parse(xhr.responseText).error.message)); - } catch (err) { - reject(new Error('Upload failed: ' + xhr.status)); - } - } else { - resolve(xhr.responseText); - } - } - }; - }); -} - -module.exports = { - status: function () { - return fetch('get', ''); - }, - - Tokens: { - - /** - * @param {String} identity - * @param {String} secret - * @param {Boolean} [wipe] Will wipe the stack before adding to it again if login was successful - * @returns {Promise} - */ - login: function (identity, secret, wipe) { - return fetch('post', 'tokens', {identity: identity, secret: secret}) - .then(response => { - if (response.token) { - if (wipe) { - Tokens.clearTokens(); - } - - // Set storage token - Tokens.addToken(response.token); - return response.token; - } else { - Tokens.clearTokens(); - throw(new Error('No token returned')); - } - }); - }, - - /** - * @returns {Promise} - */ - refresh: function () { - return fetch('get', 'tokens') - .then(response => { - if (response.token) { - Tokens.setCurrentToken(response.token); - return response.token; - } else { - Tokens.clearTokens(); - throw(new Error('No token returned')); - } - }); - } - }, - - Users: { - - /** - * @param {Number|String} user_id - * @param {Array} [expand] - * @returns {Promise} - */ - getById: function (user_id, expand) { - return fetch('get', 'users/' + user_id + (typeof expand === 'object' && expand.length ? '?expand=' + makeExpansionString(expand) : '')); - }, - - /** - * @param {Array} [expand] - * @param {String} [query] - * @returns {Promise} - */ - getAll: function (expand, query) { - return getAllObjects('users', expand, query); - }, - - /** - * @param {Object} data - * @returns {Promise} - */ - create: function (data) { - return fetch('post', 'users', data); - }, - - /** - * @param {Object} data - * @param {Number} data.id - * @returns {Promise} - */ - update: function (data) { - let id = data.id; - delete data.id; - return fetch('put', 'users/' + id, data); - }, - - /** - * @param {Number} id - * @returns {Promise} - */ - delete: function (id) { - return fetch('delete', 'users/' + id); - }, - - /** - * - * @param {Number} id - * @param {Object} auth - * @returns {Promise} - */ - setPassword: function (id, auth) { - return fetch('put', 'users/' + id + '/auth', auth); - }, - - /** - * @param {Number} id - * @returns {Promise} - */ - loginAs: function (id) { - return fetch('post', 'users/' + id + '/login'); - }, - - /** - * - * @param {Number} id - * @param {Object} perms - * @returns {Promise} - */ - setPermissions: function (id, perms) { - return fetch('put', 'users/' + id + '/permissions', perms); - } - }, - - Nginx: { - - ProxyHosts: { - /** - * @param {Array} [expand] - * @param {String} [query] - * @returns {Promise} - */ - getAll: function (expand, query) { - return getAllObjects('nginx/proxy-hosts', expand, query); - }, - - /** - * @param {Object} data - */ - create: function (data) { - return fetch('post', 'nginx/proxy-hosts', data); - }, - - /** - * @param {Object} data - * @param {Number} data.id - * @returns {Promise} - */ - update: function (data) { - let id = data.id; - delete data.id; - return fetch('put', 'nginx/proxy-hosts/' + id, data); - }, - - /** - * @param {Number} id - * @returns {Promise} - */ - delete: function (id) { - return fetch('delete', 'nginx/proxy-hosts/' + id); - }, - - /** - * @param {Number} id - * @returns {Promise} - */ - get: function (id) { - return fetch('get', 'nginx/proxy-hosts/' + id); - }, - - /** - * @param {Number} id - * @returns {Promise} - */ - enable: function (id) { - return fetch('post', 'nginx/proxy-hosts/' + id + '/enable'); - }, - - /** - * @param {Number} id - * @returns {Promise} - */ - disable: function (id) { - return fetch('post', 'nginx/proxy-hosts/' + id + '/disable'); - } - }, - - RedirectionHosts: { - /** - * @param {Array} [expand] - * @param {String} [query] - * @returns {Promise} - */ - getAll: function (expand, query) { - return getAllObjects('nginx/redirection-hosts', expand, query); - }, - - /** - * @param {Object} data - */ - create: function (data) { - return fetch('post', 'nginx/redirection-hosts', data); - }, - - /** - * @param {Object} data - * @param {Number} data.id - * @returns {Promise} - */ - update: function (data) { - let id = data.id; - delete data.id; - return fetch('put', 'nginx/redirection-hosts/' + id, data); - }, - - /** - * @param {Number} id - * @returns {Promise} - */ - delete: function (id) { - return fetch('delete', 'nginx/redirection-hosts/' + id); - }, - - /** - * @param {Number} id - * @returns {Promise} - */ - get: function (id) { - return fetch('get', 'nginx/redirection-hosts/' + id); - }, - - /** - * @param {Number} id - * @param {FormData} form_data - * @params {Promise} - */ - setCerts: function (id, form_data) { - return FileUpload('nginx/redirection-hosts/' + id + '/certificates', form_data); - }, - - /** - * @param {Number} id - * @returns {Promise} - */ - enable: function (id) { - return fetch('post', 'nginx/redirection-hosts/' + id + '/enable'); - }, - - /** - * @param {Number} id - * @returns {Promise} - */ - disable: function (id) { - return fetch('post', 'nginx/redirection-hosts/' + id + '/disable'); - } - }, - - Streams: { - /** - * @param {Array} [expand] - * @param {String} [query] - * @returns {Promise} - */ - getAll: function (expand, query) { - return getAllObjects('nginx/streams', expand, query); - }, - - /** - * @param {Object} data - */ - create: function (data) { - return fetch('post', 'nginx/streams', data); - }, - - /** - * @param {Object} data - * @param {Number} data.id - * @returns {Promise} - */ - update: function (data) { - let id = data.id; - delete data.id; - return fetch('put', 'nginx/streams/' + id, data); - }, - - /** - * @param {Number} id - * @returns {Promise} - */ - delete: function (id) { - return fetch('delete', 'nginx/streams/' + id); - }, - - /** - * @param {Number} id - * @returns {Promise} - */ - get: function (id) { - return fetch('get', 'nginx/streams/' + id); - }, - - /** - * @param {Number} id - * @returns {Promise} - */ - enable: function (id) { - return fetch('post', 'nginx/streams/' + id + '/enable'); - }, - - /** - * @param {Number} id - * @returns {Promise} - */ - disable: function (id) { - return fetch('post', 'nginx/streams/' + id + '/disable'); - } - }, - - DeadHosts: { - /** - * @param {Array} [expand] - * @param {String} [query] - * @returns {Promise} - */ - getAll: function (expand, query) { - return getAllObjects('nginx/dead-hosts', expand, query); - }, - - /** - * @param {Object} data - */ - create: function (data) { - return fetch('post', 'nginx/dead-hosts', data); - }, - - /** - * @param {Object} data - * @param {Number} data.id - * @returns {Promise} - */ - update: function (data) { - let id = data.id; - delete data.id; - return fetch('put', 'nginx/dead-hosts/' + id, data); - }, - - /** - * @param {Number} id - * @returns {Promise} - */ - delete: function (id) { - return fetch('delete', 'nginx/dead-hosts/' + id); - }, - - /** - * @param {Number} id - * @returns {Promise} - */ - get: function (id) { - return fetch('get', 'nginx/dead-hosts/' + id); - }, - - /** - * @param {Number} id - * @param {FormData} form_data - * @params {Promise} - */ - setCerts: function (id, form_data) { - return FileUpload('nginx/dead-hosts/' + id + '/certificates', form_data); - }, - - /** - * @param {Number} id - * @returns {Promise} - */ - enable: function (id) { - return fetch('post', 'nginx/dead-hosts/' + id + '/enable'); - }, - - /** - * @param {Number} id - * @returns {Promise} - */ - disable: function (id) { - return fetch('post', 'nginx/dead-hosts/' + id + '/disable'); - } - }, - - AccessLists: { - /** - * @param {Array} [expand] - * @param {String} [query] - * @returns {Promise} - */ - getAll: function (expand, query) { - return getAllObjects('nginx/access-lists', expand, query); - }, - - /** - * @param {Object} data - */ - create: function (data) { - return fetch('post', 'nginx/access-lists', data); - }, - - /** - * @param {Object} data - * @param {Number} data.id - * @returns {Promise} - */ - update: function (data) { - let id = data.id; - delete data.id; - return fetch('put', 'nginx/access-lists/' + id, data); - }, - - /** - * @param {Number} id - * @returns {Promise} - */ - delete: function (id) { - return fetch('delete', 'nginx/access-lists/' + id); - } - }, - - Certificates: { - /** - * @param {Array} [expand] - * @param {String} [query] - * @returns {Promise} - */ - getAll: function (expand, query) { - return getAllObjects('nginx/certificates', expand, query); - }, - - /** - * @param {Object} data - */ - create: function (data) { - - const timeout = 180000 + (data && data.meta && data.meta.propagation_seconds ? Number(data.meta.propagation_seconds) * 1000 : 0); - return fetch('post', 'nginx/certificates', data, {timeout}); - }, - - /** - * @param {Object} data - * @param {Number} data.id - * @returns {Promise} - */ - update: function (data) { - let id = data.id; - delete data.id; - return fetch('put', 'nginx/certificates/' + id, data); - }, - - /** - * @param {Number} id - * @returns {Promise} - */ - delete: function (id) { - return fetch('delete', 'nginx/certificates/' + id); - }, - - /** - * @param {Number} id - * @param {FormData} form_data - * @params {Promise} - */ - upload: function (id, form_data) { - return FileUpload('nginx/certificates/' + id + '/upload', form_data); - }, - - /** - * @param {FormData} form_data - * @params {Promise} - */ - validate: function (form_data) { - return FileUpload('nginx/certificates/validate', form_data); - }, - - /** - * @param {Number} id - * @returns {Promise} - */ - renew: function (id, timeout = 180000) { - return fetch('post', 'nginx/certificates/' + id + '/renew', undefined, {timeout}); - } - } - }, - - AuditLog: { - /** - * @param {Array} [expand] - * @param {String} [query] - * @returns {Promise} - */ - getAll: function (expand, query) { - return getAllObjects('audit-log', expand, query); - } - }, - - Reports: { - - /** - * @returns {Promise} - */ - getHostStats: function () { - return fetch('get', 'reports/hosts'); - } - }, - - Settings: { - - /** - * @param {String} setting_id - * @returns {Promise} - */ - getById: function (setting_id) { - return fetch('get', 'settings/' + setting_id); - }, - - /** - * @returns {Promise} - */ - getAll: function () { - return getAllObjects('settings'); - }, - - /** - * @param {Object} data - * @param {Number} data.id - * @returns {Promise} - */ - update: function (data) { - let id = data.id; - delete data.id; - return fetch('put', 'settings/' + id, data); - } - } -}; diff --git a/frontend/js/app/audit-log/list/item.ejs b/frontend/js/app/audit-log/list/item.ejs deleted file mode 100644 index 84743c8d..00000000 --- a/frontend/js/app/audit-log/list/item.ejs +++ /dev/null @@ -1,80 +0,0 @@ - -
    - -
    - - -
    - <% if (user.is_deleted) { - %> - <%- user.name %> - <% - } else { - %> - <%- user.name %> - <% - } - %> -
    - - -
    - <% - var items = []; - switch (object_type) { - case 'proxy-host': - %> <% - items = meta.domain_names; - break; - case 'redirection-host': - %> <% - items = meta.domain_names; - break; - case 'stream': - %> <% - items.push(meta.incoming_port); - break; - case 'dead-host': - %> <% - items = meta.domain_names; - break; - case 'access-list': - %> <% - items.push(meta.name); - break; - case 'user': - %> <% - items.push(meta.name); - break; - case 'certificate': - %> <% - if (meta.provider === 'letsencrypt') { - items = meta.domain_names; - } else { - items.push(meta.nice_name); - } - break; - } - %> <%- i18n('audit-log', action, {name: i18n('audit-log', object_type)}) %> - — - <% - if (items && items.length) { - items.map(function(item) { - %> - <%- item %> - <% - }); - } else { - %> - #<%- object_id %> - <% - } - %> -
    -
    - <%- formatDbDate(created_on, 'Do MMMM YYYY, h:mm a') %> -
    - - - <%- i18n('audit-log', 'view-meta') %> - diff --git a/frontend/js/app/audit-log/list/item.js b/frontend/js/app/audit-log/list/item.js deleted file mode 100644 index 862ffc22..00000000 --- a/frontend/js/app/audit-log/list/item.js +++ /dev/null @@ -1,32 +0,0 @@ -const Mn = require('backbone.marionette'); -const Controller = require('../../controller'); -const template = require('./item.ejs'); - -module.exports = Mn.View.extend({ - template: template, - tagName: 'tr', - - ui: { - meta: 'a.meta' - }, - - events: { - 'click @ui.meta': function (e) { - e.preventDefault(); - Controller.showAuditMeta(this.model); - } - }, - - templateContext: { - more: function() { - switch (this.object_type) { - case 'redirection-host': - case 'stream': - case 'proxy-host': - return this.meta.domain_names.join(', '); - } - - return '#' + (this.object_id || '?'); - } - } -}); diff --git a/frontend/js/app/audit-log/list/main.ejs b/frontend/js/app/audit-log/list/main.ejs deleted file mode 100644 index ec3cf2a2..00000000 --- a/frontend/js/app/audit-log/list/main.ejs +++ /dev/null @@ -1,9 +0,0 @@ - -   - User - Event -   - - - - diff --git a/frontend/js/app/audit-log/list/main.js b/frontend/js/app/audit-log/list/main.js deleted file mode 100644 index 9d3e26fb..00000000 --- a/frontend/js/app/audit-log/list/main.js +++ /dev/null @@ -1,27 +0,0 @@ -const Mn = require('backbone.marionette'); -const ItemView = require('./item'); -const template = require('./main.ejs'); - -const TableBody = Mn.CollectionView.extend({ - tagName: 'tbody', - childView: ItemView -}); - -module.exports = Mn.View.extend({ - tagName: 'table', - className: 'table table-hover table-outline table-vcenter card-table', - template: template, - - regions: { - body: { - el: 'tbody', - replaceElement: true - } - }, - - onRender: function () { - this.showChildView('body', new TableBody({ - collection: this.collection - })); - } -}); diff --git a/frontend/js/app/audit-log/main.ejs b/frontend/js/app/audit-log/main.ejs deleted file mode 100644 index acaa8b49..00000000 --- a/frontend/js/app/audit-log/main.ejs +++ /dev/null @@ -1,15 +0,0 @@ -
    -
    -
    -

    <%- i18n('audit-log', 'title') %>

    -
    -
    -
    -
    -
    - -
    -
    - -
    -
    diff --git a/frontend/js/app/audit-log/main.js b/frontend/js/app/audit-log/main.js deleted file mode 100644 index ec9b5368..00000000 --- a/frontend/js/app/audit-log/main.js +++ /dev/null @@ -1,53 +0,0 @@ -const Mn = require('backbone.marionette'); -const App = require('../main'); -const AuditLogModel = require('../../models/audit-log'); -const ListView = require('./list/main'); -const template = require('./main.ejs'); -const ErrorView = require('../error/main'); -const EmptyView = require('../empty/main'); - -module.exports = Mn.View.extend({ - id: 'audit-log', - template: template, - - ui: { - list_region: '.list-region', - dimmer: '.dimmer' - }, - - regions: { - list_region: '@ui.list_region' - }, - - onRender: function () { - let view = this; - - App.Api.AuditLog.getAll(['user']) - .then(response => { - if (!view.isDestroyed() && response && response.length) { - view.showChildView('list_region', new ListView({ - collection: new AuditLogModel.Collection(response) - })); - } else { - view.showChildView('list_region', new EmptyView({ - title: App.i18n('audit-log', 'empty'), - subtitle: App.i18n('audit-log', 'empty-subtitle') - })); - } - }) - .catch(err => { - view.showChildView('list_region', new ErrorView({ - code: err.code, - message: err.message, - retry: function () { - App.Controller.showAuditLog(); - } - })); - - console.error(err); - }) - .then(() => { - view.ui.dimmer.removeClass('active'); - }); - } -}); diff --git a/frontend/js/app/audit-log/meta.ejs b/frontend/js/app/audit-log/meta.ejs deleted file mode 100644 index 98a2d973..00000000 --- a/frontend/js/app/audit-log/meta.ejs +++ /dev/null @@ -1,27 +0,0 @@ - diff --git a/frontend/js/app/audit-log/meta.js b/frontend/js/app/audit-log/meta.js deleted file mode 100644 index 815cdfac..00000000 --- a/frontend/js/app/audit-log/meta.js +++ /dev/null @@ -1,7 +0,0 @@ -const Mn = require('backbone.marionette'); -const template = require('./meta.ejs'); - -module.exports = Mn.View.extend({ - template: template, - className: 'modal-dialog wide' -}); diff --git a/frontend/js/app/cache.js b/frontend/js/app/cache.js deleted file mode 100644 index 6d1fbc4f..00000000 --- a/frontend/js/app/cache.js +++ /dev/null @@ -1,10 +0,0 @@ -const UserModel = require('../models/user'); - -let cache = { - User: new UserModel.Model(), - locale: 'en', - version: null -}; - -module.exports = cache; - diff --git a/frontend/js/app/controller.js b/frontend/js/app/controller.js deleted file mode 100644 index 902659be..00000000 --- a/frontend/js/app/controller.js +++ /dev/null @@ -1,434 +0,0 @@ -const Backbone = require('backbone'); -const Cache = require('./cache'); -const Tokens = require('./tokens'); - -module.exports = { - - /** - * @param {String} route - * @param {Object} [options] - * @returns {Boolean} - */ - navigate: function (route, options) { - options = options || {}; - Backbone.history.navigate(route.toString(), options); - return true; - }, - - /** - * Login - */ - showLogin: function () { - window.location = '/login'; - }, - - /** - * Users - */ - showUsers: function () { - let controller = this; - if (Cache.User.isAdmin()) { - require(['./main', './users/main'], (App, View) => { - controller.navigate('/users'); - App.UI.showAppContent(new View()); - }); - } else { - this.showDashboard(); - } - }, - - /** - * User Form - * - * @param [model] - */ - showUserForm: function (model) { - if (Cache.User.isAdmin()) { - require(['./main', './user/form'], function (App, View) { - App.UI.showModalDialog(new View({model: model})); - }); - } - }, - - /** - * User Permissions Form - * - * @param model - */ - showUserPermissions: function (model) { - if (Cache.User.isAdmin()) { - require(['./main', './user/permissions'], function (App, View) { - App.UI.showModalDialog(new View({model: model})); - }); - } - }, - - /** - * User Password Form - * - * @param model - */ - showUserPasswordForm: function (model) { - if (Cache.User.isAdmin() || model.get('id') === Cache.User.get('id')) { - require(['./main', './user/password'], function (App, View) { - App.UI.showModalDialog(new View({model: model})); - }); - } - }, - - /** - * User Delete Confirm - * - * @param model - */ - showUserDeleteConfirm: function (model) { - if (Cache.User.isAdmin() && model.get('id') !== Cache.User.get('id')) { - require(['./main', './user/delete'], function (App, View) { - App.UI.showModalDialog(new View({model: model})); - }); - } - }, - - /** - * Dashboard - */ - showDashboard: function () { - let controller = this; - - require(['./main', './dashboard/main'], (App, View) => { - controller.navigate('/'); - App.UI.showAppContent(new View()); - }); - }, - - /** - * Nginx Proxy Hosts - */ - showNginxProxy: function () { - if (Cache.User.isAdmin() || Cache.User.canView('proxy_hosts')) { - let controller = this; - - require(['./main', './nginx/proxy/main'], (App, View) => { - controller.navigate('/nginx/proxy'); - App.UI.showAppContent(new View()); - }); - } - }, - - /** - * Nginx Proxy Host Form - * - * @param [model] - */ - showNginxProxyForm: function (model) { - if (Cache.User.isAdmin() || Cache.User.canManage('proxy_hosts')) { - require(['./main', './nginx/proxy/form'], function (App, View) { - App.UI.showModalDialog(new View({model: model})); - }); - } - }, - - /** - * Proxy Host Delete Confirm - * - * @param model - */ - showNginxProxyDeleteConfirm: function (model) { - if (Cache.User.isAdmin() || Cache.User.canManage('proxy_hosts')) { - require(['./main', './nginx/proxy/delete'], function (App, View) { - App.UI.showModalDialog(new View({model: model})); - }); - } - }, - - /** - * Nginx Redirection Hosts - */ - showNginxRedirection: function () { - if (Cache.User.isAdmin() || Cache.User.canView('redirection_hosts')) { - let controller = this; - - require(['./main', './nginx/redirection/main'], (App, View) => { - controller.navigate('/nginx/redirection'); - App.UI.showAppContent(new View()); - }); - } - }, - - /** - * Nginx Redirection Host Form - * - * @param [model] - */ - showNginxRedirectionForm: function (model) { - if (Cache.User.isAdmin() || Cache.User.canManage('redirection_hosts')) { - require(['./main', './nginx/redirection/form'], function (App, View) { - App.UI.showModalDialog(new View({model: model})); - }); - } - }, - - /** - * Proxy Redirection Delete Confirm - * - * @param model - */ - showNginxRedirectionDeleteConfirm: function (model) { - if (Cache.User.isAdmin() || Cache.User.canManage('redirection_hosts')) { - require(['./main', './nginx/redirection/delete'], function (App, View) { - App.UI.showModalDialog(new View({model: model})); - }); - } - }, - - /** - * Nginx Stream Hosts - */ - showNginxStream: function () { - if (Cache.User.isAdmin() || Cache.User.canView('streams')) { - let controller = this; - - require(['./main', './nginx/stream/main'], (App, View) => { - controller.navigate('/nginx/stream'); - App.UI.showAppContent(new View()); - }); - } - }, - - /** - * Stream Form - * - * @param [model] - */ - showNginxStreamForm: function (model) { - if (Cache.User.isAdmin() || Cache.User.canManage('streams')) { - require(['./main', './nginx/stream/form'], function (App, View) { - App.UI.showModalDialog(new View({model: model})); - }); - } - }, - - /** - * Stream Delete Confirm - * - * @param model - */ - showNginxStreamDeleteConfirm: function (model) { - if (Cache.User.isAdmin() || Cache.User.canManage('streams')) { - require(['./main', './nginx/stream/delete'], function (App, View) { - App.UI.showModalDialog(new View({model: model})); - }); - } - }, - - /** - * Nginx Dead Hosts - */ - showNginxDead: function () { - if (Cache.User.isAdmin() || Cache.User.canView('dead_hosts')) { - let controller = this; - - require(['./main', './nginx/dead/main'], (App, View) => { - controller.navigate('/nginx/404'); - App.UI.showAppContent(new View()); - }); - } - }, - - /** - * Dead Host Form - * - * @param [model] - */ - showNginxDeadForm: function (model) { - if (Cache.User.isAdmin() || Cache.User.canManage('dead_hosts')) { - require(['./main', './nginx/dead/form'], function (App, View) { - App.UI.showModalDialog(new View({model: model})); - }); - } - }, - - /** - * Dead Host Delete Confirm - * - * @param model - */ - showNginxDeadDeleteConfirm: function (model) { - if (Cache.User.isAdmin() || Cache.User.canManage('dead_hosts')) { - require(['./main', './nginx/dead/delete'], function (App, View) { - App.UI.showModalDialog(new View({model: model})); - }); - } - }, - - /** - * Help Dialog - * - * @param {String} title - * @param {String} content - */ - showHelp: function (title, content) { - require(['./main', './help/main'], function (App, View) { - App.UI.showModalDialog(new View({title: title, content: content})); - }); - }, - - /** - * Nginx Access - */ - showNginxAccess: function () { - if (Cache.User.isAdmin() || Cache.User.canView('access_lists')) { - let controller = this; - - require(['./main', './nginx/access/main'], (App, View) => { - controller.navigate('/nginx/access'); - App.UI.showAppContent(new View()); - }); - } - }, - - /** - * Nginx Access List Form - * - * @param [model] - */ - showNginxAccessListForm: function (model) { - if (Cache.User.isAdmin() || Cache.User.canManage('access_lists')) { - require(['./main', './nginx/access/form'], function (App, View) { - App.UI.showModalDialog(new View({model: model})); - }); - } - }, - - /** - * Access List Delete Confirm - * - * @param model - */ - showNginxAccessListDeleteConfirm: function (model) { - if (Cache.User.isAdmin() || Cache.User.canManage('access_lists')) { - require(['./main', './nginx/access/delete'], function (App, View) { - App.UI.showModalDialog(new View({model: model})); - }); - } - }, - - /** - * Nginx Certificates - */ - showNginxCertificates: function () { - if (Cache.User.isAdmin() || Cache.User.canView('certificates')) { - let controller = this; - - require(['./main', './nginx/certificates/main'], (App, View) => { - controller.navigate('/nginx/certificates'); - App.UI.showAppContent(new View()); - }); - } - }, - - /** - * Nginx Certificate Form - * - * @param [model] - */ - showNginxCertificateForm: function (model) { - if (Cache.User.isAdmin() || Cache.User.canManage('certificates')) { - require(['./main', './nginx/certificates/form'], function (App, View) { - App.UI.showModalDialog(new View({model: model})); - }); - } - }, - - /** - * Certificate Renew - * - * @param model - */ - showNginxCertificateRenew: function (model) { - if (Cache.User.isAdmin() || Cache.User.canManage('certificates')) { - require(['./main', './nginx/certificates/renew'], function (App, View) { - App.UI.showModalDialog(new View({model: model})); - }); - } - }, - - /** - * Certificate Delete Confirm - * - * @param model - */ - showNginxCertificateDeleteConfirm: function (model) { - if (Cache.User.isAdmin() || Cache.User.canManage('certificates')) { - require(['./main', './nginx/certificates/delete'], function (App, View) { - App.UI.showModalDialog(new View({model: model})); - }); - } - }, - - /** - * Audit Log - */ - showAuditLog: function () { - let controller = this; - if (Cache.User.isAdmin()) { - require(['./main', './audit-log/main'], (App, View) => { - controller.navigate('/audit-log'); - App.UI.showAppContent(new View()); - }); - } else { - this.showDashboard(); - } - }, - - /** - * Audit Log Metadata - * - * @param model - */ - showAuditMeta: function (model) { - if (Cache.User.isAdmin()) { - require(['./main', './audit-log/meta'], function (App, View) { - App.UI.showModalDialog(new View({model: model})); - }); - } - }, - - /** - * Settings - */ - showSettings: function () { - let controller = this; - if (Cache.User.isAdmin()) { - require(['./main', './settings/main'], (App, View) => { - controller.navigate('/settings'); - App.UI.showAppContent(new View()); - }); - } else { - this.showDashboard(); - } - }, - - /** - * Settings Item Form - * - * @param model - */ - showSettingForm: function (model) { - if (Cache.User.isAdmin()) { - if (model.get('id') === 'default-site') { - require(['./main', './settings/default-site/main'], function (App, View) { - App.UI.showModalDialog(new View({model: model})); - }); - } - } - }, - - /** - * Logout - */ - logout: function () { - Tokens.dropTopToken(); - this.showLogin(); - } -}; diff --git a/frontend/js/app/dashboard/main.ejs b/frontend/js/app/dashboard/main.ejs deleted file mode 100644 index c00aa6d0..00000000 --- a/frontend/js/app/dashboard/main.ejs +++ /dev/null @@ -1,67 +0,0 @@ - - -<% if (columns) { %> -
    - <% if (canShow('proxy_hosts')) { %> - - <% } %> - - <% if (canShow('redirection_hosts')) { %> - - <% } %> - - <% if (canShow('streams')) { %> - - <% } %> - - <% if (canShow('dead_hosts')) { %> - - <% } %> -
    -<% } %> diff --git a/frontend/js/app/dashboard/main.js b/frontend/js/app/dashboard/main.js deleted file mode 100644 index c2e82f85..00000000 --- a/frontend/js/app/dashboard/main.js +++ /dev/null @@ -1,92 +0,0 @@ -const Mn = require('backbone.marionette'); -const Cache = require('../cache'); -const Controller = require('../controller'); -const Api = require('../api'); -const Helpers = require('../../lib/helpers'); -const template = require('./main.ejs'); - -module.exports = Mn.View.extend({ - template: template, - id: 'dashboard', - columns: 0, - - stats: {}, - - ui: { - links: 'a' - }, - - events: { - 'click @ui.links': function (e) { - e.preventDefault(); - Controller.navigate($(e.currentTarget).attr('href'), true); - } - }, - - templateContext: function () { - let view = this; - - return { - getUserName: function () { - return Cache.User.get('nickname') || Cache.User.get('name'); - }, - - getHostStat: function (type) { - if (view.stats && typeof view.stats.hosts !== 'undefined' && typeof view.stats.hosts[type] !== 'undefined') { - return Helpers.niceNumber(view.stats.hosts[type]); - } - - return '-'; - }, - - canShow: function (perm) { - return Cache.User.isAdmin() || Cache.User.canView(perm); - }, - - columns: view.columns - }; - }, - - onRender: function () { - let view = this; - - if (typeof view.stats.hosts === 'undefined') { - Api.Reports.getHostStats() - .then(response => { - if (!view.isDestroyed()) { - view.stats.hosts = response; - view.render(); - } - }) - .catch(err => { - console.log(err); - }); - } - }, - - /** - * @param {Object} [model] - */ - preRender: function (model) { - this.columns = 0; - - // calculate the available columns based on permissions for the objects - // and store as a variable - //let view = this; - let perms = ['proxy_hosts', 'redirection_hosts', 'streams', 'dead_hosts']; - - perms.map(perm => { - this.columns += Cache.User.isAdmin() || Cache.User.canView(perm) ? 1 : 0; - }); - - // Prevent double rendering on initial calls - if (typeof model !== 'undefined') { - this.render(); - } - }, - - initialize: function () { - this.preRender(); - this.listenTo(Cache.User, 'change', this.preRender); - } -}); diff --git a/frontend/js/app/empty/main.ejs b/frontend/js/app/empty/main.ejs deleted file mode 100644 index 11633dfc..00000000 --- a/frontend/js/app/empty/main.ejs +++ /dev/null @@ -1,11 +0,0 @@ -<% if (title) { %> -

    <%- title %>

    -<% } - -if (subtitle) { %> -

    <%- subtitle %>

    -<% } - -if (link) { %> - <%- link %> -<% } %> diff --git a/frontend/js/app/empty/main.js b/frontend/js/app/empty/main.js deleted file mode 100644 index 74998d65..00000000 --- a/frontend/js/app/empty/main.js +++ /dev/null @@ -1,33 +0,0 @@ -const Mn = require('backbone.marionette'); -const template = require('./main.ejs'); - -module.exports = Mn.View.extend({ - className: 'text-center m-7', - template: template, - - options: { - btn_color: 'teal' - }, - - ui: { - action: 'a' - }, - - events: { - 'click @ui.action': function (e) { - e.preventDefault(); - this.getOption('action')(); - } - }, - - templateContext: function () { - return { - title: this.getOption('title'), - subtitle: this.getOption('subtitle'), - link: this.getOption('link'), - action: typeof this.getOption('action') === 'function', - btn_color: this.getOption('btn_color') - }; - } - -}); diff --git a/frontend/js/app/error/main.ejs b/frontend/js/app/error/main.ejs deleted file mode 100644 index f7fd709b..00000000 --- a/frontend/js/app/error/main.ejs +++ /dev/null @@ -1,7 +0,0 @@ - -<%= code ? '' + code + ' — ' : '' %> -<%- message %> - -<% if (retry) { %> -

    <%- i18n('str', 'try-again') %> -<% } %> diff --git a/frontend/js/app/error/main.js b/frontend/js/app/error/main.js deleted file mode 100644 index 6fa85fc8..00000000 --- a/frontend/js/app/error/main.js +++ /dev/null @@ -1,27 +0,0 @@ -const Mn = require('backbone.marionette'); -const template = require('./main.ejs'); - -module.exports = Mn.View.extend({ - template: template, - className: 'alert alert-icon alert-warning m-5', - - ui: { - retry: 'a.retry' - }, - - events: { - 'click @ui.retry': function (e) { - e.preventDefault(); - this.getOption('retry')(); - } - }, - - templateContext: function () { - return { - message: this.getOption('message'), - code: this.getOption('code'), - retry: typeof this.getOption('retry') === 'function' - }; - } - -}); diff --git a/frontend/js/app/help/main.ejs b/frontend/js/app/help/main.ejs deleted file mode 100644 index 6fb79e66..00000000 --- a/frontend/js/app/help/main.ejs +++ /dev/null @@ -1,12 +0,0 @@ - diff --git a/frontend/js/app/help/main.js b/frontend/js/app/help/main.js deleted file mode 100644 index b0f54374..00000000 --- a/frontend/js/app/help/main.js +++ /dev/null @@ -1,16 +0,0 @@ -const Mn = require('backbone.marionette'); -const template = require('./main.ejs'); - -module.exports = Mn.View.extend({ - template: template, - className: 'modal-dialog wide', - - templateContext: function () { - let content = this.getOption('content').split("\n"); - - return { - title: this.getOption('title'), - content: '

    ' + content.join('

    ') + '

    ' - }; - } -}); diff --git a/frontend/js/app/i18n.js b/frontend/js/app/i18n.js deleted file mode 100644 index c63cdc07..00000000 --- a/frontend/js/app/i18n.js +++ /dev/null @@ -1,23 +0,0 @@ -const Cache = ('./cache'); -const messages = require('../i18n/messages.json'); - -/** - * @param {String} namespace - * @param {String} key - * @param {Object} [data] - */ -module.exports = function (namespace, key, data) { - let locale = Cache.locale; - // check that the locale exists - if (typeof messages[locale] === 'undefined') { - locale = 'en'; - } - - if (typeof messages[locale][namespace] !== 'undefined' && typeof messages[locale][namespace][key] !== 'undefined') { - return messages[locale][namespace][key](data); - } else if (locale !== 'en' && typeof messages['en'][namespace] !== 'undefined' && typeof messages['en'][namespace][key] !== 'undefined') { - return messages['en'][namespace][key](data); - } - - return '(MISSING: ' + namespace + '/' + key + ')'; -}; diff --git a/frontend/js/app/main.js b/frontend/js/app/main.js deleted file mode 100644 index e85b4f62..00000000 --- a/frontend/js/app/main.js +++ /dev/null @@ -1,155 +0,0 @@ -const _ = require('underscore'); -const Backbone = require('backbone'); -const Mn = require('../lib/marionette'); -const Cache = require('./cache'); -const Controller = require('./controller'); -const Router = require('./router'); -const Api = require('./api'); -const Tokens = require('./tokens'); -const UI = require('./ui/main'); -const i18n = require('./i18n'); - -const App = Mn.Application.extend({ - - Cache: Cache, - Api: Api, - UI: null, - i18n: i18n, - Controller: Controller, - - region: { - el: '#app', - replaceElement: true - }, - - onStart: function (app, options) { - console.log(i18n('main', 'welcome')); - - // Check if token is coming through - if (this.getParam('token')) { - Tokens.addToken(this.getParam('token')); - } - - // Check if we are still logged in by refreshing the token - Api.status() - .then(result => { - Cache.version = [result.version.major, result.version.minor, result.version.revision].join('.'); - }) - .then(Api.Tokens.refresh) - .then(this.bootstrap) - .then(() => { - console.info(i18n('main', 'logged-in', Cache.User.attributes)); - this.bootstrapTimer(); - this.refreshTokenTimer(); - - this.UI = new UI(); - this.UI.on('render', () => { - new Router(options); - Backbone.history.start({pushState: true}); - - // Ask the admin use to change their details - if (Cache.User.get('email') === 'admin@example.com') { - Controller.showUserForm(Cache.User); - } - }); - - this.getRegion().show(this.UI); - }) - .catch(err => { - console.warn('Not logged in:', err.message); - Controller.showLogin(); - }); - }, - - History: { - replace: function (data) { - window.history.replaceState(_.extend(window.history.state || {}, data), document.title); - }, - - get: function (attr) { - return window.history.state ? window.history.state[attr] : undefined; - } - }, - - getParam: function (name) { - name = name.replace(/[\[\]]/g, '\\$&'); - let url = window.location.href; - let regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'); - let results = regex.exec(url); - - if (!results) { - return null; - } - - if (!results[2]) { - return ''; - } - - return decodeURIComponent(results[2].replace(/\+/g, ' ')); - }, - - /** - * Get user and other base info to start prime the cache and the application - * - * @returns {Promise} - */ - bootstrap: function () { - return Api.Users.getById('me', ['permissions']) - .then(response => { - Cache.User.set(response); - Tokens.setCurrentName(response.nickname || response.name); - }); - }, - - /** - * Bootstraps the user from time to time - */ - bootstrapTimer: function () { - setTimeout(() => { - Api.status() - .then(result => { - let version = [result.version.major, result.version.minor, result.version.revision].join('.'); - if (version !== Cache.version) { - document.location.reload(); - } - }) - .then(this.bootstrap) - .then(() => { - this.bootstrapTimer(); - }) - .catch(err => { - if (err.message !== 'timeout' && err.code && err.code !== 400) { - console.log(err); - console.error(err.message); - console.info('Not logged in?'); - Controller.showLogin(); - } else { - this.bootstrapTimer(); - } - }); - }, 30 * 1000); // 30 seconds - }, - - refreshTokenTimer: function () { - setTimeout(() => { - return Api.Tokens.refresh() - .then(this.bootstrap) - .then(() => { - this.refreshTokenTimer(); - }) - .catch(err => { - if (err.message !== 'timeout' && err.code && err.code !== 400) { - console.log(err); - console.error(err.message); - console.info('Not logged in?'); - Controller.showLogin(); - } else { - this.refreshTokenTimer(); - } - }); - }, 10 * 60 * 1000); - } -}); - -const app = new App(); -module.exports = app; diff --git a/frontend/js/app/nginx/access/delete.ejs b/frontend/js/app/nginx/access/delete.ejs deleted file mode 100644 index 3833549a..00000000 --- a/frontend/js/app/nginx/access/delete.ejs +++ /dev/null @@ -1,23 +0,0 @@ - diff --git a/frontend/js/app/nginx/access/delete.js b/frontend/js/app/nginx/access/delete.js deleted file mode 100644 index 4af91ab1..00000000 --- a/frontend/js/app/nginx/access/delete.js +++ /dev/null @@ -1,32 +0,0 @@ -const Mn = require('backbone.marionette'); -const App = require('../../main'); -const template = require('./delete.ejs'); - -module.exports = Mn.View.extend({ - template: template, - className: 'modal-dialog', - - ui: { - form: 'form', - buttons: '.modal-footer button', - cancel: 'button.cancel', - save: 'button.save' - }, - - events: { - - 'click @ui.save': function (e) { - e.preventDefault(); - - App.Api.Nginx.AccessLists.delete(this.model.get('id')) - .then(() => { - App.Controller.showNginxAccess(); - App.UI.closeModal(); - }) - .catch(err => { - alert(err.message); - this.ui.buttons.prop('disabled', false).removeClass('btn-disabled'); - }); - } - } -}); diff --git a/frontend/js/app/nginx/access/form.ejs b/frontend/js/app/nginx/access/form.ejs deleted file mode 100644 index 79220b14..00000000 --- a/frontend/js/app/nginx/access/form.ejs +++ /dev/null @@ -1,108 +0,0 @@ - diff --git a/frontend/js/app/nginx/access/form.js b/frontend/js/app/nginx/access/form.js deleted file mode 100644 index bb075548..00000000 --- a/frontend/js/app/nginx/access/form.js +++ /dev/null @@ -1,153 +0,0 @@ -const Mn = require('backbone.marionette'); -const App = require('../../main'); -const AccessListModel = require('../../../models/access-list'); -const template = require('./form.ejs'); -const ItemView = require('./form/item'); -const ClientView = require('./form/client'); - -require('jquery-serializejson'); - -const ItemsView = Mn.CollectionView.extend({ - childView: ItemView -}); - -const ClientsView = Mn.CollectionView.extend({ - childView: ClientView -}); - -module.exports = Mn.View.extend({ - template: template, - className: 'modal-dialog', - - ui: { - items_region: '.items', - clients_region: '.clients', - form: 'form', - buttons: '.modal-footer button', - cancel: 'button.cancel', - save: 'button.save', - access_add: 'button.access_add', - auth_add: 'button.auth_add' - }, - - regions: { - items_region: '@ui.items_region', - clients_region: '@ui.clients_region' - }, - - events: { - 'click @ui.save': function (e) { - e.preventDefault(); - - if (!this.ui.form[0].checkValidity()) { - $('').hide().appendTo(this.ui.form).click().remove(); - return; - } - - let view = this; - let form_data = this.ui.form.serializeJSON(); - let items_data = []; - let clients_data = []; - - form_data.username.map(function (val, idx) { - if (val.trim().length) { - items_data.push({ - username: val.trim(), - password: form_data.password[idx] - }); - } - }); - - form_data.address.map(function (val, idx) { - if (val.trim().length) { - clients_data.push({ - address: val.trim(), - directive: form_data.directive[idx] - }) - } - }); - - if (!items_data.length && !clients_data.length) { - alert('You must specify at least 1 Authorization or Access rule'); - return; - } - - let data = { - name: form_data.name, - satisfy_any: !!form_data.satisfy_any, - pass_auth: !!form_data.pass_auth, - items: items_data, - clients: clients_data - }; - - console.log(data); - - let method = App.Api.Nginx.AccessLists.create; - let is_new = true; - - if (this.model.get('id')) { - // edit - is_new = false; - method = App.Api.Nginx.AccessLists.update; - data.id = this.model.get('id'); - } - - this.ui.buttons.prop('disabled', true).addClass('btn-disabled'); - method(data) - .then(result => { - view.model.set(result); - - App.UI.closeModal(function () { - if (is_new) { - App.Controller.showNginxAccess(); - } - }); - }) - .catch(err => { - alert(err.message); - this.ui.buttons.prop('disabled', false).removeClass('btn-disabled'); - }); - }, - 'click @ui.access_add': function (e) { - e.preventDefault(); - - let clients = this.model.get('clients'); - clients.push({}); - this.showChildView('clients_region', new ClientsView({ - collection: new Backbone.Collection(clients) - })); - }, - 'click @ui.auth_add': function (e) { - e.preventDefault(); - - let items = this.model.get('items'); - items.push({}); - this.showChildView('items_region', new ItemsView({ - collection: new Backbone.Collection(items) - })); - } - }, - - onRender: function () { - let items = this.model.get('items'); - let clients = this.model.get('clients'); - - // Ensure at least one field is shown initally - if (!items.length) items.push({}); - if (!clients.length) clients.push({}); - - this.showChildView('items_region', new ItemsView({ - collection: new Backbone.Collection(items) - })); - - this.showChildView('clients_region', new ClientsView({ - collection: new Backbone.Collection(clients) - })); - }, - - initialize: function (options) { - if (typeof options.model === 'undefined' || !options.model) { - this.model = new AccessListModel.Model(); - } - } -}); diff --git a/frontend/js/app/nginx/access/form/client.ejs b/frontend/js/app/nginx/access/form/client.ejs deleted file mode 100644 index 6b767b83..00000000 --- a/frontend/js/app/nginx/access/form/client.ejs +++ /dev/null @@ -1,13 +0,0 @@ -
    -
    - -
    -
    -
    -
    - -
    -
    diff --git a/frontend/js/app/nginx/access/form/client.js b/frontend/js/app/nginx/access/form/client.js deleted file mode 100644 index b4c00e2e..00000000 --- a/frontend/js/app/nginx/access/form/client.js +++ /dev/null @@ -1,7 +0,0 @@ -const Mn = require('backbone.marionette'); -const template = require('./client.ejs'); - -module.exports = Mn.View.extend({ - template: template, - className: 'row' -}); diff --git a/frontend/js/app/nginx/access/form/item.ejs b/frontend/js/app/nginx/access/form/item.ejs deleted file mode 100644 index c2435ecb..00000000 --- a/frontend/js/app/nginx/access/form/item.ejs +++ /dev/null @@ -1,10 +0,0 @@ -
    -
    - -
    -
    -
    -
    - -
    -
    diff --git a/frontend/js/app/nginx/access/form/item.js b/frontend/js/app/nginx/access/form/item.js deleted file mode 100644 index f15238dc..00000000 --- a/frontend/js/app/nginx/access/form/item.js +++ /dev/null @@ -1,7 +0,0 @@ -const Mn = require('backbone.marionette'); -const template = require('./item.ejs'); - -module.exports = Mn.View.extend({ - template: template, - className: 'row' -}); diff --git a/frontend/js/app/nginx/access/list/item.ejs b/frontend/js/app/nginx/access/list/item.ejs deleted file mode 100644 index 2ee37a50..00000000 --- a/frontend/js/app/nginx/access/list/item.ejs +++ /dev/null @@ -1,42 +0,0 @@ - -
    - -
    - - -
    - <%- name %> -
    -
    - <%- i18n('str', 'created-on', {date: formatDbDate(created_on, 'Do MMMM YYYY')}) %> -
    - - - <%- i18n('access-lists', 'item-count', {count: items.length || 0}) %> - - - <%- i18n('access-lists', 'client-count', {count: clients.length || 0}) %> - - - <% if (satisfy_any) { %> - <%- i18n('str', 'any') %> - <%} else { %> - <%- i18n('str', 'all') %> - <% } %> - - - <%- i18n('access-lists', 'proxy-host-count', {count: proxy_host_count}) %> - -<% if (canManage) { %> - - - -<% } %> diff --git a/frontend/js/app/nginx/access/list/item.js b/frontend/js/app/nginx/access/list/item.js deleted file mode 100644 index 4f68aead..00000000 --- a/frontend/js/app/nginx/access/list/item.js +++ /dev/null @@ -1,33 +0,0 @@ -const Mn = require('backbone.marionette'); -const App = require('../../../main'); -const template = require('./item.ejs'); - -module.exports = Mn.View.extend({ - template: template, - tagName: 'tr', - - ui: { - edit: 'a.edit', - delete: 'a.delete' - }, - - events: { - 'click @ui.edit': function (e) { - e.preventDefault(); - App.Controller.showNginxAccessListForm(this.model); - }, - - 'click @ui.delete': function (e) { - e.preventDefault(); - App.Controller.showNginxAccessListDeleteConfirm(this.model); - } - }, - - templateContext: { - canManage: App.Cache.User.canManage('access_lists') - }, - - initialize: function () { - this.listenTo(this.model, 'change', this.render); - } -}); diff --git a/frontend/js/app/nginx/access/list/main.ejs b/frontend/js/app/nginx/access/list/main.ejs deleted file mode 100644 index 7988e0c2..00000000 --- a/frontend/js/app/nginx/access/list/main.ejs +++ /dev/null @@ -1,14 +0,0 @@ - -   - <%- i18n('str', 'name') %> - <%- i18n('access-lists', 'authorization') %> - <%- i18n('access-lists', 'access') %> - <%- i18n('access-lists', 'satisfy') %> - <%- i18n('proxy-hosts', 'title') %> - <% if (canManage) { %> -   - <% } %> - - - - diff --git a/frontend/js/app/nginx/access/list/main.js b/frontend/js/app/nginx/access/list/main.js deleted file mode 100644 index 577a77ef..00000000 --- a/frontend/js/app/nginx/access/list/main.js +++ /dev/null @@ -1,32 +0,0 @@ -const Mn = require('backbone.marionette'); -const App = require('../../../main'); -const ItemView = require('./item'); -const template = require('./main.ejs'); - -const TableBody = Mn.CollectionView.extend({ - tagName: 'tbody', - childView: ItemView -}); - -module.exports = Mn.View.extend({ - tagName: 'table', - className: 'table table-hover table-outline table-vcenter card-table', - template: template, - - regions: { - body: { - el: 'tbody', - replaceElement: true - } - }, - - templateContext: { - canManage: App.Cache.User.canManage('access_lists') - }, - - onRender: function () { - this.showChildView('body', new TableBody({ - collection: this.collection - })); - } -}); diff --git a/frontend/js/app/nginx/access/main.ejs b/frontend/js/app/nginx/access/main.ejs deleted file mode 100644 index c245ff4a..00000000 --- a/frontend/js/app/nginx/access/main.ejs +++ /dev/null @@ -1,20 +0,0 @@ -
    -
    -
    -

    <%- i18n('access-lists', 'title') %>

    -
    - - <% if (showAddButton) { %> - <%- i18n('access-lists', 'add') %> - <% } %> -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    diff --git a/frontend/js/app/nginx/access/main.js b/frontend/js/app/nginx/access/main.js deleted file mode 100644 index d14a9eb4..00000000 --- a/frontend/js/app/nginx/access/main.js +++ /dev/null @@ -1,81 +0,0 @@ -const Mn = require('backbone.marionette'); -const App = require('../../main'); -const AccessListModel = require('../../../models/access-list'); -const ListView = require('./list/main'); -const ErrorView = require('../../error/main'); -const EmptyView = require('../../empty/main'); -const template = require('./main.ejs'); - -module.exports = Mn.View.extend({ - id: 'nginx-access', - template: template, - - ui: { - list_region: '.list-region', - add: '.add-item', - help: '.help', - dimmer: '.dimmer' - }, - - regions: { - list_region: '@ui.list_region' - }, - - events: { - 'click @ui.add': function (e) { - e.preventDefault(); - App.Controller.showNginxAccessListForm(); - }, - - 'click @ui.help': function (e) { - e.preventDefault(); - App.Controller.showHelp(App.i18n('access-lists', 'help-title'), App.i18n('access-lists', 'help-content')); - } - }, - - templateContext: { - showAddButton: App.Cache.User.canManage('access_lists') - }, - - onRender: function () { - let view = this; - - App.Api.Nginx.AccessLists.getAll(['owner', 'items', 'clients']) - .then(response => { - if (!view.isDestroyed()) { - if (response && response.length) { - view.showChildView('list_region', new ListView({ - collection: new AccessListModel.Collection(response) - })); - } else { - let manage = App.Cache.User.canManage('access_lists'); - - view.showChildView('list_region', new EmptyView({ - title: App.i18n('access-lists', 'empty'), - subtitle: App.i18n('all-hosts', 'empty-subtitle', {manage: manage}), - link: manage ? App.i18n('access-lists', 'add') : null, - btn_color: 'teal', - permission: 'access_lists', - action: function () { - App.Controller.showNginxAccessListForm(); - } - })); - } - } - }) - .catch(err => { - view.showChildView('list_region', new ErrorView({ - code: err.code, - message: err.message, - retry: function () { - App.Controller.showNginxAccess(); - } - })); - - console.error(err); - }) - .then(() => { - view.ui.dimmer.removeClass('active'); - }); - } -}); diff --git a/frontend/js/app/nginx/certificates-list-item.ejs b/frontend/js/app/nginx/certificates-list-item.ejs deleted file mode 100644 index aa4b53ad..00000000 --- a/frontend/js/app/nginx/certificates-list-item.ejs +++ /dev/null @@ -1,18 +0,0 @@ -
    - <% if (id === 'new') { %> -
    - <%- i18n('all-hosts', 'new-cert') %> -
    - <%- i18n('all-hosts', 'with-le') %> - <% } else if (id > 0) { %> -
    - <%- provider === 'other' ? nice_name : domain_names.join(', ') %> -
    - <%- i18n('ssl', provider) %> – Expires: <%- formatDbDate(expires_on, 'Do MMMM YYYY, h:mm a') %> - <% } else { %> -
    - <%- i18n('all-hosts', 'none') %> -
    - <%- i18n('all-hosts', 'no-ssl') %> - <% } %> -
    diff --git a/frontend/js/app/nginx/certificates/delete.ejs b/frontend/js/app/nginx/certificates/delete.ejs deleted file mode 100644 index b4e06866..00000000 --- a/frontend/js/app/nginx/certificates/delete.ejs +++ /dev/null @@ -1,19 +0,0 @@ - diff --git a/frontend/js/app/nginx/certificates/delete.js b/frontend/js/app/nginx/certificates/delete.js deleted file mode 100644 index 89a2e5e8..00000000 --- a/frontend/js/app/nginx/certificates/delete.js +++ /dev/null @@ -1,34 +0,0 @@ -const Mn = require('backbone.marionette'); -const App = require('../../main'); -const template = require('./delete.ejs'); - -module.exports = Mn.View.extend({ - template: template, - className: 'modal-dialog', - - ui: { - form: 'form', - buttons: '.modal-footer button', - cancel: 'button.cancel', - save: 'button.save' - }, - - events: { - 'click @ui.save': function (e) { - e.preventDefault(); - this.ui.save.addClass('btn-loading'); - this.ui.buttons.prop('disabled', true).addClass('btn-disabled'); - - App.Api.Nginx.Certificates.delete(this.model.get('id')) - .then(() => { - App.Controller.showNginxCertificates(); - App.UI.closeModal(); - }) - .catch(err => { - alert(err.message); - this.ui.buttons.prop('disabled', false).removeClass('btn-disabled'); - this.ui.save.removeClass('btn-loading'); - }); - } - } -}); diff --git a/frontend/js/app/nginx/certificates/form.ejs b/frontend/js/app/nginx/certificates/form.ejs deleted file mode 100644 index c8b1369f..00000000 --- a/frontend/js/app/nginx/certificates/form.ejs +++ /dev/null @@ -1,177 +0,0 @@ - diff --git a/frontend/js/app/nginx/certificates/form.js b/frontend/js/app/nginx/certificates/form.js deleted file mode 100644 index d5d3acd3..00000000 --- a/frontend/js/app/nginx/certificates/form.js +++ /dev/null @@ -1,267 +0,0 @@ -const _ = require('underscore'); -const Mn = require('backbone.marionette'); -const App = require('../../main'); -const CertificateModel = require('../../../models/certificate'); -const template = require('./form.ejs'); -const i18n = require('../../i18n'); -const dns_providers = sortProvidersAlphabetically(require('../../../../../global/certbot-dns-plugins')); - -require('jquery-serializejson'); -require('selectize'); - -function sortProvidersAlphabetically(obj) { - return Object.entries(obj) - .sort((a,b) => a[1].display_name.toLowerCase() > b[1].display_name.toLowerCase()) - .reduce((result, entry) => { - result[entry[0]] = entry[1]; - return result; - }, {}); -} - -module.exports = Mn.View.extend({ - template: template, - className: 'modal-dialog', - max_file_size: 102400, - - ui: { - form: 'form', - loader_content: '.loader-content', - non_loader_content: '.non-loader-content', - le_error_info: '#le-error-info', - domain_names: 'input[name="domain_names"]', - buttons: '.modal-footer button', - cancel: 'button.cancel', - save: 'button.save', - other_certificate: '#other_certificate', - other_certificate_label: '#other_certificate_label', - other_certificate_key: '#other_certificate_key', - dns_challenge_switch: 'input[name="meta[dns_challenge]"]', - dns_challenge_content: '.dns-challenge', - dns_provider: 'select[name="meta[dns_provider]"]', - credentials_file_content: '.credentials-file-content', - dns_provider_credentials: 'textarea[name="meta[dns_provider_credentials]"]', - propagation_seconds: 'input[name="meta[propagation_seconds]"]', - other_certificate_key_label: '#other_certificate_key_label', - other_intermediate_certificate: '#other_intermediate_certificate', - other_intermediate_certificate_label: '#other_intermediate_certificate_label' - }, - - events: { - 'change @ui.dns_challenge_switch': function () { - const checked = this.ui.dns_challenge_switch.prop('checked'); - if (checked) { - this.ui.dns_provider.prop('required', 'required'); - const selected_provider = this.ui.dns_provider[0].options[this.ui.dns_provider[0].selectedIndex].value; - if(selected_provider != '' && dns_providers[selected_provider].credentials !== false){ - this.ui.dns_provider_credentials.prop('required', 'required'); - } - this.ui.dns_challenge_content.show(); - } else { - this.ui.dns_provider.prop('required', false); - this.ui.dns_provider_credentials.prop('required', false); - this.ui.dns_challenge_content.hide(); - } - }, - - 'change @ui.dns_provider': function () { - const selected_provider = this.ui.dns_provider[0].options[this.ui.dns_provider[0].selectedIndex].value; - if (selected_provider != '' && dns_providers[selected_provider].credentials !== false) { - this.ui.dns_provider_credentials.prop('required', 'required'); - this.ui.dns_provider_credentials[0].value = dns_providers[selected_provider].credentials; - this.ui.credentials_file_content.show(); - } else { - this.ui.dns_provider_credentials.prop('required', false); - this.ui.credentials_file_content.hide(); - } - }, - - 'click @ui.save': function (e) { - e.preventDefault(); - this.ui.le_error_info.hide(); - - if (!this.ui.form[0].checkValidity()) { - $('').hide().appendTo(this.ui.form).click().remove(); - $(this).removeClass('btn-loading'); - return; - } - - let data = this.ui.form.serializeJSON(); - data.provider = this.model.get('provider'); - let ssl_files = []; - - if (data.provider === 'letsencrypt') { - if (typeof data.meta === 'undefined') data.meta = {}; - - let domain_err = false; - if (!data.meta.dns_challenge) { - data.domain_names.split(',').map(function (name) { - if (name.match(/\*/im)) { - domain_err = true; - } - }); - } - - if (domain_err) { - alert(i18n('ssl', 'no-wildcard-without-dns')); - return; - } - - // Manipulate - data.meta.letsencrypt_agree = data.meta.letsencrypt_agree == 1; - data.meta.dns_challenge = data.meta.dns_challenge == 1; - - if(!data.meta.dns_challenge){ - data.meta.dns_provider = undefined; - data.meta.dns_provider_credentials = undefined; - data.meta.propagation_seconds = undefined; - } else { - if(data.meta.propagation_seconds === '') data.meta.propagation_seconds = undefined; - } - - if (typeof data.domain_names === 'string' && data.domain_names) { - data.domain_names = data.domain_names.split(','); - } - } else if (data.provider === 'other' && !this.model.hasSslFiles()) { - // check files are attached - if (!this.ui.other_certificate[0].files.length || !this.ui.other_certificate[0].files[0].size) { - alert('Certificate file is not attached'); - return; - } else { - if (this.ui.other_certificate[0].files[0].size > this.max_file_size) { - alert('Certificate file is too large (> 100kb)'); - return; - } - ssl_files.push({name: 'certificate', file: this.ui.other_certificate[0].files[0]}); - } - - if (!this.ui.other_certificate_key[0].files.length || !this.ui.other_certificate_key[0].files[0].size) { - alert('Certificate key file is not attached'); - return; - } else { - if (this.ui.other_certificate_key[0].files[0].size > this.max_file_size) { - alert('Certificate key file is too large (> 100kb)'); - return; - } - ssl_files.push({name: 'certificate_key', file: this.ui.other_certificate_key[0].files[0]}); - } - - if (this.ui.other_intermediate_certificate[0].files.length && this.ui.other_intermediate_certificate[0].files[0].size) { - if (this.ui.other_intermediate_certificate[0].files[0].size > this.max_file_size) { - alert('Intermediate Certificate file is too large (> 100kb)'); - return; - } - ssl_files.push({name: 'intermediate_certificate', file: this.ui.other_intermediate_certificate[0].files[0]}); - } - } - - this.ui.loader_content.show(); - this.ui.non_loader_content.hide(); - - // compile file data - let form_data = new FormData(); - if (data.provider === 'other' && ssl_files.length) { - ssl_files.map(function (file) { - form_data.append(file.name, file.file); - }); - } - - new Promise(resolve => { - if (data.provider === 'other') { - resolve(App.Api.Nginx.Certificates.validate(form_data)); - } else { - resolve(); - } - }) - .then(() => { - return App.Api.Nginx.Certificates.create(data); - }) - .then(result => { - this.model.set(result); - - // Now upload the certs if we need to - if (data.provider === 'other') { - return App.Api.Nginx.Certificates.upload(this.model.get('id'), form_data) - .then(result => { - this.model.set('meta', _.assign({}, this.model.get('meta'), result)); - }); - } - }) - .then(() => { - App.UI.closeModal(function () { - App.Controller.showNginxCertificates(); - }); - }) - .catch(err => { - let more_info = ''; - if (err.code === 500 && err.debug) { - try{ - more_info = JSON.parse(err.debug).debug.stack.join("\n"); - } catch(e) {} - } - this.ui.le_error_info[0].innerHTML = `${err.message}${more_info !== '' ? `
    ${more_info}
    `:''}`; - this.ui.le_error_info.show(); - this.ui.le_error_info[0].scrollIntoView(); - this.ui.loader_content.hide(); - this.ui.non_loader_content.show(); - }); - }, - 'change @ui.other_certificate_key': function(e){ - this.setFileName("other_certificate_key_label", e) - }, - 'change @ui.other_certificate': function(e){ - this.setFileName("other_certificate_label", e) - }, - 'change @ui.other_intermediate_certificate': function(e){ - this.setFileName("other_intermediate_certificate_label", e) - } - }, - setFileName(ui, e){ - this.getUI(ui).text(e.target.files[0].name) - }, - templateContext: { - getLetsencryptEmail: function () { - return typeof this.meta.letsencrypt_email !== 'undefined' ? this.meta.letsencrypt_email : App.Cache.User.get('email'); - }, - getLetsencryptAgree: function () { - return typeof this.meta.letsencrypt_agree !== 'undefined' ? this.meta.letsencrypt_agree : false; - }, - getUseDnsChallenge: function () { - return typeof this.meta.dns_challenge !== 'undefined' ? this.meta.dns_challenge : false; - }, - getDnsProvider: function () { - return typeof this.meta.dns_provider !== 'undefined' && this.meta.dns_provider != '' ? this.meta.dns_provider : null; - }, - getDnsProviderCredentials: function () { - return typeof this.meta.dns_provider_credentials !== 'undefined' ? this.meta.dns_provider_credentials : ''; - }, - getPropagationSeconds: function () { - return typeof this.meta.propagation_seconds !== 'undefined' ? this.meta.propagation_seconds : ''; - }, - dns_plugins: dns_providers, - }, - - onRender: function () { - this.ui.domain_names.selectize({ - delimiter: ',', - persist: false, - maxOptions: 15, - create: function (input) { - return { - value: input, - text: input - }; - }, - createFilter: /^(?:[^.]+\.?)+[^.]$/ - }); - this.ui.dns_challenge_content.hide(); - this.ui.credentials_file_content.hide(); - this.ui.loader_content.hide(); - this.ui.le_error_info.hide(); - }, - - initialize: function (options) { - if (typeof options.model === 'undefined' || !options.model) { - this.model = new CertificateModel.Model({provider: 'letsencrypt'}); - } - } -}); diff --git a/frontend/js/app/nginx/certificates/list/item.ejs b/frontend/js/app/nginx/certificates/list/item.ejs deleted file mode 100644 index 87930dce..00000000 --- a/frontend/js/app/nginx/certificates/list/item.ejs +++ /dev/null @@ -1,50 +0,0 @@ - -
    - -
    - - -
    - <% - if (provider === 'letsencrypt') { - domain_names.map(function(host) { - if (host.indexOf('*') === -1) { - %> - <%- host %> - <% - } else { - %> - <%- host %> - <% - } - }); - } else { - %><%- nice_name %><% - } - %> -
    -
    - <%- i18n('str', 'created-on', {date: formatDbDate(created_on, 'Do MMMM YYYY')}) %> -
    - - - <%- i18n('ssl', provider) %><% if (meta.dns_provider) { %> - <%- dns_providers[meta.dns_provider].display_name %><% } %> - - - <%- formatDbDate(expires_on, 'Do MMMM YYYY, h:mm a') %> - -<% if (canManage) { %> - - - -<% } %> \ No newline at end of file diff --git a/frontend/js/app/nginx/certificates/list/item.js b/frontend/js/app/nginx/certificates/list/item.js deleted file mode 100644 index c967fdb8..00000000 --- a/frontend/js/app/nginx/certificates/list/item.js +++ /dev/null @@ -1,46 +0,0 @@ -const Mn = require('backbone.marionette'); -const moment = require('moment'); -const App = require('../../../main'); -const template = require('./item.ejs'); -const dns_providers = require('../../../../../../global/certbot-dns-plugins') - -module.exports = Mn.View.extend({ - template: template, - tagName: 'tr', - - ui: { - host_link: '.host-link', - renew: 'a.renew', - delete: 'a.delete' - }, - - events: { - 'click @ui.renew': function (e) { - e.preventDefault(); - App.Controller.showNginxCertificateRenew(this.model); - }, - - 'click @ui.delete': function (e) { - e.preventDefault(); - App.Controller.showNginxCertificateDeleteConfirm(this.model); - }, - - 'click @ui.host_link': function (e) { - e.preventDefault(); - let win = window.open($(e.currentTarget).attr('rel'), '_blank'); - win.focus(); - } - }, - - templateContext: { - canManage: App.Cache.User.canManage('certificates'), - isExpired: function () { - return moment(this.expires_on).isBefore(moment()); - }, - dns_providers: dns_providers - }, - - initialize: function () { - this.listenTo(this.model, 'change', this.render); - } -}); diff --git a/frontend/js/app/nginx/certificates/list/main.ejs b/frontend/js/app/nginx/certificates/list/main.ejs deleted file mode 100644 index aa49a27f..00000000 --- a/frontend/js/app/nginx/certificates/list/main.ejs +++ /dev/null @@ -1,12 +0,0 @@ - -   - <%- i18n('str', 'name') %> - <%- i18n('all-hosts', 'cert-provider') %> - <%- i18n('str', 'expires') %> - <% if (canManage) { %> -   - <% } %> - - - - diff --git a/frontend/js/app/nginx/certificates/list/main.js b/frontend/js/app/nginx/certificates/list/main.js deleted file mode 100644 index d96b43e8..00000000 --- a/frontend/js/app/nginx/certificates/list/main.js +++ /dev/null @@ -1,32 +0,0 @@ -const Mn = require('backbone.marionette'); -const App = require('../../../main'); -const ItemView = require('./item'); -const template = require('./main.ejs'); - -const TableBody = Mn.CollectionView.extend({ - tagName: 'tbody', - childView: ItemView -}); - -module.exports = Mn.View.extend({ - tagName: 'table', - className: 'table table-hover table-outline table-vcenter card-table', - template: template, - - regions: { - body: { - el: 'tbody', - replaceElement: true - } - }, - - templateContext: { - canManage: App.Cache.User.canManage('certificates') - }, - - onRender: function () { - this.showChildView('body', new TableBody({ - collection: this.collection - })); - } -}); diff --git a/frontend/js/app/nginx/certificates/main.ejs b/frontend/js/app/nginx/certificates/main.ejs deleted file mode 100644 index cc3624d5..00000000 --- a/frontend/js/app/nginx/certificates/main.ejs +++ /dev/null @@ -1,28 +0,0 @@ -
    -
    -
    -

    <%- i18n('certificates', 'title') %>

    -
    - - <% if (showAddButton) { %> - - <% } %> -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    diff --git a/frontend/js/app/nginx/certificates/main.js b/frontend/js/app/nginx/certificates/main.js deleted file mode 100644 index e1148468..00000000 --- a/frontend/js/app/nginx/certificates/main.js +++ /dev/null @@ -1,82 +0,0 @@ -const Mn = require('backbone.marionette'); -const App = require('../../main'); -const CertificateModel = require('../../../models/certificate'); -const ListView = require('./list/main'); -const ErrorView = require('../../error/main'); -const EmptyView = require('../../empty/main'); -const template = require('./main.ejs'); - -module.exports = Mn.View.extend({ - id: 'nginx-certificates', - template: template, - - ui: { - list_region: '.list-region', - add: '.add-item', - help: '.help', - dimmer: '.dimmer' - }, - - regions: { - list_region: '@ui.list_region' - }, - - events: { - 'click @ui.add': function (e) { - e.preventDefault(); - let model = new CertificateModel.Model({provider: $(e.currentTarget).data('cert')}); - App.Controller.showNginxCertificateForm(model); - }, - - 'click @ui.help': function (e) { - e.preventDefault(); - App.Controller.showHelp(App.i18n('certificates', 'help-title'), App.i18n('certificates', 'help-content')); - } - }, - - templateContext: { - showAddButton: App.Cache.User.canManage('certificates') - }, - - onRender: function () { - let view = this; - - App.Api.Nginx.Certificates.getAll(['owner']) - .then(response => { - if (!view.isDestroyed()) { - if (response && response.length) { - view.showChildView('list_region', new ListView({ - collection: new CertificateModel.Collection(response) - })); - } else { - let manage = App.Cache.User.canManage('certificates'); - - view.showChildView('list_region', new EmptyView({ - title: App.i18n('certificates', 'empty'), - subtitle: App.i18n('all-hosts', 'empty-subtitle', {manage: manage}), - link: manage ? App.i18n('certificates', 'add') : null, - btn_color: 'pink', - permission: 'certificates', - action: function () { - App.Controller.showNginxCertificateForm(); - } - })); - } - } - }) - .catch(err => { - view.showChildView('list_region', new ErrorView({ - code: err.code, - message: err.message, - retry: function () { - App.Controller.showNginxCertificates(); - } - })); - - console.error(err); - }) - .then(() => { - view.ui.dimmer.removeClass('active'); - }); - } -}); diff --git a/frontend/js/app/nginx/certificates/renew.ejs b/frontend/js/app/nginx/certificates/renew.ejs deleted file mode 100644 index 4af186d0..00000000 --- a/frontend/js/app/nginx/certificates/renew.ejs +++ /dev/null @@ -1,14 +0,0 @@ - diff --git a/frontend/js/app/nginx/certificates/renew.js b/frontend/js/app/nginx/certificates/renew.js deleted file mode 100644 index 73632881..00000000 --- a/frontend/js/app/nginx/certificates/renew.js +++ /dev/null @@ -1,31 +0,0 @@ -const Mn = require('backbone.marionette'); -const App = require('../../main'); -const template = require('./renew.ejs'); - -module.exports = Mn.View.extend({ - template: template, - className: 'modal-dialog', - - ui: { - waiting: '.waiting', - error: '.error', - close: 'button.cancel' - }, - - onRender: function () { - this.ui.error.hide(); - - App.Api.Nginx.Certificates.renew(this.model.get('id')) - .then((result) => { - this.model.set(result); - setTimeout(() => { - App.UI.closeModal(); - }, 1000); - }) - .catch((err) => { - this.ui.waiting.hide(); - this.ui.error.text(err.message).show(); - this.ui.close.prop('disabled', false); - }); - } -}); diff --git a/frontend/js/app/nginx/dead/delete.ejs b/frontend/js/app/nginx/dead/delete.ejs deleted file mode 100644 index cf720e86..00000000 --- a/frontend/js/app/nginx/dead/delete.ejs +++ /dev/null @@ -1,23 +0,0 @@ - diff --git a/frontend/js/app/nginx/dead/delete.js b/frontend/js/app/nginx/dead/delete.js deleted file mode 100644 index d497d068..00000000 --- a/frontend/js/app/nginx/dead/delete.js +++ /dev/null @@ -1,32 +0,0 @@ -const Mn = require('backbone.marionette'); -const App = require('../../main'); -const template = require('./delete.ejs'); - -module.exports = Mn.View.extend({ - template: template, - className: 'modal-dialog', - - ui: { - form: 'form', - buttons: '.modal-footer button', - cancel: 'button.cancel', - save: 'button.save' - }, - - events: { - - 'click @ui.save': function (e) { - e.preventDefault(); - - App.Api.Nginx.DeadHosts.delete(this.model.get('id')) - .then(() => { - App.Controller.showNginxDead(); - App.UI.closeModal(); - }) - .catch(err => { - alert(err.message); - this.ui.buttons.prop('disabled', false).removeClass('btn-disabled'); - }); - } - } -}); diff --git a/frontend/js/app/nginx/dead/form.ejs b/frontend/js/app/nginx/dead/form.ejs deleted file mode 100644 index 253c4b6f..00000000 --- a/frontend/js/app/nginx/dead/form.ejs +++ /dev/null @@ -1,206 +0,0 @@ - diff --git a/frontend/js/app/nginx/dead/form.js b/frontend/js/app/nginx/dead/form.js deleted file mode 100644 index 8f6774f6..00000000 --- a/frontend/js/app/nginx/dead/form.js +++ /dev/null @@ -1,286 +0,0 @@ -const Mn = require('backbone.marionette'); -const App = require('../../main'); -const DeadHostModel = require('../../../models/dead-host'); -const template = require('./form.ejs'); -const certListItemTemplate = require('../certificates-list-item.ejs'); -const Helpers = require('../../../lib/helpers'); -const i18n = require('../../i18n'); -const dns_providers = require('../../../../../global/certbot-dns-plugins'); - -require('jquery-serializejson'); -require('selectize'); - -module.exports = Mn.View.extend({ - template: template, - className: 'modal-dialog', - - ui: { - form: 'form', - domain_names: 'input[name="domain_names"]', - buttons: '.modal-footer button', - cancel: 'button.cancel', - save: 'button.save', - le_error_info: '#le-error-info', - certificate_select: 'select[name="certificate_id"]', - ssl_forced: 'input[name="ssl_forced"]', - hsts_enabled: 'input[name="hsts_enabled"]', - hsts_subdomains: 'input[name="hsts_subdomains"]', - http2_support: 'input[name="http2_support"]', - dns_challenge_switch: 'input[name="meta[dns_challenge]"]', - dns_challenge_content: '.dns-challenge', - dns_provider: 'select[name="meta[dns_provider]"]', - credentials_file_content: '.credentials-file-content', - dns_provider_credentials: 'textarea[name="meta[dns_provider_credentials]"]', - propagation_seconds: 'input[name="meta[propagation_seconds]"]', - letsencrypt: '.letsencrypt' - }, - - events: { - 'change @ui.certificate_select': function () { - let id = this.ui.certificate_select.val(); - if (id === 'new') { - this.ui.letsencrypt.show().find('input').prop('disabled', false); - this.ui.dns_challenge_content.hide(); - } else { - this.ui.letsencrypt.hide().find('input').prop('disabled', true); - } - - - let enabled = id === 'new' || parseInt(id, 10) > 0; - - let inputs = this.ui.ssl_forced.add(this.ui.http2_support); - inputs - .prop('disabled', !enabled) - .parents('.form-group') - .css('opacity', enabled ? 1 : 0.5); - - if (!enabled) { - inputs.prop('checked', false); - } - - inputs.trigger('change'); - }, - - 'change @ui.ssl_forced': function () { - let checked = this.ui.ssl_forced.prop('checked'); - this.ui.hsts_enabled - .prop('disabled', !checked) - .parents('.form-group') - .css('opacity', checked ? 1 : 0.5); - - if (!checked) { - this.ui.hsts_enabled.prop('checked', false); - } - - this.ui.hsts_enabled.trigger('change'); - }, - - 'change @ui.hsts_enabled': function () { - let checked = this.ui.hsts_enabled.prop('checked'); - this.ui.hsts_subdomains - .prop('disabled', !checked) - .parents('.form-group') - .css('opacity', checked ? 1 : 0.5); - - if (!checked) { - this.ui.hsts_subdomains.prop('checked', false); - } - }, - - 'change @ui.dns_challenge_switch': function () { - const checked = this.ui.dns_challenge_switch.prop('checked'); - if (checked) { - this.ui.dns_provider.prop('required', 'required'); - const selected_provider = this.ui.dns_provider[0].options[this.ui.dns_provider[0].selectedIndex].value; - if(selected_provider != '' && dns_providers[selected_provider].credentials !== false){ - this.ui.dns_provider_credentials.prop('required', 'required'); - } - this.ui.dns_challenge_content.show(); - } else { - this.ui.dns_provider.prop('required', false); - this.ui.dns_provider_credentials.prop('required', false); - this.ui.dns_challenge_content.hide(); - } - }, - - 'change @ui.dns_provider': function () { - const selected_provider = this.ui.dns_provider[0].options[this.ui.dns_provider[0].selectedIndex].value; - if (selected_provider != '' && dns_providers[selected_provider].credentials !== false) { - this.ui.dns_provider_credentials.prop('required', 'required'); - this.ui.dns_provider_credentials[0].value = dns_providers[selected_provider].credentials; - this.ui.credentials_file_content.show(); - } else { - this.ui.dns_provider_credentials.prop('required', false); - this.ui.credentials_file_content.hide(); - } - }, - - 'click @ui.save': function (e) { - e.preventDefault(); - this.ui.le_error_info.hide(); - - if (!this.ui.form[0].checkValidity()) { - $('').hide().appendTo(this.ui.form).click().remove(); - return; - } - - let view = this; - let data = this.ui.form.serializeJSON(); - - // Manipulate - data.hsts_enabled = !!data.hsts_enabled; - data.hsts_subdomains = !!data.hsts_subdomains; - data.http2_support = !!data.http2_support; - data.ssl_forced = !!data.ssl_forced; - - if (typeof data.meta === 'undefined') data.meta = {}; - data.meta.letsencrypt_agree = data.meta.letsencrypt_agree == 1; - data.meta.dns_challenge = data.meta.dns_challenge == 1; - - if(!data.meta.dns_challenge){ - data.meta.dns_provider = undefined; - data.meta.dns_provider_credentials = undefined; - data.meta.propagation_seconds = undefined; - } else { - if(data.meta.propagation_seconds === '') data.meta.propagation_seconds = undefined; - } - - if (typeof data.domain_names === 'string' && data.domain_names) { - data.domain_names = data.domain_names.split(','); - } - - // Check for any domain names containing wildcards, which are not allowed with letsencrypt - if (data.certificate_id === 'new') { - let domain_err = false; - if (!data.meta.dns_challenge) { - data.domain_names.map(function (name) { - if (name.match(/\*/im)) { - domain_err = true; - } - }); - } - - if (domain_err) { - alert(i18n('ssl', 'no-wildcard-without-dns')); - return; - } - } else { - data.certificate_id = parseInt(data.certificate_id, 10); - } - - let method = App.Api.Nginx.DeadHosts.create; - let is_new = true; - - if (this.model.get('id')) { - // edit - is_new = false; - method = App.Api.Nginx.DeadHosts.update; - data.id = this.model.get('id'); - } - - this.ui.buttons.prop('disabled', true).addClass('btn-disabled'); - this.ui.save.addClass('btn-loading'); - - method(data) - .then(result => { - view.model.set(result); - - App.UI.closeModal(function () { - if (is_new) { - App.Controller.showNginxDead(); - } - }); - }) - .catch(err => { - let more_info = ''; - if(err.code === 500 && err.debug){ - try{ - more_info = JSON.parse(err.debug).debug.stack.join("\n"); - } catch(e) {} - } - this.ui.le_error_info[0].innerHTML = `${err.message}${more_info !== '' ? `
    ${more_info}
    `:''}`; - this.ui.le_error_info.show(); - this.ui.le_error_info[0].scrollIntoView(); - this.ui.buttons.prop('disabled', false).removeClass('btn-disabled'); - this.ui.save.removeClass('btn-loading'); - }); - } - }, - - templateContext: { - getLetsencryptEmail: function () { - return App.Cache.User.get('email'); - }, - getUseDnsChallenge: function () { - return typeof this.meta.dns_challenge !== 'undefined' ? this.meta.dns_challenge : false; - }, - getDnsProvider: function () { - return typeof this.meta.dns_provider !== 'undefined' && this.meta.dns_provider != '' ? this.meta.dns_provider : null; - }, - getDnsProviderCredentials: function () { - return typeof this.meta.dns_provider_credentials !== 'undefined' ? this.meta.dns_provider_credentials : ''; - }, - getPropagationSeconds: function () { - return typeof this.meta.propagation_seconds !== 'undefined' ? this.meta.propagation_seconds : ''; - }, - dns_plugins: dns_providers, - }, - - onRender: function () { - let view = this; - - // Domain names - this.ui.domain_names.selectize({ - delimiter: ',', - persist: false, - maxOptions: 15, - create: function (input) { - return { - value: input, - text: input - }; - }, - createFilter: /^(?:\*\.)?(?:[^.*]+\.?)+[^.]$/ - }); - - // Certificates - this.ui.le_error_info.hide(); - this.ui.dns_challenge_content.hide(); - this.ui.credentials_file_content.hide(); - this.ui.letsencrypt.hide(); - this.ui.certificate_select.selectize({ - valueField: 'id', - labelField: 'nice_name', - searchField: ['nice_name', 'domain_names'], - create: false, - preload: true, - allowEmptyOption: true, - render: { - option: function (item) { - item.i18n = App.i18n; - item.formatDbDate = Helpers.formatDbDate; - return certListItemTemplate(item); - } - }, - load: function (query, callback) { - App.Api.Nginx.Certificates.getAll() - .then(rows => { - callback(rows); - }) - .catch(err => { - console.error(err); - callback(); - }); - }, - onLoad: function () { - view.ui.certificate_select[0].selectize.setValue(view.model.get('certificate_id')); - } - }); - }, - - initialize: function (options) { - if (typeof options.model === 'undefined' || !options.model) { - this.model = new DeadHostModel.Model(); - } - } -}); diff --git a/frontend/js/app/nginx/dead/list/item.ejs b/frontend/js/app/nginx/dead/list/item.ejs deleted file mode 100644 index d447bd1e..00000000 --- a/frontend/js/app/nginx/dead/list/item.ejs +++ /dev/null @@ -1,54 +0,0 @@ - -
    - -
    - - -
    - <% domain_names.map(function(host) { - if (host.indexOf('*') === -1) { - %> - <%- host %> - <% - } else { - %> - <%- host %> - <% - } - }); - %> -
    -
    - <%- i18n('str', 'created-on', {date: formatDbDate(created_on, 'Do MMMM YYYY')}) %> -
    - - -
    <%- certificate ? i18n('ssl', certificate.provider) : i18n('ssl', 'none') %>
    - - - <% - var o = isOnline(); - if (!enabled) { %> - <%- i18n('str', 'disabled') %> - <% } else if (o === true) { %> - <%- i18n('str', 'online') %> - <% } else if (o === false) { %> - <%- i18n('str', 'offline') %> - <% } else { %> - <%- i18n('str', 'unknown') %> - <% } %> - -<% if (canManage) { %> - - - -<% } %> \ No newline at end of file diff --git a/frontend/js/app/nginx/dead/list/item.js b/frontend/js/app/nginx/dead/list/item.js deleted file mode 100644 index a477dbfa..00000000 --- a/frontend/js/app/nginx/dead/list/item.js +++ /dev/null @@ -1,61 +0,0 @@ -const Mn = require('backbone.marionette'); -const App = require('../../../main'); -const template = require('./item.ejs'); - -module.exports = Mn.View.extend({ - template: template, - tagName: 'tr', - - ui: { - able: 'a.able', - edit: 'a.edit', - delete: 'a.delete', - host_link: '.host-link' - }, - - events: { - 'click @ui.able': function (e) { - e.preventDefault(); - let id = this.model.get('id'); - App.Api.Nginx.DeadHosts[this.model.get('enabled') ? 'disable' : 'enable'](id) - .then(() => { - return App.Api.Nginx.DeadHosts.get(id) - .then(row => { - this.model.set(row); - }); - }); - }, - - 'click @ui.edit': function (e) { - e.preventDefault(); - App.Controller.showNginxDeadForm(this.model); - }, - - 'click @ui.delete': function (e) { - e.preventDefault(); - App.Controller.showNginxDeadDeleteConfirm(this.model); - }, - - 'click @ui.host_link': function (e) { - e.preventDefault(); - let win = window.open($(e.currentTarget).attr('rel'), '_blank'); - win.focus(); - } - }, - - templateContext: { - canManage: App.Cache.User.canManage('dead_hosts'), - - isOnline: function () { - return typeof this.meta.nginx_online === 'undefined' ? null : this.meta.nginx_online; - }, - - getOfflineError: function () { - return this.meta.nginx_err || ''; - } - }, - - initialize: function () { - this.listenTo(this.model, 'change', this.render); - } -}); diff --git a/frontend/js/app/nginx/dead/list/main.ejs b/frontend/js/app/nginx/dead/list/main.ejs deleted file mode 100644 index e018a74b..00000000 --- a/frontend/js/app/nginx/dead/list/main.ejs +++ /dev/null @@ -1,12 +0,0 @@ - -   - <%- i18n('str', 'source') %> - <%- i18n('str', 'ssl') %> - <%- i18n('str', 'status') %> - <% if (canManage) { %> -   - <% } %> - - - - diff --git a/frontend/js/app/nginx/dead/list/main.js b/frontend/js/app/nginx/dead/list/main.js deleted file mode 100644 index 57931419..00000000 --- a/frontend/js/app/nginx/dead/list/main.js +++ /dev/null @@ -1,32 +0,0 @@ -const Mn = require('backbone.marionette'); -const App = require('../../../main'); -const ItemView = require('./item'); -const template = require('./main.ejs'); - -const TableBody = Mn.CollectionView.extend({ - tagName: 'tbody', - childView: ItemView -}); - -module.exports = Mn.View.extend({ - tagName: 'table', - className: 'table table-hover table-outline table-vcenter card-table', - template: template, - - regions: { - body: { - el: 'tbody', - replaceElement: true - } - }, - - templateContext: { - canManage: App.Cache.User.canManage('dead_hosts') - }, - - onRender: function () { - this.showChildView('body', new TableBody({ - collection: this.collection - })); - } -}); diff --git a/frontend/js/app/nginx/dead/main.ejs b/frontend/js/app/nginx/dead/main.ejs deleted file mode 100644 index 508280ae..00000000 --- a/frontend/js/app/nginx/dead/main.ejs +++ /dev/null @@ -1,20 +0,0 @@ -
    -
    -
    -

    <%- i18n('dead-hosts', 'title') %>

    -
    - - <% if (showAddButton) { %> - <%- i18n('dead-hosts', 'add') %> - <% } %> -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    diff --git a/frontend/js/app/nginx/dead/main.js b/frontend/js/app/nginx/dead/main.js deleted file mode 100644 index ac3cb7f1..00000000 --- a/frontend/js/app/nginx/dead/main.js +++ /dev/null @@ -1,81 +0,0 @@ -const Mn = require('backbone.marionette'); -const App = require('../../main'); -const DeadHostModel = require('../../../models/dead-host'); -const ListView = require('./list/main'); -const ErrorView = require('../../error/main'); -const EmptyView = require('../../empty/main'); -const template = require('./main.ejs'); - -module.exports = Mn.View.extend({ - id: 'nginx-dead', - template: template, - - ui: { - list_region: '.list-region', - add: '.add-item', - help: '.help', - dimmer: '.dimmer' - }, - - regions: { - list_region: '@ui.list_region' - }, - - events: { - 'click @ui.add': function (e) { - e.preventDefault(); - App.Controller.showNginxDeadForm(); - }, - - 'click @ui.help': function (e) { - e.preventDefault(); - App.Controller.showHelp(App.i18n('dead-hosts', 'help-title'), App.i18n('dead-hosts', 'help-content')); - } - }, - - templateContext: { - showAddButton: App.Cache.User.canManage('dead_hosts') - }, - - onRender: function () { - let view = this; - - App.Api.Nginx.DeadHosts.getAll(['owner', 'certificate']) - .then(response => { - if (!view.isDestroyed()) { - if (response && response.length) { - view.showChildView('list_region', new ListView({ - collection: new DeadHostModel.Collection(response) - })); - } else { - let manage = App.Cache.User.canManage('dead_hosts'); - - view.showChildView('list_region', new EmptyView({ - title: App.i18n('dead-hosts', 'empty'), - subtitle: App.i18n('all-hosts', 'empty-subtitle', {manage: manage}), - link: manage ? App.i18n('dead-hosts', 'add') : null, - btn_color: 'danger', - permission: 'dead_hosts', - action: function () { - App.Controller.showNginxDeadForm(); - } - })); - } - } - }) - .catch(err => { - view.showChildView('list_region', new ErrorView({ - code: err.code, - message: err.message, - retry: function () { - App.Controller.showNginxDead(); - } - })); - - console.error(err); - }) - .then(() => { - view.ui.dimmer.removeClass('active'); - }); - } -}); diff --git a/frontend/js/app/nginx/proxy/access-list-item.ejs b/frontend/js/app/nginx/proxy/access-list-item.ejs deleted file mode 100644 index e5a7e116..00000000 --- a/frontend/js/app/nginx/proxy/access-list-item.ejs +++ /dev/null @@ -1,13 +0,0 @@ -
    - <% if (id > 0) { %> -
    - <%- name %> -
    - <%- i18n('access-lists', 'item-count', {count: items.length || 0}) %>, <%- i18n('access-lists', 'client-count', {count: clients.length || 0}) %> – Created: <%- formatDbDate(created_on, 'Do MMMM YYYY, h:mm a') %> - <% } else { %> -
    - <%- i18n('access-lists', 'public') %> -
    - <%- i18n('access-lists', 'public-sub') %> - <% } %> -
    diff --git a/frontend/js/app/nginx/proxy/delete.ejs b/frontend/js/app/nginx/proxy/delete.ejs deleted file mode 100644 index 2fe099fa..00000000 --- a/frontend/js/app/nginx/proxy/delete.ejs +++ /dev/null @@ -1,23 +0,0 @@ - diff --git a/frontend/js/app/nginx/proxy/delete.js b/frontend/js/app/nginx/proxy/delete.js deleted file mode 100644 index 63a8e020..00000000 --- a/frontend/js/app/nginx/proxy/delete.js +++ /dev/null @@ -1,32 +0,0 @@ -const Mn = require('backbone.marionette'); -const App = require('../../main'); -const template = require('./delete.ejs'); - -module.exports = Mn.View.extend({ - template: template, - className: 'modal-dialog', - - ui: { - form: 'form', - buttons: '.modal-footer button', - cancel: 'button.cancel', - save: 'button.save' - }, - - events: { - - 'click @ui.save': function (e) { - e.preventDefault(); - - App.Api.Nginx.ProxyHosts.delete(this.model.get('id')) - .then(() => { - App.Controller.showNginxProxy(); - App.UI.closeModal(); - }) - .catch(err => { - alert(err.message); - this.ui.buttons.prop('disabled', false).removeClass('btn-disabled'); - }); - } - } -}); diff --git a/frontend/js/app/nginx/proxy/form.ejs b/frontend/js/app/nginx/proxy/form.ejs deleted file mode 100644 index 1a498301..00000000 --- a/frontend/js/app/nginx/proxy/form.ejs +++ /dev/null @@ -1,280 +0,0 @@ - diff --git a/frontend/js/app/nginx/proxy/form.js b/frontend/js/app/nginx/proxy/form.js deleted file mode 100644 index 8802b958..00000000 --- a/frontend/js/app/nginx/proxy/form.js +++ /dev/null @@ -1,369 +0,0 @@ -const Mn = require('backbone.marionette'); -const App = require('../../main'); -const ProxyHostModel = require('../../../models/proxy-host'); -const ProxyLocationModel = require('../../../models/proxy-host-location'); -const template = require('./form.ejs'); -const certListItemTemplate = require('../certificates-list-item.ejs'); -const accessListItemTemplate = require('./access-list-item.ejs'); -const CustomLocation = require('./location'); -const Helpers = require('../../../lib/helpers'); -const i18n = require('../../i18n'); -const dns_providers = require('../../../../../global/certbot-dns-plugins'); - - -require('jquery-serializejson'); -require('selectize'); - -module.exports = Mn.View.extend({ - template: template, - className: 'modal-dialog', - - locationsCollection: new ProxyLocationModel.Collection(), - - ui: { - form: 'form', - domain_names: 'input[name="domain_names"]', - forward_host: 'input[name="forward_host"]', - buttons: '.modal-footer button', - cancel: 'button.cancel', - save: 'button.save', - add_location_btn: 'button.add_location', - locations_container: '.locations_container', - le_error_info: '#le-error-info', - certificate_select: 'select[name="certificate_id"]', - access_list_select: 'select[name="access_list_id"]', - ssl_forced: 'input[name="ssl_forced"]', - hsts_enabled: 'input[name="hsts_enabled"]', - hsts_subdomains: 'input[name="hsts_subdomains"]', - http2_support: 'input[name="http2_support"]', - dns_challenge_switch: 'input[name="meta[dns_challenge]"]', - dns_challenge_content: '.dns-challenge', - dns_provider: 'select[name="meta[dns_provider]"]', - credentials_file_content: '.credentials-file-content', - dns_provider_credentials: 'textarea[name="meta[dns_provider_credentials]"]', - propagation_seconds: 'input[name="meta[propagation_seconds]"]', - forward_scheme: 'select[name="forward_scheme"]', - letsencrypt: '.letsencrypt' - }, - - regions: { - locations_regions: '@ui.locations_container' - }, - - events: { - 'change @ui.certificate_select': function () { - let id = this.ui.certificate_select.val(); - if (id === 'new') { - this.ui.letsencrypt.show().find('input').prop('disabled', false); - this.ui.dns_challenge_content.hide(); - } else { - this.ui.letsencrypt.hide().find('input').prop('disabled', true); - } - - let enabled = id === 'new' || parseInt(id, 10) > 0; - - let inputs = this.ui.ssl_forced.add(this.ui.http2_support); - inputs - .prop('disabled', !enabled) - .parents('.form-group') - .css('opacity', enabled ? 1 : 0.5); - - if (!enabled) { - inputs.prop('checked', false); - } - - inputs.trigger('change'); - }, - - 'change @ui.ssl_forced': function () { - let checked = this.ui.ssl_forced.prop('checked'); - this.ui.hsts_enabled - .prop('disabled', !checked) - .parents('.form-group') - .css('opacity', checked ? 1 : 0.5); - - if (!checked) { - this.ui.hsts_enabled.prop('checked', false); - } - - this.ui.hsts_enabled.trigger('change'); - }, - - 'change @ui.hsts_enabled': function () { - let checked = this.ui.hsts_enabled.prop('checked'); - this.ui.hsts_subdomains - .prop('disabled', !checked) - .parents('.form-group') - .css('opacity', checked ? 1 : 0.5); - - if (!checked) { - this.ui.hsts_subdomains.prop('checked', false); - } - }, - - 'change @ui.dns_challenge_switch': function () { - const checked = this.ui.dns_challenge_switch.prop('checked'); - if (checked) { - this.ui.dns_provider.prop('required', 'required'); - const selected_provider = this.ui.dns_provider[0].options[this.ui.dns_provider[0].selectedIndex].value; - if(selected_provider != '' && dns_providers[selected_provider].credentials !== false){ - this.ui.dns_provider_credentials.prop('required', 'required'); - } - this.ui.dns_challenge_content.show(); - } else { - this.ui.dns_provider.prop('required', false); - this.ui.dns_provider_credentials.prop('required', false); - this.ui.dns_challenge_content.hide(); - } - }, - - 'change @ui.dns_provider': function () { - const selected_provider = this.ui.dns_provider[0].options[this.ui.dns_provider[0].selectedIndex].value; - if (selected_provider != '' && dns_providers[selected_provider].credentials !== false) { - this.ui.dns_provider_credentials.prop('required', 'required'); - this.ui.dns_provider_credentials[0].value = dns_providers[selected_provider].credentials; - this.ui.credentials_file_content.show(); - } else { - this.ui.dns_provider_credentials.prop('required', false); - this.ui.credentials_file_content.hide(); - } - }, - - 'click @ui.add_location_btn': function (e) { - e.preventDefault(); - - const model = new ProxyLocationModel.Model(); - this.locationsCollection.add(model); - }, - - 'click @ui.save': function (e) { - e.preventDefault(); - this.ui.le_error_info.hide(); - - if (!this.ui.form[0].checkValidity()) { - $('').hide().appendTo(this.ui.form).click().remove(); - return; - } - - let view = this; - let data = this.ui.form.serializeJSON(); - - // Add locations - data.locations = []; - this.locationsCollection.models.forEach((location) => { - data.locations.push(location.toJSON()); - }); - - // Serialize collects path from custom locations - // This field must be removed from root object - delete data.path; - - // Manipulate - data.forward_port = parseInt(data.forward_port, 10); - data.block_exploits = !!data.block_exploits; - data.caching_enabled = !!data.caching_enabled; - data.allow_websocket_upgrade = !!data.allow_websocket_upgrade; - data.http2_support = !!data.http2_support; - data.hsts_enabled = !!data.hsts_enabled; - data.hsts_subdomains = !!data.hsts_subdomains; - data.ssl_forced = !!data.ssl_forced; - - if (typeof data.meta === 'undefined') data.meta = {}; - data.meta.letsencrypt_agree = data.meta.letsencrypt_agree == 1; - data.meta.dns_challenge = data.meta.dns_challenge == 1; - - if(!data.meta.dns_challenge){ - data.meta.dns_provider = undefined; - data.meta.dns_provider_credentials = undefined; - data.meta.propagation_seconds = undefined; - } else { - if(data.meta.propagation_seconds === '') data.meta.propagation_seconds = undefined; - } - - if (typeof data.domain_names === 'string' && data.domain_names) { - data.domain_names = data.domain_names.split(','); - } - - // Check for any domain names containing wildcards, which are not allowed with letsencrypt - if (data.certificate_id === 'new') { - let domain_err = false; - if (!data.meta.dns_challenge) { - data.domain_names.map(function (name) { - if (name.match(/\*/im)) { - domain_err = true; - } - }); - } - - if (domain_err) { - alert(i18n('ssl', 'no-wildcard-without-dns')); - return; - } - } else { - data.certificate_id = parseInt(data.certificate_id, 10); - } - - let method = App.Api.Nginx.ProxyHosts.create; - let is_new = true; - - if (this.model.get('id')) { - // edit - is_new = false; - method = App.Api.Nginx.ProxyHosts.update; - data.id = this.model.get('id'); - } - - this.ui.buttons.prop('disabled', true).addClass('btn-disabled'); - this.ui.save.addClass('btn-loading'); - - method(data) - .then(result => { - view.model.set(result); - - App.UI.closeModal(function () { - if (is_new) { - App.Controller.showNginxProxy(); - } - }); - }) - .catch(err => { - let more_info = ''; - if(err.code === 500 && err.debug){ - try{ - more_info = JSON.parse(err.debug).debug.stack.join("\n"); - } catch(e) {} - } - this.ui.le_error_info[0].innerHTML = `${err.message}${more_info !== '' ? `
    ${more_info}
    `:''}`; - this.ui.le_error_info.show(); - this.ui.le_error_info[0].scrollIntoView(); - this.ui.buttons.prop('disabled', false).removeClass('btn-disabled'); - this.ui.save.removeClass('btn-loading'); - }); - } - }, - - templateContext: { - getLetsencryptEmail: function () { - return App.Cache.User.get('email'); - }, - getUseDnsChallenge: function () { - return typeof this.meta.dns_challenge !== 'undefined' ? this.meta.dns_challenge : false; - }, - getDnsProvider: function () { - return typeof this.meta.dns_provider !== 'undefined' && this.meta.dns_provider != '' ? this.meta.dns_provider : null; - }, - getDnsProviderCredentials: function () { - return typeof this.meta.dns_provider_credentials !== 'undefined' ? this.meta.dns_provider_credentials : ''; - }, - getPropagationSeconds: function () { - return typeof this.meta.propagation_seconds !== 'undefined' ? this.meta.propagation_seconds : ''; - }, - dns_plugins: dns_providers, - }, - - onRender: function () { - let view = this; - - this.ui.ssl_forced.trigger('change'); - this.ui.hsts_enabled.trigger('change'); - - // Domain names - this.ui.domain_names.selectize({ - delimiter: ',', - persist: false, - maxOptions: 15, - create: function (input) { - return { - value: input, - text: input - }; - }, - createFilter: /^(?:\.)?(?:[^.*]+\.?)+[^.]$/ - }); - - // Access Lists - this.ui.access_list_select.selectize({ - valueField: 'id', - labelField: 'name', - searchField: ['name'], - create: false, - preload: true, - allowEmptyOption: true, - render: { - option: function (item) { - item.i18n = App.i18n; - item.formatDbDate = Helpers.formatDbDate; - return accessListItemTemplate(item); - } - }, - load: function (query, callback) { - App.Api.Nginx.AccessLists.getAll(['items', 'clients']) - .then(rows => { - callback(rows); - }) - .catch(err => { - console.error(err); - callback(); - }); - }, - onLoad: function () { - view.ui.access_list_select[0].selectize.setValue(view.model.get('access_list_id')); - } - }); - - // Certificates - this.ui.le_error_info.hide(); - this.ui.dns_challenge_content.hide(); - this.ui.credentials_file_content.hide(); - this.ui.letsencrypt.hide(); - this.ui.certificate_select.selectize({ - valueField: 'id', - labelField: 'nice_name', - searchField: ['nice_name', 'domain_names'], - create: false, - preload: true, - allowEmptyOption: true, - render: { - option: function (item) { - item.i18n = App.i18n; - item.formatDbDate = Helpers.formatDbDate; - return certListItemTemplate(item); - } - }, - load: function (query, callback) { - App.Api.Nginx.Certificates.getAll() - .then(rows => { - callback(rows); - }) - .catch(err => { - console.error(err); - callback(); - }); - }, - onLoad: function () { - view.ui.certificate_select[0].selectize.setValue(view.model.get('certificate_id')); - } - }); - }, - - initialize: function (options) { - if (typeof options.model === 'undefined' || !options.model) { - this.model = new ProxyHostModel.Model(); - } - - this.locationsCollection = new ProxyLocationModel.Collection(); - - // Custom locations - this.showChildView('locations_regions', new CustomLocation.LocationCollectionView({ - collection: this.locationsCollection - })); - - // Check wether there are any location defined - if (options.model && Array.isArray(options.model.attributes.locations)) { - options.model.attributes.locations.forEach((location) => { - let m = new ProxyLocationModel.Model(location); - this.locationsCollection.add(m); - }); - } - } -}); diff --git a/frontend/js/app/nginx/proxy/list/item.ejs b/frontend/js/app/nginx/proxy/list/item.ejs deleted file mode 100644 index a5936804..00000000 --- a/frontend/js/app/nginx/proxy/list/item.ejs +++ /dev/null @@ -1,60 +0,0 @@ - -
    - -
    - - -
    - <% domain_names.map(function(host) { - if (host.indexOf('*') === -1) { - %> - <%- host %> - <% - } else { - %> - <%- host %> - <% - } - }); - %> -
    -
    - <%- i18n('str', 'created-on', {date: formatDbDate(created_on, 'Do MMMM YYYY')}) %> -
    - - -
    <%- forward_scheme %>://<%- forward_host %>:<%- forward_port %>
    - - -
    <%- certificate && certificate_id ? i18n('ssl', certificate.provider) : i18n('ssl', 'none') %>
    - - -
    <%- access_list_id ? access_list.name : i18n('str', 'public') %>
    - - - <% - var o = isOnline(); - if (!enabled) { %> - <%- i18n('str', 'disabled') %> - <% } else if (o === true) { %> - <%- i18n('str', 'online') %> - <% } else if (o === false) { %> - <%- i18n('str', 'offline') %> - <% } else { %> - <%- i18n('str', 'unknown') %> - <% } %> - -<% if (canManage) { %> - - - -<% } %> \ No newline at end of file diff --git a/frontend/js/app/nginx/proxy/list/item.js b/frontend/js/app/nginx/proxy/list/item.js deleted file mode 100644 index 37d199b4..00000000 --- a/frontend/js/app/nginx/proxy/list/item.js +++ /dev/null @@ -1,61 +0,0 @@ -const Mn = require('backbone.marionette'); -const App = require('../../../main'); -const template = require('./item.ejs'); - -module.exports = Mn.View.extend({ - template: template, - tagName: 'tr', - - ui: { - able: 'a.able', - edit: 'a.edit', - delete: 'a.delete', - host_link: '.host-link' - }, - - events: { - 'click @ui.able': function (e) { - e.preventDefault(); - let id = this.model.get('id'); - App.Api.Nginx.ProxyHosts[this.model.get('enabled') ? 'disable' : 'enable'](id) - .then(() => { - return App.Api.Nginx.ProxyHosts.get(id) - .then(row => { - this.model.set(row); - }); - }); - }, - - 'click @ui.edit': function (e) { - e.preventDefault(); - App.Controller.showNginxProxyForm(this.model); - }, - - 'click @ui.delete': function (e) { - e.preventDefault(); - App.Controller.showNginxProxyDeleteConfirm(this.model); - }, - - 'click @ui.host_link': function (e) { - e.preventDefault(); - let win = window.open($(e.currentTarget).attr('rel'), '_blank'); - win.focus(); - } - }, - - templateContext: { - canManage: App.Cache.User.canManage('proxy_hosts'), - - isOnline: function () { - return typeof this.meta.nginx_online === 'undefined' ? null : this.meta.nginx_online; - }, - - getOfflineError: function () { - return this.meta.nginx_err || ''; - } - }, - - initialize: function () { - this.listenTo(this.model, 'change', this.render); - } -}); diff --git a/frontend/js/app/nginx/proxy/list/main.ejs b/frontend/js/app/nginx/proxy/list/main.ejs deleted file mode 100644 index 6de5b9c6..00000000 --- a/frontend/js/app/nginx/proxy/list/main.ejs +++ /dev/null @@ -1,14 +0,0 @@ - -   - <%- i18n('str', 'source') %> - <%- i18n('str', 'destination') %> - <%- i18n('str', 'ssl') %> - <%- i18n('str', 'access') %> - <%- i18n('str', 'status') %> - <% if (canManage) { %> -   - <% } %> - - - - diff --git a/frontend/js/app/nginx/proxy/list/main.js b/frontend/js/app/nginx/proxy/list/main.js deleted file mode 100644 index 09e984e6..00000000 --- a/frontend/js/app/nginx/proxy/list/main.js +++ /dev/null @@ -1,32 +0,0 @@ -const Mn = require('backbone.marionette'); -const App = require('../../../main'); -const ItemView = require('./item'); -const template = require('./main.ejs'); - -const TableBody = Mn.CollectionView.extend({ - tagName: 'tbody', - childView: ItemView -}); - -module.exports = Mn.View.extend({ - tagName: 'table', - className: 'table table-hover table-outline table-vcenter card-table', - template: template, - - regions: { - body: { - el: 'tbody', - replaceElement: true - } - }, - - templateContext: { - canManage: App.Cache.User.canManage('proxy_hosts') - }, - - onRender: function () { - this.showChildView('body', new TableBody({ - collection: this.collection - })); - } -}); diff --git a/frontend/js/app/nginx/proxy/location-item.ejs b/frontend/js/app/nginx/proxy/location-item.ejs deleted file mode 100644 index 39445f7b..00000000 --- a/frontend/js/app/nginx/proxy/location-item.ejs +++ /dev/null @@ -1,64 +0,0 @@ -
    -
    -
    -
    -
    - -
    -
    -
    - - location - - -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    - - -
    -
    -
    -
    - - - <%- i18n('proxy-hosts', 'custom-forward-host-help') %> -
    -
    -
    -
    - - -
    -
    -
    -
    -
    -
    - -
    -
    -
    - - - <%- i18n('locations', 'delete') %> - -
    -
    diff --git a/frontend/js/app/nginx/proxy/location.js b/frontend/js/app/nginx/proxy/location.js deleted file mode 100644 index e9513a48..00000000 --- a/frontend/js/app/nginx/proxy/location.js +++ /dev/null @@ -1,54 +0,0 @@ -const locationItemTemplate = require('./location-item.ejs'); -const Mn = require('backbone.marionette'); -const App = require('../../main'); - -const LocationView = Mn.View.extend({ - template: locationItemTemplate, - className: 'location_block', - - ui: { - toggle: 'input[type="checkbox"]', - config: '.config', - delete: '.location-delete' - }, - - events: { - 'change @ui.toggle': function(el) { - if (el.target.checked) { - this.ui.config.show(); - } else { - this.ui.config.hide(); - } - }, - - 'change .model': function (e) { - const map = {}; - map[e.target.name] = e.target.value; - this.model.set(map); - }, - - 'click @ui.delete': function () { - this.model.destroy(); - } - }, - - onRender: function() { - $(this.ui.config).hide(); - }, - - templateContext: function() { - return { - i18n: App.i18n - } - } -}); - -const LocationCollectionView = Mn.CollectionView.extend({ - className: 'locations_container', - childView: LocationView -}); - -module.exports = { - LocationCollectionView, - LocationView -} \ No newline at end of file diff --git a/frontend/js/app/nginx/proxy/main.ejs b/frontend/js/app/nginx/proxy/main.ejs deleted file mode 100644 index a5114de6..00000000 --- a/frontend/js/app/nginx/proxy/main.ejs +++ /dev/null @@ -1,20 +0,0 @@ -
    -
    -
    -

    <%- i18n('proxy-hosts', 'title') %>

    -
    - - <% if (showAddButton) { %> - <%- i18n('proxy-hosts', 'add') %> - <% } %> -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    diff --git a/frontend/js/app/nginx/proxy/main.js b/frontend/js/app/nginx/proxy/main.js deleted file mode 100644 index 83cedfba..00000000 --- a/frontend/js/app/nginx/proxy/main.js +++ /dev/null @@ -1,81 +0,0 @@ -const Mn = require('backbone.marionette'); -const App = require('../../main'); -const ProxyHostModel = require('../../../models/proxy-host'); -const ListView = require('./list/main'); -const ErrorView = require('../../error/main'); -const EmptyView = require('../../empty/main'); -const template = require('./main.ejs'); - -module.exports = Mn.View.extend({ - id: 'nginx-proxy', - template: template, - - ui: { - list_region: '.list-region', - add: '.add-item', - help: '.help', - dimmer: '.dimmer' - }, - - regions: { - list_region: '@ui.list_region' - }, - - events: { - 'click @ui.add': function (e) { - e.preventDefault(); - App.Controller.showNginxProxyForm(); - }, - - 'click @ui.help': function (e) { - e.preventDefault(); - App.Controller.showHelp(App.i18n('proxy-hosts', 'help-title'), App.i18n('proxy-hosts', 'help-content')); - } - }, - - templateContext: { - showAddButton: App.Cache.User.canManage('proxy_hosts') - }, - - onRender: function () { - let view = this; - - App.Api.Nginx.ProxyHosts.getAll(['owner', 'access_list', 'certificate']) - .then(response => { - if (!view.isDestroyed()) { - if (response && response.length) { - view.showChildView('list_region', new ListView({ - collection: new ProxyHostModel.Collection(response) - })); - } else { - let manage = App.Cache.User.canManage('proxy_hosts'); - - view.showChildView('list_region', new EmptyView({ - title: App.i18n('proxy-hosts', 'empty'), - subtitle: App.i18n('all-hosts', 'empty-subtitle', {manage: manage}), - link: manage ? App.i18n('proxy-hosts', 'add') : null, - btn_color: 'success', - permission: 'proxy_hosts', - action: function () { - App.Controller.showNginxProxyForm(); - } - })); - } - } - }) - .catch(err => { - view.showChildView('list_region', new ErrorView({ - code: err.code, - message: err.message, - retry: function () { - App.Controller.showNginxProxy(); - } - })); - - console.error(err); - }) - .then(() => { - view.ui.dimmer.removeClass('active'); - }); - } -}); diff --git a/frontend/js/app/nginx/redirection/delete.ejs b/frontend/js/app/nginx/redirection/delete.ejs deleted file mode 100644 index 8353d1b7..00000000 --- a/frontend/js/app/nginx/redirection/delete.ejs +++ /dev/null @@ -1,23 +0,0 @@ - diff --git a/frontend/js/app/nginx/redirection/delete.js b/frontend/js/app/nginx/redirection/delete.js deleted file mode 100644 index 6d2862f6..00000000 --- a/frontend/js/app/nginx/redirection/delete.js +++ /dev/null @@ -1,32 +0,0 @@ -const Mn = require('backbone.marionette'); -const App = require('../../main'); -const template = require('./delete.ejs'); - -module.exports = Mn.View.extend({ - template: template, - className: 'modal-dialog', - - ui: { - form: 'form', - buttons: '.modal-footer button', - cancel: 'button.cancel', - save: 'button.save' - }, - - events: { - - 'click @ui.save': function (e) { - e.preventDefault(); - - App.Api.Nginx.RedirectionHosts.delete(this.model.get('id')) - .then(() => { - App.Controller.showNginxRedirection(); - App.UI.closeModal(); - }) - .catch(err => { - alert(err.message); - this.ui.buttons.prop('disabled', false).removeClass('btn-disabled'); - }); - } - } -}); diff --git a/frontend/js/app/nginx/redirection/form.ejs b/frontend/js/app/nginx/redirection/form.ejs deleted file mode 100644 index 7e190719..00000000 --- a/frontend/js/app/nginx/redirection/form.ejs +++ /dev/null @@ -1,253 +0,0 @@ - diff --git a/frontend/js/app/nginx/redirection/form.js b/frontend/js/app/nginx/redirection/form.js deleted file mode 100644 index 1f81feeb..00000000 --- a/frontend/js/app/nginx/redirection/form.js +++ /dev/null @@ -1,288 +0,0 @@ -const Mn = require('backbone.marionette'); -const App = require('../../main'); -const RedirectionHostModel = require('../../../models/redirection-host'); -const template = require('./form.ejs'); -const certListItemTemplate = require('../certificates-list-item.ejs'); -const Helpers = require('../../../lib/helpers'); -const i18n = require('../../i18n'); -const dns_providers = require('../../../../../global/certbot-dns-plugins'); - - -require('jquery-serializejson'); -require('selectize'); - -module.exports = Mn.View.extend({ - template: template, - className: 'modal-dialog', - - ui: { - form: 'form', - domain_names: 'input[name="domain_names"]', - buttons: '.modal-footer button', - cancel: 'button.cancel', - save: 'button.save', - le_error_info: '#le-error-info', - certificate_select: 'select[name="certificate_id"]', - ssl_forced: 'input[name="ssl_forced"]', - hsts_enabled: 'input[name="hsts_enabled"]', - hsts_subdomains: 'input[name="hsts_subdomains"]', - http2_support: 'input[name="http2_support"]', - dns_challenge_switch: 'input[name="meta[dns_challenge]"]', - dns_challenge_content: '.dns-challenge', - dns_provider: 'select[name="meta[dns_provider]"]', - credentials_file_content: '.credentials-file-content', - dns_provider_credentials: 'textarea[name="meta[dns_provider_credentials]"]', - propagation_seconds: 'input[name="meta[propagation_seconds]"]', - letsencrypt: '.letsencrypt' - }, - - events: { - 'change @ui.certificate_select': function () { - let id = this.ui.certificate_select.val(); - if (id === 'new') { - this.ui.letsencrypt.show().find('input').prop('disabled', false); - this.ui.dns_challenge_content.hide(); - } else { - this.ui.letsencrypt.hide().find('input').prop('disabled', true); - } - - let enabled = id === 'new' || parseInt(id, 10) > 0; - - let inputs = this.ui.ssl_forced.add(this.ui.http2_support); - inputs - .prop('disabled', !enabled) - .parents('.form-group') - .css('opacity', enabled ? 1 : 0.5); - - if (!enabled) { - inputs.prop('checked', false); - } - - inputs.trigger('change'); - }, - - 'change @ui.ssl_forced': function () { - let checked = this.ui.ssl_forced.prop('checked'); - this.ui.hsts_enabled - .prop('disabled', !checked) - .parents('.form-group') - .css('opacity', checked ? 1 : 0.5); - - if (!checked) { - this.ui.hsts_enabled.prop('checked', false); - } - - this.ui.hsts_enabled.trigger('change'); - }, - - 'change @ui.hsts_enabled': function () { - let checked = this.ui.hsts_enabled.prop('checked'); - this.ui.hsts_subdomains - .prop('disabled', !checked) - .parents('.form-group') - .css('opacity', checked ? 1 : 0.5); - - if (!checked) { - this.ui.hsts_subdomains.prop('checked', false); - } - }, - - 'change @ui.dns_challenge_switch': function () { - const checked = this.ui.dns_challenge_switch.prop('checked'); - if (checked) { - this.ui.dns_provider.prop('required', 'required'); - const selected_provider = this.ui.dns_provider[0].options[this.ui.dns_provider[0].selectedIndex].value; - if(selected_provider != '' && dns_providers[selected_provider].credentials !== false){ - this.ui.dns_provider_credentials.prop('required', 'required'); - } - this.ui.dns_challenge_content.show(); - } else { - this.ui.dns_provider.prop('required', false); - this.ui.dns_provider_credentials.prop('required', false); - this.ui.dns_challenge_content.hide(); - } - }, - - 'change @ui.dns_provider': function () { - const selected_provider = this.ui.dns_provider[0].options[this.ui.dns_provider[0].selectedIndex].value; - if (selected_provider != '' && dns_providers[selected_provider].credentials !== false) { - this.ui.dns_provider_credentials.prop('required', 'required'); - this.ui.dns_provider_credentials[0].value = dns_providers[selected_provider].credentials; - this.ui.credentials_file_content.show(); - } else { - this.ui.dns_provider_credentials.prop('required', false); - this.ui.credentials_file_content.hide(); - } - }, - - 'click @ui.save': function (e) { - e.preventDefault(); - this.ui.le_error_info.hide(); - - if (!this.ui.form[0].checkValidity()) { - $('').hide().appendTo(this.ui.form).click().remove(); - return; - } - - let view = this; - let data = this.ui.form.serializeJSON(); - - // Manipulate - data.block_exploits = !!data.block_exploits; - data.preserve_path = !!data.preserve_path; - data.http2_support = !!data.http2_support; - data.hsts_enabled = !!data.hsts_enabled; - data.hsts_subdomains = !!data.hsts_subdomains; - data.ssl_forced = !!data.ssl_forced; - - if (typeof data.meta === 'undefined') data.meta = {}; - data.meta.letsencrypt_agree = data.meta.letsencrypt_agree == 1; - data.meta.dns_challenge = data.meta.dns_challenge == 1; - - if(!data.meta.dns_challenge){ - data.meta.dns_provider = undefined; - data.meta.dns_provider_credentials = undefined; - data.meta.propagation_seconds = undefined; - } else { - if(data.meta.propagation_seconds === '') data.meta.propagation_seconds = undefined; - } - - if (typeof data.domain_names === 'string' && data.domain_names) { - data.domain_names = data.domain_names.split(','); - } - - // Check for any domain names containing wildcards, which are not allowed with letsencrypt - if (data.certificate_id === 'new') { - let domain_err = false; - if (!data.meta.dns_challenge) { - data.domain_names.map(function (name) { - if (name.match(/\*/im)) { - domain_err = true; - } - }); - } - - if (domain_err) { - alert(i18n('ssl', 'no-wildcard-without-dns')); - return; - } - } else { - data.certificate_id = parseInt(data.certificate_id, 10); - } - - let method = App.Api.Nginx.RedirectionHosts.create; - let is_new = true; - - if (this.model.get('id')) { - // edit - is_new = false; - method = App.Api.Nginx.RedirectionHosts.update; - data.id = this.model.get('id'); - } - - this.ui.buttons.prop('disabled', true).addClass('btn-disabled'); - this.ui.save.addClass('btn-loading'); - - method(data) - .then(result => { - view.model.set(result); - - App.UI.closeModal(function () { - if (is_new) { - App.Controller.showNginxRedirection(); - } - }); - }) - .catch(err => { - let more_info = ''; - if(err.code === 500 && err.debug){ - try{ - more_info = JSON.parse(err.debug).debug.stack.join("\n"); - } catch(e) {} - } - this.ui.le_error_info[0].innerHTML = `${err.message}${more_info !== '' ? `
    ${more_info}
    `:''}`; - this.ui.le_error_info.show(); - this.ui.le_error_info[0].scrollIntoView(); - this.ui.buttons.prop('disabled', false).removeClass('btn-disabled'); - this.ui.save.removeClass('btn-loading'); - }); - } - }, - - templateContext: { - getLetsencryptEmail: function () { - return App.Cache.User.get('email'); - }, - getUseDnsChallenge: function () { - return typeof this.meta.dns_challenge !== 'undefined' ? this.meta.dns_challenge : false; - }, - getDnsProvider: function () { - return typeof this.meta.dns_provider !== 'undefined' && this.meta.dns_provider != '' ? this.meta.dns_provider : null; - }, - getDnsProviderCredentials: function () { - return typeof this.meta.dns_provider_credentials !== 'undefined' ? this.meta.dns_provider_credentials : ''; - }, - getPropagationSeconds: function () { - return typeof this.meta.propagation_seconds !== 'undefined' ? this.meta.propagation_seconds : ''; - }, - dns_plugins: dns_providers, - }, - - onRender: function () { - let view = this; - - // Domain names - this.ui.domain_names.selectize({ - delimiter: ',', - persist: false, - maxOptions: 15, - create: function (input) { - return { - value: input, - text: input - }; - }, - createFilter: /^(?:\*\.)?(?:[^.*]+\.?)+[^.]$/ - }); - - // Certificates - this.ui.le_error_info.hide(); - this.ui.dns_challenge_content.hide(); - this.ui.credentials_file_content.hide(); - this.ui.letsencrypt.hide(); - this.ui.certificate_select.selectize({ - valueField: 'id', - labelField: 'nice_name', - searchField: ['nice_name', 'domain_names'], - create: false, - preload: true, - allowEmptyOption: true, - render: { - option: function (item) { - item.i18n = App.i18n; - item.formatDbDate = Helpers.formatDbDate; - return certListItemTemplate(item); - } - }, - load: function (query, callback) { - App.Api.Nginx.Certificates.getAll() - .then(rows => { - callback(rows); - }) - .catch(err => { - console.error(err); - callback(); - }); - }, - onLoad: function () { - view.ui.certificate_select[0].selectize.setValue(view.model.get('certificate_id')); - } - }); - }, - - initialize: function (options) { - if (typeof options.model === 'undefined' || !options.model) { - this.model = new RedirectionHostModel.Model(); - } - } -}); diff --git a/frontend/js/app/nginx/redirection/list/item.ejs b/frontend/js/app/nginx/redirection/list/item.ejs deleted file mode 100644 index 4f25d973..00000000 --- a/frontend/js/app/nginx/redirection/list/item.ejs +++ /dev/null @@ -1,63 +0,0 @@ - -
    - -
    - - -
    - <% domain_names.map(function(host) { - if (host.indexOf('*') === -1) { - %> - <%- host %> - <% - } else { - %> - <%- host %> - <% - } - }); - %> -
    -
    - <%- i18n('str', 'created-on', {date: formatDbDate(created_on, 'Do MMMM YYYY')}) %> -
    - - -
    <%- forward_http_code %>
    - - -
    <%- forward_scheme == '$scheme' ? 'auto' : forward_scheme %>
    - - -
    <%- forward_domain_name %>
    - - -
    <%- certificate ? i18n('ssl', certificate.provider) : i18n('ssl', 'none') %>
    - - - <% - var o = isOnline(); - if (!enabled) { %> - <%- i18n('str', 'disabled') %> - <% } else if (o === true) { %> - <%- i18n('str', 'online') %> - <% } else if (o === false) { %> - <%- i18n('str', 'offline') %> - <% } else { %> - <%- i18n('str', 'unknown') %> - <% } %> - -<% if (canManage) { %> - - - -<% } %> diff --git a/frontend/js/app/nginx/redirection/list/item.js b/frontend/js/app/nginx/redirection/list/item.js deleted file mode 100644 index 05adc251..00000000 --- a/frontend/js/app/nginx/redirection/list/item.js +++ /dev/null @@ -1,61 +0,0 @@ -const Mn = require('backbone.marionette'); -const App = require('../../../main'); -const template = require('./item.ejs'); - -module.exports = Mn.View.extend({ - template: template, - tagName: 'tr', - - ui: { - able: 'a.able', - edit: 'a.edit', - delete: 'a.delete', - host_link: '.host-link' - }, - - events: { - 'click @ui.able': function (e) { - e.preventDefault(); - let id = this.model.get('id'); - App.Api.Nginx.RedirectionHosts[this.model.get('enabled') ? 'disable' : 'enable'](id) - .then(() => { - return App.Api.Nginx.RedirectionHosts.get(id) - .then(row => { - this.model.set(row); - }); - }); - }, - - 'click @ui.edit': function (e) { - e.preventDefault(); - App.Controller.showNginxRedirectionForm(this.model); - }, - - 'click @ui.delete': function (e) { - e.preventDefault(); - App.Controller.showNginxRedirectionDeleteConfirm(this.model); - }, - - 'click @ui.host_link': function (e) { - e.preventDefault(); - let win = window.open($(e.currentTarget).attr('rel'), '_blank'); - win.focus(); - } - }, - - templateContext: { - canManage: App.Cache.User.canManage('redirection_hosts'), - - isOnline: function () { - return typeof this.meta.nginx_online === 'undefined' ? null : this.meta.nginx_online; - }, - - getOfflineError: function () { - return this.meta.nginx_err || ''; - } - }, - - initialize: function () { - this.listenTo(this.model, 'change', this.render); - } -}); diff --git a/frontend/js/app/nginx/redirection/list/main.ejs b/frontend/js/app/nginx/redirection/list/main.ejs deleted file mode 100644 index 8b6930d6..00000000 --- a/frontend/js/app/nginx/redirection/list/main.ejs +++ /dev/null @@ -1,15 +0,0 @@ - -   - <%- i18n('str', 'source') %> - <%- i18n('redirection-hosts', 'forward-http-status-code') %> - <%- i18n('redirection-hosts', 'forward-scheme') %> - <%- i18n('str', 'destination') %> - <%- i18n('str', 'ssl') %> - <%- i18n('str', 'status') %> - <% if (canManage) { %> -   - <% } %> - - - - diff --git a/frontend/js/app/nginx/redirection/list/main.js b/frontend/js/app/nginx/redirection/list/main.js deleted file mode 100644 index d368cf6a..00000000 --- a/frontend/js/app/nginx/redirection/list/main.js +++ /dev/null @@ -1,32 +0,0 @@ -const Mn = require('backbone.marionette'); -const App = require('../../../main'); -const ItemView = require('./item'); -const template = require('./main.ejs'); - -const TableBody = Mn.CollectionView.extend({ - tagName: 'tbody', - childView: ItemView -}); - -module.exports = Mn.View.extend({ - tagName: 'table', - className: 'table table-hover table-outline table-vcenter card-table', - template: template, - - regions: { - body: { - el: 'tbody', - replaceElement: true - } - }, - - templateContext: { - canManage: App.Cache.User.canManage('redirection_hosts') - }, - - onRender: function () { - this.showChildView('body', new TableBody({ - collection: this.collection - })); - } -}); diff --git a/frontend/js/app/nginx/redirection/main.ejs b/frontend/js/app/nginx/redirection/main.ejs deleted file mode 100644 index 4345a7e8..00000000 --- a/frontend/js/app/nginx/redirection/main.ejs +++ /dev/null @@ -1,20 +0,0 @@ -
    -
    -
    -

    Redirection Hosts

    -
    - - <% if (showAddButton) { %> - Add Redirection Host - <% } %> -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    diff --git a/frontend/js/app/nginx/redirection/main.js b/frontend/js/app/nginx/redirection/main.js deleted file mode 100644 index f45f9a07..00000000 --- a/frontend/js/app/nginx/redirection/main.js +++ /dev/null @@ -1,81 +0,0 @@ -const Mn = require('backbone.marionette'); -const App = require('../../main'); -const RedirectionHostModel = require('../../../models/redirection-host'); -const ListView = require('./list/main'); -const ErrorView = require('../../error/main'); -const EmptyView = require('../../empty/main'); -const template = require('./main.ejs'); - -module.exports = Mn.View.extend({ - id: 'nginx-redirection', - template: template, - - ui: { - list_region: '.list-region', - add: '.add-item', - help: '.help', - dimmer: '.dimmer' - }, - - regions: { - list_region: '@ui.list_region' - }, - - events: { - 'click @ui.add': function (e) { - e.preventDefault(); - App.Controller.showNginxRedirectionForm(); - }, - - 'click @ui.help': function (e) { - e.preventDefault(); - App.Controller.showHelp(App.i18n('redirection-hosts', 'help-title'), App.i18n('redirection-hosts', 'help-content')); - } - }, - - templateContext: { - showAddButton: App.Cache.User.canManage('proxy_hosts') - }, - - onRender: function () { - let view = this; - - App.Api.Nginx.RedirectionHosts.getAll(['owner', 'certificate']) - .then(response => { - if (!view.isDestroyed()) { - if (response && response.length) { - view.showChildView('list_region', new ListView({ - collection: new RedirectionHostModel.Collection(response) - })); - } else { - let manage = App.Cache.User.canManage('redirection_hosts'); - - view.showChildView('list_region', new EmptyView({ - title: App.i18n('redirection-hosts', 'empty'), - subtitle: App.i18n('all-hosts', 'empty-subtitle', {manage: manage}), - link: manage ? App.i18n('redirection-hosts', 'add') : null, - btn_color: 'yellow', - permission: 'redirection_hosts', - action: function () { - App.Controller.showNginxRedirectionForm(); - } - })); - } - } - }) - .catch(err => { - view.showChildView('list_region', new ErrorView({ - code: err.code, - message: err.message, - retry: function () { - App.Controller.showNginxRedirection(); - } - })); - - console.error(err); - }) - .then(() => { - view.ui.dimmer.removeClass('active'); - }); - } -}); diff --git a/frontend/js/app/nginx/stream/delete.ejs b/frontend/js/app/nginx/stream/delete.ejs deleted file mode 100644 index d7ba3a21..00000000 --- a/frontend/js/app/nginx/stream/delete.ejs +++ /dev/null @@ -1,19 +0,0 @@ - diff --git a/frontend/js/app/nginx/stream/delete.js b/frontend/js/app/nginx/stream/delete.js deleted file mode 100644 index 71eff18c..00000000 --- a/frontend/js/app/nginx/stream/delete.js +++ /dev/null @@ -1,32 +0,0 @@ -const Mn = require('backbone.marionette'); -const App = require('../../main'); -const template = require('./delete.ejs'); - -module.exports = Mn.View.extend({ - template: template, - className: 'modal-dialog', - - ui: { - form: 'form', - buttons: '.modal-footer button', - cancel: 'button.cancel', - save: 'button.save' - }, - - events: { - - 'click @ui.save': function (e) { - e.preventDefault(); - - App.Api.Nginx.Streams.delete(this.model.get('id')) - .then(() => { - App.Controller.showNginxStream(); - App.UI.closeModal(); - }) - .catch(err => { - alert(err.message); - this.ui.buttons.prop('disabled', false).removeClass('btn-disabled'); - }); - } - } -}); diff --git a/frontend/js/app/nginx/stream/form.ejs b/frontend/js/app/nginx/stream/form.ejs deleted file mode 100644 index b0a72e48..00000000 --- a/frontend/js/app/nginx/stream/form.ejs +++ /dev/null @@ -1,55 +0,0 @@ - diff --git a/frontend/js/app/nginx/stream/form.js b/frontend/js/app/nginx/stream/form.js deleted file mode 100644 index 2133c3da..00000000 --- a/frontend/js/app/nginx/stream/form.js +++ /dev/null @@ -1,91 +0,0 @@ -const Mn = require('backbone.marionette'); -const App = require('../../main'); -const StreamModel = require('../../../models/stream'); -const template = require('./form.ejs'); - -require('jquery-serializejson'); -require('jquery-mask-plugin'); -require('selectize'); - -module.exports = Mn.View.extend({ - template: template, - className: 'modal-dialog', - - ui: { - form: 'form', - forward_ip: 'input[name="forward_ip"]', - type_error: '.forward-type-error', - buttons: '.modal-footer button', - switches: '.custom-switch-input', - cancel: 'button.cancel', - save: 'button.save' - }, - - events: { - 'change @ui.switches': function () { - this.ui.type_error.hide(); - }, - - 'click @ui.save': function (e) { - e.preventDefault(); - - if (!this.ui.form[0].checkValidity()) { - $('').hide().appendTo(this.ui.form).click().remove(); - return; - } - - let view = this; - let data = this.ui.form.serializeJSON(); - - if (!data.tcp_forwarding && !data.udp_forwarding) { - this.ui.type_error.show(); - return; - } - - // Manipulate - data.incoming_port = parseInt(data.incoming_port, 10); - data.forwarding_port = parseInt(data.forwarding_port, 10); - data.tcp_forwarding = !!data.tcp_forwarding; - data.udp_forwarding = !!data.udp_forwarding; - - let method = App.Api.Nginx.Streams.create; - let is_new = true; - - if (this.model.get('id')) { - // edit - is_new = false; - method = App.Api.Nginx.Streams.update; - data.id = this.model.get('id'); - } - - this.ui.buttons.prop('disabled', true).addClass('btn-disabled'); - method(data) - .then(result => { - view.model.set(result); - - App.UI.closeModal(function () { - if (is_new) { - App.Controller.showNginxStream(); - } - }); - }) - .catch(err => { - alert(err.message); - this.ui.buttons.prop('disabled', false).removeClass('btn-disabled'); - }); - } - }, - - onRender: function () { - this.ui.forward_ip.mask('099.099.099.099', { - clearIfNotMatch: true, - placeholder: '000.000.000.000' - }); - }, - - initialize: function (options) { - if (typeof options.model === 'undefined' || !options.model) { - this.model = new StreamModel.Model(); - } - } -}); diff --git a/frontend/js/app/nginx/stream/list/item.ejs b/frontend/js/app/nginx/stream/list/item.ejs deleted file mode 100644 index 2c04667f..00000000 --- a/frontend/js/app/nginx/stream/list/item.ejs +++ /dev/null @@ -1,53 +0,0 @@ - -
    - -
    - - -
    - <%- incoming_port %> -
    -
    - <%- i18n('str', 'created-on', {date: formatDbDate(created_on, 'Do MMMM YYYY')}) %> -
    - - -
    <%- forward_ip %>:<%- forwarding_port %>
    - - -
    - <% if (tcp_forwarding) { %> - <%- i18n('streams', 'tcp') %> - <% } - if (udp_forwarding) { %> - <%- i18n('streams', 'udp') %> - <% } %> -
    - - - <% - var o = isOnline(); - if (!enabled) { %> - <%- i18n('str', 'disabled') %> - <% } else if (o === true) { %> - <%- i18n('str', 'online') %> - <% } else if (o === false) { %> - <%- i18n('str', 'offline') %> - <% } else { %> - <%- i18n('str', 'unknown') %> - <% } %> - -<% if (canManage) { %> - - - -<% } %> \ No newline at end of file diff --git a/frontend/js/app/nginx/stream/list/item.js b/frontend/js/app/nginx/stream/list/item.js deleted file mode 100644 index a6892ee2..00000000 --- a/frontend/js/app/nginx/stream/list/item.js +++ /dev/null @@ -1,54 +0,0 @@ -const Mn = require('backbone.marionette'); -const App = require('../../../main'); -const template = require('./item.ejs'); - -module.exports = Mn.View.extend({ - template: template, - tagName: 'tr', - - ui: { - able: 'a.able', - edit: 'a.edit', - delete: 'a.delete' - }, - - events: { - 'click @ui.able': function (e) { - e.preventDefault(); - let id = this.model.get('id'); - App.Api.Nginx.Streams[this.model.get('enabled') ? 'disable' : 'enable'](id) - .then(() => { - return App.Api.Nginx.Streams.get(id) - .then(row => { - this.model.set(row); - }); - }); - }, - - 'click @ui.edit': function (e) { - e.preventDefault(); - App.Controller.showNginxStreamForm(this.model); - }, - - 'click @ui.delete': function (e) { - e.preventDefault(); - App.Controller.showNginxStreamDeleteConfirm(this.model); - } - }, - - templateContext: { - canManage: App.Cache.User.canManage('streams'), - - isOnline: function () { - return typeof this.meta.nginx_online === 'undefined' ? null : this.meta.nginx_online; - }, - - getOfflineError: function () { - return this.meta.nginx_err || ''; - } - }, - - initialize: function () { - this.listenTo(this.model, 'change', this.render); - } -}); diff --git a/frontend/js/app/nginx/stream/list/main.ejs b/frontend/js/app/nginx/stream/list/main.ejs deleted file mode 100644 index 5304f614..00000000 --- a/frontend/js/app/nginx/stream/list/main.ejs +++ /dev/null @@ -1,13 +0,0 @@ - -   - <%- i18n('streams', 'incoming-port') %> - <%- i18n('str', 'destination') %> - <%- i18n('streams', 'protocol') %> - <%- i18n('str', 'status') %> - <% if (canManage) { %> -   - <% } %> - - - - diff --git a/frontend/js/app/nginx/stream/list/main.js b/frontend/js/app/nginx/stream/list/main.js deleted file mode 100644 index 36be621d..00000000 --- a/frontend/js/app/nginx/stream/list/main.js +++ /dev/null @@ -1,32 +0,0 @@ -const Mn = require('backbone.marionette'); -const App = require('../../../main'); -const ItemView = require('./item'); -const template = require('./main.ejs'); - -const TableBody = Mn.CollectionView.extend({ - tagName: 'tbody', - childView: ItemView -}); - -module.exports = Mn.View.extend({ - tagName: 'table', - className: 'table table-hover table-outline table-vcenter card-table', - template: template, - - regions: { - body: { - el: 'tbody', - replaceElement: true - } - }, - - templateContext: { - canManage: App.Cache.User.canManage('streams') - }, - - onRender: function () { - this.showChildView('body', new TableBody({ - collection: this.collection - })); - } -}); diff --git a/frontend/js/app/nginx/stream/main.ejs b/frontend/js/app/nginx/stream/main.ejs deleted file mode 100644 index c01414ce..00000000 --- a/frontend/js/app/nginx/stream/main.ejs +++ /dev/null @@ -1,20 +0,0 @@ -
    -
    -
    -

    <%- i18n('streams', 'title') %>

    -
    - - <% if (showAddButton) { %> - <%- i18n('streams', 'add') %> - <% } %> -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    diff --git a/frontend/js/app/nginx/stream/main.js b/frontend/js/app/nginx/stream/main.js deleted file mode 100644 index a8eda92c..00000000 --- a/frontend/js/app/nginx/stream/main.js +++ /dev/null @@ -1,81 +0,0 @@ -const Mn = require('backbone.marionette'); -const App = require('../../main'); -const StreamModel = require('../../../models/stream'); -const ListView = require('./list/main'); -const ErrorView = require('../../error/main'); -const EmptyView = require('../../empty/main'); -const template = require('./main.ejs'); - -module.exports = Mn.View.extend({ - id: 'nginx-stream', - template: template, - - ui: { - list_region: '.list-region', - add: '.add-item', - help: '.help', - dimmer: '.dimmer' - }, - - regions: { - list_region: '@ui.list_region' - }, - - events: { - 'click @ui.add': function (e) { - e.preventDefault(); - App.Controller.showNginxStreamForm(); - }, - - 'click @ui.help': function (e) { - e.preventDefault(); - App.Controller.showHelp(App.i18n('streams', 'help-title'), App.i18n('streams', 'help-content')); - } - }, - - templateContext: { - showAddButton: App.Cache.User.canManage('streams') - }, - - onRender: function () { - let view = this; - - App.Api.Nginx.Streams.getAll(['owner']) - .then(response => { - if (!view.isDestroyed()) { - if (response && response.length) { - view.showChildView('list_region', new ListView({ - collection: new StreamModel.Collection(response) - })); - } else { - let manage = App.Cache.User.canManage('streams'); - - view.showChildView('list_region', new EmptyView({ - title: App.i18n('streams', 'empty'), - subtitle: App.i18n('all-hosts', 'empty-subtitle', {manage: manage}), - link: manage ? App.i18n('streams', 'add') : null, - btn_color: 'blue', - permission: 'streams', - action: function () { - App.Controller.showNginxStreamForm(); - } - })); - } - } - }) - .catch(err => { - view.showChildView('list_region', new ErrorView({ - code: err.code, - message: err.message, - retry: function () { - App.Controller.showNginxStream(); - } - })); - - console.error(err); - }) - .then(() => { - view.ui.dimmer.removeClass('active'); - }); - } -}); diff --git a/frontend/js/app/router.js b/frontend/js/app/router.js deleted file mode 100644 index a036bfc5..00000000 --- a/frontend/js/app/router.js +++ /dev/null @@ -1,19 +0,0 @@ -const AppRouter = require('marionette.approuter'); -const Controller = require('./controller'); - -module.exports = AppRouter.default.extend({ - controller: Controller, - appRoutes: { - users: 'showUsers', - logout: 'logout', - 'nginx/proxy': 'showNginxProxy', - 'nginx/redirection': 'showNginxRedirection', - 'nginx/404': 'showNginxDead', - 'nginx/stream': 'showNginxStream', - 'nginx/access': 'showNginxAccess', - 'nginx/certificates': 'showNginxCertificates', - 'audit-log': 'showAuditLog', - 'settings': 'showSettings', - '*default': 'showDashboard' - } -}); diff --git a/frontend/js/app/settings/default-site/main.ejs b/frontend/js/app/settings/default-site/main.ejs deleted file mode 100644 index 126c9d0a..00000000 --- a/frontend/js/app/settings/default-site/main.ejs +++ /dev/null @@ -1,53 +0,0 @@ - diff --git a/frontend/js/app/settings/default-site/main.js b/frontend/js/app/settings/default-site/main.js deleted file mode 100644 index 06a45b8b..00000000 --- a/frontend/js/app/settings/default-site/main.js +++ /dev/null @@ -1,69 +0,0 @@ -const Mn = require('backbone.marionette'); -const App = require('../../main'); -const template = require('./main.ejs'); - -require('jquery-serializejson'); -require('selectize'); - -module.exports = Mn.View.extend({ - template: template, - className: 'modal-dialog', - - ui: { - form: 'form', - buttons: '.modal-footer button', - cancel: 'button.cancel', - save: 'button.save', - options: '.option-item', - value: 'input[name="value"]', - redirect: '.redirect-input', - html: '.html-content' - }, - - events: { - 'change @ui.value': function (e) { - let val = this.ui.value.filter(':checked').val(); - this.ui.options.hide(); - this.ui.options.filter('.option-' + val).show(); - }, - - 'click @ui.save': function (e) { - e.preventDefault(); - - let val = this.ui.value.filter(':checked').val(); - - // Clear redirect field before validation - if (val !== 'redirect') { - this.ui.redirect.val('').attr('required', false); - } else { - this.ui.redirect.attr('required', true); - } - - this.ui.html.attr('required', val === 'html'); - - if (!this.ui.form[0].checkValidity()) { - $('').hide().appendTo(this.ui.form).click().remove(); - return; - } - - let view = this; - let data = this.ui.form.serializeJSON(); - data.id = this.model.get('id'); - - this.ui.buttons.prop('disabled', true).addClass('btn-disabled'); - App.Api.Settings.update(data) - .then(result => { - view.model.set(result); - App.UI.closeModal(); - }) - .catch(err => { - alert(err.message); - this.ui.buttons.prop('disabled', false).removeClass('btn-disabled'); - }); - } - }, - - onRender: function () { - this.ui.value.trigger('change'); - } -}); diff --git a/frontend/js/app/settings/list/item.ejs b/frontend/js/app/settings/list/item.ejs deleted file mode 100644 index 4f81b450..00000000 --- a/frontend/js/app/settings/list/item.ejs +++ /dev/null @@ -1,21 +0,0 @@ - -
    <%- name %>
    -
    - <%- description %> -
    - - -
    - <% if (id === 'default-site') { %> - <%- i18n('settings', 'default-site-' + value) %> - <% } %> -
    - - - - \ No newline at end of file diff --git a/frontend/js/app/settings/list/item.js b/frontend/js/app/settings/list/item.js deleted file mode 100644 index 03f9ac05..00000000 --- a/frontend/js/app/settings/list/item.js +++ /dev/null @@ -1,23 +0,0 @@ -const Mn = require('backbone.marionette'); -const App = require('../../main'); -const template = require('./item.ejs'); - -module.exports = Mn.View.extend({ - template: template, - tagName: 'tr', - - ui: { - edit: 'a.edit' - }, - - events: { - 'click @ui.edit': function (e) { - e.preventDefault(); - App.Controller.showSettingForm(this.model); - } - }, - - initialize: function () { - this.listenTo(this.model, 'change', this.render); - } -}); diff --git a/frontend/js/app/settings/list/main.ejs b/frontend/js/app/settings/list/main.ejs deleted file mode 100644 index c96e923a..00000000 --- a/frontend/js/app/settings/list/main.ejs +++ /dev/null @@ -1,8 +0,0 @@ - - <%- i18n('str', 'name') %> - <%- i18n('str', 'value') %> -   - - - - diff --git a/frontend/js/app/settings/list/main.js b/frontend/js/app/settings/list/main.js deleted file mode 100644 index 9d3e26fb..00000000 --- a/frontend/js/app/settings/list/main.js +++ /dev/null @@ -1,27 +0,0 @@ -const Mn = require('backbone.marionette'); -const ItemView = require('./item'); -const template = require('./main.ejs'); - -const TableBody = Mn.CollectionView.extend({ - tagName: 'tbody', - childView: ItemView -}); - -module.exports = Mn.View.extend({ - tagName: 'table', - className: 'table table-hover table-outline table-vcenter card-table', - template: template, - - regions: { - body: { - el: 'tbody', - replaceElement: true - } - }, - - onRender: function () { - this.showChildView('body', new TableBody({ - collection: this.collection - })); - } -}); diff --git a/frontend/js/app/settings/main.ejs b/frontend/js/app/settings/main.ejs deleted file mode 100644 index 2b02769f..00000000 --- a/frontend/js/app/settings/main.ejs +++ /dev/null @@ -1,14 +0,0 @@ -
    -
    -
    -

    <%- i18n('settings', 'title') %>

    -
    -
    -
    -
    -
    - -
    -
    -
    -
    diff --git a/frontend/js/app/settings/main.js b/frontend/js/app/settings/main.js deleted file mode 100644 index 96b2941f..00000000 --- a/frontend/js/app/settings/main.js +++ /dev/null @@ -1,48 +0,0 @@ -const Mn = require('backbone.marionette'); -const App = require('../main'); -const SettingModel = require('../../models/setting'); -const ListView = require('./list/main'); -const ErrorView = require('../error/main'); -const template = require('./main.ejs'); - -module.exports = Mn.View.extend({ - id: 'settings', - template: template, - - ui: { - list_region: '.list-region', - add: '.add-item', - dimmer: '.dimmer' - }, - - regions: { - list_region: '@ui.list_region' - }, - - onRender: function () { - let view = this; - - App.Api.Settings.getAll() - .then(response => { - if (!view.isDestroyed() && response && response.length) { - view.showChildView('list_region', new ListView({ - collection: new SettingModel.Collection(response) - })); - } - }) - .catch(err => { - view.showChildView('list_region', new ErrorView({ - code: err.code, - message: err.message, - retry: function () { - App.Controller.showSettings(); - } - })); - - console.error(err); - }) - .then(() => { - view.ui.dimmer.removeClass('active'); - }); - } -}); diff --git a/frontend/js/app/tokens.js b/frontend/js/app/tokens.js deleted file mode 100644 index 4a56bcab..00000000 --- a/frontend/js/app/tokens.js +++ /dev/null @@ -1,126 +0,0 @@ -const STORAGE_NAME = 'nginx-proxy-manager-tokens'; - -/** - * @returns {Array} - */ -const getStorageTokens = function () { - let json = window.localStorage.getItem(STORAGE_NAME); - if (json) { - try { - return JSON.parse(json); - } catch (err) { - return []; - } - } - - return []; -}; - -/** - * @param {Array} tokens - */ -const setStorageTokens = function (tokens) { - window.localStorage.setItem(STORAGE_NAME, JSON.stringify(tokens)); -}; - -const Tokens = { - - /** - * @returns {Number} - */ - getTokenCount: () => { - return getStorageTokens().length; - }, - - /** - * @returns {Object} t,n - */ - getTopToken: () => { - let tokens = getStorageTokens(); - if (tokens && tokens.length) { - return tokens[0]; - } - - return null; - }, - - /** - * @returns {String} - */ - getNextTokenName: () => { - let tokens = getStorageTokens(); - if (tokens && tokens.length > 1 && typeof tokens[1] !== 'undefined' && typeof tokens[1].n !== 'undefined') { - return tokens[1].n; - } - - return null; - }, - - /** - * - * @param {String} token - * @param {String} [name] - * @returns {Number} - */ - addToken: (token, name) => { - // Get top token and if it's the same, ignore this call - let top = Tokens.getTopToken(); - if (!top || top.t !== token) { - let tokens = getStorageTokens(); - tokens.unshift({t: token, n: name || null}); - setStorageTokens(tokens); - } - - return Tokens.getTokenCount(); - }, - - /** - * @param {String} token - * @returns {Boolean} - */ - setCurrentToken: token => { - let tokens = getStorageTokens(); - if (tokens.length) { - tokens[0].t = token; - setStorageTokens(tokens); - return true; - } - - return false; - }, - - /** - * @param {String} name - * @returns {Boolean} - */ - setCurrentName: name => { - let tokens = getStorageTokens(); - if (tokens.length) { - tokens[0].n = name; - setStorageTokens(tokens); - return true; - } - - return false; - }, - - /** - * @returns {Number} - */ - dropTopToken: () => { - let tokens = getStorageTokens(); - tokens.shift(); - setStorageTokens(tokens); - return tokens.length; - }, - - /** - * - */ - clearTokens: () => { - window.localStorage.removeItem(STORAGE_NAME); - } - -}; - -module.exports = Tokens; diff --git a/frontend/js/app/ui/footer/main.ejs b/frontend/js/app/ui/footer/main.ejs deleted file mode 100644 index 562e71c2..00000000 --- a/frontend/js/app/ui/footer/main.ejs +++ /dev/null @@ -1,16 +0,0 @@ -
    - -
    - <%- i18n('main', 'version', {version: getVersion()}) %> - <%= i18n('footer', 'copy', {url: 'https://jc21.com?utm_source=nginx-proxy-manager'}) %> - <%= i18n('footer', 'theme', {url: 'https://tabler.github.io/?utm_source=nginx-proxy-manager'}) %> -
    -
    diff --git a/frontend/js/app/ui/footer/main.js b/frontend/js/app/ui/footer/main.js deleted file mode 100644 index 73f515e6..00000000 --- a/frontend/js/app/ui/footer/main.js +++ /dev/null @@ -1,14 +0,0 @@ -const Mn = require('backbone.marionette'); -const template = require('./main.ejs'); -const Cache = require('../../cache'); - -module.exports = Mn.View.extend({ - className: 'container', - template: template, - - templateContext: { - getVersion: function () { - return Cache.version || '0.0.0'; - } - } -}); diff --git a/frontend/js/app/ui/header/main.ejs b/frontend/js/app/ui/header/main.ejs deleted file mode 100644 index 5f8a6278..00000000 --- a/frontend/js/app/ui/header/main.ejs +++ /dev/null @@ -1,31 +0,0 @@ - diff --git a/frontend/js/app/ui/header/main.js b/frontend/js/app/ui/header/main.js deleted file mode 100644 index 9779b45c..00000000 --- a/frontend/js/app/ui/header/main.js +++ /dev/null @@ -1,67 +0,0 @@ -const $ = require('jquery'); -const Mn = require('backbone.marionette'); -const i18n = require('../../i18n'); -const Cache = require('../../cache'); -const Controller = require('../../controller'); -const Tokens = require('../../tokens'); -const template = require('./main.ejs'); - -module.exports = Mn.View.extend({ - id: 'header', - className: 'header', - template: template, - - ui: { - link: 'a', - details: 'a.edit-details', - password: 'a.change-password' - }, - - events: { - 'click @ui.details': function (e) { - e.preventDefault(); - Controller.showUserForm(Cache.User); - }, - - 'click @ui.password': function (e) { - e.preventDefault(); - Controller.showUserPasswordForm(Cache.User); - }, - - 'click @ui.link': function (e) { - e.preventDefault(); - let href = $(e.currentTarget).attr('href'); - - switch (href) { - case '/': - Controller.showDashboard(); - break; - case '/logout': - Controller.logout(); - break; - } - } - }, - - templateContext: { - getUserField: function (field, default_val) { - return Cache.User.get(field) || default_val; - }, - - getRole: function () { - return i18n('roles', Cache.User.isAdmin() ? 'admin' : 'user'); - }, - - getLogoutText: function () { - if (Tokens.getTokenCount() > 1) { - return i18n('main', 'sign-in-as', {name: Tokens.getNextTokenName()}); - } - - return i18n('str', 'sign-out'); - } - }, - - initialize: function () { - this.listenTo(Cache.User, 'change', this.render); - } -}); diff --git a/frontend/js/app/ui/main.ejs b/frontend/js/app/ui/main.ejs deleted file mode 100644 index 7c97cf7d..00000000 --- a/frontend/js/app/ui/main.ejs +++ /dev/null @@ -1,19 +0,0 @@ -
    - - -
    -
    - -
    -
    -
    - -
    - -
    - - diff --git a/frontend/js/app/ui/main.js b/frontend/js/app/ui/main.js deleted file mode 100644 index c90c61d5..00000000 --- a/frontend/js/app/ui/main.js +++ /dev/null @@ -1,98 +0,0 @@ -const Mn = require('backbone.marionette'); -const template = require('./main.ejs'); -const HeaderView = require('./header/main'); -const MenuView = require('./menu/main'); -const FooterView = require('./footer/main'); -const Cache = require('../cache'); - -module.exports = Mn.View.extend({ - id: 'app', - className: 'page', - template: template, - modal_setup: false, - - modal: null, - - ui: { - modal: '#modal-dialog' - }, - - regions: { - header_region: { - el: '#header', - replaceElement: true - }, - menu_region: { - el: '#menu', - replaceElement: true - }, - footer_region: '.footer', - app_content_region: '#app-content', - modal_region: '#modal-dialog' - }, - - /** - * @param {Object} view - */ - showAppContent: function (view) { - this.showChildView('app_content_region', view); - }, - - /** - * @param {Object} view - * @param {Function} [show_callback] - * @param {Function} [shown_callback] - */ - showModalDialog: function (view, show_callback, shown_callback) { - this.showChildView('modal_region', view); - let modal = this.getRegion('modal_region').$el.modal('show'); - - modal.on('hidden.bs.modal', function (/*e*/) { - if (show_callback) { - modal.off('show.bs.modal', show_callback); - } - - if (shown_callback) { - modal.off('shown.bs.modal', shown_callback); - } - - modal.off('hidden.bs.modal'); - view.destroy(); - }); - - if (show_callback) { - modal.on('show.bs.modal', show_callback); - } - - if (shown_callback) { - modal.on('shown.bs.modal', shown_callback); - } - }, - - /** - * - * @param {Function} [hidden_callback] - */ - closeModal: function (hidden_callback) { - let modal = this.getRegion('modal_region').$el.modal('hide'); - - if (hidden_callback) { - modal.on('hidden.bs.modal', hidden_callback); - } - }, - - onRender: function () { - this.showChildView('header_region', new HeaderView({ - model: Cache.User - })); - - this.showChildView('menu_region', new MenuView()); - this.showChildView('footer_region', new FooterView()); - }, - - reset: function () { - this.getRegion('header_region').reset(); - this.getRegion('footer_region').reset(); - this.getRegion('modal_region').reset(); - } -}); diff --git a/frontend/js/app/ui/menu/main.ejs b/frontend/js/app/ui/menu/main.ejs deleted file mode 100644 index 671b4e3b..00000000 --- a/frontend/js/app/ui/menu/main.ejs +++ /dev/null @@ -1,52 +0,0 @@ -
    -
    -
    - -
    -
    -
    diff --git a/frontend/js/app/ui/menu/main.js b/frontend/js/app/ui/menu/main.js deleted file mode 100644 index dabe26d3..00000000 --- a/frontend/js/app/ui/menu/main.js +++ /dev/null @@ -1,39 +0,0 @@ -const $ = require('jquery'); -const Mn = require('backbone.marionette'); -const Controller = require('../../controller'); -const Cache = require('../../cache'); -const template = require('./main.ejs'); - -module.exports = Mn.View.extend({ - id: 'menu', - className: 'header collapse d-lg-flex p-0', - template: template, - - ui: { - links: 'a' - }, - - events: { - 'click @ui.links': function (e) { - let href = $(e.currentTarget).attr('href'); - if (href !== '#') { - e.preventDefault(); - Controller.navigate(href, true); - } - } - }, - - templateContext: { - isAdmin: function () { - return Cache.User.isAdmin(); - }, - - canShow: function (perm) { - return Cache.User.isAdmin() || Cache.User.canView(perm); - } - }, - - initialize: function () { - this.listenTo(Cache.User, 'change', this.render); - } -}); diff --git a/frontend/js/app/user/delete.ejs b/frontend/js/app/user/delete.ejs deleted file mode 100644 index 484e2786..00000000 --- a/frontend/js/app/user/delete.ejs +++ /dev/null @@ -1,19 +0,0 @@ - diff --git a/frontend/js/app/user/delete.js b/frontend/js/app/user/delete.js deleted file mode 100644 index e8ed5c32..00000000 --- a/frontend/js/app/user/delete.js +++ /dev/null @@ -1,34 +0,0 @@ -const Mn = require('backbone.marionette'); -const template = require('./delete.ejs'); -const App = require('../main'); - -require('jquery-serializejson'); - -module.exports = Mn.View.extend({ - template: template, - className: 'modal-dialog', - - ui: { - form: 'form', - buttons: '.modal-footer button', - cancel: 'button.cancel', - save: 'button.save' - }, - - events: { - - 'click @ui.save': function (e) { - e.preventDefault(); - - App.Api.Users.delete(this.model.get('id')) - .then(() => { - App.Controller.showUsers(); - App.UI.closeModal(); - }) - .catch(err => { - alert(err.message); - this.ui.buttons.prop('disabled', false).removeClass('btn-disabled'); - }); - } - } -}); diff --git a/frontend/js/app/user/form.ejs b/frontend/js/app/user/form.ejs deleted file mode 100644 index aeb268f7..00000000 --- a/frontend/js/app/user/form.ejs +++ /dev/null @@ -1,58 +0,0 @@ - diff --git a/frontend/js/app/user/form.js b/frontend/js/app/user/form.js deleted file mode 100644 index ef92ec3e..00000000 --- a/frontend/js/app/user/form.js +++ /dev/null @@ -1,108 +0,0 @@ -const Mn = require('backbone.marionette'); -const App = require('../main'); -const UserModel = require('../../models/user'); -const template = require('./form.ejs'); - -require('jquery-serializejson'); - -module.exports = Mn.View.extend({ - template: template, - className: 'modal-dialog', - - ui: { - form: 'form', - buttons: '.modal-footer button', - cancel: 'button.cancel', - save: 'button.save', - error: '.secret-error' - }, - - events: { - - 'click @ui.save': function (e) { - e.preventDefault(); - this.ui.error.hide(); - let view = this; - let data = this.ui.form.serializeJSON(); - - let show_password = this.model.get('email') === 'admin@example.com'; - - // admin@example.com is not allowed - if (data.email === 'admin@example.com') { - this.ui.error.text(App.i18n('users', 'default_error')).show(); - this.ui.buttons.prop('disabled', false).removeClass('btn-disabled'); - return; - } - - // Manipulate - data.roles = []; - if ((this.model.get('id') === App.Cache.User.get('id') && this.model.isAdmin()) || (typeof data.is_admin !== 'undefined' && data.is_admin)) { - data.roles.push('admin'); - delete data.is_admin; - } - - data.is_disabled = typeof data.is_disabled !== 'undefined' ? !!data.is_disabled : false; - this.ui.buttons.prop('disabled', true).addClass('btn-disabled'); - let method = App.Api.Users.create; - - if (this.model.get('id')) { - // edit - method = App.Api.Users.update; - data.id = this.model.get('id'); - } - - method(data) - .then(result => { - if (result.id === App.Cache.User.get('id')) { - App.Cache.User.set(result); - } - - if (view.model.get('id') !== App.Cache.User.get('id')) { - App.Controller.showUsers(); - } - - view.model.set(result); - App.UI.closeModal(function () { - if (method === App.Api.Users.create) { - // Show permissions dialog immediately - App.Controller.showUserPermissions(view.model); - } else if (show_password) { - App.Controller.showUserPasswordForm(view.model); - } - }); - }) - .catch(err => { - this.ui.error.text(err.message).show(); - this.ui.buttons.prop('disabled', false).removeClass('btn-disabled'); - }); - } - }, - - templateContext: function () { - let view = this; - - return { - isSelf: function () { - return view.model.get('id') === App.Cache.User.get('id'); - }, - - isAdmin: function () { - return App.Cache.User.isAdmin(); - }, - - isAdminUser: function () { - return view.model.isAdmin(); - }, - - isDisabled: function () { - return view.model.isDisabled(); - } - }; - }, - - initialize: function (options) { - if (typeof options.model === 'undefined' || !options.model) { - this.model = new UserModel.Model(); - } - } -}); diff --git a/frontend/js/app/user/password.ejs b/frontend/js/app/user/password.ejs deleted file mode 100644 index a45cc7ed..00000000 --- a/frontend/js/app/user/password.ejs +++ /dev/null @@ -1,31 +0,0 @@ - diff --git a/frontend/js/app/user/password.js b/frontend/js/app/user/password.js deleted file mode 100644 index 84030750..00000000 --- a/frontend/js/app/user/password.js +++ /dev/null @@ -1,69 +0,0 @@ -const Mn = require('backbone.marionette'); -const App = require('../main'); -const template = require('./password.ejs'); - -require('jquery-serializejson'); - -module.exports = Mn.View.extend({ - template: template, - className: 'modal-dialog', - - ui: { - form: 'form', - buttons: '.modal-footer button', - cancel: 'button.cancel', - save: 'button.save', - newSecretError: '.new-secret-error', - generalError: '#error-info', - }, - - events: { - 'click @ui.save': function (e) { - e.preventDefault(); - this.ui.newSecretError.hide(); - this.ui.generalError.hide(); - let form = this.ui.form.serializeJSON(); - - if (form.new_password1 !== form.new_password2) { - this.ui.newSecretError.text('Passwords do not match!').show(); - return; - } - - let data = { - type: 'password', - current: form.current_password, - secret: form.new_password1 - }; - - this.ui.buttons.prop('disabled', true).addClass('btn-disabled'); - App.Api.Users.setPassword(this.model.get('id'), data) - .then(() => { - App.UI.closeModal(); - App.Controller.showUsers(); - }) - .catch(err => { - // Change error message to make it a little clearer - if (err.message === 'Invalid password') { - err.message = 'Current password is invalid'; - } - this.ui.generalError.text(err.message).show(); - this.ui.buttons.prop('disabled', false).removeClass('btn-disabled'); - }); - } - }, - - isSelf: function () { - return App.Cache.User.get('id') === this.model.get('id'); - }, - - templateContext: function () { - return { - isSelf: this.isSelf.bind(this) - }; - }, - - onRender: function () { - this.ui.newSecretError.hide(); - this.ui.generalError.hide(); - }, -}); diff --git a/frontend/js/app/user/permissions.ejs b/frontend/js/app/user/permissions.ejs deleted file mode 100644 index b6161796..00000000 --- a/frontend/js/app/user/permissions.ejs +++ /dev/null @@ -1,68 +0,0 @@ - diff --git a/frontend/js/app/user/permissions.js b/frontend/js/app/user/permissions.js deleted file mode 100644 index af8049ce..00000000 --- a/frontend/js/app/user/permissions.js +++ /dev/null @@ -1,95 +0,0 @@ -const Mn = require('backbone.marionette'); -const App = require('../main'); -const UserModel = require('../../models/user'); -const template = require('./permissions.ejs'); - -require('jquery-serializejson'); - -module.exports = Mn.View.extend({ - template: template, - className: 'modal-dialog', - - ui: { - form: 'form', - buttons: '.modal-footer button', - cancel: 'button.cancel', - save: 'button.save', - error: '.secret-error' - }, - - events: { - - 'click @ui.save': function (e) { - e.preventDefault(); - - let view = this; - let data = this.ui.form.serializeJSON(); - - // Manipulate - if (view.model.isAdmin()) { - // Force some attributes for admin - data = _.assign({}, data, { - access_lists: 'manage', - dead_hosts: 'manage', - proxy_hosts: 'manage', - redirection_hosts: 'manage', - streams: 'manage', - certificates: 'manage' - }); - } - - this.ui.buttons.prop('disabled', true).addClass('btn-disabled'); - - App.Api.Users.setPermissions(view.model.get('id'), data) - .then(() => { - if (view.model.get('id') === App.Cache.User.get('id')) { - App.Cache.User.set({permissions: data}); - } - - view.model.set({permissions: data}); - App.UI.closeModal(); - }) - .catch(err => { - this.ui.error.text(err.message).show(); - this.ui.buttons.prop('disabled', false).removeClass('btn-disabled'); - }); - } - }, - - templateContext: function () { - let perms = this.model.get('permissions'); - let is_admin = this.model.isAdmin(); - - return { - getPerm: function (key) { - if (perms !== null && typeof perms[key] !== 'undefined') { - return perms[key]; - } - - return null; - }, - - getPermProps: function (key, item, forced_admin) { - if (forced_admin && is_admin) { - return 'checked disabled'; - } else if (is_admin) { - return 'disabled'; - } else if (perms !== null && typeof perms[key] !== 'undefined' && perms[key] === item) { - return 'checked'; - } - - return ''; - }, - - isAdmin: function () { - return is_admin; - } - }; - }, - - initialize: function (options) { - if (typeof options.model === 'undefined' || !options.model) { - this.model = new UserModel.Model(); - } - } -}); diff --git a/frontend/js/app/users/list/item.ejs b/frontend/js/app/users/list/item.ejs deleted file mode 100644 index fab5585b..00000000 --- a/frontend/js/app/users/list/item.ejs +++ /dev/null @@ -1,45 +0,0 @@ - -
    - -
    - - -
    <%- name %>
    -
    - <%- i18n('str', 'created-on', {date: formatDbDate(created_on, 'Do MMMM YYYY')}) %> -
    - - -
    <%- email %>
    - - -
    - <% - var r = []; - roles.map(function(role) { - if (role) { - r.push(i18n('roles', role)); - } - }); - %> - <%- r.join(', ') %> -
    - - - - diff --git a/frontend/js/app/users/list/item.js b/frontend/js/app/users/list/item.js deleted file mode 100644 index 4645a5c4..00000000 --- a/frontend/js/app/users/list/item.js +++ /dev/null @@ -1,68 +0,0 @@ -const Mn = require('backbone.marionette'); -const App = require('../../main'); -const Tokens = require('../../tokens'); -const template = require('./item.ejs'); - -module.exports = Mn.View.extend({ - template: template, - tagName: 'tr', - - ui: { - edit: 'a.edit-user', - permissions: 'a.edit-permissions', - password: 'a.set-password', - login: 'a.login', - delete: 'a.delete-user' - }, - - events: { - 'click @ui.edit': function (e) { - e.preventDefault(); - App.Controller.showUserForm(this.model); - }, - - 'click @ui.permissions': function (e) { - e.preventDefault(); - App.Controller.showUserPermissions(this.model); - }, - - 'click @ui.password': function (e) { - e.preventDefault(); - App.Controller.showUserPasswordForm(this.model); - }, - - 'click @ui.delete': function (e) { - e.preventDefault(); - App.Controller.showUserDeleteConfirm(this.model); - }, - - 'click @ui.login': function (e) { - e.preventDefault(); - - if (App.Cache.User.get('id') !== this.model.get('id')) { - this.ui.login.prop('disabled', true).addClass('btn-disabled'); - - App.Api.Users.loginAs(this.model.get('id')) - .then(res => { - Tokens.addToken(res.token, res.user.nickname || res.user.name); - window.location = '/'; - window.location.reload(); - }) - .catch(err => { - alert(err.message); - this.ui.login.prop('disabled', false).removeClass('btn-disabled'); - }); - } - } - }, - - templateContext: { - isSelf: function () { - return App.Cache.User.get('id') === this.id; - } - }, - - initialize: function () { - this.listenTo(this.model, 'change', this.render); - } -}); diff --git a/frontend/js/app/users/list/main.ejs b/frontend/js/app/users/list/main.ejs deleted file mode 100644 index c85c9cb1..00000000 --- a/frontend/js/app/users/list/main.ejs +++ /dev/null @@ -1,10 +0,0 @@ - -   - <%- i18n('str', 'name') %> - <%- i18n('str', 'email') %> - <%- i18n('str', 'roles') %> -   - - - - diff --git a/frontend/js/app/users/list/main.js b/frontend/js/app/users/list/main.js deleted file mode 100644 index 9d3e26fb..00000000 --- a/frontend/js/app/users/list/main.js +++ /dev/null @@ -1,27 +0,0 @@ -const Mn = require('backbone.marionette'); -const ItemView = require('./item'); -const template = require('./main.ejs'); - -const TableBody = Mn.CollectionView.extend({ - tagName: 'tbody', - childView: ItemView -}); - -module.exports = Mn.View.extend({ - tagName: 'table', - className: 'table table-hover table-outline table-vcenter card-table', - template: template, - - regions: { - body: { - el: 'tbody', - replaceElement: true - } - }, - - onRender: function () { - this.showChildView('body', new TableBody({ - collection: this.collection - })); - } -}); diff --git a/frontend/js/app/users/main.ejs b/frontend/js/app/users/main.ejs deleted file mode 100644 index 8f0d3aaf..00000000 --- a/frontend/js/app/users/main.ejs +++ /dev/null @@ -1,18 +0,0 @@ -
    -
    -
    -

    <%- i18n('users', 'title') %>

    - -
    -
    -
    -
    -
    - -
    -
    - -
    -
    diff --git a/frontend/js/app/users/main.js b/frontend/js/app/users/main.js deleted file mode 100644 index 95d42c61..00000000 --- a/frontend/js/app/users/main.js +++ /dev/null @@ -1,55 +0,0 @@ -const Mn = require('backbone.marionette'); -const App = require('../main'); -const UserModel = require('../../models/user'); -const ListView = require('./list/main'); -const ErrorView = require('../error/main'); -const template = require('./main.ejs'); - -module.exports = Mn.View.extend({ - id: 'users', - template: template, - - ui: { - list_region: '.list-region', - add: '.add-item', - dimmer: '.dimmer' - }, - - regions: { - list_region: '@ui.list_region' - }, - - events: { - 'click @ui.add': function (e) { - e.preventDefault(); - App.Controller.showUserForm(new UserModel.Model()); - } - }, - - onRender: function () { - let view = this; - - App.Api.Users.getAll(['permissions']) - .then(response => { - if (!view.isDestroyed() && response && response.length) { - view.showChildView('list_region', new ListView({ - collection: new UserModel.Collection(response) - })); - } - }) - .catch(err => { - view.showChildView('list_region', new ErrorView({ - code: err.code, - message: err.message, - retry: function () { - App.Controller.showUsers(); - } - })); - - console.error(err); - }) - .then(() => { - view.ui.dimmer.removeClass('active'); - }); - } -}); diff --git a/frontend/js/i18n/messages.json b/frontend/js/i18n/messages.json deleted file mode 100644 index 2fb4a04f..00000000 --- a/frontend/js/i18n/messages.json +++ /dev/null @@ -1,273 +0,0 @@ -{ - "en": { - "str": { - "email-address": "Email address", - "username": "Username", - "password": "Password", - "sign-in": "Sign in", - "sign-out": "Sign out", - "try-again": "Try again", - "name": "Name", - "email": "Email", - "roles": "Roles", - "created-on": "Created: {date}", - "save": "Save", - "cancel": "Cancel", - "close": "Close", - "enable": "Enable", - "disable": "Disable", - "sure": "Yes I'm Sure", - "disabled": "Disabled", - "choose-file": "Choose file", - "source": "Source", - "destination": "Destination", - "ssl": "SSL", - "access": "Access", - "public": "Public", - "edit": "Edit", - "delete": "Delete", - "logs": "Logs", - "status": "Status", - "online": "Online", - "offline": "Offline", - "unknown": "Unknown", - "expires": "Expires", - "value": "Value", - "please-wait": "Please wait...", - "all": "All", - "any": "Any" - }, - "login": { - "title": "Login to your account" - }, - "main": { - "app": "Nginx Proxy Manager", - "version": "v{version}", - "welcome": "Welcome to Nginx Proxy Manager", - "logged-in": "You are logged in as {name}", - "unknown-error": "Error loading stuff. Please reload the app.", - "unknown-user": "Unknown User", - "sign-in-as": "Sign back in as {name}" - }, - "roles": { - "title": "Roles", - "admin": "Administrator", - "user": "Apache Helicopter" - }, - "menu": { - "dashboard": "Dashboard", - "hosts": "Hosts" - }, - "footer": { - "fork-me": "Fork me on Github", - "copy": "© 2019 jc21.com.", - "theme": "Theme by Tabler" - }, - "dashboard": { - "title": "Hi {name}" - }, - "all-hosts": { - "empty-subtitle": "{manage, select, true{Why don't you create one?} other{And you don't have permission to create one.}}", - "details": "Details", - "enable-ssl": "Enable SSL", - "force-ssl": "Force SSL", - "http2-support": "HTTP/2 Support", - "domain-names": "Domain Names", - "cert-provider": "Certificate Provider", - "block-exploits": "Block Common Exploits", - "caching-enabled": "Cache Assets", - "ssl-certificate": "SSL Certificate", - "none": "None", - "new-cert": "Request a new SSL Certificate", - "with-le": "with Let's Encrypt", - "no-ssl": "This host will not use HTTPS", - "advanced": "Advanced", - "advanced-warning": "Enter your custom Nginx configuration here at your own risk!", - "advanced-config": "Custom Nginx Configuration", - "hsts-enabled": "HSTS Enabled", - "hsts-subdomains": "HSTS Subdomains", - "locations": "Custom locations" - }, - "locations": { - "new_location": "Add location", - "path": "/path", - "location_label": "Define location", - "delete": "Delete" - }, - "ssl": { - "letsencrypt": "Let's Encrypt", - "other": "Custom", - "none": "HTTP only", - "letsencrypt-email": "Email Address for Let's Encrypt", - "letsencrypt-agree": "I Agree to the Let's Encrypt Terms of Service", - "delete-ssl": "The SSL certificates attached will NOT be removed, they will need to be removed manually.", - "hosts-warning": "These domains must be already configured to point to this installation", - "no-wildcard-without-dns": "Cannot request Let's Encrypt Certificate for wildcard domains when not using DNS challenge", - "dns-challenge": "Use a DNS Challenge", - "certbot-warning": "This section requires some knowledge about Certbot and its DNS plugins. Please consult the respective plugins documentation.", - "dns-provider": "DNS Provider", - "please-choose": "Please Choose...", - "credentials-file-content": "Credentials File Content", - "credentials-file-content-info": "This plugin requires a configuration file containing an API token or other credentials to your provider", - "stored-as-plaintext-info": "This data will be stored as plaintext in the database and in a file!", - "propagation-seconds": "Propagation Seconds", - "propagation-seconds-info": "Leave empty to use the plugins default value. Number of seconds to wait for DNS propagation.", - "processing-info": "Processing... This might take a few minutes.", - "passphrase-protection-support-info": "Key files protected with a passphrase are not supported." - }, - "proxy-hosts": { - "title": "Proxy Hosts", - "empty": "There are no Proxy Hosts", - "add": "Add Proxy Host", - "form-title": "{id, select, undefined{New} other{Edit}} Proxy Host", - "forward-scheme": "Scheme", - "forward-host": "Forward Hostname / IP", - "forward-port": "Forward Port", - "delete": "Delete Proxy Host", - "delete-confirm": "Are you sure you want to delete the Proxy host for: {domains}?", - "help-title": "What is a Proxy Host?", - "help-content": "A Proxy Host is the incoming endpoint for a web service that you want to forward.\nIt provides optional SSL termination for your service that might not have SSL support built in.\nProxy Hosts are the most common use for the Nginx Proxy Manager.", - "access-list": "Access List", - "allow-websocket-upgrade": "Websockets Support", - "ignore-invalid-upstream-ssl": "Ignore Invalid SSL", - "custom-forward-host-help": "Use 1.1.1.1/path for sub-folder forwarding" - }, - "redirection-hosts": { - "title": "Redirection Hosts", - "empty": "There are no Redirection Hosts", - "add": "Add Redirection Host", - "form-title": "{id, select, undefined{New} other{Edit}} Redirection Host", - "forward-scheme": "Scheme", - "forward-http-status-code": "HTTP Code", - "forward-domain": "Forward Domain", - "preserve-path": "Preserve Path", - "delete": "Delete Proxy Host", - "delete-confirm": "Are you sure you want to delete the Redirection host for: {domains}?", - "help-title": "What is a Redirection Host?", - "help-content": "A Redirection Host will redirect requests from the incoming domain and push the viewer to another domain.\nThe most common reason to use this type of host is when your website changes domains but you still have search engine or referrer links pointing to the old domain." - }, - "dead-hosts": { - "title": "404 Hosts", - "empty": "There are no 404 Hosts", - "add": "Add 404 Host", - "form-title": "{id, select, undefined{New} other{Edit}} 404 Host", - "delete": "Delete 404 Host", - "delete-confirm": "Are you sure you want to delete this 404 Host?", - "help-title": "What is a 404 Host?", - "help-content": "A 404 Host is simply a host setup that shows a 404 page.\nThis can be useful when your domain is listed in search engines and you want to provide a nicer error page or specifically to tell the search indexers that the domain pages no longer exist.\nAnother benefit of having this host is to track the logs for hits to it and view the referrers." - }, - "streams": { - "title": "Streams", - "empty": "There are no Streams", - "add": "Add Stream", - "form-title": "{id, select, undefined{New} other{Edit}} Stream", - "incoming-port": "Incoming Port", - "forward-ip": "Forward IP", - "forwarding-port": "Forward Port", - "tcp-forwarding": "TCP Forwarding", - "udp-forwarding": "UDP Forwarding", - "forward-type-error": "At least one type of protocol must be enabled", - "protocol": "Protocol", - "tcp": "TCP", - "udp": "UDP", - "delete": "Delete Stream", - "delete-confirm": "Are you sure you want to delete this Stream?", - "help-title": "What is a Stream?", - "help-content": "A relatively new feature for Nginx, a Stream will serve to forward TCP/UDP traffic directly to another computer on the network.\nIf you're running game servers, FTP or SSH servers this can come in handy." - }, - "certificates": { - "title": "SSL Certificates", - "empty": "There are no SSL Certificates", - "add": "Add SSL Certificate", - "form-title": "Add {provider, select, letsencrypt{Let's Encrypt} other{Custom}} Certificate", - "delete": "Delete SSL Certificate", - "delete-confirm": "Are you sure you want to delete this SSL Certificate? Any hosts using it will need to be updated later.", - "help-title": "SSL Certificates", - "help-content": "SSL certificates (correctly known as TLS Certificates) are a form of encryption key which allows your site to be encrypted for the end user.\nNPM uses a service called Let's Encrypt to issue SSL certificates for free.\nIf you have any sort of personal information, passwords, or sensitive data behind NPM, it's probably a good idea to use a certificate.\nNPM also supports DNS authentication for if you're not running your site facing the internet, or if you just want a wildcard certificate.", - "other-certificate": "Certificate", - "other-certificate-key": "Certificate Key", - "other-intermediate-certificate": "Intermediate Certificate", - "force-renew": "Renew Now", - "renew-title": "Renew Let'sEncrypt Certificate" - }, - "access-lists": { - "title": "Access Lists", - "empty": "There are no Access Lists", - "add": "Add Access List", - "form-title": "{id, select, undefined{New} other{Edit}} Access List", - "delete": "Delete Access List", - "delete-confirm": "Are you sure you want to delete this access list?", - "public": "Publicly Accessible", - "public-sub": "No Access Restrictions", - "help-title": "What is an Access List?", - "help-content": "Access Lists provide a blacklist or whitelist of specific client IP addresses along with authentication for the Proxy Hosts via Basic HTTP Authentication.\nYou can configure multiple client rules, usernames and passwords for a single Access List and then apply that to a Proxy Host.\nThis is most useful for forwarded web services that do not have authentication mechanisms built in or that you want to protect from access by unknown clients.", - "item-count": "{count} {count, select, 1{User} other{Users}}", - "client-count": "{count} {count, select, 1{Rule} other{Rules}}", - "proxy-host-count": "{count} {count, select, 1{Proxy Host} other{Proxy Hosts}}", - "delete-has-hosts": "This Access List is associated with {count} Proxy Hosts. They will become publicly available upon deletion.", - "details": "Details", - "authorization": "Authorization", - "access": "Access", - "satisfy": "Satisfy", - "satisfy-any": "Satisfy Any", - "pass-auth": "Pass Auth to Host", - "access-add": "Add", - "auth-add": "Add" - }, - "users": { - "title": "Users", - "default_error": "Default email address must be changed", - "add": "Add User", - "nickname": "Nickname", - "full-name": "Full Name", - "edit-details": "Edit Details", - "change-password": "Change Password", - "edit-permissions": "Edit Permissions", - "sign-in-as": "Sign in as User", - "form-title": "{id, select, undefined{New} other{Edit}} User", - "delete": "Delete {name, select, undefined{User} other{{name}}}", - "delete-confirm": "Are you sure you want to delete {name}?", - "password-title": "Change Password{self, select, false{ for {name}} other{}}", - "current-password": "Current Password", - "new-password": "New Password", - "confirm-password": "Confirm Password", - "permissions-title": "Permissions for {name}", - "admin-perms": "This user is an Administrator and some items cannot be altered", - "perms-visibility": "Item Visibility", - "perms-visibility-user": "Created Items Only", - "perms-visibility-all": "All Items", - "perm-manage": "Manage", - "perm-view": "View Only", - "perm-hidden": "Hidden" - }, - "audit-log": { - "title": "Audit Log", - "empty": "There are no logs.", - "empty-subtitle": "As soon as you or another user changes something, history of those events will show up here.", - "proxy-host": "Proxy Host", - "redirection-host": "Redirection Host", - "dead-host": "404 Host", - "stream": "Stream", - "user": "User", - "certificate": "Certificate", - "access-list": "Access List", - "created": "Created {name}", - "updated": "Updated {name}", - "deleted": "Deleted {name}", - "enabled": "Enabled {name}", - "disabled": "Disabled {name}", - "renewed": "Renewed {name}", - "meta-title": "Details for Event", - "view-meta": "View Details", - "date": "Date" - }, - "settings": { - "title": "Settings", - "default-site": "Default Site", - "default-site-congratulations": "Congratulations Page", - "default-site-404": "404 Page", - "default-site-html": "Custom Page", - "default-site-redirect": "Redirect" - } - } -} diff --git a/frontend/js/index.js b/frontend/js/index.js deleted file mode 100644 index bfaa0175..00000000 --- a/frontend/js/index.js +++ /dev/null @@ -1,112 +0,0 @@ -// This has to exist here so that Webpack picks it up -import '../scss/styles.scss'; - -window.tabler = { - colors: { - 'blue': '#467fcf', - 'blue-darkest': '#0e1929', - 'blue-darker': '#1c3353', - 'blue-dark': '#3866a6', - 'blue-light': '#7ea5dd', - 'blue-lighter': '#c8d9f1', - 'blue-lightest': '#edf2fa', - 'azure': '#45aaf2', - 'azure-darkest': '#0e2230', - 'azure-darker': '#1c4461', - 'azure-dark': '#3788c2', - 'azure-light': '#7dc4f6', - 'azure-lighter': '#c7e6fb', - 'azure-lightest': '#ecf7fe', - 'indigo': '#6574cd', - 'indigo-darkest': '#141729', - 'indigo-darker': '#282e52', - 'indigo-dark': '#515da4', - 'indigo-light': '#939edc', - 'indigo-lighter': '#d1d5f0', - 'indigo-lightest': '#f0f1fa', - 'purple': '#a55eea', - 'purple-darkest': '#21132f', - 'purple-darker': '#42265e', - 'purple-dark': '#844bbb', - 'purple-light': '#c08ef0', - 'purple-lighter': '#e4cff9', - 'purple-lightest': '#f6effd', - 'pink': '#f66d9b', - 'pink-darkest': '#31161f', - 'pink-darker': '#622c3e', - 'pink-dark': '#c5577c', - 'pink-light': '#f999b9', - 'pink-lighter': '#fcd3e1', - 'pink-lightest': '#fef0f5', - 'red': '#e74c3c', - 'red-darkest': '#2e0f0c', - 'red-darker': '#5c1e18', - 'red-dark': '#b93d30', - 'red-light': '#ee8277', - 'red-lighter': '#f8c9c5', - 'red-lightest': '#fdedec', - 'orange': '#fd9644', - 'orange-darkest': '#331e0e', - 'orange-darker': '#653c1b', - 'orange-dark': '#ca7836', - 'orange-light': '#feb67c', - 'orange-lighter': '#fee0c7', - 'orange-lightest': '#fff5ec', - 'yellow': '#f1c40f', - 'yellow-darkest': '#302703', - 'yellow-darker': '#604e06', - 'yellow-dark': '#c19d0c', - 'yellow-light': '#f5d657', - 'yellow-lighter': '#fbedb7', - 'yellow-lightest': '#fef9e7', - 'lime': '#7bd235', - 'lime-darkest': '#192a0b', - 'lime-darker': '#315415', - 'lime-dark': '#62a82a', - 'lime-light': '#a3e072', - 'lime-lighter': '#d7f2c2', - 'lime-lightest': '#f2fbeb', - 'green': '#5eba00', - 'green-darkest': '#132500', - 'green-darker': '#264a00', - 'green-dark': '#4b9500', - 'green-light': '#8ecf4d', - 'green-lighter': '#cfeab3', - 'green-lightest': '#eff8e6', - 'teal': '#2bcbba', - 'teal-darkest': '#092925', - 'teal-darker': '#11514a', - 'teal-dark': '#22a295', - 'teal-light': '#6bdbcf', - 'teal-lighter': '#bfefea', - 'teal-lightest': '#eafaf8', - 'cyan': '#17a2b8', - 'cyan-darkest': '#052025', - 'cyan-darker': '#09414a', - 'cyan-dark': '#128293', - 'cyan-light': '#5dbecd', - 'cyan-lighter': '#b9e3ea', - 'cyan-lightest': '#e8f6f8', - 'gray': '#868e96', - 'gray-darkest': '#1b1c1e', - 'gray-darker': '#36393c', - 'gray-light': '#aab0b6', - 'gray-lighter': '#dbdde0', - 'gray-lightest': '#f3f4f5', - 'gray-dark': '#343a40', - 'gray-dark-darkest': '#0a0c0d', - 'gray-dark-darker': '#15171a', - 'gray-dark-dark': '#2a2e33', - 'gray-dark-light': '#717579', - 'gray-dark-lighter': '#c2c4c6', - 'gray-dark-lightest': '#ebebec' - } -}; - -require('tabler-core'); - -const App = require('./app/main'); - -$(document).ready(() => { - App.start(); -}); diff --git a/frontend/js/lib/helpers.js b/frontend/js/lib/helpers.js deleted file mode 100644 index 21ce7424..00000000 --- a/frontend/js/lib/helpers.js +++ /dev/null @@ -1,26 +0,0 @@ -const numeral = require('numeral'); -const moment = require('moment'); - -module.exports = { - - /** - * @param {Integer} number - * @returns {String} - */ - niceNumber: function (number) { - return numeral(number).format('0,0'); - }, - - /** - * @param {String|Number} date - * @param {String} format - * @returns {String} - */ - formatDbDate: function (date, format) { - if (typeof date === 'number') { - return moment.unix(date).format(format); - } - - return moment(date).format(format); - } -}; diff --git a/frontend/js/lib/marionette.js b/frontend/js/lib/marionette.js deleted file mode 100644 index c88368f8..00000000 --- a/frontend/js/lib/marionette.js +++ /dev/null @@ -1,15 +0,0 @@ -const _ = require('underscore'); -const Mn = require('backbone.marionette'); -const i18n = require('../app/i18n'); -const Helpers = require('./helpers'); -const TemplateCache = require('marionette.templatecache'); - -Mn.setRenderer(function (template, data, view) { - data = _.clone(data); - data.i18n = i18n; - data.formatDbDate = Helpers.formatDbDate; - - return TemplateCache.default.render.call(this, template, data, view); -}); - -module.exports = Mn; diff --git a/frontend/js/login.js b/frontend/js/login.js deleted file mode 100644 index 0094e2a2..00000000 --- a/frontend/js/login.js +++ /dev/null @@ -1,5 +0,0 @@ -const App = require('./login/main'); - -$(document).ready(() => { - App.start(); -}); diff --git a/frontend/js/login/main.js b/frontend/js/login/main.js deleted file mode 100644 index 03fdc7e5..00000000 --- a/frontend/js/login/main.js +++ /dev/null @@ -1,14 +0,0 @@ -const Mn = require('backbone.marionette'); -const LoginView = require('./ui/login'); - -const App = Mn.Application.extend({ - region: '#login', - UI: null, - - onStart: function (/*app, options*/) { - this.getRegion().show(new LoginView()); - } -}); - -const app = new App(); -module.exports = app; diff --git a/frontend/js/login/ui/login.ejs b/frontend/js/login/ui/login.ejs deleted file mode 100644 index b6f52b7a..00000000 --- a/frontend/js/login/ui/login.ejs +++ /dev/null @@ -1,37 +0,0 @@ -
    -
    - -
    -
    diff --git a/frontend/js/login/ui/login.js b/frontend/js/login/ui/login.js deleted file mode 100644 index 757eb4e3..00000000 --- a/frontend/js/login/ui/login.js +++ /dev/null @@ -1,42 +0,0 @@ -const $ = require('jquery'); -const Mn = require('backbone.marionette'); -const template = require('./login.ejs'); -const Api = require('../../app/api'); -const i18n = require('../../app/i18n'); - -module.exports = Mn.View.extend({ - template: template, - className: 'page-single', - - ui: { - form: 'form', - identity: 'input[name="identity"]', - secret: 'input[name="secret"]', - error: '.secret-error', - button: 'button' - }, - - events: { - 'submit @ui.form': function (e) { - e.preventDefault(); - this.ui.button.addClass('btn-loading').prop('disabled', true); - this.ui.error.hide(); - - Api.Tokens.login(this.ui.identity.val(), this.ui.secret.val(), true) - .then(() => { - window.location = '/'; - }) - .catch(err => { - this.ui.error.text(err.message).show(); - this.ui.button.removeClass('btn-loading').prop('disabled', false); - }); - } - }, - - templateContext: { - i18n: i18n, - getVersion: function () { - return $('#login').data('version'); - } - } -}); diff --git a/frontend/js/models/access-list.js b/frontend/js/models/access-list.js deleted file mode 100644 index 0c2c4abe..00000000 --- a/frontend/js/models/access-list.js +++ /dev/null @@ -1,25 +0,0 @@ -const Backbone = require('backbone'); - -const model = Backbone.Model.extend({ - idAttribute: 'id', - - defaults: function () { - return { - id: undefined, - created_on: null, - modified_on: null, - name: '', - items: [], - clients: [], - // The following are expansions: - owner: null - }; - } -}); - -module.exports = { - Model: model, - Collection: Backbone.Collection.extend({ - model: model - }) -}; diff --git a/frontend/js/models/audit-log.js b/frontend/js/models/audit-log.js deleted file mode 100644 index c929a0bd..00000000 --- a/frontend/js/models/audit-log.js +++ /dev/null @@ -1,18 +0,0 @@ -const Backbone = require('backbone'); - -const model = Backbone.Model.extend({ - idAttribute: 'id', - - defaults: function () { - return { - name: '' - }; - } -}); - -module.exports = { - Model: model, - Collection: Backbone.Collection.extend({ - model: model - }) -}; diff --git a/frontend/js/models/certificate.js b/frontend/js/models/certificate.js deleted file mode 100644 index c7d0b2d9..00000000 --- a/frontend/js/models/certificate.js +++ /dev/null @@ -1,38 +0,0 @@ -const Backbone = require('backbone'); - -const model = Backbone.Model.extend({ - idAttribute: 'id', - - defaults: function () { - return { - id: undefined, - created_on: null, - modified_on: null, - provider: '', - nice_name: '', - domain_names: [], - expires_on: null, - meta: {}, - // The following are expansions: - owner: null, - proxy_hosts: [], - redirection_hosts: [], - dead_hosts: [] - }; - }, - - /** - * @returns {Boolean} - */ - hasSslFiles: function () { - let meta = this.get('meta'); - return typeof meta['certificate'] !== 'undefined' && meta['certificate'] && typeof meta['certificate_key'] !== 'undefined' && meta['certificate_key']; - } -}); - -module.exports = { - Model: model, - Collection: Backbone.Collection.extend({ - model: model - }) -}; diff --git a/frontend/js/models/dead-host.js b/frontend/js/models/dead-host.js deleted file mode 100644 index 98ceef29..00000000 --- a/frontend/js/models/dead-host.js +++ /dev/null @@ -1,32 +0,0 @@ -const Backbone = require('backbone'); - -const model = Backbone.Model.extend({ - idAttribute: 'id', - - defaults: function () { - return { - id: undefined, - created_on: null, - modified_on: null, - domain_names: [], - certificate_id: 0, - ssl_forced: false, - http2_support: false, - hsts_enabled: false, - hsts_subdomains: false, - enabled: true, - meta: {}, - advanced_config: '', - // The following are expansions: - owner: null, - certificate: null - }; - } -}); - -module.exports = { - Model: model, - Collection: Backbone.Collection.extend({ - model: model - }) -}; diff --git a/frontend/js/models/proxy-host-location.js b/frontend/js/models/proxy-host-location.js deleted file mode 100644 index 2a35059f..00000000 --- a/frontend/js/models/proxy-host-location.js +++ /dev/null @@ -1,35 +0,0 @@ -const Backbone = require('backbone'); - -const model = Backbone.Model.extend({ - idAttribute: 'id', - - defaults: function() { - return { - opened: false, - path: '', - advanced_config: '', - forward_scheme: 'http', - forward_host: '', - forward_port: '80' - } - }, - - toJSON() { - const r = Object.assign({}, this.attributes); - delete r.opened; - return r; - }, - - toggleVisibility: function () { - this.save({ - opened: !this.get('opened') - }); - } -}) - -module.exports = { - Model: model, - Collection: Backbone.Collection.extend({ - model - }) -} \ No newline at end of file diff --git a/frontend/js/models/proxy-host.js b/frontend/js/models/proxy-host.js deleted file mode 100644 index b82d09fe..00000000 --- a/frontend/js/models/proxy-host.js +++ /dev/null @@ -1,40 +0,0 @@ -const Backbone = require('backbone'); - -const model = Backbone.Model.extend({ - idAttribute: 'id', - - defaults: function () { - return { - id: undefined, - created_on: null, - modified_on: null, - domain_names: [], - forward_scheme: 'http', - forward_host: '', - forward_port: null, - access_list_id: 0, - certificate_id: 0, - ssl_forced: false, - hsts_enabled: false, - hsts_subdomains: false, - caching_enabled: false, - allow_websocket_upgrade: false, - block_exploits: false, - http2_support: false, - advanced_config: '', - enabled: true, - meta: {}, - // The following are expansions: - owner: null, - access_list: null, - certificate: null - }; - } -}); - -module.exports = { - Model: model, - Collection: Backbone.Collection.extend({ - model: model - }) -}; diff --git a/frontend/js/models/redirection-host.js b/frontend/js/models/redirection-host.js deleted file mode 100644 index 1d0b0de2..00000000 --- a/frontend/js/models/redirection-host.js +++ /dev/null @@ -1,37 +0,0 @@ -const Backbone = require('backbone'); - -const model = Backbone.Model.extend({ - idAttribute: 'id', - - defaults: function () { - return { - id: undefined, - created_on: null, - modified_on: null, - domain_names: [], - forward_http_code: 0, - forward_scheme: null, - forward_domain_name: '', - preserve_path: true, - certificate_id: 0, - ssl_forced: false, - hsts_enabled: false, - hsts_subdomains: false, - block_exploits: false, - http2_support: false, - advanced_config: '', - enabled: true, - meta: {}, - // The following are expansions: - owner: null, - certificate: null - }; - } -}); - -module.exports = { - Model: model, - Collection: Backbone.Collection.extend({ - model: model - }) -}; diff --git a/frontend/js/models/setting.js b/frontend/js/models/setting.js deleted file mode 100644 index c70a4e9c..00000000 --- a/frontend/js/models/setting.js +++ /dev/null @@ -1,22 +0,0 @@ -const Backbone = require('backbone'); - -const model = Backbone.Model.extend({ - idAttribute: 'id', - - defaults: function () { - return { - id: undefined, - name: '', - description: '', - value: null, - meta: [] - }; - } -}); - -module.exports = { - Model: model, - Collection: Backbone.Collection.extend({ - model: model - }) -}; diff --git a/frontend/js/models/stream.js b/frontend/js/models/stream.js deleted file mode 100644 index e4693549..00000000 --- a/frontend/js/models/stream.js +++ /dev/null @@ -1,29 +0,0 @@ -const Backbone = require('backbone'); - -const model = Backbone.Model.extend({ - idAttribute: 'id', - - defaults: function () { - return { - id: undefined, - created_on: null, - modified_on: null, - incoming_port: null, - forward_ip: null, - forwarding_port: null, - tcp_forwarding: true, - udp_forwarding: false, - enabled: true, - meta: {}, - // The following are expansions: - owner: null - }; - } -}); - -module.exports = { - Model: model, - Collection: Backbone.Collection.extend({ - model: model - }) -}; diff --git a/frontend/js/models/user.js b/frontend/js/models/user.js deleted file mode 100644 index a8e4ed9e..00000000 --- a/frontend/js/models/user.js +++ /dev/null @@ -1,54 +0,0 @@ -const _ = require('underscore'); -const Backbone = require('backbone'); - -const model = Backbone.Model.extend({ - idAttribute: 'id', - - defaults: function () { - return { - id: undefined, - name: '', - nickname: '', - email: '', - is_disabled: false, - roles: [], - permissions: null - }; - }, - - /** - * @returns {Boolean} - */ - isAdmin: function () { - return _.indexOf(this.get('roles'), 'admin') !== -1; - }, - - /** - * Checks if the perm has either `view` or `manage` value - * - * @param {String} item - * @returns {Boolean} - */ - canView: function (item) { - let permissions = this.get('permissions'); - return permissions !== null && typeof permissions[item] !== 'undefined' && ['view', 'manage'].indexOf(permissions[item]) !== -1; - }, - - /** - * Checks if the perm has `manage` value - * - * @param {String} item - * @returns {Boolean} - */ - canManage: function (item) { - let permissions = this.get('permissions'); - return permissions !== null && typeof permissions[item] !== 'undefined' && permissions[item] === 'manage'; - } -}); - -module.exports = { - Model: model, - Collection: Backbone.Collection.extend({ - model: model - }) -}; diff --git a/frontend/package.json b/frontend/package.json index daa48f3c..979d6a30 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,48 +1,91 @@ { - "name": "nginx-proxy-manager", - "version": "0.0.0", - "description": "A beautiful interface for creating Nginx endpoints", - "main": "js/index.js", - "devDependencies": { - "@babel/core": "^7.9.0", - "babel-core": "^6.26.3", - "babel-loader": "^8.1.0", - "babel-minify-webpack-plugin": "^0.3.1", - "babel-preset-env": "^1.7.0", - "backbone": "^1.4.0", - "backbone.marionette": "^4.1.2", - "copy-webpack-plugin": "^5.1.1", - "css-loader": "^3.5.0", - "ejs-lint": "^1.0.1", - "ejs-loader": "^0.3.6", - "ejs-webpack-loader": "^2.2.2", - "file-loader": "^6.0.0", - "html-webpack-plugin": "^4.0.4", - "imports-loader": "^0.8.0", - "jquery": "^3.5.0", - "jquery-mask-plugin": "^1.14.16", - "jquery-serializejson": "^2.9.0", - "marionette.approuter": "^1.0.2", - "marionette.templatecache": "^1.0.0", - "messageformat": "^2.3.0", - "messageformat-loader": "^0.8.1", - "mini-css-extract-plugin": "^0.9.0", - "moment": "^2.24.0", - "node-sass": "^4.13.1", - "nodemon": "^2.0.2", - "numeral": "^2.0.6", - "sass-loader": "^8.0.2", - "style-loader": "^1.1.3", - "tabler-ui": "git+https://github.com/tabler/tabler.git#00f78ad823311bc3ad974ac3e5b0126198f0a813", - "underscore": "^1.12.1", - "webpack": "^4.42.1", - "webpack-cli": "^3.3.11", - "webpack-visualizer-plugin": "^0.1.11" - }, - "scripts": { - "build": "webpack --mode production", - "watch": "webpack --watch --mode development" - }, - "author": "Jamie Curnow ", - "license": "MIT" + "name": "nginxproxymanager", + "version": "1.0.0", + "private": true, + "dependencies": { + "@testing-library/jest-dom": "5.12.0", + "@testing-library/react": "11.2.7", + "@types/humps": "^2.0.0", + "@types/jest": "26.0.23", + "@types/lodash": "4.14.169", + "@types/node": "15.3.0", + "@types/react": "17.0.5", + "@types/react-dom": "17.0.5", + "@types/react-router-dom": "5.1.7", + "@types/styled-components": "5.1.9", + "@typescript-eslint/eslint-plugin": "^4.23.0", + "@typescript-eslint/parser": "^4.23.0", + "babel-eslint": "^10.1.0", + "date-fns": "2.21.3", + "eslint": "^7.26.0", + "eslint-config-prettier": "^8.3.0", + "eslint-config-react-app": "^6.0.0", + "eslint-loader": "^4.0.2", + "eslint-plugin-flowtype": "^5.7.2", + "eslint-plugin-import": "^2.23.2", + "eslint-plugin-jsx-a11y": "^6.4.1", + "eslint-plugin-prettier": "^3.4.0", + "eslint-plugin-react": "^7.23.2", + "eslint-plugin-react-hooks": "^4.2.0", + "humps": "^2.0.1", + "jest-date-mock": "1.0.8", + "jest-fetch-mock": "3.0.3", + "jest-junit": "^12.0.0", + "jest-localstorage-mock": "2.4.12", + "jest-runner-eslint": "0.10.0", + "lodash": "4.17.21", + "moment": "2.29.1", + "node-sass": "^5.0.0", + "prettier": "2.3.0", + "query-string": "7.0.0", + "react": "17.0.2", + "react-async": "10.0.1", + "react-dom": "17.0.2", + "react-router-dom": "^5.2.0", + "react-scripts": "4.0.3", + "rooks": "5.0.2", + "styled-components": "5.3.0", + "tabler-react": "^2.0.0-alpha.1", + "typescript": "^4.2.4" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "lint": "yarn jest --config jest.eslint.js --watch", + "lint:ci": "yarn lint --watchAll=false", + "test": "react-scripts test", + "test:coverage": "yarn test --coverage --watchAll=false", + "test:ci": "yarn test:coverage", + "eject": "react-scripts eject", + "prettier": "prettier \"**/*.+(js|json|yml|css|ts|tsx)\"", + "format": "yarn prettier -- --write", + "lint:fix": "eslint --fix --ext .ts --ext .tsx ." + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + }, + "jest": { + "globalSetup": "/globalSetup.js", + "collectCoverageFrom": [ + "src/**/*.{js,jsx,ts,tsx}", + "!src/testUtils/*" + ], + "coverageThreshold": { + "global": { + "branches": 1, + "functions": 1, + "lines": 1, + "statements": 1 + } + } + } } diff --git a/frontend/app-images/default-avatar.jpg b/frontend/public/images/default-avatar.jpg similarity index 100% rename from frontend/app-images/default-avatar.jpg rename to frontend/public/images/default-avatar.jpg diff --git a/frontend/app-images/favicons/android-chrome-192x192.png b/frontend/public/images/favicon/android-chrome-192x192.png similarity index 100% rename from frontend/app-images/favicons/android-chrome-192x192.png rename to frontend/public/images/favicon/android-chrome-192x192.png diff --git a/frontend/app-images/favicons/android-chrome-512x512.png b/frontend/public/images/favicon/android-chrome-512x512.png similarity index 100% rename from frontend/app-images/favicons/android-chrome-512x512.png rename to frontend/public/images/favicon/android-chrome-512x512.png diff --git a/frontend/app-images/favicons/apple-touch-icon.png b/frontend/public/images/favicon/apple-touch-icon.png similarity index 100% rename from frontend/app-images/favicons/apple-touch-icon.png rename to frontend/public/images/favicon/apple-touch-icon.png diff --git a/frontend/app-images/favicons/browserconfig.xml b/frontend/public/images/favicon/browserconfig.xml similarity index 100% rename from frontend/app-images/favicons/browserconfig.xml rename to frontend/public/images/favicon/browserconfig.xml diff --git a/frontend/app-images/favicons/favicon-16x16.png b/frontend/public/images/favicon/favicon-16x16.png similarity index 100% rename from frontend/app-images/favicons/favicon-16x16.png rename to frontend/public/images/favicon/favicon-16x16.png diff --git a/frontend/app-images/favicons/favicon-32x32.png b/frontend/public/images/favicon/favicon-32x32.png similarity index 100% rename from frontend/app-images/favicons/favicon-32x32.png rename to frontend/public/images/favicon/favicon-32x32.png diff --git a/frontend/app-images/favicons/favicon.ico b/frontend/public/images/favicon/favicon.ico similarity index 100% rename from frontend/app-images/favicons/favicon.ico rename to frontend/public/images/favicon/favicon.ico diff --git a/frontend/app-images/favicons/mstile-150x150.png b/frontend/public/images/favicon/mstile-150x150.png similarity index 100% rename from frontend/app-images/favicons/mstile-150x150.png rename to frontend/public/images/favicon/mstile-150x150.png diff --git a/frontend/app-images/favicons/safari-pinned-tab.svg b/frontend/public/images/favicon/safari-pinned-tab.svg similarity index 100% rename from frontend/app-images/favicons/safari-pinned-tab.svg rename to frontend/public/images/favicon/safari-pinned-tab.svg diff --git a/frontend/app-images/favicons/site.webmanifest b/frontend/public/images/favicon/site.webmanifest similarity index 100% rename from frontend/app-images/favicons/site.webmanifest rename to frontend/public/images/favicon/site.webmanifest diff --git a/frontend/app-images/logo-256.png b/frontend/public/images/logo-256.png similarity index 100% rename from frontend/app-images/logo-256.png rename to frontend/public/images/logo-256.png diff --git a/frontend/public/images/logo-bold-horizontal-grey.svg b/frontend/public/images/logo-bold-horizontal-grey.svg new file mode 100644 index 00000000..c87396f6 --- /dev/null +++ b/frontend/public/images/logo-bold-horizontal-grey.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/images/logo-text-horizontal-grey.png b/frontend/public/images/logo-text-horizontal-grey.png new file mode 100644 index 0000000000000000000000000000000000000000..057a83d1b7a433e79f9d7b022488f29aee1c1e0c GIT binary patch literal 22203 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%0Ai+xv4%rSdX>J#T}H=Vzde z7MCY4i9%)64EC%u>+_pL+ViE3n#mTjk`UKwz#|dxN99%`;NZb9%pJxG3?NfbvdoYv z-~M?l8n0~8mB!E0gZZz7*&J?EJW**#p(Kg4-xjW-o6q2hV4?)HFO)tLP2za-3Pz%` z%*#LwsI2v;N`gmn4-ll3$f_)29m$Im7f@-Iv1!d__(2vEWPaEL9-kgKW;7OI7>4O! zR#**W1_W#^B!o6!4KI&<=xV{dHrWb9;E_;s`~<#GszqA=u6`~$7nRkZ%80a3Nx--n zR$@d+57{7V(^~QA8^bRaOuRFe-sAqN`QjLcVU~{7GY{GYiS^d-yD`k$tY?{E`&&=?q~QhUt7dArtc>C(ihbMY^9296tuI3BU`BbWet1n2w`U zQu(&y{2Ks<0Q~8F+~>EpGu`P|O)ld27=~dwj!vruUvT!Z56^RR)HMJPIXm9jTi+Rm zVVI8HO!Tr-8QD9_N}}jqV--@ + + + + + Nginx Proxy Manager + + + + + + + + + + + + + + + + + + + + +
    + + diff --git a/frontend/scss/custom.scss b/frontend/scss/custom.scss deleted file mode 100644 index 4037dcf6..00000000 --- a/frontend/scss/custom.scss +++ /dev/null @@ -1,42 +0,0 @@ -$primary-color: #2bcbba; - -.loader { - color: $primary-color; -} - -a { - color: $primary-color; -} - -a:hover { - color: darken($primary-color, 10%); -} - -.dropdown-header { - padding-left: 1rem; -} - -.dropdown-item.active, .dropdown-item:active { - background-color: $primary-color; -} - -.custom-switch-input:checked ~ .custom-switch-indicator { - background: $primary-color; -} - -.min-100 { - min-height: 100px; -} - -.card-options .dropdown-menu a:not(.btn) { - margin-left: 0; -} - -.wrap { - display: flex; - flex-wrap: wrap; -} - -.col-login { - max-width: 48rem; -} \ No newline at end of file diff --git a/frontend/scss/fonts.scss b/frontend/scss/fonts.scss deleted file mode 100644 index f0ec1b73..00000000 --- a/frontend/scss/fonts.scss +++ /dev/null @@ -1,39 +0,0 @@ -/* source-sans-pro-regular - latin-ext_latin */ -@font-face { - font-family: 'Source Sans Pro'; - font-style: normal; - font-weight: 400; - src: local(''), - url('../fonts/source-sans-pro/source-sans-pro-v14-latin-ext_latin-regular.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */ - url('../fonts/source-sans-pro/source-sans-pro-v14-latin-ext_latin-regular.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ -} - -/* source-sans-pro-italic - latin-ext_latin */ -@font-face { - font-family: 'Source Sans Pro'; - font-style: italic; - font-weight: 400; - src: local(''), - url('../fonts/source-sans-pro/source-sans-pro-v14-latin-ext_latin-italic.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */ - url('../fonts/source-sans-pro/source-sans-pro-v14-latin-ext_latin-italic.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ -} - -/* source-sans-pro-700italic - latin-ext_latin */ -@font-face { - font-family: 'Source Sans Pro'; - font-style: italic; - font-weight: 700; - src: local(''), - url('../fonts/source-sans-pro/source-sans-pro-v14-latin-ext_latin-700italic.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */ - url('../fonts/source-sans-pro/source-sans-pro-v14-latin-ext_latin-700italic.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ -} - -/* source-sans-pro-700 - latin-ext_latin */ -@font-face { - font-family: 'Source Sans Pro'; - font-style: normal; - font-weight: 700; - src: local(''), - url('../fonts/source-sans-pro/source-sans-pro-v14-latin-ext_latin-700.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */ - url('../fonts/source-sans-pro/source-sans-pro-v14-latin-ext_latin-700.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ -} diff --git a/frontend/scss/selectize.scss b/frontend/scss/selectize.scss deleted file mode 100644 index e12d5b69..00000000 --- a/frontend/scss/selectize.scss +++ /dev/null @@ -1,196 +0,0 @@ -.selectize-dropdown-header { - position: relative; - padding: 5px 8px; - background: #f8f8f8; - border-bottom: 1px solid #d0d0d0; - -webkit-border-radius: 3px 3px 0 0; - -moz-border-radius: 3px 3px 0 0; - border-radius: 3px 3px 0 0; -} - -.selectize-dropdown-header-close { - position: absolute; - top: 50%; - right: 8px; - margin-top: -12px; - font-size: 20px !important; - line-height: 20px; - color: #303030; - opacity: 0.4; -} - -.selectize-dropdown-header-close:hover { - color: #000000; -} - -.selectize-dropdown.plugin-optgroup_columns .optgroup { - float: left; - border-top: 0 none; - border-right: 1px solid #f2f2f2; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} - -.selectize-dropdown.plugin-optgroup_columns .optgroup:last-child { - border-right: 0 none; -} - -.selectize-dropdown.plugin-optgroup_columns .optgroup:before { - display: none; -} - -.selectize-dropdown.plugin-optgroup_columns .optgroup-header { - border-top: 0 none; -} - -.selectize-control.plugin-remove_button [data-value] { - position: relative; - padding-right: 24px !important; -} - -.selectize-control.plugin-remove_button [data-value] .remove { - position: absolute; - top: 0; - right: 0; - bottom: 0; - display: inline-block; - width: 17px; - padding: 2px 0 0 0; - font-size: 12px; - font-weight: bold; - color: inherit; - text-align: center; - text-decoration: none; - vertical-align: middle; - border-left: 1px solid #0073bb; - -webkit-border-radius: 0 2px 2px 0; - -moz-border-radius: 0 2px 2px 0; - border-radius: 0 2px 2px 0; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} - -.selectize-control.plugin-remove_button [data-value] .remove:hover { - background: rgba(0, 0, 0, 0.05); -} - -.selectize-control.plugin-remove_button [data-value].active .remove { - border-left-color: #00578d; -} - -.selectize-control.plugin-remove_button .disabled [data-value] .remove:hover { - background: none; -} - -.selectize-control.plugin-remove_button .disabled [data-value] .remove { - border-left-color: #aaaaaa; -} - -.selectize-control { - position: relative; -} - -.selectize-dropdown { - font-family: inherit; - font-size: 13px; - -webkit-font-smoothing: inherit; - line-height: 18px; - color: #303030; -} - -.selectize-control.single { - display: inline-block; - cursor: text; - background: #ffffff; -} - -.selectize-dropdown { - position: absolute; - z-index: 10; - margin: -1px 0 0 0; - background: #ffffff; - border: 1px solid #d0d0d0; - border-top: 0 none; - -webkit-border-radius: 0 0 3px 3px; - -moz-border-radius: 0 0 3px 3px; - border-radius: 0 0 3px 3px; - -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} - -.selectize-dropdown [data-selectable] { - overflow: hidden; - cursor: pointer; -} - -.selectize-dropdown [data-selectable] .highlight { - background: rgba(125, 168, 208, 0.2); - -webkit-border-radius: 1px; - -moz-border-radius: 1px; - border-radius: 1px; -} - -.selectize-dropdown [data-selectable], -.selectize-dropdown .optgroup-header { - padding: 5px 8px; -} - -.selectize-dropdown .optgroup:first-child .optgroup-header { - border-top: 0 none; -} - -.selectize-dropdown .optgroup-header { - color: #303030; - cursor: default; - background: #ffffff; -} - -.selectize-dropdown .active { - color: #495c68; - background-color: #f5fafd; -} - -.selectize-dropdown .active.create { - color: #495c68; -} - -.selectize-dropdown .create { - color: rgba(48, 48, 48, 0.5); -} - -.selectize-dropdown-content { - max-height: 200px; - overflow-x: hidden; - overflow-y: auto; - - .title { - font-weight: bold; - } - - .description { - padding-left: 16px; - } -} - -.selectize-dropdown .optgroup-header { - padding-top: 7px; - font-size: 0.85em; - font-weight: bold; -} - -.selectize-dropdown .optgroup { - border-top: 1px solid #f0f0f0; -} - -.selectize-dropdown .optgroup:first-child { - border-top: 0 none; -} - -.custom-select { - height: auto; -} diff --git a/frontend/scss/styles.scss b/frontend/scss/styles.scss deleted file mode 100644 index 52733097..00000000 --- a/frontend/scss/styles.scss +++ /dev/null @@ -1,17 +0,0 @@ -@import "~tabler-ui/dist/assets/css/dashboard"; -@import "tabler-extra"; -@import "fonts"; -@import "selectize"; -@import "custom"; - -/* Before any JS content is loaded */ -#app > .loader, #login > .loader, .container > .loader { - position: absolute; - left: 49%; - top: 40%; - display: block; -} - -.no-js-warning { - margin-top: 100px; -} diff --git a/frontend/scss/tabler-extra.scss b/frontend/scss/tabler-extra.scss deleted file mode 100644 index 3ddd0ed4..00000000 --- a/frontend/scss/tabler-extra.scss +++ /dev/null @@ -1,170 +0,0 @@ -$teal: #2bcbba; -$yellow: #f1c40f; -$blue: #467fcf; -$pink: #f66d9b; - -.tag { - margin-bottom: .5em; - margin-right: .5em; -} - -.tag.hover-green:hover, .tag.hover-green:active, .tag.hover-green:focus { - background-color: #5eba00; - cursor: pointer; - color: #fff; -} - -.tag.hover-red:hover, .tag.hover-red:active, .tag.hover-red:focus { - background-color: #cd201f; - cursor: pointer; - color: #fff; -} - -/* For Card bodies where I don't want padding */ -.card-body.no-padding { - padding: 0; -} - -/* For some reason this class doesn't have 'display: flex' when it should. https://preview.tabler.io/docs/buttons.html#list-of-buttons */ -.btn-list { - display: flex; -} - -/* Teal Outline Buttons */ -.btn-outline-teal { - color: $teal; - background-color: transparent; - background-image: none; - border-color: $teal; -} - -.btn-outline-teal:hover { - color: #fff; - background-color: $teal; - border-color: $teal; -} - -.btn-outline-teal:not(:disabled):not(.disabled):active, .btn-outline-teal:not(:disabled):not(.disabled).active, .show > .btn-outline-teal.dropdown-toggle { - color: #fff; - background-color: $teal; - border-color: $teal; -} - -.tag.hover-teal:hover, .tag.hover-teal:active, .tag.hover-teal:focus { - background-color: $teal; - color: #fff; - cursor: pointer; -} - -/* Yellow Outline Buttons */ -.btn-outline-yellow { - color: $yellow; - background-color: transparent; - background-image: none; - border-color: $yellow; -} - -.btn-outline-yellow:hover { - color: #fff; - background-color: $yellow; - border-color: $yellow; -} - -.btn-outline-yellow:not(:disabled):not(.disabled):active, .btn-outline-yellow:not(:disabled):not(.disabled).active, .show > .btn-outline-yellow.dropdown-toggle { - color: #fff; - background-color: $yellow; - border-color: $yellow; -} - -.tag.hover-yellow:hover, .tag.hover-yellow:active, .tag.hover-yellow:focus { - background-color: $yellow; - cursor: pointer; - color: #fff; -} - -/* Blue Outline Buttons */ -.btn-outline-blue { - color: $blue; - background-color: transparent; - background-image: none; - border-color: $blue; -} - -.btn-outline-blue:hover { - color: #fff; - background-color: $blue; - border-color: $blue; -} - -.btn-outline-blue:not(:disabled):not(.disabled):active, .btn-outline-blue:not(:disabled):not(.disabled).active, .show > .btn-outline-blue.dropdown-toggle { - color: #fff; - background-color: $blue; - border-color: $blue; -} - -.tag.hover-blue:hover, .tag.hover-blue:active, .tag.hover-blue:focus { - background-color: $blue; - cursor: pointer; - color: #fff; -} - -/* Pink Outline Buttons */ -.btn-outline-pink { - color: $pink; - background-color: transparent; - background-image: none; - border-color: $pink; -} - -.btn-outline-pink:hover { - color: #fff; - background-color: $pink; - border-color: $pink; -} - -.btn-outline-pink:not(:disabled):not(.disabled):active, .btn-outline-pink:not(:disabled):not(.disabled).active, .show > .btn-outline-pink.dropdown-toggle { - color: #fff; - background-color: $pink; - border-color: $pink; -} - -.tag.hover-pink:hover, .tag.hover-pink:active, .tag.hover-pink:focus { - background-color: $pink; - cursor: pointer; -} - -/* dimmer */ - -.dimmer .loader { - margin-top: 50px; -} - -/* modal tabs */ - -.modal-body.has-tabs { - padding: 0; - - .nav-tabs { - margin: 0; - } - - .tab-content { - padding: 1rem; - } -} - -/* modal wide */ - -@media (min-width: 576px) { - .modal-dialog.wide { - max-width: 700px; - margin: 1.75rem auto; - } -} - - -/* Form mod */ - -textarea.form-control.text-monospace { - font-size: 12px; -} diff --git a/frontend/src/App.test.tsx b/frontend/src/App.test.tsx new file mode 100644 index 00000000..1aad9cc7 --- /dev/null +++ b/frontend/src/App.test.tsx @@ -0,0 +1,11 @@ +import * as React from "react"; + +import * as ReactDOM from "react-dom"; + +import App from "./App"; + +it("renders without crashing", () => { + const div = document.createElement("div"); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx new file mode 100644 index 00000000..ad9a2908 --- /dev/null +++ b/frontend/src/App.tsx @@ -0,0 +1,16 @@ +import React from "react"; + +import Router from "components/Router"; +import { AuthProvider, HealthProvider } from "context"; + +function App() { + return ( + + + + + + ); +} + +export default App; diff --git a/frontend/src/api/npm/base.ts b/frontend/src/api/npm/base.ts new file mode 100644 index 00000000..2d421463 --- /dev/null +++ b/frontend/src/api/npm/base.ts @@ -0,0 +1,89 @@ +import { camelizeKeys, decamelizeKeys } from "humps"; +import AuthStore from "modules/AuthStore"; +import * as queryString from "query-string"; + +interface BuildUrlArgs { + url: string; + params?: queryString.StringifiableRecord; +} + +function buildUrl({ url, params }: BuildUrlArgs) { + const endpoint = url.replace(/^\/|\/$/g, ""); + const apiParams = params ? `?${queryString.stringify(params)}` : ""; + const apiUrl = `/api/${endpoint}${apiParams}`; + return apiUrl; +} + +function buildAuthHeader(): Record | undefined { + if (AuthStore.token) { + return { Authorization: `Bearer ${AuthStore.token.token}` }; + } + return {}; +} + +function buildBody(data?: Record) { + if (data) { + return JSON.stringify(decamelizeKeys(data)); + } +} + +async function processResponse(response: Response) { + const payload = await response.json(); + if (!response.ok) { + throw new Error(payload.error.message); + } + return camelizeKeys(payload) as any; +} + +interface GetArgs { + url: string; + params?: queryString.StringifiableRecord; +} + +export async function get( + { url, params }: GetArgs, + abortController?: AbortController, +) { + const apiUrl = buildUrl({ url, params }); + const method = "GET"; + const signal = abortController?.signal; + const headers = buildAuthHeader(); + const response = await fetch(apiUrl, { method, headers, signal }); + return processResponse(response); +} + +interface PostArgs { + url: string; + data?: any; +} + +export async function post( + { url, data }: PostArgs, + abortController?: AbortController, +) { + const apiUrl = buildUrl({ url }); + const method = "POST"; + const headers = { ...buildAuthHeader(), "Content-Type": "application/json" }; + const signal = abortController?.signal; + const body = buildBody(data); + const response = await fetch(apiUrl, { method, headers, body, signal }); + return processResponse(response); +} + +interface PutArgs { + url: string; + data?: any; +} + +export async function put( + { url, data }: PutArgs, + abortController?: AbortController, +) { + const apiUrl = buildUrl({ url }); + const method = "PUT"; + const headers = { ...buildAuthHeader(), "Content-Type": "application/json" }; + const signal = abortController?.signal; + const body = buildBody(data); + const response = await fetch(apiUrl, { method, headers, body, signal }); + return processResponse(response); +} diff --git a/frontend/src/api/npm/createUser.ts b/frontend/src/api/npm/createUser.ts new file mode 100644 index 00000000..08c7f100 --- /dev/null +++ b/frontend/src/api/npm/createUser.ts @@ -0,0 +1,32 @@ +import * as api from "./base"; +import { UserResponse } from "./responseTypes"; + +interface AuthOptions { + type: string; + secret: string; +} + +interface Options { + payload: { + name: string; + nickname: string; + email: string; + roles: string[]; + isDisabled: boolean; + auth: AuthOptions; + }; +} + +export async function createUser( + { payload }: Options, + abortController?: AbortController, +): Promise { + const { result } = await api.post( + { + url: "/users", + data: payload, + }, + abortController, + ); + return result; +} diff --git a/frontend/src/api/npm/getToken.ts b/frontend/src/api/npm/getToken.ts new file mode 100644 index 00000000..ff7c5557 --- /dev/null +++ b/frontend/src/api/npm/getToken.ts @@ -0,0 +1,24 @@ +import * as api from "./base"; +import { TokenResponse } from "./responseTypes"; + +interface Options { + payload: { + type: string; + identity: string; + secret: string; + }; +} + +export async function getToken( + { payload }: Options, + abortController?: AbortController, +): Promise { + const { result } = await api.post( + { + url: "/tokens", + data: payload, + }, + abortController, + ); + return result; +} diff --git a/frontend/src/api/npm/getUser.ts b/frontend/src/api/npm/getUser.ts new file mode 100644 index 00000000..70230836 --- /dev/null +++ b/frontend/src/api/npm/getUser.ts @@ -0,0 +1,12 @@ +import * as api from "./base"; +import { UserResponse } from "./responseTypes"; + +export async function getUser( + id: number | string = "me", +): Promise { + const userId = id ? id : "me"; + const { result } = await api.get({ + url: `/users/${userId}`, + }); + return result; +} diff --git a/frontend/src/api/npm/index.ts b/frontend/src/api/npm/index.ts new file mode 100644 index 00000000..c4ffe25c --- /dev/null +++ b/frontend/src/api/npm/index.ts @@ -0,0 +1,6 @@ +export * from "./createUser"; +export * from "./getToken"; +export * from "./getUser"; +export * from "./refreshToken"; +export * from "./requestHealth"; +export * from "./responseTypes"; diff --git a/frontend/src/api/npm/refreshToken.ts b/frontend/src/api/npm/refreshToken.ts new file mode 100644 index 00000000..de31f401 --- /dev/null +++ b/frontend/src/api/npm/refreshToken.ts @@ -0,0 +1,14 @@ +import * as api from "./base"; +import { TokenResponse } from "./responseTypes"; + +export async function refreshToken( + abortController?: AbortController, +): Promise { + const { result } = await api.get( + { + url: "/tokens", + }, + abortController, + ); + return result; +} diff --git a/frontend/src/api/npm/requestHealth.ts b/frontend/src/api/npm/requestHealth.ts new file mode 100644 index 00000000..92a04006 --- /dev/null +++ b/frontend/src/api/npm/requestHealth.ts @@ -0,0 +1,15 @@ +import * as api from "./base"; +import { HealthResponse } from "./responseTypes"; + +// Request function. +export async function requestHealth( + abortController?: AbortController, +): Promise { + const { result } = await api.get( + { + url: "", + }, + abortController, + ); + return result; +} diff --git a/frontend/src/api/npm/responseTypes.ts b/frontend/src/api/npm/responseTypes.ts new file mode 100644 index 00000000..900af8ff --- /dev/null +++ b/frontend/src/api/npm/responseTypes.ts @@ -0,0 +1,33 @@ +export interface HealthResponse { + commit: string; + errorReporting: boolean; + healthy: boolean; + setup: boolean; + version: string; +} + +export interface UserAuthResponse { + id: number; + userId: number; + type: string; + createdOn: number; + updatedOn: number; +} + +export interface TokenResponse { + expires: number; + token: string; +} + +export interface UserResponse { + id: number; + name: string; + nickname: string; + email: string; + createdOn: number; + updatedOn: number; + roles: string[]; + gravatarUrl: string; + isDisabled: boolean; + auth?: UserAuthResponse; +} diff --git a/frontend/src/components/Footer.tsx b/frontend/src/components/Footer.tsx new file mode 100644 index 00000000..64d81357 --- /dev/null +++ b/frontend/src/components/Footer.tsx @@ -0,0 +1,56 @@ +import React from "react"; + +import { useHealthState } from "context"; +import styled from "styled-components"; +import { Site } from "tabler-react"; + +const FixedFooterWrapper = styled.div` + position: fixed; + bottom: 0; + width: 100%; +`; + +interface Props { + fixed?: boolean; +} +function Footer({ fixed }: Props) { + const { health } = useHealthState(); + + const footerNav = ( +
    + + User Guide + {" "} + {String.fromCharCode(183)}{" "} + + Changelog + {" "} + {String.fromCharCode(183)}{" "} + + Github + +
    + ); + + const note = + "v" + health.version + " " + String.fromCharCode(183) + " " + health.commit; + + return fixed ? ( + + + + ) : ( + + ); +} + +export { Footer }; diff --git a/frontend/src/components/Loading.tsx b/frontend/src/components/Loading.tsx new file mode 100644 index 00000000..03b0ffae --- /dev/null +++ b/frontend/src/components/Loading.tsx @@ -0,0 +1,18 @@ +import React from "react"; + +import styled from "styled-components"; +import { Loader } from "tabler-react"; + +const Root = styled.div` + text-align: center; +`; + +function Loading() { + return ( + + + + ); +} + +export { Loading }; diff --git a/frontend/src/components/Router.tsx b/frontend/src/components/Router.tsx new file mode 100644 index 00000000..553641d7 --- /dev/null +++ b/frontend/src/components/Router.tsx @@ -0,0 +1,57 @@ +import React, { lazy, Suspense } from "react"; + +import { Loading, SiteWrapper, SinglePage } from "components"; +import { useAuthState, useHealthState, UserProvider } from "context"; +import { BrowserRouter, Switch, Route } from "react-router-dom"; + +const Setup = lazy(() => import("pages/Setup")); +const Dashboard = lazy(() => import("pages/Dashboard")); +const Login = lazy(() => import("pages/Login")); + +function Router() { + const { health } = useHealthState(); + const { authenticated } = useAuthState(); + const Spinner = ( + + + + ); + + if (health.loading) { + return Spinner; + } + + if (health.healthy && !health.setup) { + return ( + + + + ); + } + + if (!authenticated) { + return ( + + + + ); + } + + return ( + + + + + + + + + + + + + + ); +} + +export default Router; diff --git a/frontend/src/components/SinglePage.tsx b/frontend/src/components/SinglePage.tsx new file mode 100644 index 00000000..09f89895 --- /dev/null +++ b/frontend/src/components/SinglePage.tsx @@ -0,0 +1,32 @@ +import React, { ReactNode } from "react"; + +import { Footer } from "components"; +import styled from "styled-components"; + +const Root = styled.div` + display: flex; + flex-direction: column; + justify-content: center; + min-height: 100%; +`; + +const Wrapper = styled.div` + flex: 1 1 auto; + display: flex; + align-items: center; + justify-content: center; +`; + +interface Props { + children?: ReactNode; +} +function SinglePage({ children }: Props) { + return ( + + {children} +

    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 literal 0 HcmV?d00001 diff --git a/docker/rootfs/var/www/html/index.html b/docker/rootfs/var/www/html/index.html index 8478b47f..92c1d8fa 100644 --- a/docker/rootfs/var/www/html/index.html +++ b/docker/rootfs/var/www/html/index.html @@ -1,24 +1,30 @@ - - - - - Default Site - - - - -