diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 05669038..beb0a9e2 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -3,48 +3,48 @@ updates: - package-ecosystem: npm directory: "/packages/dashboard" schedule: - interval: weekly + interval: monthly - package-ecosystem: npm directory: "/packages/dnslink-api" schedule: - interval: weekly + interval: monthly - package-ecosystem: npm directory: "/packages/handshake-api" schedule: - interval: weekly + interval: monthly - package-ecosystem: npm directory: "/packages/health-check" schedule: - interval: weekly + interval: monthly - package-ecosystem: npm directory: "/packages/website" schedule: - interval: weekly + interval: monthly - package-ecosystem: docker directory: "/docker/nginx" schedule: - interval: weekly + interval: monthly - package-ecosystem: docker directory: "/docker/sia" schedule: - interval: weekly + interval: monthly - package-ecosystem: docker directory: "/packages/dashboard" schedule: - interval: weekly + interval: monthly - package-ecosystem: docker directory: "/packages/dnslink-api" schedule: - interval: weekly + interval: monthly - package-ecosystem: docker directory: "/packages/handshake-api" schedule: - interval: weekly + interval: monthly - package-ecosystem: docker directory: "/packages/health-check" schedule: - interval: weekly + interval: monthly - package-ecosystem: docker directory: "/packages/website" schedule: - interval: weekly + interval: monthly diff --git a/.github/workflows/lint-dockerfiles.yml b/.github/workflows/lint-dockerfiles.yml index 467dc117..afdd6558 100644 --- a/.github/workflows/lint-dockerfiles.yml +++ b/.github/workflows/lint-dockerfiles.yml @@ -14,6 +14,7 @@ jobs: matrix: dockerfile: - docker/nginx/Dockerfile + - docker/nginx/testing/Dockerfile - docker/sia/Dockerfile - packages/dashboard/Dockerfile - packages/dashboard-v2/Dockerfile diff --git a/.github/workflows/nginx-lua-unit-tests.yml b/.github/workflows/nginx-lua-unit-tests.yml index b86a4e04..5af9c101 100644 --- a/.github/workflows/nginx-lua-unit-tests.yml +++ b/.github/workflows/nginx-lua-unit-tests.yml @@ -6,48 +6,41 @@ name: Nginx Lua Unit Tests on: push: branches: - - "master" + - master paths: - - ".github/workflows/nginx-lua-unit-tests.yml" - - "docker/nginx/libs/**.lua" + - docker/nginx/libs/** pull_request: paths: - - ".github/workflows/nginx-lua-unit-tests.yml" - - "docker/nginx/libs/**.lua" + - docker/nginx/libs/** jobs: - build: + test: runs-on: ubuntu-latest - + container: openresty/openresty:1.19.9.1-focal steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 - with: - python-version: "3.x" - architecture: "x64" + - uses: actions/checkout@v3 - name: Install Dependencies run: | - pip install hererocks - hererocks env --lua=5.1 -rlatest - source env/bin/activate + luarocks install lua-resty-http + luarocks install hasher luarocks install busted luarocks install luacov - luarocks install hasher luarocks install luacheck - - name: Lint code - run: | - source env/bin/activate - luacheck docker/nginx/libs --std ngx_lua+busted + - name: Lint Code With Luacheck + run: luacheck docker/nginx/libs --std ngx_lua+busted - - name: Unit Tests - run: | - source env/bin/activate - busted --verbose --coverage --pattern=spec --directory=docker/nginx/libs . - cd docker/nginx/libs && luacov + - name: Run Tests With Busted + # ran from root repo directory; produces luacov.stats.out file + run: docker/nginx/testing/rbusted --lpath='docker/nginx/libs/?.lua;docker/nginx/libs/?/?.lua' --verbose --coverage --pattern=spec docker/nginx/libs - - uses: codecov/codecov-action@v2 + - name: Generate Code Coverage Report With Luacov + # requires config file in cwd; produces luacov.report.out file + run: cp docker/nginx/testing/.luacov . && luacov && rm .luacov + + - uses: codecov/codecov-action@v3 with: - directory: docker/nginx/libs + root_dir: ${GITHUB_WORKSPACE} + files: ./luacov.report.out flags: nginx-lua diff --git a/.gitignore b/.gitignore index 4b85194e..8a98ee28 100644 --- a/.gitignore +++ b/.gitignore @@ -86,6 +86,10 @@ __pycache__ /.idea/ /venv* +# Luacov file +luacov.stats.out +luacov.report.out + # Setup-script log files setup-scripts/serverload.log setup-scripts/serverload.json diff --git a/README.md b/README.md index 337f9791..e2c1714f 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ ## Latest Setup Documentation Latest Skynet Webportal setup documentation and the setup process Skynet Labs -supports is located at https://docs.siasky.net/webportal-management/overview. +supports is located at https://portal-docs.skynetlabs.com/. Some scripts and setup documentation contained in this repository (`skynet-webportal`) may be outdated and generally should not be used. diff --git a/docker-compose.abuse-scanner.yml b/docker-compose.abuse-scanner.yml index 4edb6556..6080ebcd 100644 --- a/docker-compose.abuse-scanner.yml +++ b/docker-compose.abuse-scanner.yml @@ -10,7 +10,7 @@ services: abuse-scanner: # uncomment "build" and comment out "image" to build from sources # build: https://github.com/SkynetLabs/abuse-scanner.git#main - image: skynetlabs/abuse-scanner + image: skynetlabs/abuse-scanner:0.1.0 container_name: abuse-scanner restart: unless-stopped logging: *default-logging diff --git a/docker-compose.accounts.yml b/docker-compose.accounts.yml index 3c2c46a9..e4806e91 100644 --- a/docker-compose.accounts.yml +++ b/docker-compose.accounts.yml @@ -20,9 +20,11 @@ services: - ACCOUNTS_LIMIT_ACCESS=${ACCOUNTS_LIMIT_ACCESS:-authenticated} # default to authenticated access only accounts: - # uncomment "build" and comment out "image" to build from sources - # build: https://github.com/SkynetLabs/skynet-accounts.git#main - image: skynetlabs/skynet-accounts + build: + context: ./docker/accounts + dockerfile: Dockerfile + args: + branch: main container_name: accounts restart: unless-stopped logging: *default-logging @@ -55,9 +57,11 @@ services: - mongo dashboard: - build: - context: ./packages/dashboard - dockerfile: Dockerfile + # uncomment "build" and comment out "image" to build from sources + # build: + # context: https://github.com/SkynetLabs/skynet-webportal.git#master + # dockerfile: ./packages/dashboard/Dockerfile + image: skynetlabs/dashboard container_name: dashboard restart: unless-stopped logging: *default-logging @@ -78,8 +82,8 @@ services: dashboard-v2: build: - context: ./packages/dashboard-v2 - dockerfile: Dockerfile + context: . + dockerfile: packages/dashboard-v2/Dockerfile container_name: dashboard-v2 restart: unless-stopped logging: *default-logging diff --git a/docker-compose.blocker.yml b/docker-compose.blocker.yml index edcb45c0..29d29e34 100644 --- a/docker-compose.blocker.yml +++ b/docker-compose.blocker.yml @@ -15,7 +15,7 @@ services: blocker: # uncomment "build" and comment out "image" to build from sources # build: https://github.com/SkynetLabs/blocker.git#main - image: skynetlabs/blocker + image: skynetlabs/blocker:0.1.0 container_name: blocker restart: unless-stopped logging: *default-logging diff --git a/docker-compose.malware-scanner.yml b/docker-compose.malware-scanner.yml index fba60f98..ee3b6700 100644 --- a/docker-compose.malware-scanner.yml +++ b/docker-compose.malware-scanner.yml @@ -28,7 +28,7 @@ services: malware-scanner: # uncomment "build" and comment out "image" to build from sources # build: https://github.com/SkynetLabs/malware-scanner.git#main - image: skynetlabs/malware-scanner + image: skynetlabs/malware-scanner:0.1.0 container_name: malware-scanner restart: unless-stopped logging: *default-logging diff --git a/docker-compose.yml b/docker-compose.yml index 5838f136..6450a10f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -54,9 +54,11 @@ services: - ./docker/data/certbot:/etc/letsencrypt nginx: - build: - context: ./docker/nginx - dockerfile: Dockerfile + # uncomment "build" and comment out "image" to build from sources + # build: + # context: https://github.com/SkynetLabs/skynet-webportal.git#master + # dockerfile: ./docker/nginx/Dockerfile + image: skynetlabs/nginx container_name: nginx restart: unless-stopped logging: *default-logging @@ -69,6 +71,10 @@ services: - ./docker/data/nginx/skynet:/data/nginx/skynet:ro - ./docker/data/sia/apipassword:/data/sia/apipassword:ro - ./docker/data/certbot:/etc/letsencrypt + - ./docker/nginx/libs:/etc/nginx/libs + - ./docker/nginx/conf.d:/etc/nginx/conf.d + - ./docker/nginx/conf.d.templates:/etc/nginx/templates + - ./docker/nginx/nginx.conf:/usr/local/openresty/nginx/conf/nginx.conf networks: shared: ipv4_address: 10.10.10.30 diff --git a/docker/accounts/Dockerfile b/docker/accounts/Dockerfile new file mode 100644 index 00000000..5cbf359a --- /dev/null +++ b/docker/accounts/Dockerfile @@ -0,0 +1,22 @@ +FROM golang:1.16.7 +LABEL maintainer="SkynetLabs " + +ENV GOOS linux +ENV GOARCH amd64 + +ARG branch=main + +WORKDIR /root + +RUN git clone --single-branch --branch ${branch} https://github.com/SkynetLabs/skynet-accounts.git && \ + cd skynet-accounts && \ + go mod download && \ + make release + +ENV SKYNET_DB_HOST="localhost" +ENV SKYNET_DB_PORT="27017" +ENV SKYNET_DB_USER="username" +ENV SKYNET_DB_PASS="password" +ENV SKYNET_ACCOUNTS_PORT=3000 + +ENTRYPOINT ["skynet-accounts"] diff --git a/docker/nginx/Dockerfile b/docker/nginx/Dockerfile index eca1e3d8..f35c0aff 100644 --- a/docker/nginx/Dockerfile +++ b/docker/nginx/Dockerfile @@ -2,25 +2,20 @@ FROM openresty/openresty:1.19.9.1-focal WORKDIR / -RUN luarocks install lua-resty-http && \ - luarocks install hasher && \ - openssl req -new -newkey rsa:2048 -days 3650 -nodes -x509 \ - -subj '/CN=local-certificate' \ - -keyout /etc/ssl/local-certificate.key \ - -out /etc/ssl/local-certificate.crt +RUN apt-get update && apt-get --no-install-recommends -y install bc=1.07.1-2build1 && \ + apt-get clean && rm -rf /var/lib/apt/lists/* && \ + luarocks install lua-resty-http && \ + luarocks install hasher -COPY mo ./ -COPY libs /etc/nginx/libs -COPY conf.d /etc/nginx/conf.d -COPY conf.d.templates /etc/nginx/conf.d.templates -COPY nginx.conf /usr/local/openresty/nginx/conf/nginx.conf +# reload nginx every 6 hours (for reloading certificates) +ENV NGINX_ENTRYPOINT_RELOAD_EVERY_X_HOURS 6 -CMD [ "bash", "-c", \ - "./mo < /etc/nginx/conf.d.templates/server.account.conf > /etc/nginx/conf.d/server.account.conf ; \ - ./mo < /etc/nginx/conf.d.templates/server.api.conf > /etc/nginx/conf.d/server.api.conf; \ - ./mo < /etc/nginx/conf.d.templates/server.dnslink.conf > /etc/nginx/conf.d/server.dnslink.conf; \ - ./mo < /etc/nginx/conf.d.templates/server.hns.conf > /etc/nginx/conf.d/server.hns.conf; \ - ./mo < /etc/nginx/conf.d.templates/server.skylink.conf > /etc/nginx/conf.d/server.skylink.conf ; \ - while :; do sleep 6h & wait ${!}; /usr/local/openresty/bin/openresty -s reload; done & \ - /usr/local/openresty/bin/openresty '-g daemon off;'" \ - ] +# copy entrypoint and entrypoint scripts +COPY docker/nginx/docker-entrypoint.sh / +COPY docker/nginx/docker-entrypoint.d /docker-entrypoint.d + +ENTRYPOINT ["/docker-entrypoint.sh"] + +STOPSIGNAL SIGQUIT + +CMD ["nginx", "-g", "daemon off;"] diff --git a/docker/nginx/conf.d.templates/server.account.conf b/docker/nginx/conf.d.templates/server.account.conf deleted file mode 100644 index af3b7c4d..00000000 --- a/docker/nginx/conf.d.templates/server.account.conf +++ /dev/null @@ -1,45 +0,0 @@ -{{#ACCOUNTS_ENABLED}} - {{#PORTAL_DOMAIN}} - server { - server_name account.{{PORTAL_DOMAIN}}; # example: account.siasky.net - - include /etc/nginx/conf.d/server/server.http; - } - - server { - server_name account.{{PORTAL_DOMAIN}}; # example: account.siasky.net - - set_by_lua_block $skynet_portal_domain { return "{{PORTAL_DOMAIN}}" } - set_by_lua_block $skynet_server_domain { - -- fall back to portal domain if server domain is not defined - if "{{SERVER_DOMAIN}}" == "" then - return "{{PORTAL_DOMAIN}}" - end - return "{{SERVER_DOMAIN}}" - } - - include /etc/nginx/conf.d/server/server.account; - } - {{/PORTAL_DOMAIN}} - - {{#SERVER_DOMAIN}} - server { - server_name account.{{SERVER_DOMAIN}}; # example: account.eu-ger-1.siasky.net - - include /etc/nginx/conf.d/server/server.http; - - set_by_lua_block $server_alias { return string.match("{{SERVER_DOMAIN}}", "^([^.]+)") } - } - - server { - server_name account.{{SERVER_DOMAIN}}; # example: account.eu-ger-1.siasky.net - - set_by_lua_block $skynet_portal_domain { return "{{SERVER_DOMAIN}}" } - set_by_lua_block $skynet_server_domain { return "{{SERVER_DOMAIN}}" } - - include /etc/nginx/conf.d/server/server.account; - - set_by_lua_block $server_alias { return string.match("{{SERVER_DOMAIN}}", "^([^.]+)") } - } - {{/SERVER_DOMAIN}} -{{/ACCOUNTS_ENABLED}} diff --git a/docker/nginx/conf.d.templates/server.account.conf.template b/docker/nginx/conf.d.templates/server.account.conf.template new file mode 100644 index 00000000..8507c407 --- /dev/null +++ b/docker/nginx/conf.d.templates/server.account.conf.template @@ -0,0 +1,44 @@ +server { + server_name account.${PORTAL_DOMAIN}; # example: account.siasky.net + + include /etc/nginx/conf.d/server/server.http; +} + +server { + server_name account.${PORTAL_DOMAIN}; # example: account.siasky.net + + set_by_lua_block $skynet_portal_domain { return "${PORTAL_DOMAIN}" } + set_by_lua_block $skynet_server_domain { + -- fall back to portal domain if server domain is not defined + if "${SERVER_DOMAIN}" == "" then + return "${PORTAL_DOMAIN}" + end + return "${SERVER_DOMAIN}" + } + + include /etc/nginx/conf.d/server/server.account; +} + +server { + server_name account.${SERVER_DOMAIN}; # example: account.eu-ger-1.siasky.net + + include /etc/nginx/conf.d/server/server.http; + + set_by_lua_block $server_alias { return string.match("${SERVER_DOMAIN}", "^([^.]+)") } +} + +server { + server_name account.${SERVER_DOMAIN}; # example: account.eu-ger-1.siasky.net + + set_by_lua_block $skynet_portal_domain { + -- when accessing portal directly through server domain, portal domain should be set to server domain + -- motivation: skynet-js uses Skynet-Portal-Api header (that is set to $skynet_portal_domain) to detect current + -- portal address and it should be server domain when accessing specific server by its domain address + return "${SERVER_DOMAIN}" + } + set_by_lua_block $skynet_server_domain { return "${SERVER_DOMAIN}" } + + include /etc/nginx/conf.d/server/server.account; + + set_by_lua_block $server_alias { return string.match("${SERVER_DOMAIN}", "^([^.]+)") } +} diff --git a/docker/nginx/conf.d.templates/server.api.conf b/docker/nginx/conf.d.templates/server.api.conf deleted file mode 100644 index 591212ba..00000000 --- a/docker/nginx/conf.d.templates/server.api.conf +++ /dev/null @@ -1,43 +0,0 @@ -{{#PORTAL_DOMAIN}} -server { - server_name {{PORTAL_DOMAIN}}; # example: siasky.net - - include /etc/nginx/conf.d/server/server.http; -} - -server { - server_name {{PORTAL_DOMAIN}}; # example: siasky.net - - set_by_lua_block $skynet_portal_domain { return "{{PORTAL_DOMAIN}}" } - set_by_lua_block $skynet_server_domain { - -- fall back to portal domain if server domain is not defined - if "{{SERVER_DOMAIN}}" == "" then - return "{{PORTAL_DOMAIN}}" - end - return "{{SERVER_DOMAIN}}" - } - - include /etc/nginx/conf.d/server/server.api; -} -{{/PORTAL_DOMAIN}} - -{{#SERVER_DOMAIN}} -server { - server_name {{SERVER_DOMAIN}}; # example: eu-ger-1.siasky.net - - include /etc/nginx/conf.d/server/server.http; - - set_by_lua_block $server_alias { return string.match("{{SERVER_DOMAIN}}", "^([^.]+)") } -} - -server { - server_name {{SERVER_DOMAIN}}; # example: eu-ger-1.siasky.net - - set_by_lua_block $skynet_portal_domain { return "{{SERVER_DOMAIN}}" } - set_by_lua_block $skynet_server_domain { return "{{SERVER_DOMAIN}}" } - - include /etc/nginx/conf.d/server/server.api; - - set_by_lua_block $server_alias { return string.match("{{SERVER_DOMAIN}}", "^([^.]+)") } -} -{{/SERVER_DOMAIN}} diff --git a/docker/nginx/conf.d.templates/server.api.conf.template b/docker/nginx/conf.d.templates/server.api.conf.template new file mode 100644 index 00000000..5f742127 --- /dev/null +++ b/docker/nginx/conf.d.templates/server.api.conf.template @@ -0,0 +1,44 @@ +server { + server_name ${PORTAL_DOMAIN}; # example: siasky.net + + include /etc/nginx/conf.d/server/server.http; +} + +server { + server_name ${PORTAL_DOMAIN}; # example: siasky.net + + set_by_lua_block $skynet_portal_domain { return "${PORTAL_DOMAIN}" } + set_by_lua_block $skynet_server_domain { + -- fall back to portal domain if server domain is not defined + if "${SERVER_DOMAIN}" == "" then + return "${PORTAL_DOMAIN}" + end + return "${SERVER_DOMAIN}" + } + + include /etc/nginx/conf.d/server/server.api; +} + +server { + server_name ${SERVER_DOMAIN}; # example: eu-ger-1.siasky.net + + include /etc/nginx/conf.d/server/server.http; + + set_by_lua_block $server_alias { return string.match("${SERVER_DOMAIN}", "^([^.]+)") } +} + +server { + server_name ${SERVER_DOMAIN}; # example: eu-ger-1.siasky.net + + set_by_lua_block $skynet_portal_domain { + -- when accessing portal directly through server domain, portal domain should be set to server domain + -- motivation: skynet-js uses Skynet-Portal-Api header (that is set to $skynet_portal_domain) to detect current + -- portal address and it should be server domain when accessing specific server by its domain address + return "${SERVER_DOMAIN}" + } + set_by_lua_block $skynet_server_domain { return "${SERVER_DOMAIN}" } + + include /etc/nginx/conf.d/server/server.api; + + set_by_lua_block $server_alias { return string.match("${SERVER_DOMAIN}", "^([^.]+)") } +} diff --git a/docker/nginx/conf.d.templates/server.dnslink.conf b/docker/nginx/conf.d.templates/server.dnslink.conf.template similarity index 71% rename from docker/nginx/conf.d.templates/server.dnslink.conf rename to docker/nginx/conf.d.templates/server.dnslink.conf.template index d42ee245..95c623b6 100644 --- a/docker/nginx/conf.d.templates/server.dnslink.conf +++ b/docker/nginx/conf.d.templates/server.dnslink.conf.template @@ -12,13 +12,13 @@ server { ssl_certificate /etc/ssl/local-certificate.crt; ssl_certificate_key /etc/ssl/local-certificate.key; - set_by_lua_block $skynet_portal_domain { return "{{PORTAL_DOMAIN}}" } + set_by_lua_block $skynet_portal_domain { return "${PORTAL_DOMAIN}" } set_by_lua_block $skynet_server_domain { -- fall back to portal domain if server domain is not defined - if "{{SERVER_DOMAIN}}" == "" then - return "{{PORTAL_DOMAIN}}" + if "${SERVER_DOMAIN}" == "" then + return "${PORTAL_DOMAIN}" end - return "{{SERVER_DOMAIN}}" + return "${SERVER_DOMAIN}" } include /etc/nginx/conf.d/server/server.dnslink; diff --git a/docker/nginx/conf.d.templates/server.hns.conf b/docker/nginx/conf.d.templates/server.hns.conf deleted file mode 100644 index 0e4f21f3..00000000 --- a/docker/nginx/conf.d.templates/server.hns.conf +++ /dev/null @@ -1,45 +0,0 @@ -{{#PORTAL_DOMAIN}} -server { - server_name *.hns.{{PORTAL_DOMAIN}}; # example: *.hns.siasky.net - - include /etc/nginx/conf.d/server/server.http; -} - -server { - server_name *.hns.{{PORTAL_DOMAIN}}; # example: *.hns.siasky.net - - set_by_lua_block $skynet_portal_domain { return "{{PORTAL_DOMAIN}}" } - set_by_lua_block $skynet_server_domain { - -- fall back to portal domain if server domain is not defined - if "{{SERVER_DOMAIN}}" == "" then - return "{{PORTAL_DOMAIN}}" - end - return "{{SERVER_DOMAIN}}" - } - - proxy_set_header Host {{PORTAL_DOMAIN}}; - include /etc/nginx/conf.d/server/server.hns; -} -{{/PORTAL_DOMAIN}} - -{{#SERVER_DOMAIN}} -server { - server_name *.hns.{{SERVER_DOMAIN}}; # example: *.hns.eu-ger-1.siasky.net - - include /etc/nginx/conf.d/server/server.http; - - set_by_lua_block $server_alias { return string.match("{{SERVER_DOMAIN}}", "^([^.]+)") } -} - -server { - server_name *.hns.{{SERVER_DOMAIN}}; # example: *.hns.eu-ger-1.siasky.net - - set_by_lua_block $skynet_portal_domain { return "{{SERVER_DOMAIN}}" } - set_by_lua_block $skynet_server_domain { return "{{SERVER_DOMAIN}}" } - - proxy_set_header Host {{SERVER_DOMAIN}}; - include /etc/nginx/conf.d/server/server.hns; - - set_by_lua_block $server_alias { return string.match("{{SERVER_DOMAIN}}", "^([^.]+)") } -} -{{/SERVER_DOMAIN}} diff --git a/docker/nginx/conf.d.templates/server.hns.conf.template b/docker/nginx/conf.d.templates/server.hns.conf.template new file mode 100644 index 00000000..f7bb43fb --- /dev/null +++ b/docker/nginx/conf.d.templates/server.hns.conf.template @@ -0,0 +1,46 @@ +server { + server_name *.hns.${PORTAL_DOMAIN}; # example: *.hns.siasky.net + + include /etc/nginx/conf.d/server/server.http; +} + +server { + server_name *.hns.${PORTAL_DOMAIN}; # example: *.hns.siasky.net + + set_by_lua_block $skynet_portal_domain { return "${PORTAL_DOMAIN}" } + set_by_lua_block $skynet_server_domain { + -- fall back to portal domain if server domain is not defined + if "${SERVER_DOMAIN}" == "" then + return "${PORTAL_DOMAIN}" + end + return "${SERVER_DOMAIN}" + } + + proxy_set_header Host ${PORTAL_DOMAIN}; + include /etc/nginx/conf.d/server/server.hns; +} + +server { + server_name *.hns.${SERVER_DOMAIN}; # example: *.hns.eu-ger-1.siasky.net + + include /etc/nginx/conf.d/server/server.http; + + set_by_lua_block $server_alias { return string.match("${SERVER_DOMAIN}", "^([^.]+)") } +} + +server { + server_name *.hns.${SERVER_DOMAIN}; # example: *.hns.eu-ger-1.siasky.net + + set_by_lua_block $skynet_portal_domain { + -- when accessing portal directly through server domain, portal domain should be set to server domain + -- motivation: skynet-js uses Skynet-Portal-Api header (that is set to $skynet_portal_domain) to detect current + -- portal address and it should be server domain when accessing specific server by its domain address + return "${SERVER_DOMAIN}" + } + set_by_lua_block $skynet_server_domain { return "${SERVER_DOMAIN}" } + + proxy_set_header Host ${SERVER_DOMAIN}; + include /etc/nginx/conf.d/server/server.hns; + + set_by_lua_block $server_alias { return string.match("${SERVER_DOMAIN}", "^([^.]+)") } +} diff --git a/docker/nginx/conf.d.templates/server.skylink.conf b/docker/nginx/conf.d.templates/server.skylink.conf deleted file mode 100644 index a97e240c..00000000 --- a/docker/nginx/conf.d.templates/server.skylink.conf +++ /dev/null @@ -1,43 +0,0 @@ -{{#PORTAL_DOMAIN}} -server { - server_name *.{{PORTAL_DOMAIN}}; # example: *.siasky.net - - include /etc/nginx/conf.d/server/server.http; -} - -server { - server_name *.{{PORTAL_DOMAIN}}; # example: *.siasky.net - - set_by_lua_block $skynet_portal_domain { return "{{PORTAL_DOMAIN}}" } - set_by_lua_block $skynet_server_domain { - -- fall back to portal domain if server domain is not defined - if "{{SERVER_DOMAIN}}" == "" then - return "{{PORTAL_DOMAIN}}" - end - return "{{SERVER_DOMAIN}}" - } - - include /etc/nginx/conf.d/server/server.skylink; -} -{{/PORTAL_DOMAIN}} - -{{#SERVER_DOMAIN}} -server { - server_name *.{{SERVER_DOMAIN}}; # example: *.eu-ger-1.siasky.net - - include /etc/nginx/conf.d/server/server.http; - - set_by_lua_block $server_alias { return string.match("{{SERVER_DOMAIN}}", "^([^.]+)") } -} - -server { - server_name *.{{SERVER_DOMAIN}}; # example: *.eu-ger-1.siasky.net - - set_by_lua_block $skynet_portal_domain { return "{{SERVER_DOMAIN}}" } - set_by_lua_block $skynet_server_domain { return "{{SERVER_DOMAIN}}" } - - include /etc/nginx/conf.d/server/server.skylink; - - set_by_lua_block $server_alias { return string.match("{{SERVER_DOMAIN}}", "^([^.]+)") } -} -{{/SERVER_DOMAIN}} diff --git a/docker/nginx/conf.d.templates/server.skylink.conf.template b/docker/nginx/conf.d.templates/server.skylink.conf.template new file mode 100644 index 00000000..0d337abb --- /dev/null +++ b/docker/nginx/conf.d.templates/server.skylink.conf.template @@ -0,0 +1,44 @@ +server { + server_name *.${PORTAL_DOMAIN}; # example: *.siasky.net + + include /etc/nginx/conf.d/server/server.http; +} + +server { + server_name *.${PORTAL_DOMAIN}; # example: *.siasky.net + + set_by_lua_block $skynet_portal_domain { return "${PORTAL_DOMAIN}" } + set_by_lua_block $skynet_server_domain { + -- fall back to portal domain if server domain is not defined + if "${SERVER_DOMAIN}" == "" then + return "${PORTAL_DOMAIN}" + end + return "${SERVER_DOMAIN}" + } + + include /etc/nginx/conf.d/server/server.skylink; +} + +server { + server_name *.${SERVER_DOMAIN}; # example: *.eu-ger-1.siasky.net + + include /etc/nginx/conf.d/server/server.http; + + set_by_lua_block $server_alias { return string.match("${SERVER_DOMAIN}", "^([^.]+)") } +} + +server { + server_name *.${SERVER_DOMAIN}; # example: *.eu-ger-1.siasky.net + + set_by_lua_block $skynet_portal_domain { + -- when accessing portal directly through server domain, portal domain should be set to server domain + -- motivation: skynet-js uses Skynet-Portal-Api header (that is set to $skynet_portal_domain) to detect current + -- portal address and it should be server domain when accessing specific server by its domain address + return "${SERVER_DOMAIN}" + } + set_by_lua_block $skynet_server_domain { return "${SERVER_DOMAIN}" } + + include /etc/nginx/conf.d/server/server.skylink; + + set_by_lua_block $server_alias { return string.match("${SERVER_DOMAIN}", "^([^.]+)") } +} diff --git a/docker/nginx/conf.d/include/location-skylink b/docker/nginx/conf.d/include/location-skylink index 995a6e2d..b214e3a9 100644 --- a/docker/nginx/conf.d/include/location-skylink +++ b/docker/nginx/conf.d/include/location-skylink @@ -1,5 +1,4 @@ include /etc/nginx/conf.d/include/cors; -include /etc/nginx/conf.d/include/track-download; limit_conn downloads_by_ip 100; # ddos protection: max 100 downloads at a time @@ -37,3 +36,18 @@ proxy_read_timeout 600; proxy_set_header User-Agent: Sia-Agent; proxy_pass http://sia:9980/skynet/skylink/$skylink$path$is_args$args; + +log_by_lua_block { + local skynet_account = require("skynet.account") + local skynet_modules = require("skynet.modules") + local skynet_scanner = require("skynet.scanner") + local skynet_tracker = require("skynet.tracker") + + if skynet_modules.is_enabled("a") then + skynet_tracker.track_download(ngx.header["Skynet-Skylink"], ngx.status, skynet_account.get_auth_headers(), ngx.var.body_bytes_sent) + end + + if skynet_modules.is_enabled("s") then + skynet_scanner.scan_skylink(ngx.header["Skynet-Skylink"]) + end +} diff --git a/docker/nginx/conf.d/include/location-skynet-registry b/docker/nginx/conf.d/include/location-skynet-registry index 33838f70..cd450be9 100644 --- a/docker/nginx/conf.d/include/location-skynet-registry +++ b/docker/nginx/conf.d/include/location-skynet-registry @@ -1,6 +1,5 @@ include /etc/nginx/conf.d/include/cors; include /etc/nginx/conf.d/include/sia-auth; -include /etc/nginx/conf.d/include/track-registry; limit_req zone=registry_access_by_ip burst=600 nodelay; limit_req zone=registry_access_by_ip_throttled burst=200 nodelay; @@ -30,3 +29,10 @@ access_by_lua_block { end end } + +log_by_lua_block { + local skynet_account = require("skynet.account") + local skynet_tracker = require("skynet.tracker") + + skynet_tracker.track_registry(ngx.status, skynet_account.get_auth_headers(), ngx.req.get_method()) +} diff --git a/docker/nginx/conf.d/include/track-download b/docker/nginx/conf.d/include/track-download deleted file mode 100644 index 4e12fd41..00000000 --- a/docker/nginx/conf.d/include/track-download +++ /dev/null @@ -1,55 +0,0 @@ -log_by_lua_block { - local skynet_account = require("skynet.account") - - -- tracking runs only when request comes from authenticated user - if skynet_account.is_authenticated() then - local function track(premature, skylink, status, body_bytes_sent, auth_headers) - if premature then return end - - local httpc = require("resty.http").new() - local query = table.concat({ "status=" .. status, "bytes=" .. body_bytes_sent }, "&") - - -- 10.10.10.70 points to accounts service (alias not available when using resty-http) - local res, err = httpc:request_uri("http://10.10.10.70:3000/track/download/" .. skylink .. "?" .. query, { - method = "POST", - headers = auth_headers, - }) - - if err or (res and res.status ~= ngx.HTTP_NO_CONTENT) then - local error_response = err or ("[HTTP " .. res.status .. "] " .. res.body) - ngx.log(ngx.ERR, "Failed accounts service request /track/download/" .. skylink .. ": ", error_response) - end - end - - if ngx.header["Skynet-Skylink"] and ngx.status >= ngx.HTTP_OK and ngx.status < ngx.HTTP_SPECIAL_RESPONSE then - local auth_headers = skynet_account.get_auth_headers() - local ok, err = ngx.timer.at(0, track, ngx.header["Skynet-Skylink"], ngx.status, ngx.var.body_bytes_sent, auth_headers) - if err then ngx.log(ngx.ERR, "Failed to create timer: ", err) end - end - end - - -- this block runs only when scanner module is enabled - if os.getenv("PORTAL_MODULES"):match("s") then - local function scan(premature, skylink) - if premature then return end - - local httpc = require("resty.http").new() - - -- 10.10.10.101 points to malware-scanner service (alias not available when using resty-http) - local res, err = httpc:request_uri("http://10.10.10.101:4000/scan/" .. skylink, { - method = "POST", - }) - - if err or (res and res.status ~= ngx.HTTP_OK) then - local error_response = err or ("[HTTP " .. res.status .. "] " .. res.body) - ngx.log(ngx.ERR, "Failed malware-scanner request /scan/" .. skylink .. ": ", error_response) - end - end - - -- scan all skylinks but make sure to only run if skylink is present (empty if request failed) - if ngx.header["Skynet-Skylink"] then - local ok, err = ngx.timer.at(0, scan, ngx.header["Skynet-Skylink"]) - if err then ngx.log(ngx.ERR, "Failed to create timer: ", err) end - end - end -} diff --git a/docker/nginx/conf.d/include/track-registry b/docker/nginx/conf.d/include/track-registry deleted file mode 100644 index 2c840491..00000000 --- a/docker/nginx/conf.d/include/track-registry +++ /dev/null @@ -1,33 +0,0 @@ -log_by_lua_block { - local skynet_account = require("skynet.account") - - -- tracking runs only when request comes from authenticated user - if skynet_account.is_authenticated() then - local function track(premature, request_method, auth_headers) - if premature then return end - - local httpc = require("resty.http").new() - - -- based on request method we assign a registry action string used - -- in track endpoint namely "read" for GET and "write" for POST - local registry_action = request_method == "GET" and "read" or "write" - - -- 10.10.10.70 points to accounts service (alias not available when using resty-http) - local res, err = httpc:request_uri("http://10.10.10.70:3000/track/registry/" .. registry_action, { - method = "POST", - headers = auth_headers, - }) - - if err or (res and res.status ~= ngx.HTTP_NO_CONTENT) then - local error_response = err or ("[HTTP " .. res.status .. "] " .. res.body) - ngx.log(ngx.ERR, "Failed accounts service request /track/registry/" .. registry_action .. ": ", error_response) - end - end - - if ngx.status == ngx.HTTP_OK or ngx.status == ngx.HTTP_NOT_FOUND then - local auth_headers = skynet_account.get_auth_headers() - local ok, err = ngx.timer.at(0, track, ngx.req.get_method(), auth_headers) - if err then ngx.log(ngx.ERR, "Failed to create timer: ", err) end - end - end -} diff --git a/docker/nginx/conf.d/include/track-upload b/docker/nginx/conf.d/include/track-upload deleted file mode 100644 index 36b12b9e..00000000 --- a/docker/nginx/conf.d/include/track-upload +++ /dev/null @@ -1,55 +0,0 @@ -log_by_lua_block { - local skynet_account = require("skynet.account") - - -- tracking runs only when request comes from authenticated user - if skynet_account.is_authenticated() then - local function track(premature, skylink, auth_headers) - if premature then return end - - local httpc = require("resty.http").new() - - -- 10.10.10.70 points to accounts service (alias not available when using resty-http) - local res, err = httpc:request_uri("http://10.10.10.70:3000/track/upload/" .. skylink, { - method = "POST", - headers = auth_headers, - }) - - if err or (res and res.status ~= ngx.HTTP_NO_CONTENT) then - local error_response = err or ("[HTTP " .. res.status .. "] " .. res.body) - ngx.log(ngx.ERR, "Failed accounts service request /track/upload/" .. skylink .. ": ", error_response) - end - end - - -- report all skylinks (header empty if request failed) but only if jwt is preset (user is authenticated) - if ngx.header["Skynet-Skylink"] then - local auth_headers = skynet_account.get_auth_headers() - local ok, err = ngx.timer.at(0, track, ngx.header["Skynet-Skylink"], auth_headers) - if err then ngx.log(ngx.ERR, "Failed to create timer: ", err) end - end - end - - -- this block runs only when scanner module is enabled - if os.getenv("PORTAL_MODULES"):match("s") then - local function scan(premature, skylink) - if premature then return end - - local httpc = require("resty.http").new() - - -- 10.10.10.101 points to malware-scanner service (alias not available when using resty-http) - local res, err = httpc:request_uri("http://10.10.10.101:4000/scan/" .. skylink, { - method = "POST", - }) - - if err or (res and res.status ~= ngx.HTTP_OK) then - local error_response = err or ("[HTTP " .. res.status .. "] " .. res.body) - ngx.log(ngx.ERR, "Failed malware-scanner request /scan/" .. skylink .. ": ", error_response) - end - end - - -- scan all skylinks but make sure to only run if skylink is present (empty if request failed) - if ngx.header["Skynet-Skylink"] then - local ok, err = ngx.timer.at(0, scan, ngx.header["Skynet-Skylink"]) - if err then ngx.log(ngx.ERR, "Failed to create timer: ", err) end - end - end -} diff --git a/docker/nginx/conf.d/server/server.account b/docker/nginx/conf.d/server/server.account index 127ba4bf..9d444296 100644 --- a/docker/nginx/conf.d/server/server.account +++ b/docker/nginx/conf.d/server/server.account @@ -3,6 +3,11 @@ listen 443 ssl http2; include /etc/nginx/conf.d/include/ssl-settings; include /etc/nginx/conf.d/include/init-optional-variables; +# Uncomment to launch new Dashboard under /v2 path +# location /v2 { +# proxy_pass http://dashboard-v2:9000; +# } + location / { proxy_pass http://dashboard:3000; } diff --git a/docker/nginx/conf.d/server/server.api b/docker/nginx/conf.d/server/server.api index 5bd1aa61..0243ab90 100644 --- a/docker/nginx/conf.d/server/server.api +++ b/docker/nginx/conf.d/server/server.api @@ -206,7 +206,6 @@ location /skynet/registry/subscription { location /skynet/skyfile { include /etc/nginx/conf.d/include/cors; include /etc/nginx/conf.d/include/sia-auth; - include /etc/nginx/conf.d/include/track-upload; include /etc/nginx/conf.d/include/generate-siapath; include /etc/nginx/conf.d/include/portal-access-check; @@ -228,12 +227,26 @@ location /skynet/skyfile { # proxy this call to siad endpoint (make sure the ip is correct) proxy_pass http://sia:9980/skynet/skyfile/$dir1/$dir2/$dir3$is_args$args; + + log_by_lua_block { + local skynet_account = require("skynet.account") + local skynet_modules = require("skynet.modules") + local skynet_scanner = require("skynet.scanner") + local skynet_tracker = require("skynet.tracker") + + if skynet_modules.is_enabled("a") then + skynet_tracker.track_upload(ngx.header["Skynet-Skylink"], ngx.status, skynet_account.get_auth_headers()) + end + + if skynet_modules.is_enabled("s") then + skynet_scanner.scan_skylink(ngx.header["Skynet-Skylink"]) + end + } } # endpoint implementing resumable file uploads open protocol https://tus.io location /skynet/tus { include /etc/nginx/conf.d/include/cors-headers; # include cors headers but do not overwrite OPTIONS response - include /etc/nginx/conf.d/include/track-upload; limit_req zone=uploads_by_ip burst=10 nodelay; limit_req zone=uploads_by_ip_throttled; @@ -294,12 +307,26 @@ location /skynet/tus { end end } + + log_by_lua_block { + local skynet_account = require("skynet.account") + local skynet_modules = require("skynet.modules") + local skynet_scanner = require("skynet.scanner") + local skynet_tracker = require("skynet.tracker") + + if skynet_modules.is_enabled("a") then + skynet_tracker.track_upload(ngx.header["Skynet-Skylink"], ngx.status, skynet_account.get_auth_headers()) + end + + if skynet_modules.is_enabled("s") then + skynet_scanner.scan_skylink(ngx.header["Skynet-Skylink"]) + end + } } location /skynet/pin { include /etc/nginx/conf.d/include/cors; include /etc/nginx/conf.d/include/sia-auth; - include /etc/nginx/conf.d/include/track-upload; include /etc/nginx/conf.d/include/generate-siapath; include /etc/nginx/conf.d/include/portal-access-check; @@ -311,6 +338,21 @@ location /skynet/pin { proxy_set_header User-Agent: Sia-Agent; proxy_pass http://sia:9980$uri?siapath=$dir1/$dir2/$dir3&$args; + + log_by_lua_block { + local skynet_account = require("skynet.account") + local skynet_modules = require("skynet.modules") + local skynet_scanner = require("skynet.scanner") + local skynet_tracker = require("skynet.tracker") + + if skynet_modules.is_enabled("a") then + skynet_tracker.track_upload(ngx.header["Skynet-Skylink"], ngx.status, skynet_account.get_auth_headers()) + end + + if skynet_modules.is_enabled("s") then + skynet_scanner.scan_skylink(ngx.header["Skynet-Skylink"]) + end + } } location /skynet/metadata { @@ -357,7 +399,6 @@ location ~ "^/file/(([a-zA-Z0-9-_]{46}|[a-z0-9]{55})(/.*)?)$" { location /skynet/trustless/basesector { include /etc/nginx/conf.d/include/cors; - include /etc/nginx/conf.d/include/track-download; limit_conn downloads_by_ip 100; # ddos protection: max 100 downloads at a time @@ -391,6 +432,21 @@ location /skynet/trustless/basesector { proxy_set_header User-Agent: Sia-Agent; proxy_pass http://sia:9980; + + log_by_lua_block { + local skynet_account = require("skynet.account") + local skynet_modules = require("skynet.modules") + local skynet_scanner = require("skynet.scanner") + local skynet_tracker = require("skynet.tracker") + + if skynet_modules.is_enabled("a") then + skynet_tracker.track_download(ngx.header["Skynet-Skylink"], ngx.status, skynet_account.get_auth_headers(), ngx.var.body_bytes_sent) + end + + if skynet_modules.is_enabled("s") then + skynet_scanner.scan_skylink(ngx.header["Skynet-Skylink"]) + end + } } location /__internal/do/not/use/accounts { diff --git a/docker/nginx/docker-entrypoint.d/20-envsubst-on-templates.sh b/docker/nginx/docker-entrypoint.d/20-envsubst-on-templates.sh new file mode 100755 index 00000000..be9c5f8e --- /dev/null +++ b/docker/nginx/docker-entrypoint.d/20-envsubst-on-templates.sh @@ -0,0 +1,59 @@ +#!/bin/sh + +# https://github.com/nginxinc/docker-nginx/blob/master/entrypoint/20-envsubst-on-templates.sh +# https://github.com/nginxinc/docker-nginx/blob/master/LICENSE + +# Copyright (C) 2011-2016 Nginx, Inc. +# All rights reserved. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. + +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. + +set -e + +ME=$(basename $0) + +auto_envsubst() { + local template_dir="${NGINX_ENVSUBST_TEMPLATE_DIR:-/etc/nginx/templates}" + local suffix="${NGINX_ENVSUBST_TEMPLATE_SUFFIX:-.template}" + local output_dir="${NGINX_ENVSUBST_OUTPUT_DIR:-/etc/nginx/conf.d}" + + local template defined_envs relative_path output_path subdir + defined_envs=$(printf '${%s} ' $(env | cut -d= -f1)) + [ -d "$template_dir" ] || return 0 + if [ ! -w "$output_dir" ]; then + echo >&3 "$ME: ERROR: $template_dir exists, but $output_dir is not writable" + return 0 + fi + find "$template_dir" -follow -type f -name "*$suffix" -print | while read -r template; do + relative_path="${template#$template_dir/}" + output_path="$output_dir/${relative_path%$suffix}" + subdir=$(dirname "$relative_path") + # create a subdirectory where the template file exists + mkdir -p "$output_dir/$subdir" + echo >&3 "$ME: Running envsubst on $template to $output_path" + envsubst "$defined_envs" < "$template" > "$output_path" + done +} + +auto_envsubst + +exit 0 diff --git a/docker/nginx/docker-entrypoint.d/40-reload-every-x-hours.sh b/docker/nginx/docker-entrypoint.d/40-reload-every-x-hours.sh new file mode 100755 index 00000000..cdd7d17e --- /dev/null +++ b/docker/nginx/docker-entrypoint.d/40-reload-every-x-hours.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +# source: https://github.com/nginxinc/docker-nginx/pull/509 + +set -e + +ME=$(basename $0) + +[ "${NGINX_ENTRYPOINT_RELOAD_EVERY_X_HOURS:-}" ] || exit 0 +if [ $(echo "$NGINX_ENTRYPOINT_RELOAD_EVERY_X_HOURS > 0" | bc) = 0 ]; then + echo >&3 "$ME: Error. Provide integer or floating point number greater that 0. See 'man sleep'." + exit 1 +fi + +start_background_reload() { + echo >&3 "$ME: Reloading Nginx every $NGINX_ENTRYPOINT_RELOAD_EVERY_X_HOURS hour(s)" + while :; do sleep ${NGINX_ENTRYPOINT_RELOAD_EVERY_X_HOURS}h; echo >&3 "$ME: Reloading Nginx ..." && nginx -s reload; done & +} + +start_background_reload diff --git a/docker/nginx/docker-entrypoint.d/50-generate-local-certificate.sh b/docker/nginx/docker-entrypoint.d/50-generate-local-certificate.sh new file mode 100755 index 00000000..f5ecada1 --- /dev/null +++ b/docker/nginx/docker-entrypoint.d/50-generate-local-certificate.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +# Generate locally signed ssl certificate to be used on routes +# that do not require certificate issued by trusted CA + +set -e + +ME=$(basename $0) + +generate_local_certificate() { + echo >&3 "$ME: Generating locally signed ssl certificate" + openssl req -new -newkey rsa:2048 -days 3650 -nodes -x509 \ + -subj '/CN=local-certificate' \ + -keyout /etc/ssl/local-certificate.key \ + -out /etc/ssl/local-certificate.crt +} + +generate_local_certificate diff --git a/docker/nginx/docker-entrypoint.sh b/docker/nginx/docker-entrypoint.sh new file mode 100755 index 00000000..d45a6d9a --- /dev/null +++ b/docker/nginx/docker-entrypoint.sh @@ -0,0 +1,65 @@ +#!/bin/sh +# vim:sw=4:ts=4:et + +# https://github.com/nginxinc/docker-nginx/blob/master/entrypoint/docker-entrypoint.sh +# https://github.com/nginxinc/docker-nginx/blob/master/LICENSE + +# Copyright (C) 2011-2016 Nginx, Inc. +# All rights reserved. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. + +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. + +set -e + +if [ -z "${NGINX_ENTRYPOINT_QUIET_LOGS:-}" ]; then + exec 3>&1 +else + exec 3>/dev/null +fi + +if [ "$1" = "nginx" -o "$1" = "nginx-debug" ]; then + if /usr/bin/find "/docker-entrypoint.d/" -mindepth 1 -maxdepth 1 -type f -print -quit 2>/dev/null | read v; then + echo >&3 "$0: /docker-entrypoint.d/ is not empty, will attempt to perform configuration" + + echo >&3 "$0: Looking for shell scripts in /docker-entrypoint.d/" + find "/docker-entrypoint.d/" -follow -type f -print | sort -V | while read -r f; do + case "$f" in + *.sh) + if [ -x "$f" ]; then + echo >&3 "$0: Launching $f"; + "$f" + else + # warn on shell scripts without exec bit + echo >&3 "$0: Ignoring $f, not executable"; + fi + ;; + *) echo >&3 "$0: Ignoring $f";; + esac + done + + echo >&3 "$0: Configuration complete; ready for start up" + else + echo >&3 "$0: No files found in /docker-entrypoint.d/, skipping configuration" + fi +fi + +exec "$@" diff --git a/docker/nginx/libs/skynet/account.lua b/docker/nginx/libs/skynet/account.lua index 6fa2c4d2..709d8130 100644 --- a/docker/nginx/libs/skynet/account.lua +++ b/docker/nginx/libs/skynet/account.lua @@ -59,7 +59,9 @@ function _M.exit_access_forbidden(message) end function _M.accounts_enabled() - return os.getenv("PORTAL_MODULES"):match("a") ~= nil + local skynet_modules = require("skynet.modules") + + return skynet_modules.is_enabled("a") end function _M.get_account_limits() @@ -74,9 +76,16 @@ function _M.get_account_limits() if ngx.var.account_limits == "" then local httpc = require("resty.http").new() + local uri = "http://10.10.10.70:3000/user/limits" + + -- include skylink if it is available in the context of request + -- todo: this should not rely on skylink variable to be defined + if ngx.var.skylink ~= nil and ngx.var.skylink ~= "" then + uri = uri .. "/" .. ngx.var.skylink + end -- 10.10.10.70 points to accounts service (alias not available when using resty-http) - local res, err = httpc:request_uri("http://10.10.10.70:3000/user/limits?unit=byte", { + local res, err = httpc:request_uri(uri .. "?unit=byte", { headers = auth_headers, }) diff --git a/docker/nginx/libs/skynet/modules.lua b/docker/nginx/libs/skynet/modules.lua new file mode 100644 index 00000000..607e6d8e --- /dev/null +++ b/docker/nginx/libs/skynet/modules.lua @@ -0,0 +1,23 @@ +local _M = {} + +local utils = require("utils") + +function _M.is_enabled(module_abbr) + if type(module_abbr) ~= "string" or module_abbr:len() ~= 1 then + error("Module abbreviation '" .. tostring(module_abbr) .. "' should be exactly one character long string") + end + + local enabled_modules = utils.getenv("PORTAL_MODULES") + + if not enabled_modules then + return false + end + + return enabled_modules:find(module_abbr) ~= nil +end + +function _M.is_disabled(module_abbr) + return not _M.is_enabled(module_abbr) +end + +return _M diff --git a/docker/nginx/libs/skynet/modules.spec.lua b/docker/nginx/libs/skynet/modules.spec.lua new file mode 100644 index 00000000..0eaaf081 --- /dev/null +++ b/docker/nginx/libs/skynet/modules.spec.lua @@ -0,0 +1,95 @@ +-- luacheck: ignore os + +local skynet_modules = require("skynet.modules") + +describe("is_enabled", function() + before_each(function() + stub(os, "getenv") + end) + + after_each(function() + os.getenv:revert() + end) + + it("should return false if PORTAL_MODULES are not defined", function() + os.getenv.on_call_with("PORTAL_MODULES").returns(nil) + + assert.is_false(skynet_modules.is_enabled("a")) + end) + + it("should return false if PORTAL_MODULES are empty", function() + os.getenv.on_call_with("PORTAL_MODULES").returns("") + + assert.is_false(skynet_modules.is_enabled("a")) + end) + + it("should return false if module is not enabled", function() + os.getenv.on_call_with("PORTAL_MODULES").returns("qwerty") + + assert.is_false(skynet_modules.is_enabled("a")) + end) + + it("should return true if module is enabled", function() + os.getenv.on_call_with("PORTAL_MODULES").returns("asdfg") + + assert.is_true(skynet_modules.is_enabled("a")) + end) + + it("should throw an error for empty module", function() + assert.has_error(function() + skynet_modules.is_enabled() + end, "Module abbreviation 'nil' should be exactly one character long string") + end) + + it("should throw an error for too long module", function() + assert.has_error(function() + skynet_modules.is_enabled("gandalf") + end, "Module abbreviation 'gandalf' should be exactly one character long string") + end) +end) + +describe("is_disabled", function() + before_each(function() + stub(os, "getenv") + end) + + after_each(function() + os.getenv:revert() + end) + + it("should return true if PORTAL_MODULES are not defined", function() + os.getenv.on_call_with("PORTAL_MODULES").returns(nil) + + assert.is_true(skynet_modules.is_disabled("a")) + end) + + it("should return true if PORTAL_MODULES are empty", function() + os.getenv.on_call_with("PORTAL_MODULES").returns("") + + assert.is_true(skynet_modules.is_disabled("a")) + end) + + it("should return true if module is not enabled", function() + os.getenv.on_call_with("PORTAL_MODULES").returns("qwerty") + + assert.is_true(skynet_modules.is_disabled("a")) + end) + + it("should return false if module is enabled", function() + os.getenv.on_call_with("PORTAL_MODULES").returns("asdfg") + + assert.is_false(skynet_modules.is_disabled("a")) + end) + + it("should throw an error for empty module", function() + assert.has_error(function() + skynet_modules.is_disabled() + end, "Module abbreviation 'nil' should be exactly one character long string") + end) + + it("should throw an error for too long module", function() + assert.has_error(function() + skynet_modules.is_disabled("gandalf") + end, "Module abbreviation 'gandalf' should be exactly one character long string") + end) +end) diff --git a/docker/nginx/libs/skynet/scanner.lua b/docker/nginx/libs/skynet/scanner.lua new file mode 100644 index 00000000..445f1ae9 --- /dev/null +++ b/docker/nginx/libs/skynet/scanner.lua @@ -0,0 +1,26 @@ +local _M = {} + +function _M.scan_skylink_timer(premature, skylink) + if premature then return end + + local httpc = require("resty.http").new() + + -- 10.10.10.101 points to malware-scanner service (alias not available when using resty-http) + local res, err = httpc:request_uri("http://10.10.10.101:4000/scan/" .. skylink, { + method = "POST", + }) + + if err or (res and res.status ~= ngx.HTTP_OK) then + local error_response = err or ("[HTTP " .. res.status .. "] " .. res.body) + ngx.log(ngx.ERR, "Failed malware-scanner request /scan/" .. skylink .. ": ", error_response) + end +end + +function _M.scan_skylink(skylink) + if not skylink then return end + + local ok, err = ngx.timer.at(0, _M.scan_skylink_timer, skylink) + if not ok then ngx.log(ngx.ERR, "Failed to create timer: ", err) end +end + +return _M diff --git a/docker/nginx/libs/skynet/scanner.spec.lua b/docker/nginx/libs/skynet/scanner.spec.lua new file mode 100644 index 00000000..533ef44c --- /dev/null +++ b/docker/nginx/libs/skynet/scanner.spec.lua @@ -0,0 +1,119 @@ +-- luacheck: ignore ngx + +local skynet_scanner = require("skynet.scanner") +local skylink = "AQBG8n_sgEM_nlEp3G0w3vLjmdvSZ46ln8ZXHn-eObZNjA" + +describe("scan_skylink", function() + before_each(function() + stub(ngx.timer, "at") + end) + + after_each(function() + ngx.timer.at:revert() + end) + + it("should schedule a timer when skylink is provided", function() + ngx.timer.at.invokes(function() return true, nil end) + + skynet_scanner.scan_skylink(skylink) + + assert.stub(ngx.timer.at).was_called_with(0, skynet_scanner.scan_skylink_timer, skylink) + end) + + it("should log an error if timer failed to create", function() + stub(ngx, "log") + + ngx.timer.at.invokes(function() return false, "such a failure" end) + + skynet_scanner.scan_skylink(skylink) + + assert.stub(ngx.timer.at).was_called_with(0, skynet_scanner.scan_skylink_timer, skylink) + assert.stub(ngx.log).was_called_with(ngx.ERR, "Failed to create timer: ", "such a failure") + + ngx.log:revert() + end) + + it("should not schedule a timer if skylink is not provided", function() + skynet_scanner.scan_skylink() + + assert.stub(ngx.timer.at).was_not_called() + end) +end) + +describe("scan_skylink_timer", function() + before_each(function() + stub(ngx, "log") + end) + + after_each(function() + local resty_http = require("resty.http") + + ngx.log:revert() + resty_http.new:revert() + end) + + it("should exit early on premature", function() + local resty_http = require("resty.http") + local request_uri = spy.new() + local httpc = mock({ request_uri = request_uri }) + stub(resty_http, "new").returns(httpc) + + skynet_scanner.scan_skylink_timer(true, skylink) + + assert.stub(request_uri).was_not_called() + assert.stub(ngx.log).was_not_called() + end) + + it("should make a post request with skylink to scanner service", function() + local resty_http = require("resty.http") + local request_uri = spy.new(function() + return { status = 200 } -- return 200 success + end) + local httpc = mock({ request_uri = request_uri }) + stub(resty_http, "new").returns(httpc) + + skynet_scanner.scan_skylink_timer(false, skylink) + + local uri = "http://10.10.10.101:4000/scan/" .. skylink + assert.stub(request_uri).was_called_with(httpc, uri, { method = "POST" }) + assert.stub(ngx.log).was_not_called() + end) + + it("should log message on scanner request failure with response code", function() + local resty_http = require("resty.http") + local request_uri = spy.new(function() + return { status = 404, body = "baz" } -- return 404 failure + end) + local httpc = mock({ request_uri = request_uri }) + stub(resty_http, "new").returns(httpc) + + skynet_scanner.scan_skylink_timer(false, skylink) + + local uri = "http://10.10.10.101:4000/scan/" .. skylink + assert.stub(request_uri).was_called_with(httpc, uri, { method = "POST" }) + assert.stub(ngx.log).was_called_with( + ngx.ERR, + "Failed malware-scanner request /scan/AQBG8n_sgEM_nlEp3G0w3vLjmdvSZ46ln8ZXHn-eObZNjA: ", + "[HTTP 404] baz" + ) + end) + + it("should log message on scanner request error", function() + local resty_http = require("resty.http") + local request_uri = spy.new(function() + return nil, "foo != bar" -- return error + end) + local httpc = mock({ request_uri = request_uri }) + stub(resty_http, "new").returns(httpc) + + skynet_scanner.scan_skylink_timer(false, skylink) + + local uri = "http://10.10.10.101:4000/scan/" .. skylink + assert.stub(request_uri).was_called_with(httpc, uri, { method = "POST" }) + assert.stub(ngx.log).was_called_with( + ngx.ERR, + "Failed malware-scanner request /scan/AQBG8n_sgEM_nlEp3G0w3vLjmdvSZ46ln8ZXHn-eObZNjA: ", + "foo != bar" + ) + end) +end) diff --git a/docker/nginx/libs/skynet/tracker.lua b/docker/nginx/libs/skynet/tracker.lua new file mode 100644 index 00000000..56f9dcc4 --- /dev/null +++ b/docker/nginx/libs/skynet/tracker.lua @@ -0,0 +1,90 @@ +local _M = {} + +local utils = require("utils") + +function _M.track_download_timer(premature, skylink, status, auth_headers, body_bytes_sent) + if premature then return end + + local httpc = require("resty.http").new() + local query = table.concat({ "status=" .. status, "bytes=" .. body_bytes_sent }, "&") + + -- 10.10.10.70 points to accounts service (alias not available when using resty-http) + local res, err = httpc:request_uri("http://10.10.10.70:3000/track/download/" .. skylink .. "?" .. query, { + method = "POST", + headers = auth_headers, + }) + + if err or (res and res.status ~= 204) then + local error_response = err or ("[HTTP " .. res.status .. "] " .. res.body) + ngx.log(ngx.ERR, "Failed accounts service request /track/download/" .. skylink .. ": ", error_response) + end +end + +function _M.track_download(skylink, status_code, auth_headers, body_bytes_sent) + local has_auth_headers = not utils.is_table_empty(auth_headers) + local status_success = status_code >= 200 and status_code <= 299 + + if skylink and status_success and has_auth_headers then + local ok, err = ngx.timer.at(0, _M.track_download_timer, skylink, status_code, auth_headers, body_bytes_sent) + if not ok then ngx.log(ngx.ERR, "Failed to create timer: ", err) end + end +end + +function _M.track_upload_timer(premature, skylink, auth_headers) + if premature then return end + + local httpc = require("resty.http").new() + + -- 10.10.10.70 points to accounts service (alias not available when using resty-http) + local res, err = httpc:request_uri("http://10.10.10.70:3000/track/upload/" .. skylink, { + method = "POST", + headers = auth_headers, + }) + + if err or (res and res.status ~= 204) then + local error_response = err or ("[HTTP " .. res.status .. "] " .. res.body) + ngx.log(ngx.ERR, "Failed accounts service request /track/upload/" .. skylink .. ": ", error_response) + end +end + +function _M.track_upload(skylink, status_code, auth_headers) + local status_success = status_code >= 200 and status_code <= 299 + + if skylink and status_success then + local ok, err = ngx.timer.at(0, _M.track_upload_timer, skylink, auth_headers) + if not ok then ngx.log(ngx.ERR, "Failed to create timer: ", err) end + end +end + +function _M.track_registry_timer(premature, auth_headers, request_method) + if premature then return end + + local httpc = require("resty.http").new() + + -- based on request method we assign a registry action string used + -- in track endpoint namely "read" for GET and "write" for POST + local registry_action = request_method == "GET" and "read" or "write" + + -- 10.10.10.70 points to accounts service (alias not available when using resty-http) + local res, err = httpc:request_uri("http://10.10.10.70:3000/track/registry/" .. registry_action, { + method = "POST", + headers = auth_headers, + }) + + if err or (res and res.status ~= 204) then + local error_response = err or ("[HTTP " .. res.status .. "] " .. res.body) + ngx.log(ngx.ERR, "Failed accounts service request /track/registry/" .. registry_action .. ": ", error_response) + end +end + +function _M.track_registry(status_code, auth_headers, request_method) + local has_auth_headers = not utils.is_table_empty(auth_headers) + local tracked_status = status_code == 200 or status_code == 404 + + if tracked_status and has_auth_headers then + local ok, err = ngx.timer.at(0, _M.track_registry_timer, auth_headers, request_method) + if not ok then ngx.log(ngx.ERR, "Failed to create timer: ", err) end + end +end + +return _M diff --git a/docker/nginx/libs/skynet/tracker.spec.lua b/docker/nginx/libs/skynet/tracker.spec.lua new file mode 100644 index 00000000..d6c59c4a --- /dev/null +++ b/docker/nginx/libs/skynet/tracker.spec.lua @@ -0,0 +1,555 @@ +-- luacheck: ignore ngx + +local skynet_tracker = require("skynet.tracker") + +local valid_skylink = "AQBG8n_sgEM_nlEp3G0w3vLjmdvSZ46ln8ZXHn-eObZNjA" +local valid_status_code = 200 +local valid_auth_headers = { ["Skynet-Api-Key"] = "foo" } + +describe("track_download", function() + local valid_body_bytes_sent = 12345 + + before_each(function() + stub(ngx.timer, "at") + end) + + after_each(function() + ngx.timer.at:revert() + end) + + it("should schedule a timer when conditions are met", function() + ngx.timer.at.invokes(function() return true, nil end) + + skynet_tracker.track_download(valid_skylink, valid_status_code, valid_auth_headers, valid_body_bytes_sent) + + assert.stub(ngx.timer.at).was_called_with( + 0, + skynet_tracker.track_download_timer, + valid_skylink, + valid_status_code, + valid_auth_headers, + valid_body_bytes_sent + ) + end) + + it("should not schedule a timer if skylink is empty", function() + ngx.timer.at.invokes(function() return true, nil end) + + skynet_tracker.track_download(nil, valid_status_code, valid_auth_headers, valid_body_bytes_sent) + + assert.stub(ngx.timer.at).was_not_called() + end) + + it("should not schedule a timer if status code is not in 2XX range", function() + ngx.timer.at.invokes(function() return true, nil end) + + -- couple of example of 4XX and 5XX codes + skynet_tracker.track_download(valid_skylink, 401, valid_auth_headers, valid_body_bytes_sent) + skynet_tracker.track_download(valid_skylink, 403, valid_auth_headers, valid_body_bytes_sent) + skynet_tracker.track_download(valid_skylink, 490, valid_auth_headers, valid_body_bytes_sent) + skynet_tracker.track_download(valid_skylink, 500, valid_auth_headers, valid_body_bytes_sent) + skynet_tracker.track_download(valid_skylink, 502, valid_auth_headers, valid_body_bytes_sent) + + assert.stub(ngx.timer.at).was_not_called() + end) + + it("should not schedule a timer if auth headers are empty", function() + ngx.timer.at.invokes(function() return true, nil end) + + skynet_tracker.track_download(valid_skylink, valid_status_code, {}, valid_body_bytes_sent) + + assert.stub(ngx.timer.at).was_not_called() + end) + + it("should log an error if timer failed to create", function() + stub(ngx, "log") + ngx.timer.at.invokes(function() return false, "such a failure" end) + + skynet_tracker.track_download(valid_skylink, valid_status_code, valid_auth_headers, valid_body_bytes_sent) + + assert.stub(ngx.timer.at).was_called_with( + 0, + skynet_tracker.track_download_timer, + valid_skylink, + valid_status_code, + valid_auth_headers, + valid_body_bytes_sent + ) + assert.stub(ngx.log).was_called_with(ngx.ERR, "Failed to create timer: ", "such a failure") + + ngx.log:revert() + end) + + describe("track_download_timer", function() + before_each(function() + stub(ngx, "log") + end) + + after_each(function() + local resty_http = require("resty.http") + + ngx.log:revert() + resty_http.new:revert() + end) + + it("should exit early on premature", function() + local resty_http = require("resty.http") + local request_uri = spy.new(function() + return { status = 200 } -- return 200 success + end) + local httpc = mock({ request_uri = request_uri }) + stub(resty_http, "new").returns(httpc) + + skynet_tracker.track_download_timer( + true, + valid_skylink, + valid_status_code, + valid_auth_headers, + valid_body_bytes_sent + ) + + assert.stub(request_uri).was_not_called() + assert.stub(ngx.log).was_not_called() + end) + + it("should make a post request to tracker service", function() + local resty_http = require("resty.http") + local request_uri = spy.new(function() + return { status = 204 } -- return 204 success + end) + local httpc = mock({ request_uri = request_uri }) + stub(resty_http, "new").returns(httpc) + + skynet_tracker.track_download_timer( + false, + valid_skylink, + valid_status_code, + valid_auth_headers, + valid_body_bytes_sent + ) + + local uri_params = "status=" .. valid_status_code .. "&bytes=" .. valid_body_bytes_sent + local uri = "http://10.10.10.70:3000/track/download/" .. valid_skylink .. "?" .. uri_params + assert.stub(request_uri).was_called_with(httpc, uri, { method = "POST", headers = valid_auth_headers }) + assert.stub(ngx.log).was_not_called() + end) + + it("should log message on tracker request failure with response code", function() + local resty_http = require("resty.http") + local request_uri = spy.new(function() + return { status = 404, body = "baz" } -- return 404 failure + end) + local httpc = mock({ request_uri = request_uri }) + stub(resty_http, "new").returns(httpc) + + skynet_tracker.track_download_timer( + false, + valid_skylink, + valid_status_code, + valid_auth_headers, + valid_body_bytes_sent + ) + + local uri_params = "status=" .. valid_status_code .. "&bytes=" .. valid_body_bytes_sent + local uri = "http://10.10.10.70:3000/track/download/" .. valid_skylink .. "?" .. uri_params + assert.stub(request_uri).was_called_with(httpc, uri, { method = "POST", headers = valid_auth_headers }) + assert.stub(ngx.log).was_called_with( + ngx.ERR, + "Failed accounts service request /track/download/" .. valid_skylink .. ": ", + "[HTTP 404] baz" + ) + end) + + it("should log message on tracker request error", function() + local resty_http = require("resty.http") + local request_uri = spy.new(function() + return nil, "foo != bar" -- return error + end) + local httpc = mock({ request_uri = request_uri }) + stub(resty_http, "new").returns(httpc) + + skynet_tracker.track_download_timer( + false, + valid_skylink, + valid_status_code, + valid_auth_headers, + valid_body_bytes_sent + ) + + local uri_params = "status=" .. valid_status_code .. "&bytes=" .. valid_body_bytes_sent + local uri = "http://10.10.10.70:3000/track/download/" .. valid_skylink .. "?" .. uri_params + assert.stub(request_uri).was_called_with(httpc, uri, { method = "POST", headers = valid_auth_headers }) + assert.stub(ngx.log).was_called_with( + ngx.ERR, + "Failed accounts service request /track/download/" .. valid_skylink .. ": ", + "foo != bar" + ) + end) + end) +end) + +describe("track_upload", function() + before_each(function() + stub(ngx.timer, "at") + end) + + after_each(function() + ngx.timer.at:revert() + end) + + it("should schedule a timer when conditions are met", function() + ngx.timer.at.invokes(function() return true, nil end) + + skynet_tracker.track_upload(valid_skylink, valid_status_code, valid_auth_headers) + + assert.stub(ngx.timer.at).was_called_with( + 0, + skynet_tracker.track_upload_timer, + valid_skylink, + valid_auth_headers + ) + end) + + it("should not schedule a timer if skylink is empty", function() + ngx.timer.at.invokes(function() return true, nil end) + + skynet_tracker.track_upload(nil, valid_status_code, valid_auth_headers) + + assert.stub(ngx.timer.at).was_not_called() + end) + + it("should not schedule a timer if status code is not in 2XX range", function() + ngx.timer.at.invokes(function() return true, nil end) + + -- couple of example of 4XX and 5XX codes + skynet_tracker.track_upload(valid_skylink, 401, valid_auth_headers) + skynet_tracker.track_upload(valid_skylink, 403, valid_auth_headers) + skynet_tracker.track_upload(valid_skylink, 490, valid_auth_headers) + skynet_tracker.track_upload(valid_skylink, 500, valid_auth_headers) + skynet_tracker.track_upload(valid_skylink, 502, valid_auth_headers) + + assert.stub(ngx.timer.at).was_not_called() + end) + + it("should schedule a timer if auth headers are empty", function() + ngx.timer.at.invokes(function() return true, nil end) + + skynet_tracker.track_upload(valid_skylink, valid_status_code, {}) + + assert.stub(ngx.timer.at).was_called_with( + 0, + skynet_tracker.track_upload_timer, + valid_skylink, + {} + ) + end) + + it("should log an error if timer failed to create", function() + stub(ngx, "log") + ngx.timer.at.invokes(function() return false, "such a failure" end) + + skynet_tracker.track_upload(valid_skylink, valid_status_code, valid_auth_headers) + + assert.stub(ngx.timer.at).was_called_with( + 0, + skynet_tracker.track_upload_timer, + valid_skylink, + valid_auth_headers + ) + assert.stub(ngx.log).was_called_with(ngx.ERR, "Failed to create timer: ", "such a failure") + + ngx.log:revert() + end) + + describe("track_upload_timer", function() + before_each(function() + stub(ngx, "log") + end) + + after_each(function() + local resty_http = require("resty.http") + + ngx.log:revert() + resty_http.new:revert() + end) + + it("should exit early on premature", function() + local resty_http = require("resty.http") + local request_uri = spy.new(function() + return { status = 200 } -- return 200 success + end) + local httpc = mock({ request_uri = request_uri }) + stub(resty_http, "new").returns(httpc) + + skynet_tracker.track_upload_timer( + true, + valid_skylink, + valid_auth_headers + ) + + assert.stub(request_uri).was_not_called() + assert.stub(ngx.log).was_not_called() + end) + + it("should make a post request to tracker service", function() + local resty_http = require("resty.http") + local request_uri = spy.new(function() + return { status = 204 } -- return 204 success + end) + local httpc = mock({ request_uri = request_uri }) + stub(resty_http, "new").returns(httpc) + + skynet_tracker.track_upload_timer( + false, + valid_skylink, + valid_auth_headers + ) + + local uri = "http://10.10.10.70:3000/track/upload/" .. valid_skylink + assert.stub(request_uri).was_called_with(httpc, uri, { method = "POST", headers = valid_auth_headers }) + assert.stub(ngx.log).was_not_called() + end) + + it("should log message on tracker request failure with response code", function() + local resty_http = require("resty.http") + local request_uri = spy.new(function() + return { status = 404, body = "baz" } -- return 404 failure + end) + local httpc = mock({ request_uri = request_uri }) + stub(resty_http, "new").returns(httpc) + + skynet_tracker.track_upload_timer( + false, + valid_skylink, + valid_auth_headers + ) + + local uri = "http://10.10.10.70:3000/track/upload/" .. valid_skylink + assert.stub(request_uri).was_called_with(httpc, uri, { method = "POST", headers = valid_auth_headers }) + assert.stub(ngx.log).was_called_with( + ngx.ERR, + "Failed accounts service request /track/upload/" .. valid_skylink .. ": ", + "[HTTP 404] baz" + ) + end) + + it("should log message on tracker request error", function() + local resty_http = require("resty.http") + local request_uri = spy.new(function() + return nil, "foo != bar" -- return error + end) + local httpc = mock({ request_uri = request_uri }) + stub(resty_http, "new").returns(httpc) + + skynet_tracker.track_upload_timer( + false, + valid_skylink, + valid_auth_headers + ) + + local uri = "http://10.10.10.70:3000/track/upload/" .. valid_skylink + assert.stub(request_uri).was_called_with(httpc, uri, { method = "POST", headers = valid_auth_headers }) + assert.stub(ngx.log).was_called_with( + ngx.ERR, + "Failed accounts service request /track/upload/" .. valid_skylink .. ": ", + "foo != bar" + ) + end) + end) +end) + +describe("track_registry", function() + local status_code_ok = 200 + local status_code_not_found = 404 + local request_method_write = "POST" + local request_method_read = "GET" + + before_each(function() + stub(ngx.timer, "at") + end) + + after_each(function() + ngx.timer.at:revert() + end) + + it("should schedule a timer when status code was 200", function() + ngx.timer.at.invokes(function() return true, nil end) + + skynet_tracker.track_registry(status_code_ok, valid_auth_headers, request_method_write) + + assert.stub(ngx.timer.at).was_called_with( + 0, + skynet_tracker.track_registry_timer, + valid_auth_headers, + request_method_write + ) + end) + + it("should schedule a timer when status code was 404", function() + ngx.timer.at.invokes(function() return true, nil end) + + skynet_tracker.track_registry(status_code_not_found, valid_auth_headers, request_method_write) + + assert.stub(ngx.timer.at).was_called_with( + 0, + skynet_tracker.track_registry_timer, + valid_auth_headers, + request_method_write + ) + end) + + it("should not schedule a timer if status code is not in 200 or 404", function() + ngx.timer.at.invokes(function() return true, nil end) + + -- couple of example of invalid 2XX, 4XX and 5XX codes + skynet_tracker.track_registry(204, valid_auth_headers, request_method_write) + skynet_tracker.track_registry(206, valid_auth_headers, request_method_write) + skynet_tracker.track_registry(401, valid_auth_headers, request_method_write) + skynet_tracker.track_registry(403, valid_auth_headers, request_method_write) + skynet_tracker.track_registry(490, valid_auth_headers, request_method_write) + skynet_tracker.track_registry(500, valid_auth_headers, request_method_write) + skynet_tracker.track_registry(502, valid_auth_headers, request_method_write) + + assert.stub(ngx.timer.at).was_not_called() + end) + + it("should not schedule a timer if auth headers are empty", function() + ngx.timer.at.invokes(function() return true, nil end) + + skynet_tracker.track_registry(status_code_ok, {}, request_method_write) + + assert.stub(ngx.timer.at).was_not_called() + end) + + it("should log an error if timer failed to create", function() + stub(ngx, "log") + ngx.timer.at.invokes(function() return false, "such a failure" end) + + skynet_tracker.track_registry(status_code_ok, valid_auth_headers, request_method_write) + + assert.stub(ngx.timer.at).was_called_with( + 0, + skynet_tracker.track_registry_timer, + valid_auth_headers, + request_method_write + ) + assert.stub(ngx.log).was_called_with(ngx.ERR, "Failed to create timer: ", "such a failure") + + ngx.log:revert() + end) + + describe("track_registry_timer", function() + before_each(function() + stub(ngx, "log") + end) + + after_each(function() + local resty_http = require("resty.http") + + ngx.log:revert() + resty_http.new:revert() + end) + + it("should exit early on premature", function() + local resty_http = require("resty.http") + local request_uri = spy.new(function() + return { status = 200 } -- return 200 success + end) + local httpc = mock({ request_uri = request_uri }) + stub(resty_http, "new").returns(httpc) + + skynet_tracker.track_registry_timer( + true, + valid_auth_headers, + request_method_write + ) + + assert.stub(request_uri).was_not_called() + assert.stub(ngx.log).was_not_called() + end) + + it("should make a post request to registry write tracker service", function() + local resty_http = require("resty.http") + local request_uri = spy.new(function() + return { status = 204 } -- return 204 success + end) + local httpc = mock({ request_uri = request_uri }) + stub(resty_http, "new").returns(httpc) + + skynet_tracker.track_registry_timer( + false, + valid_auth_headers, + request_method_write + ) + + local uri = "http://10.10.10.70:3000/track/registry/write" + assert.stub(request_uri).was_called_with(httpc, uri, { method = "POST", headers = valid_auth_headers }) + assert.stub(ngx.log).was_not_called() + end) + + it("should make a post request to registry read tracker service", function() + local resty_http = require("resty.http") + local request_uri = spy.new(function() + return { status = 204 } -- return 204 success + end) + local httpc = mock({ request_uri = request_uri }) + stub(resty_http, "new").returns(httpc) + + skynet_tracker.track_registry_timer( + false, + valid_auth_headers, + request_method_read + ) + + local uri = "http://10.10.10.70:3000/track/registry/read" + assert.stub(request_uri).was_called_with(httpc, uri, { method = "POST", headers = valid_auth_headers }) + assert.stub(ngx.log).was_not_called() + end) + + it("should log message on tracker request failure with response code", function() + local resty_http = require("resty.http") + local request_uri = spy.new(function() + return { status = 404, body = "baz" } -- return 404 failure + end) + local httpc = mock({ request_uri = request_uri }) + stub(resty_http, "new").returns(httpc) + + skynet_tracker.track_registry_timer( + false, + valid_auth_headers, + request_method_write + ) + + local uri = "http://10.10.10.70:3000/track/registry/write" + assert.stub(request_uri).was_called_with(httpc, uri, { method = "POST", headers = valid_auth_headers }) + assert.stub(ngx.log).was_called_with( + ngx.ERR, + "Failed accounts service request /track/registry/write: ", + "[HTTP 404] baz" + ) + end) + + it("should log message on tracker request error", function() + local resty_http = require("resty.http") + local request_uri = spy.new(function() + return nil, "foo != bar" -- return error + end) + local httpc = mock({ request_uri = request_uri }) + stub(resty_http, "new").returns(httpc) + + skynet_tracker.track_registry_timer( + false, + valid_auth_headers, + request_method_write + ) + + local uri = "http://10.10.10.70:3000/track/registry/write" + assert.stub(request_uri).was_called_with(httpc, uri, { method = "POST", headers = valid_auth_headers }) + assert.stub(ngx.log).was_called_with( + ngx.ERR, + "Failed accounts service request /track/registry/write: ", + "foo != bar" + ) + end) + end) +end) \ No newline at end of file diff --git a/docker/nginx/libs/skynet/utils.lua b/docker/nginx/libs/skynet/utils.lua index adee23b2..05755f7b 100644 --- a/docker/nginx/libs/skynet/utils.lua +++ b/docker/nginx/libs/skynet/utils.lua @@ -1,13 +1,20 @@ local _M = {} +local ngx_base64 = require("ngx.base64") +local utils = require("utils") + function _M.authorization_header() -- read api password from env variable - local apipassword = os.getenv("SIA_API_PASSWORD") + local apipassword = utils.getenv("SIA_API_PASSWORD") -- if api password is not available as env variable, read it from disk - if apipassword == nil or apipassword == "" then + if not apipassword then -- open apipassword file for reading (b flag is required for some reason) -- (file /etc/.sia/apipassword has to be mounted from the host system) local apipassword_file = io.open("/data/sia/apipassword", "rb") + -- make sure to throw a meaningful error if apipassword file does not exist + if not apipassword_file then + error("Error reading /data/sia/apipassword file") + end -- read apipassword file contents and trim newline (important) apipassword = apipassword_file:read("*all"):gsub("%s+", "") -- make sure to close file after reading the password @@ -15,7 +22,7 @@ function _M.authorization_header() end -- encode the user:password authorization string -- (in our case user is empty so it is just :password) - local content = require("ngx.base64").encode_base64url(":" .. apipassword) + local content = ngx_base64.encode_base64url(":" .. apipassword) -- set authorization header with proper base64 encoded string return "Basic " .. content end diff --git a/docker/nginx/libs/skynet/utils.spec.lua b/docker/nginx/libs/skynet/utils.spec.lua new file mode 100644 index 00000000..171be4fa --- /dev/null +++ b/docker/nginx/libs/skynet/utils.spec.lua @@ -0,0 +1,65 @@ +-- luacheck: ignore io + +local utils = require('utils') +local skynet_utils = require('skynet.utils') + +describe("authorization_header", function() + local apipassword = "ddd0c1430fbf2708" + local expected_header = "Basic OmRkZDBjMTQzMGZiZjI3MDg" + + it("reads SIA_API_PASSWORD from env variable and returns a header", function() + -- stub getenv on SIA_API_PASSWORD + stub(utils, "getenv") + utils.getenv.on_call_with("SIA_API_PASSWORD").returns(apipassword) + + local header = skynet_utils.authorization_header() + + assert.is_equal(header, expected_header) + + -- revert stub to original function + utils.getenv:revert() + end) + + it("uses /data/sia/apipassword file if SIA_API_PASSWORD env var is missing", function() + -- stub /data/sia/apipassword file + stub(io, "open") + io.open.on_call_with("/data/sia/apipassword", "rb").returns(mock({ + read = spy.new(function() return apipassword end), + close = spy.new() + })) + + local header = skynet_utils.authorization_header() + + assert.is_equal(header, expected_header) + + -- revert stub to original function + io.open:revert() + end) + + it("should choose env variable over file if both are available", function() + -- stub getenv on SIA_API_PASSWORD + stub(utils, "getenv") + utils.getenv.on_call_with("SIA_API_PASSWORD").returns(apipassword) + + -- stub /data/sia/apipassword file + stub(io, "open") + io.open.on_call_with("/data/sia/apipassword", "rb").returns(mock({ + read = spy.new(function() return "foooooooooooooo" end), + close = spy.new() + })) + + local header = skynet_utils.authorization_header() + + assert.is_equal(header, "Basic OmRkZDBjMTQzMGZiZjI3MDg") + + -- revert stubs to original function + utils.getenv:revert() + io.open:revert() + end) + + it("should error out if neither env variable is available nor file exists", function() + assert.has_error(function() + skynet_utils.authorization_header() + end, "Error reading /data/sia/apipassword file") + end) +end) diff --git a/docker/nginx/libs/utils.lua b/docker/nginx/libs/utils.lua index 4330c94c..8b77d802 100644 --- a/docker/nginx/libs/utils.lua +++ b/docker/nginx/libs/utils.lua @@ -42,4 +42,42 @@ function _M.extract_cookie_value(cookie_string, name_matcher) return string.sub(cookie, value_start) end +-- utility function that builds on os.getenv to get environment variable value +-- * will always return nil for both unset and empty env vars +-- * parse "boolean": "true" and "1" as true, "false" and "0" as false, throws for others +-- * parse "integer": any numerical string gets converted, otherwise returns nil +function _M.getenv(name, parse) + local value = os.getenv(name) + + -- treat empty string value as nil to simplify comparisons + if value == nil or value == "" then + return nil + end + + -- do not parse the value + if parse == nil then + return value + end + + -- try to parse as boolean + if parse == "boolean" then + if string.lower(value) == "true" or value == "1" then + return true + end + + if string.lower(value) == "false" or value == "0" then + return false + end + + error("utils.getenv: Parsing value '" .. tostring(value) .. "' to boolean is not supported") + end + + -- try to parse as integer + if parse == "integer" then + return tonumber(value) + end + + error("utils.getenv: Parsing to '" .. parse .. "' is not supported") +end + return _M diff --git a/docker/nginx/libs/utils.spec.lua b/docker/nginx/libs/utils.spec.lua index c853c8cd..71ef086c 100644 --- a/docker/nginx/libs/utils.spec.lua +++ b/docker/nginx/libs/utils.spec.lua @@ -1,3 +1,5 @@ +-- luacheck: ignore os + local utils = require('utils') describe("is_table_empty", function() @@ -77,3 +79,137 @@ describe("extract_cookie_value", function() assert.are.equals(value, "MTY0NzUyr8jD-ytiWtspm0tGabKfooxeIDuWcXhJ3lnY0eEw==") end) end) + +describe("getenv", function() + before_each(function() + stub(os, "getenv") + end) + + after_each(function() + os.getenv:revert() + end) + + it("should return nil for not existing env var", function() + os.getenv.on_call_with("foo").returns(nil) + + assert.is_nil(utils.getenv("foo")) + end) + + it("should return nil for env var that is an empty string", function() + os.getenv.on_call_with("foo").returns("") + + assert.is_nil(utils.getenv("foo")) + end) + + it("should return the value as is when it is non empty string", function() + os.getenv.on_call_with("foo").returns("bar") + + assert.is_equal(utils.getenv("foo"), "bar") + end) + + describe("parse", function() + it("should throw on not supported parser", function() + os.getenv.on_call_with("foo").returns("test") + + assert.has_error(function() + utils.getenv("foo", "starwars") + end, "utils.getenv: Parsing to 'starwars' is not supported") + end) + + describe("as boolean", function() + it("should return nil for not existing env var", function() + os.getenv.on_call_with("foo").returns(nil) + + assert.is_nil(utils.getenv("foo", "boolean")) + end) + + it("should return nil for env var that is an empty string", function() + os.getenv.on_call_with("foo").returns("") + + assert.is_nil(utils.getenv("foo", "boolean")) + end) + + it("should parse 'true' string as true", function() + os.getenv.on_call_with("foo").returns("true") + + assert.is_true(utils.getenv("foo", "boolean")) + end) + + it("should parse 'True' string as true", function() + os.getenv.on_call_with("foo").returns("True") + + assert.is_true(utils.getenv("foo", "boolean")) + end) + + it("should parse '1' string as true", function() + os.getenv.on_call_with("foo").returns("1") + + assert.is_true(utils.getenv("foo", "boolean")) + end) + + it("should parse 'false' string as false", function() + os.getenv.on_call_with("foo").returns("false") + + assert.is_false(utils.getenv("foo", "boolean")) + end) + + it("should parse 'False' string as false", function() + os.getenv.on_call_with("foo").returns("False") + + assert.is_false(utils.getenv("foo", "boolean")) + end) + + it("should parse '0' string as false", function() + os.getenv.on_call_with("foo").returns("0") + + assert.is_false(utils.getenv("foo", "boolean")) + end) + + it("should throw an error for not supported string", function() + os.getenv.on_call_with("foo").returns("mandalorian") + + assert.has_error(function() + utils.getenv("foo", "boolean") + end, "utils.getenv: Parsing value 'mandalorian' to boolean is not supported") + end) + end) + + describe("as integer", function() + it("should return nil for not existing env var", function() + os.getenv.on_call_with("foo").returns(nil) + + assert.is_nil(utils.getenv("foo", "integer")) + end) + + it("should return nil for env var that is an empty string", function() + os.getenv.on_call_with("foo").returns("") + + assert.is_nil(utils.getenv("foo", "integer")) + end) + + it("should parse '0' string as 0", function() + os.getenv.on_call_with("foo").returns("0") + + assert.equals(utils.getenv("foo", "integer"), 0) + end) + + it("should parse '1' string as 1", function() + os.getenv.on_call_with("foo").returns("1") + + assert.equals(utils.getenv("foo", "integer"), 1) + end) + + it("should parse '-1' string as -1", function() + os.getenv.on_call_with("foo").returns("-1") + + assert.equals(utils.getenv("foo", "integer"), -1) + end) + + it("should return nil for non numerical string", function() + os.getenv.on_call_with("foo").returns("test") + + assert.is_nil(utils.getenv("foo", "integer")) + end) + end) + end) +end) diff --git a/docker/nginx/mo b/docker/nginx/mo deleted file mode 100755 index ba8e48d1..00000000 --- a/docker/nginx/mo +++ /dev/null @@ -1,1106 +0,0 @@ -#!/usr/bin/env bash -# -#/ Mo is a mustache template rendering software written in bash. It inserts -#/ environment variables into templates. -#/ -#/ Simply put, mo will change {{VARIABLE}} into the value of that -#/ environment variable. You can use {{#VARIABLE}}content{{/VARIABLE}} to -#/ conditionally display content or iterate over the values of an array. -#/ -#/ Learn more about mustache templates at https://mustache.github.io/ -#/ -#/ Simple usage: -#/ -#/ mo [OPTIONS] filenames... -#/ -#/ Options: -#/ -#/ -u, --fail-not-set -#/ Fail upon expansion of an unset variable. -#/ -x, --fail-on-function -#/ Fail when a function returns a non-zero status code. -#/ -e, --false -#/ Treat the string "false" as empty for conditionals. -#/ -h, --help -#/ This message. -#/ -s=FILE, --source=FILE -#/ Load FILE into the environment before processing templates. -#/ Can be used multiple times. -# -# Mo is under a MIT style licence with an additional non-advertising clause. -# See LICENSE.md for the full text. -# -# This is open source! Please feel free to contribute. -# -# https://github.com/tests-always-included/mo - - -# Public: Template parser function. Writes templates to stdout. -# -# $0 - Name of the mo file, used for getting the help message. -# $@ - Filenames to parse. -# -# Options: -# -# --allow-function-arguments -# -# Permit functions in templates to be called with additional arguments. This -# puts template data directly in to the path of an eval statement. Use with -# caution. Not listed in the help because it only makes sense when mo is -# sourced. -# -# -u, --fail-not-set -# -# Fail upon expansion of an unset variable. Default behavior is to silently -# ignore and expand into empty string. -# -# -x, --fail-on-function -# -# Fail when a function used by a template returns an error status code. -# Alternately, ou may set the MO_FAIL_ON_FUNCTION environment variable to a -# non-empty value to enable this behavior. -# -# -e, --false -# -# Treat "false" as an empty value. You may set the MO_FALSE_IS_EMPTY -# environment variable instead to a non-empty value to enable this behavior. -# -# -h, --help -# -# Display a help message. -# -# -s=FILE, --source=FILE -# -# Source a file into the environment before processing template files. -# This can be used multiple times. -# -# -- -# -# Used to indicate the end of options. You may optionally use this when -# filenames may start with two hyphens. -# -# Mo uses the following environment variables: -# -# MO_ALLOW_FUNCTION_ARGUMENTS - When set to a non-empty value, this allows -# functions referenced in templates to receive additional -# options and arguments. This puts the content from the -# template directly into an eval statement. Use with extreme -# care. -# MO_FUNCTION_ARGS - Arguments passed to the function -# MO_FAIL_ON_FUNCTION - If a function returns a non-zero status code, abort -# with an error. -# MO_FAIL_ON_UNSET - When set to a non-empty value, expansion of an unset env -# variable will be aborted with an error. -# MO_FALSE_IS_EMPTY - When set to a non-empty value, the string "false" will be -# treated as an empty value for the purposes of conditionals. -# MO_ORIGINAL_COMMAND - Used to find the `mo` program in order to generate a -# help message. -# -# Returns nothing. -mo() ( - # This function executes in a subshell so IFS is reset. - # Namespace this variable so we don't conflict with desired values. - local moContent f2source files doubleHyphens - - IFS=$' \n\t' - files=() - doubleHyphens=false - - if [[ $# -gt 0 ]]; then - for arg in "$@"; do - if $doubleHyphens; then - #: After we encounter two hyphens together, all the rest - #: of the arguments are files. - files=("${files[@]}" "$arg") - else - case "$arg" in - -h|--h|--he|--hel|--help|-\?) - moUsage "$0" - exit 0 - ;; - - --allow-function-arguments) - # shellcheck disable=SC2030 - MO_ALLOW_FUNCTION_ARGUMENTS=true - ;; - - -u | --fail-not-set) - # shellcheck disable=SC2030 - MO_FAIL_ON_UNSET=true - ;; - - -x | --fail-on-function) - # shellcheck disable=SC2030 - MO_FAIL_ON_FUNCTION=true - ;; - - -e | --false) - # shellcheck disable=SC2030 - MO_FALSE_IS_EMPTY=true - ;; - - -s=* | --source=*) - if [[ "$arg" == --source=* ]]; then - f2source="${arg#--source=}" - else - f2source="${arg#-s=}" - fi - - if [[ -f "$f2source" ]]; then - # shellcheck disable=SC1090 - . "$f2source" - else - echo "No such file: $f2source" >&2 - exit 1 - fi - ;; - - --) - #: Set a flag indicating we've encountered double hyphens - doubleHyphens=true - ;; - - *) - #: Every arg that is not a flag or a option should be a file - files=(${files[@]+"${files[@]}"} "$arg") - ;; - esac - fi - done - fi - - moGetContent moContent "${files[@]}" || return 1 - moParse "$moContent" "" true -) - - -# Internal: Call a function. -# -# $1 - Variable for output -# $2 - Function to call -# $3 - Content to pass -# $4 - Additional arguments as a single string -# -# This can be dangerous, especially if you are using tags like -# {{someFunction ; rm -rf / }} -# -# Returns nothing. -moCallFunction() { - local moArgs moContent moFunctionArgs moFunctionResult - - moArgs=() - moTrimWhitespace moFunctionArgs "$4" - - # shellcheck disable=SC2031 - if [[ -n "${MO_ALLOW_FUNCTION_ARGUMENTS-}" ]]; then - # Intentionally bad behavior - # shellcheck disable=SC2206 - moArgs=($4) - fi - - moContent=$(echo -n "$3" | MO_FUNCTION_ARGS="$moFunctionArgs" eval "$2" "${moArgs[@]}") || { - moFunctionResult=$? - # shellcheck disable=SC2031 - if [[ -n "${MO_FAIL_ON_FUNCTION-}" && "$moFunctionResult" != 0 ]]; then - echo "Function '$2' with args (${moArgs[*]+"${moArgs[@]}"}) failed with status code $moFunctionResult" - exit "$moFunctionResult" - fi - } - - # shellcheck disable=SC2031 - local "$1" && moIndirect "$1" "$moContent" -} - - -# Internal: Scan content until the right end tag is found. Creates an array -# with the following members: -# -# [0] = Content before end tag -# [1] = End tag (complete tag) -# [2] = Content after end tag -# -# Everything using this function uses the "standalone tags" logic. -# -# $1 - Name of variable for the array -# $2 - Content -# $3 - Name of end tag -# $4 - If -z, do standalone tag processing before finishing -# -# Returns nothing. -moFindEndTag() { - local content remaining scanned standaloneBytes tag - - #: Find open tags - scanned="" - moSplit content "$2" '{{' '}}' - - while [[ "${#content[@]}" -gt 1 ]]; do - moTrimWhitespace tag "${content[1]}" - - #: Restore content[1] before we start using it - content[1]='{{'"${content[1]}"'}}' - - case $tag in - '#'* | '^'*) - #: Start another block - scanned="${scanned}${content[0]}${content[1]}" - moTrimWhitespace tag "${tag:1}" - moFindEndTag content "${content[2]}" "$tag" "loop" - scanned="${scanned}${content[0]}${content[1]}" - remaining=${content[2]} - ;; - - '/'*) - #: End a block - could be ours - moTrimWhitespace tag "${tag:1}" - scanned="$scanned${content[0]}" - - if [[ "$tag" == "$3" ]]; then - #: Found our end tag - if [[ -z "${4-}" ]] && moIsStandalone standaloneBytes "$scanned" "${content[2]}" true; then - #: This is also a standalone tag - clean up whitespace - #: and move those whitespace bytes to the "tag" element - # shellcheck disable=SC2206 - standaloneBytes=( $standaloneBytes ) - content[1]="${scanned:${standaloneBytes[0]}}${content[1]}${content[2]:0:${standaloneBytes[1]}}" - scanned="${scanned:0:${standaloneBytes[0]}}" - content[2]="${content[2]:${standaloneBytes[1]}}" - fi - - local "$1" && moIndirectArray "$1" "$scanned" "${content[1]}" "${content[2]}" - return 0 - fi - - scanned="$scanned${content[1]}" - remaining=${content[2]} - ;; - - *) - #: Ignore all other tags - scanned="${scanned}${content[0]}${content[1]}" - remaining=${content[2]} - ;; - esac - - moSplit content "$remaining" '{{' '}}' - done - - #: Did not find our closing tag - scanned="$scanned${content[0]}" - local "$1" && moIndirectArray "$1" "${scanned}" "" "" -} - - -# Internal: Find the first index of a substring. If not found, sets the -# index to -1. -# -# $1 - Destination variable for the index -# $2 - Haystack -# $3 - Needle -# -# Returns nothing. -moFindString() { - local pos string - - string=${2%%$3*} - [[ "$string" == "$2" ]] && pos=-1 || pos=${#string} - local "$1" && moIndirect "$1" "$pos" -} - - -# Internal: Generate a dotted name based on current context and target name. -# -# $1 - Target variable to store results -# $2 - Context name -# $3 - Desired variable name -# -# Returns nothing. -moFullTagName() { - if [[ -z "${2-}" ]] || [[ "$2" == *.* ]]; then - local "$1" && moIndirect "$1" "$3" - else - local "$1" && moIndirect "$1" "${2}.${3}" - fi -} - - -# Internal: Fetches the content to parse into a variable. Can be a list of -# partials for files or the content from stdin. -# -# $1 - Variable name to assign this content back as -# $2-@ - File names (optional) -# -# Returns nothing. -moGetContent() { - local moContent moFilename moTarget - - moTarget=$1 - shift - if [[ "${#@}" -gt 0 ]]; then - moContent="" - - for moFilename in "$@"; do - #: This is so relative paths work from inside template files - moContent="$moContent"'{{>'"$moFilename"'}}' - done - else - moLoadFile moContent || return 1 - fi - - local "$moTarget" && moIndirect "$moTarget" "$moContent" -} - - -# Internal: Indent a string, placing the indent at the beginning of every -# line that has any content. -# -# $1 - Name of destination variable to get an array of lines -# $2 - The indent string -# $3 - The string to reindent -# -# Returns nothing. -moIndentLines() { - local content fragment len posN posR result trimmed - - result="" - - #: Remove the period from the end of the string. - len=$((${#3} - 1)) - content=${3:0:$len} - - if [[ -z "${2-}" ]]; then - local "$1" && moIndirect "$1" "$content" - - return 0 - fi - - moFindString posN "$content" $'\n' - moFindString posR "$content" $'\r' - - while [[ "$posN" -gt -1 ]] || [[ "$posR" -gt -1 ]]; do - if [[ "$posN" -gt -1 ]]; then - fragment="${content:0:$posN + 1}" - content=${content:$posN + 1} - else - fragment="${content:0:$posR + 1}" - content=${content:$posR + 1} - fi - - moTrimChars trimmed "$fragment" false true " " $'\t' $'\n' $'\r' - - if [[ -n "$trimmed" ]]; then - fragment="$2$fragment" - fi - - result="$result$fragment" - - moFindString posN "$content" $'\n' - moFindString posR "$content" $'\r' - - # If the content ends in a newline, do not indent. - if [[ "$posN" -eq ${#content} ]]; then - # Special clause for \r\n - if [[ "$posR" -eq "$((posN - 1))" ]]; then - posR=-1 - fi - - posN=-1 - fi - - if [[ "$posR" -eq ${#content} ]]; then - posR=-1 - fi - done - - moTrimChars trimmed "$content" false true " " $'\t' - - if [[ -n "$trimmed" ]]; then - content="$2$content" - fi - - result="$result$content" - - local "$1" && moIndirect "$1" "$result" -} - - -# Internal: Send a variable up to the parent of the caller of this function. -# -# $1 - Variable name -# $2 - Value -# -# Examples -# -# callFunc () { -# local "$1" && moIndirect "$1" "the value" -# } -# callFunc dest -# echo "$dest" # writes "the value" -# -# Returns nothing. -moIndirect() { - unset -v "$1" - printf -v "$1" '%s' "$2" -} - - -# Internal: Send an array as a variable up to caller of a function -# -# $1 - Variable name -# $2-@ - Array elements -# -# Examples -# -# callFunc () { -# local myArray=(one two three) -# local "$1" && moIndirectArray "$1" "${myArray[@]}" -# } -# callFunc dest -# echo "${dest[@]}" # writes "one two three" -# -# Returns nothing. -moIndirectArray() { - unset -v "$1" - - # IFS must be set to a string containing space or unset in order for - # the array slicing to work regardless of the current IFS setting on - # bash 3. This is detailed further at - # https://github.com/fidian/gg-core/pull/7 - eval "$(printf "IFS= %s=(\"\${@:2}\") IFS=%q" "$1" "$IFS")" -} - - -# Internal: Determine if a given environment variable exists and if it is -# an array. -# -# $1 - Name of environment variable -# -# Be extremely careful. Even if strict mode is enabled, it is not honored -# in newer versions of Bash. Any errors that crop up here will not be -# caught automatically. -# -# Examples -# -# var=(abc) -# if moIsArray var; then -# echo "This is an array" -# echo "Make sure you don't accidentally use \$var" -# fi -# -# Returns 0 if the name is not empty, 1 otherwise. -moIsArray() { - # Namespace this variable so we don't conflict with what we're testing. - local moTestResult - - moTestResult=$(declare -p "$1" 2>/dev/null) || return 1 - [[ "${moTestResult:0:10}" == "declare -a" ]] && return 0 - [[ "${moTestResult:0:10}" == "declare -A" ]] && return 0 - - return 1 -} - - -# Internal: Determine if the given name is a defined function. -# -# $1 - Function name to check -# -# Be extremely careful. Even if strict mode is enabled, it is not honored -# in newer versions of Bash. Any errors that crop up here will not be -# caught automatically. -# -# Examples -# -# moo () { -# echo "This is a function" -# } -# if moIsFunction moo; then -# echo "moo is a defined function" -# fi -# -# Returns 0 if the name is a function, 1 otherwise. -moIsFunction() { - local functionList functionName - - functionList=$(declare -F) - # shellcheck disable=SC2206 - functionList=( ${functionList//declare -f /} ) - - for functionName in "${functionList[@]}"; do - if [[ "$functionName" == "$1" ]]; then - return 0 - fi - done - - return 1 -} - - -# Internal: Determine if the tag is a standalone tag based on whitespace -# before and after the tag. -# -# Passes back a string containing two numbers in the format "BEFORE AFTER" -# like "27 10". It indicates the number of bytes remaining in the "before" -# string (27) and the number of bytes to trim in the "after" string (10). -# Useful for string manipulation: -# -# $1 - Variable to set for passing data back -# $2 - Content before the tag -# $3 - Content after the tag -# $4 - true/false: is this the beginning of the content? -# -# Examples -# -# moIsStandalone RESULT "$before" "$after" false || return 0 -# RESULT_ARRAY=( $RESULT ) -# echo "${before:0:${RESULT_ARRAY[0]}}...${after:${RESULT_ARRAY[1]}}" -# -# Returns nothing. -moIsStandalone() { - local afterTrimmed beforeTrimmed char - - moTrimChars beforeTrimmed "$2" false true " " $'\t' - moTrimChars afterTrimmed "$3" true false " " $'\t' - char=$((${#beforeTrimmed} - 1)) - char=${beforeTrimmed:$char} - - # If the content before didn't end in a newline - if [[ "$char" != $'\n' ]] && [[ "$char" != $'\r' ]]; then - # and there was content or this didn't start the file - if [[ -n "$char" ]] || ! $4; then - # then this is not a standalone tag. - return 1 - fi - fi - - char=${afterTrimmed:0:1} - - # If the content after doesn't start with a newline and it is something - if [[ "$char" != $'\n' ]] && [[ "$char" != $'\r' ]] && [[ -n "$char" ]]; then - # then this is not a standalone tag. - return 2 - fi - - if [[ "$char" == $'\r' ]] && [[ "${afterTrimmed:1:1}" == $'\n' ]]; then - char="$char"$'\n' - fi - - local "$1" && moIndirect "$1" "$((${#beforeTrimmed})) $((${#3} + ${#char} - ${#afterTrimmed}))" -} - - -# Internal: Join / implode an array -# -# $1 - Variable name to receive the joined content -# $2 - Joiner -# $3-$* - Elements to join -# -# Returns nothing. -moJoin() { - local joiner part result target - - target=$1 - joiner=$2 - result=$3 - shift 3 - - for part in "$@"; do - result="$result$joiner$part" - done - - local "$target" && moIndirect "$target" "$result" -} - - -# Internal: Read a file into a variable. -# -# $1 - Variable name to receive the file's content -# $2 - Filename to load - if empty, defaults to /dev/stdin -# -# Returns nothing. -moLoadFile() { - local content len - - # The subshell removes any trailing newlines. We forcibly add - # a dot to the content to preserve all newlines. - # As a future optimization, it would be worth considering removing - # cat and replacing this with a read loop. - - content=$(cat -- "${2:-/dev/stdin}" && echo '.') || return 1 - len=$((${#content} - 1)) - content=${content:0:$len} # Remove last dot - - local "$1" && moIndirect "$1" "$content" -} - - -# Internal: Process a chunk of content some number of times. Writes output -# to stdout. -# -# $1 - Content to parse repeatedly -# $2 - Tag prefix (context name) -# $3-@ - Names to insert into the parsed content -# -# Returns nothing. -moLoop() { - local content context contextBase - - content=$1 - contextBase=$2 - shift 2 - - while [[ "${#@}" -gt 0 ]]; do - moFullTagName context "$contextBase" "$1" - moParse "$content" "$context" false - shift - done -} - - -# Internal: Parse a block of text, writing the result to stdout. -# -# $1 - Block of text to change -# $2 - Current name (the variable NAME for what {{.}} means) -# $3 - true when no content before this, false otherwise -# -# Returns nothing. -moParse() { - # Keep naming variables mo* here to not overwrite needed variables - # used in the string replacements - local moArgs moBlock moContent moCurrent moIsBeginning moNextIsBeginning moTag - - moCurrent=$2 - moIsBeginning=$3 - - # Find open tags - moSplit moContent "$1" '{{' '}}' - - while [[ "${#moContent[@]}" -gt 1 ]]; do - moTrimWhitespace moTag "${moContent[1]}" - moNextIsBeginning=false - - case $moTag in - '#'*) - # Loop, if/then, or pass content through function - # Sets context - moStandaloneAllowed moContent "${moContent[@]}" "$moIsBeginning" - moTrimWhitespace moTag "${moTag:1}" - - # Split arguments from the tag name. Arguments are passed to - # functions. - moArgs=$moTag - moTag=${moTag%% *} - moTag=${moTag%%$'\t'*} - moArgs=${moArgs:${#moTag}} - moFindEndTag moBlock "$moContent" "$moTag" - moFullTagName moTag "$moCurrent" "$moTag" - - if moTest "$moTag"; then - # Show / loop / pass through function - if moIsFunction "$moTag"; then - moCallFunction moContent "$moTag" "${moBlock[0]}" "$moArgs" - moParse "$moContent" "$moCurrent" false - moContent="${moBlock[2]}" - elif moIsArray "$moTag"; then - eval "moLoop \"\${moBlock[0]}\" \"$moTag\" \"\${!${moTag}[@]}\"" - else - moParse "${moBlock[0]}" "$moCurrent" true - fi - fi - - moContent="${moBlock[2]}" - ;; - - '>'*) - # Load partial - get name of file relative to cwd - moPartial moContent "${moContent[@]}" "$moIsBeginning" "$moCurrent" - moNextIsBeginning=${moContent[1]} - moContent=${moContent[0]} - ;; - - '/'*) - # Closing tag - If hit in this loop, we simply ignore - # Matching tags are found in moFindEndTag - moStandaloneAllowed moContent "${moContent[@]}" "$moIsBeginning" - ;; - - '^'*) - # Display section if named thing does not exist - moStandaloneAllowed moContent "${moContent[@]}" "$moIsBeginning" - moTrimWhitespace moTag "${moTag:1}" - moFindEndTag moBlock "$moContent" "$moTag" - moFullTagName moTag "$moCurrent" "$moTag" - - if ! moTest "$moTag"; then - moParse "${moBlock[0]}" "$moCurrent" false "$moCurrent" - fi - - moContent="${moBlock[2]}" - ;; - - '!'*) - # Comment - ignore the tag content entirely - # Trim spaces/tabs before the comment - moStandaloneAllowed moContent "${moContent[@]}" "$moIsBeginning" - ;; - - .) - # Current content (environment variable or function) - moStandaloneDenied moContent "${moContent[@]}" - moShow "$moCurrent" "$moCurrent" - ;; - - '=') - # Change delimiters - # Any two non-whitespace sequences separated by whitespace. - # This tag is ignored. - moStandaloneAllowed moContent "${moContent[@]}" "$moIsBeginning" - ;; - - '{'*) - # Unescaped - split on }}} not }} - moStandaloneDenied moContent "${moContent[@]}" - moContent="${moTag:1}"'}}'"$moContent" - moSplit moContent "$moContent" '}}}' - moTrimWhitespace moTag "${moContent[0]}" - moArgs=$moTag - moTag=${moTag%% *} - moTag=${moTag%%$'\t'*} - moArgs=${moArgs:${#moTag}} - moFullTagName moTag "$moCurrent" "$moTag" - moContent=${moContent[1]} - - # Now show the value - # Quote moArgs here, do not quote it later. - moShow "$moTag" "$moCurrent" "$moArgs" - ;; - - '&'*) - # Unescaped - moStandaloneDenied moContent "${moContent[@]}" - moTrimWhitespace moTag "${moTag:1}" - moFullTagName moTag "$moCurrent" "$moTag" - moShow "$moTag" "$moCurrent" - ;; - - *) - # Normal environment variable or function call - moStandaloneDenied moContent "${moContent[@]}" - moArgs=$moTag - moTag=${moTag%% *} - moTag=${moTag%%$'\t'*} - moArgs=${moArgs:${#moTag}} - moFullTagName moTag "$moCurrent" "$moTag" - - # Quote moArgs here, do not quote it later. - moShow "$moTag" "$moCurrent" "$moArgs" - ;; - esac - - moIsBeginning=$moNextIsBeginning - moSplit moContent "$moContent" '{{' '}}' - done - - echo -n "${moContent[0]}" -} - - -# Internal: Process a partial. -# -# Indentation should be applied to the entire partial. -# -# This sends back the "is beginning" flag because the newline after a -# standalone partial is consumed. That newline is very important in the middle -# of content. We send back this flag to reset the processing loop's -# `moIsBeginning` variable, so the software thinks we are back at the -# beginning of a file and standalone processing continues to work. -# -# Prefix all variables. -# -# $1 - Name of destination variable. Element [0] is the content, [1] is the -# true/false flag indicating if we are at the beginning of content. -# $2 - Content before the tag that was not yet written -# $3 - Tag content -# $4 - Content after the tag -# $5 - true/false: is this the beginning of the content? -# $6 - Current context name -# -# Returns nothing. -moPartial() { - # Namespace variables here to prevent conflicts. - local moContent moFilename moIndent moIsBeginning moPartial moStandalone moUnindented - - if moIsStandalone moStandalone "$2" "$4" "$5"; then - # shellcheck disable=SC2206 - moStandalone=( $moStandalone ) - echo -n "${2:0:${moStandalone[0]}}" - moIndent=${2:${moStandalone[0]}} - moContent=${4:${moStandalone[1]}} - moIsBeginning=true - else - moIndent="" - echo -n "$2" - moContent=$4 - moIsBeginning=$5 - fi - - moTrimWhitespace moFilename "${3:1}" - - # Execute in subshell to preserve current cwd and environment - ( - # It would be nice to remove `dirname` and use a function instead, - # but that's difficult when you're only given filenames. - cd "$(dirname -- "$moFilename")" || exit 1 - moUnindented="$( - moLoadFile moPartial "${moFilename##*/}" || exit 1 - moParse "${moPartial}" "$6" true - - # Fix bash handling of subshells and keep trailing whitespace. - # This is removed in moIndentLines. - echo -n "." - )" || exit 1 - moIndentLines moPartial "$moIndent" "$moUnindented" - echo -n "$moPartial" - ) || exit 1 - - # If this is a standalone tag, the trailing newline after the tag is - # removed and the contents of the partial are added, which typically - # contain a newline. We need to send a signal back to the processing - # loop that the moIsBeginning flag needs to be turned on again. - # - # [0] is the content, [1] is that flag. - local "$1" && moIndirectArray "$1" "$moContent" "$moIsBeginning" -} - - -# Internal: Show an environment variable or the output of a function to -# stdout. -# -# Limit/prefix any variables used. -# -# $1 - Name of environment variable or function -# $2 - Current context -# $3 - Arguments string if $1 is a function -# -# Returns nothing. -moShow() { - # Namespace these variables - local moJoined moNameParts moContent - - if moIsFunction "$1"; then - moCallFunction moContent "$1" "" "$3" - moParse "$moContent" "$2" false - return 0 - fi - - moSplit moNameParts "$1" "." - - if [[ -z "${moNameParts[1]-}" ]]; then - if moIsArray "$1"; then - eval moJoin moJoined "," "\${$1[@]}" - echo -n "$moJoined" - else - # shellcheck disable=SC2031 - if moTestVarSet "$1"; then - echo -n "${!1}" - elif [[ -n "${MO_FAIL_ON_UNSET-}" ]]; then - echo "Env variable not set: $1" >&2 - exit 1 - fi - fi - else - # Further subindexes are disallowed - eval "echo -n \"\${${moNameParts[0]}[${moNameParts[1]%%.*}]}\"" - fi -} - - -# Internal: Split a larger string into an array. -# -# $1 - Destination variable -# $2 - String to split -# $3 - Starting delimiter -# $4 - Ending delimiter (optional) -# -# Returns nothing. -moSplit() { - local pos result - - result=( "$2" ) - moFindString pos "${result[0]}" "$3" - - if [[ "$pos" -ne -1 ]]; then - # The first delimiter was found - result[1]=${result[0]:$pos + ${#3}} - result[0]=${result[0]:0:$pos} - - if [[ -n "${4-}" ]]; then - moFindString pos "${result[1]}" "$4" - - if [[ "$pos" -ne -1 ]]; then - # The second delimiter was found - result[2]="${result[1]:$pos + ${#4}}" - result[1]="${result[1]:0:$pos}" - fi - fi - fi - - local "$1" && moIndirectArray "$1" "${result[@]}" -} - - -# Internal: Handle the content for a standalone tag. This means removing -# whitespace (not newlines) before a tag and whitespace and a newline after -# a tag. That is, assuming, that the line is otherwise empty. -# -# $1 - Name of destination "content" variable. -# $2 - Content before the tag that was not yet written -# $3 - Tag content (not used) -# $4 - Content after the tag -# $5 - true/false: is this the beginning of the content? -# -# Returns nothing. -moStandaloneAllowed() { - local bytes - - if moIsStandalone bytes "$2" "$4" "$5"; then - # shellcheck disable=SC2206 - bytes=( $bytes ) - echo -n "${2:0:${bytes[0]}}" - local "$1" && moIndirect "$1" "${4:${bytes[1]}}" - else - echo -n "$2" - local "$1" && moIndirect "$1" "$4" - fi -} - - -# Internal: Handle the content for a tag that is never "standalone". No -# adjustments are made for newlines and whitespace. -# -# $1 - Name of destination "content" variable. -# $2 - Content before the tag that was not yet written -# $3 - Tag content (not used) -# $4 - Content after the tag -# -# Returns nothing. -moStandaloneDenied() { - echo -n "$2" - local "$1" && moIndirect "$1" "$4" -} - - -# Internal: Determines if the named thing is a function or if it is a -# non-empty environment variable. When MO_FALSE_IS_EMPTY is set to a -# non-empty value, then "false" is also treated is an empty value. -# -# Do not use variables without prefixes here if possible as this needs to -# check if any name exists in the environment -# -# $1 - Name of environment variable or function -# $2 - Current value (our context) -# MO_FALSE_IS_EMPTY - When set to a non-empty value, this will say the -# string value "false" is empty. -# -# Returns 0 if the name is not empty, 1 otherwise. When MO_FALSE_IS_EMPTY -# is set, this returns 1 if the name is "false". -moTest() { - # Test for functions - moIsFunction "$1" && return 0 - - if moIsArray "$1"; then - # Arrays must have at least 1 element - eval "[[ \"\${#${1}[@]}\" -gt 0 ]]" && return 0 - else - # If MO_FALSE_IS_EMPTY is set, then return 1 if the value of - # the variable is "false". - # shellcheck disable=SC2031 - [[ -n "${MO_FALSE_IS_EMPTY-}" ]] && [[ "${!1-}" == "false" ]] && return 1 - - # Environment variables must not be empty - [[ -n "${!1-}" ]] && return 0 - fi - - return 1 -} - -# Internal: Determine if a variable is assigned, even if it is assigned an empty -# value. -# -# $1 - Variable name to check. -# -# Returns true (0) if the variable is set, 1 if the variable is unset. -moTestVarSet() { - [[ "${!1-a}" == "${!1-b}" ]] -} - - -# Internal: Trim the leading whitespace only. -# -# $1 - Name of destination variable -# $2 - The string -# $3 - true/false - trim front? -# $4 - true/false - trim end? -# $5-@ - Characters to trim -# -# Returns nothing. -moTrimChars() { - local back current front last target varName - - target=$1 - current=$2 - front=$3 - back=$4 - last="" - shift 4 # Remove target, string, trim front flag, trim end flag - - while [[ "$current" != "$last" ]]; do - last=$current - - for varName in "$@"; do - $front && current="${current/#$varName}" - $back && current="${current/%$varName}" - done - done - - local "$target" && moIndirect "$target" "$current" -} - - -# Internal: Trim leading and trailing whitespace from a string. -# -# $1 - Name of variable to store trimmed string -# $2 - The string -# -# Returns nothing. -moTrimWhitespace() { - local result - - moTrimChars result "$2" true true $'\r' $'\n' $'\t' " " - local "$1" && moIndirect "$1" "$result" -} - - -# Internal: Displays the usage for mo. Pulls this from the file that -# contained the `mo` function. Can only work when the right filename -# comes is the one argument, and that only happens when `mo` is called -# with `$0` set to this file. -# -# $1 - Filename that has the help message -# -# Returns nothing. -moUsage() { - grep '^#/' "${MO_ORIGINAL_COMMAND}" | cut -c 4- - echo "" - echo "MO_VERSION=$MO_VERSION" -} - - -# Save the original command's path for usage later -MO_ORIGINAL_COMMAND="$(cd "${BASH_SOURCE[0]%/*}" || exit 1; pwd)/${BASH_SOURCE[0]##*/}" -MO_VERSION="2.2.0" - -# If sourced, load all functions. -# If executed, perform the actions as expected. -if [[ "$0" == "${BASH_SOURCE[0]}" ]] || [[ -z "${BASH_SOURCE[0]}" ]]; then - mo "$@" -fi diff --git a/docker/nginx/nginx.conf b/docker/nginx/nginx.conf index 3517a6bc..de55d276 100644 --- a/docker/nginx/nginx.conf +++ b/docker/nginx/nginx.conf @@ -19,6 +19,9 @@ user root; worker_processes auto; +# Enables the use of JIT for regular expressions to speed-up their processing. +pcre_jit on; + #error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info; diff --git a/docker/nginx/testing/.luacov b/docker/nginx/testing/.luacov new file mode 100644 index 00000000..2c55f3da --- /dev/null +++ b/docker/nginx/testing/.luacov @@ -0,0 +1,6 @@ +exclude = { + "/usr/local/openresty", -- internal openresty libraries + "rbusted", -- busted executable + "basexx", -- external library https://github.com/aiq/basexx +} +includeuntestedfiles = true diff --git a/docker/nginx/testing/Dockerfile b/docker/nginx/testing/Dockerfile new file mode 100644 index 00000000..ed7740b6 --- /dev/null +++ b/docker/nginx/testing/Dockerfile @@ -0,0 +1,11 @@ +FROM openresty/openresty:1.19.9.1-focal + +WORKDIR /etc/nginx + +RUN luarocks install lua-resty-http && \ + luarocks install hasher && \ + luarocks install busted + +COPY rbusted /etc/nginx/ + +CMD ["/etc/nginx/rbusted", "--verbose", "--pattern=spec", "/usr/local/openresty/site/lualib"] diff --git a/docker/nginx/testing/README.md b/docker/nginx/testing/README.md new file mode 100644 index 00000000..f40e8d95 --- /dev/null +++ b/docker/nginx/testing/README.md @@ -0,0 +1,3 @@ +# Running tests locally + +`docker run -v $(pwd)/docker/nginx/libs:/usr/local/openresty/site/lualib --rm -it $(docker build -q docker/nginx/testing)` diff --git a/docker/nginx/testing/rbusted b/docker/nginx/testing/rbusted new file mode 100755 index 00000000..94149350 --- /dev/null +++ b/docker/nginx/testing/rbusted @@ -0,0 +1,8 @@ +#!/usr/bin/env resty + +setmetatable(_G, nil) + +pcall(require, "luarocks.loader") + +-- Busted command-line runner +require "busted.runner"({ standalone = false }) diff --git a/packages/dashboard-v2/Dockerfile b/packages/dashboard-v2/Dockerfile index 70790cfa..4c834e0c 100644 --- a/packages/dashboard-v2/Dockerfile +++ b/packages/dashboard-v2/Dockerfile @@ -2,13 +2,17 @@ FROM node:16.14.2-alpine WORKDIR /usr/app -COPY package.json yarn.lock ./ +COPY packages/dashboard-v2/package.json \ + packages/dashboard-v2/yarn.lock \ + ./ RUN yarn --frozen-lockfile -COPY static ./static -COPY src ./src -COPY gatsby*.js ./ -COPY postcss.config.js tailwind.config.js ./ +COPY packages/dashboard-v2/static ./static +COPY packages/dashboard-v2/src ./src +COPY packages/dashboard-v2/gatsby*.js \ + packages/dashboard-v2/postcss.config.js \ + packages/dashboard-v2/tailwind.config.js \ + ./ CMD ["sh", "-c", "yarn build && yarn serve --host 0.0.0.0 -p 9000"] diff --git a/packages/dashboard-v2/README.md b/packages/dashboard-v2/README.md index ab0421f8..e8924bc3 100644 --- a/packages/dashboard-v2/README.md +++ b/packages/dashboard-v2/README.md @@ -11,15 +11,20 @@ This is a Gatsby application. To run it locally, all you need is: ## Accessing remote APIs -To be able to log in on a local environment with your production credentials, you'll need to make the browser believe you're actually on the same domain, otherwise the browser will block the session cookie. +To have a fully functioning local environment, you'll need to make the browser believe you're actually on the same domain as a working API (i.e. a remote dev or production server) -- otherwise the browser will block the session cookie. +To do the trick, configure proper environment variables in the `.env.development` file. +This file allows to easily control which domain name you want to use locally and which API you'd like to access. -To do the trick, edit your `/etc/hosts` file and add a record like this: +Example: -``` -127.0.0.1 local.skynetpro.net +```env +GATSBY_PORTAL_DOMAIN=skynetfree.net # Use skynetfree.net APIs +GATSBY_HOST=local.skynetfree.net # Address of your local build ``` -then run `yarn develop:secure` -- it will run `gatsby develop` with `--https --host=local.skynetpro.net -p=443` options. -If you're on macOS, you may need to `sudo` the command to successfully bind to port `443`. +> It's recommended to keep the 2LD the same, so any cookies dispatched by the API work without issues. -> **NOTE:** This should become easier once we have a docker image for the new dashboard. +With the file configured, run `yarn develop:secure` -- it will run `gatsby develop` with `--https -p=443` options. +If you're on macOS, you may need to `sudo` the command to successfully bind to port `443` (https). + +Gatsby will automatically add a proper entry to your `/etc/hosts` file and clean it up when process exits. diff --git a/packages/dashboard-v2/gatsby-config.js b/packages/dashboard-v2/gatsby-config.js index 2280f99b..d087fa65 100644 --- a/packages/dashboard-v2/gatsby-config.js +++ b/packages/dashboard-v2/gatsby-config.js @@ -1,10 +1,17 @@ +require("dotenv").config({ + path: `.env.${process.env.NODE_ENV}`, +}); + const { createProxyMiddleware } = require("http-proxy-middleware"); +const { GATSBY_PORTAL_DOMAIN } = process.env; + module.exports = { siteMetadata: { - title: "Skynet Account", - siteUrl: `https://account.${process.env.GATSBY_PORTAL_DOMAIN}/`, + title: `Account Dashboard`, + siteUrl: `https://account.${GATSBY_PORTAL_DOMAIN}`, }, + pathPrefix: "/v2", trailingSlash: "never", plugins: [ "gatsby-plugin-image", @@ -24,13 +31,27 @@ module.exports = { }, ], developMiddleware: (app) => { + // Proxy Accounts service API requests: app.use( "/api/", createProxyMiddleware({ - target: "https://account.skynetpro.net", + target: `https://account.${GATSBY_PORTAL_DOMAIN}`, secure: false, // Do not reject self-signed certificates. changeOrigin: true, }) ); + + // Proxy /skynet requests (e.g. uploads) + app.use( + ["/skynet", "/__internal/"], + createProxyMiddleware({ + target: `https://${GATSBY_PORTAL_DOMAIN}`, + secure: false, // Do not reject self-signed certificates. + changeOrigin: true, + pathRewrite: { + "^/skynet": "", + }, + }) + ); }, }; diff --git a/packages/dashboard-v2/package.json b/packages/dashboard-v2/package.json index b760bf48..80070815 100644 --- a/packages/dashboard-v2/package.json +++ b/packages/dashboard-v2/package.json @@ -9,10 +9,10 @@ ], "scripts": { "develop": "gatsby develop", - "develop:secure": "gatsby develop --https --host=local.skynetpro.net -p=443", + "develop:secure": "dotenv -e .env.development -- gatsby develop --https -p=443", "start": "gatsby develop", - "build": "gatsby build", - "serve": "gatsby serve", + "build": "gatsby build --prefix-paths", + "serve": "gatsby serve --prefix-paths", "clean": "gatsby clean", "lint": "eslint .", "prettier": "prettier .", @@ -60,6 +60,8 @@ "babel-loader": "^8.2.3", "babel-plugin-preval": "^5.1.0", "babel-plugin-styled-components": "^2.0.2", + "dotenv": "^16.0.0", + "dotenv-cli": "^5.1.0", "eslint": "^8.9.0", "eslint-config-react-app": "^7.0.0", "eslint-plugin-storybook": "^0.5.6", diff --git a/packages/dashboard-v2/src/components/APIKeyList/APIKey.js b/packages/dashboard-v2/src/components/APIKeyList/APIKey.js index 3269bb9f..b90a539e 100644 --- a/packages/dashboard-v2/src/components/APIKeyList/APIKey.js +++ b/packages/dashboard-v2/src/components/APIKeyList/APIKey.js @@ -4,7 +4,7 @@ import { useCallback, useState } from "react"; import { Alert } from "../Alert"; import { Button } from "../Button"; -import { AddSkylinkToAPIKeyForm } from "../forms/AddSkylinkToAPIKeyForm"; +import { AddSkylinkToSponsorKeyForm } from "../forms/AddSkylinkToSponsorKeyForm"; import { CogIcon, TrashIcon } from "../Icons"; import { Modal } from "../Modal"; @@ -13,7 +13,7 @@ import { useAPIKeyRemoval } from "./useAPIKeyRemoval"; export const APIKey = ({ apiKey, onRemoved, onEdited, onRemovalError }) => { const { id, name, createdAt, skylinks } = apiKey; - const isPublic = apiKey.public === "true"; + const isSponsorKey = apiKey.public === "true"; const [error, setError] = useState(null); const onSkylinkListEdited = useCallback(() => { @@ -53,9 +53,9 @@ export const APIKey = ({ apiKey, onRemoved, onEdited, onRemovalError }) => { }, [abortEdit]); const skylinksNumber = skylinks?.length ?? 0; - const isNotConfigured = isPublic && skylinksNumber === 0; + const isNotConfigured = isSponsorKey && skylinksNumber === 0; const skylinksPhrasePrefix = skylinksNumber === 0 ? "No" : skylinksNumber; - const skylinksPhrase = `${skylinksPhrasePrefix} ${skylinksNumber === 1 ? "skylink" : "skylinks"} configured`; + const skylinksPhrase = `${skylinksPhrasePrefix} ${skylinksNumber === 1 ? "skylink" : "skylinks"} sponsored`; return (
  • { {name || "unnamed key"} - + {isSponsorKey && ( + + )} {dayjs(createdAt).format("MMM DD, YYYY")} - {isPublic && ( + {isSponsorKey && ( diff --git a/packages/dashboard-v2/src/components/AvatarUploader/AvatarUploader.js b/packages/dashboard-v2/src/components/AvatarUploader/AvatarUploader.js index 9f5bbc82..f97ca2d5 100644 --- a/packages/dashboard-v2/src/components/AvatarUploader/AvatarUploader.js +++ b/packages/dashboard-v2/src/components/AvatarUploader/AvatarUploader.js @@ -1,16 +1,16 @@ import { useEffect, useState } from "react"; import { useUser } from "../../contexts/user"; -import { SimpleUploadIcon } from "../Icons"; +// import { SimpleUploadIcon } from "../Icons"; -const AVATAR_PLACEHOLDER = "/images/avatar-placeholder.svg"; +import avatarPlaceholder from "../../../static/images/avatar-placeholder.svg"; export const AvatarUploader = (props) => { const { user } = useUser(); - const [imageUrl, setImageUrl] = useState(AVATAR_PLACEHOLDER); + const [imageUrl, setImageUrl] = useState(avatarPlaceholder); useEffect(() => { - setImageUrl(user.avatarUrl ?? AVATAR_PLACEHOLDER); + setImageUrl(user.avatarUrl ?? avatarPlaceholder); }, [user]); return ( @@ -20,6 +20,7 @@ export const AvatarUploader = (props) => { > + {/* TODO: uncomment when avatar uploads work
    - {/* TODO: actual uploading */}
    + */} ); }; diff --git a/packages/dashboard-v2/src/components/CurrentPlan/CurrentPlan.js b/packages/dashboard-v2/src/components/CurrentPlan/CurrentPlan.js index f8a5cf9e..d6df8506 100644 --- a/packages/dashboard-v2/src/components/CurrentPlan/CurrentPlan.js +++ b/packages/dashboard-v2/src/components/CurrentPlan/CurrentPlan.js @@ -1,5 +1,6 @@ import dayjs from "dayjs"; import relativeTime from "dayjs/plugin/relativeTime"; +import prettyBytes from "pretty-bytes"; import { useUser } from "../../contexts/user"; import useActivePlan from "../../hooks/useActivePlan"; @@ -28,17 +29,20 @@ const CurrentPlan = () => { } return ( -
    +

    {activePlan.name}

    -
    - {activePlan.price === 0 &&

    100GB without paying a dime! 🎉

    } +
    + {activePlan.price === 0 && activePlan.limits && ( +

    {prettyBytes(activePlan.limits.storageLimit, { binary: true })} without paying a dime! 🎉

    + )} {activePlan.price !== 0 && (user.subscriptionCancelAtPeriodEnd ? (

    Your subscription expires {dayjs(user.subscribedUntil).fromNow()}

    ) : (

    {dayjs(user.subscribedUntil).fromNow(true)} until the next payment

    ))} - + + {user.subscriptionStatus && }
    diff --git a/packages/dashboard-v2/src/components/CurrentUsage/CurrentUsage.js b/packages/dashboard-v2/src/components/CurrentUsage/CurrentUsage.js index 081b9cca..f9dbbc36 100644 --- a/packages/dashboard-v2/src/components/CurrentUsage/CurrentUsage.js +++ b/packages/dashboard-v2/src/components/CurrentUsage/CurrentUsage.js @@ -1,6 +1,7 @@ import { useEffect, useMemo, useState } from "react"; import fileSize from "pretty-bytes"; import { Link } from "gatsby"; +import cn from "classnames"; import useSWR from "swr"; import { useUser } from "../../contexts/user"; @@ -44,7 +45,7 @@ const useUsageData = () => { }; const size = (bytes) => { - const text = fileSize(bytes ?? 0, { maximumFractionDigits: 0 }); + const text = fileSize(bytes ?? 0, { maximumFractionDigits: 0, binary: true }); const [value, unit] = text.split(" "); return { @@ -62,7 +63,9 @@ const ErrorMessage = () => ( ); export default function CurrentUsage() { + const { activePlan, plans } = useActivePlan(); const { usage, error, loading } = useUsageData(); + const nextPlan = useMemo(() => plans.find(({ tier }) => tier > activePlan?.tier), [plans, activePlan]); const storageUsage = size(usage.storageUsed); const storageLimit = size(usage.storageLimit); const filesUsedLabel = useMemo(() => ({ value: usage.filesUsed, unit: "files" }), [usage.filesUsed]); @@ -89,7 +92,7 @@ export default function CurrentUsage() { {storageLimit.text}
    - +
    @@ -97,7 +100,10 @@ export default function CurrentUsage() { UPGRADE {" "} diff --git a/packages/dashboard-v2/src/components/CurrentUsage/GraphBar.js b/packages/dashboard-v2/src/components/CurrentUsage/GraphBar.js index 1afab541..fd9a015e 100644 --- a/packages/dashboard-v2/src/components/CurrentUsage/GraphBar.js +++ b/packages/dashboard-v2/src/components/CurrentUsage/GraphBar.js @@ -21,11 +21,11 @@ const BarLabel = styled.span.attrs({ `} `; -export const GraphBar = ({ value, limit, label }) => { +export const GraphBar = ({ value, limit, label, className }) => { const percentage = typeof limit !== "number" || limit === 0 ? 0 : (value / limit) * 100; return ( -
    +
    diff --git a/packages/dashboard-v2/src/components/CurrentUsage/UsageGraph.js b/packages/dashboard-v2/src/components/CurrentUsage/UsageGraph.js index 3f6f23c2..de4e7e46 100644 --- a/packages/dashboard-v2/src/components/CurrentUsage/UsageGraph.js +++ b/packages/dashboard-v2/src/components/CurrentUsage/UsageGraph.js @@ -1,9 +1,11 @@ import styled from "styled-components"; +import usageGraphBg from "../../../static/images/usage-graph-bg.svg"; + export const UsageGraph = styled.div.attrs({ className: "w-full my-3 grid grid-flow-row grid-rows-2", })` height: 146px; - background: url(/images/usage-graph-bg.svg) no-repeat; + background: url(${usageGraphBg}) no-repeat; background-size: cover; `; diff --git a/packages/dashboard-v2/src/components/FileList/useFormattedFilesData.js b/packages/dashboard-v2/src/components/FileList/useFormattedFilesData.js index 82d95090..87bf1af6 100644 --- a/packages/dashboard-v2/src/components/FileList/useFormattedFilesData.js +++ b/packages/dashboard-v2/src/components/FileList/useFormattedFilesData.js @@ -1,6 +1,7 @@ import { useMemo } from "react"; import prettyBytes from "pretty-bytes"; import dayjs from "dayjs"; +import { DATE_FORMAT } from "../../lib/config"; const parseFileName = (fileName) => { const lastDotIndex = Math.max(0, fileName.lastIndexOf(".")) || Infinity; @@ -10,7 +11,7 @@ const parseFileName = (fileName) => { const formatItem = ({ size, name: rawFileName, uploadedOn, downloadedOn, ...rest }) => { const [name, type] = parseFileName(rawFileName); - const date = dayjs(uploadedOn || downloadedOn).format("MM/DD/YYYY; HH:MM"); + const date = dayjs(uploadedOn || downloadedOn).format(DATE_FORMAT); return { ...rest, diff --git a/packages/dashboard-v2/src/components/Footer/Footer.js b/packages/dashboard-v2/src/components/Footer/Footer.js index 501d502d..8096337a 100644 --- a/packages/dashboard-v2/src/components/Footer/Footer.js +++ b/packages/dashboard-v2/src/components/Footer/Footer.js @@ -1,8 +1,19 @@ import * as React from "react"; +import styled from "styled-components"; + import { PageContainer } from "../PageContainer"; +const FooterLink = styled.a.attrs({ + className: "text-palette-400 underline decoration-dotted decoration-offset-4 decoration-1", + rel: "noreferrer", + target: "_blank", +})``; + export const Footer = () => ( -

    © Skynet Labs Inc. All rights reserved.

    +

    + Made by Skynet Labs. Open-sourced{" "} + on Github. +

    ); diff --git a/packages/dashboard-v2/src/components/HighlightedLink.js b/packages/dashboard-v2/src/components/HighlightedLink.js index 5fa6b079..f189b98b 100644 --- a/packages/dashboard-v2/src/components/HighlightedLink.js +++ b/packages/dashboard-v2/src/components/HighlightedLink.js @@ -2,5 +2,5 @@ import { Link } from "gatsby"; import styled from "styled-components"; export default styled(Link).attrs({ - className: "text-primary underline-offset-3 decoration-dotted hover:text-primary-light hover:underline", + className: "text-primary underline-offset-2 decoration-1 decoration-dotted hover:text-primary-light hover:underline", })``; diff --git a/packages/dashboard-v2/src/components/LatestActivity/ActivityTable.js b/packages/dashboard-v2/src/components/LatestActivity/ActivityTable.js index 2ee95e0f..3bea206f 100644 --- a/packages/dashboard-v2/src/components/LatestActivity/ActivityTable.js +++ b/packages/dashboard-v2/src/components/LatestActivity/ActivityTable.js @@ -3,13 +3,13 @@ import useSWR from "swr"; import { Table, TableBody, TableCell, TableRow } from "../Table"; import { ContainerLoadingIndicator } from "../LoadingIndicator"; +import useFormattedFilesData from "../FileList/useFormattedFilesData"; import { ViewAllLink } from "./ViewAllLink"; -import useFormattedActivityData from "./useFormattedActivityData"; export default function ActivityTable({ type }) { const { data, error } = useSWR(`user/${type}?pageSize=3`); - const items = useFormattedActivityData(data?.items || []); + const items = useFormattedFilesData(data?.items || []); if (!items.length) { return ( diff --git a/packages/dashboard-v2/src/components/LatestActivity/LatestActivity.js b/packages/dashboard-v2/src/components/LatestActivity/LatestActivity.js index 535bceba..627314b7 100644 --- a/packages/dashboard-v2/src/components/LatestActivity/LatestActivity.js +++ b/packages/dashboard-v2/src/components/LatestActivity/LatestActivity.js @@ -1,23 +1,13 @@ import * as React from "react"; import { Panel } from "../Panel"; -import { Tab, TabPanel, Tabs } from "../Tabs"; import ActivityTable from "./ActivityTable"; export default function LatestActivity() { return ( - - - - - - - - - - - + + ); } diff --git a/packages/dashboard-v2/src/components/LatestActivity/useFormattedActivityData.js b/packages/dashboard-v2/src/components/LatestActivity/useFormattedActivityData.js deleted file mode 100644 index cf55703c..00000000 --- a/packages/dashboard-v2/src/components/LatestActivity/useFormattedActivityData.js +++ /dev/null @@ -1,26 +0,0 @@ -import { useMemo } from "react"; -import prettyBytes from "pretty-bytes"; -import dayjs from "dayjs"; - -const parseFileName = (fileName) => { - const lastDotIndex = Math.max(0, fileName.lastIndexOf(".")) || Infinity; - - return [fileName.substr(0, lastDotIndex), fileName.substr(lastDotIndex)]; -}; - -const formatItem = ({ size, name: rawFileName, uploadedOn, downloadedOn, ...rest }) => { - const [name, type] = parseFileName(rawFileName); - const date = dayjs(uploadedOn || downloadedOn).format("MM/DD/YYYY; HH:MM"); - - return { - ...rest, - date, - size: prettyBytes(size), - type, - name, - }; -}; - -const useFormattedActivityData = (items) => useMemo(() => items.map(formatItem), [items]); - -export default useFormattedActivityData; diff --git a/packages/dashboard-v2/src/components/Tooltip/index.js b/packages/dashboard-v2/src/components/Tooltip/index.js new file mode 100644 index 00000000..46bb2f24 --- /dev/null +++ b/packages/dashboard-v2/src/components/Tooltip/index.js @@ -0,0 +1 @@ +export * from "./Tooltip"; diff --git a/packages/dashboard-v2/src/components/Uploader/Uploader.js b/packages/dashboard-v2/src/components/Uploader/Uploader.js index ab26d6c6..7b9a15df 100644 --- a/packages/dashboard-v2/src/components/Uploader/Uploader.js +++ b/packages/dashboard-v2/src/components/Uploader/Uploader.js @@ -118,7 +118,7 @@ const Uploader = ({ mode }) => {
    {uploads.length > 0 && ( -
    +
    {uploads.map((upload) => ( ))} diff --git a/packages/dashboard-v2/src/components/forms/AccountRemovalForm.js b/packages/dashboard-v2/src/components/forms/AccountRemovalForm.js index bdd7196e..02e465fe 100644 --- a/packages/dashboard-v2/src/components/forms/AccountRemovalForm.js +++ b/packages/dashboard-v2/src/components/forms/AccountRemovalForm.js @@ -34,8 +34,9 @@ export const AccountRemovalForm = ({ abort, onSuccess }) => {

    Delete account

    +

    This will completely delete your account.

    - This will completely delete your account. This process can't be undone. + This process cannot be undone.

    diff --git a/packages/dashboard-v2/src/components/forms/AddAPIKeyForm.js b/packages/dashboard-v2/src/components/forms/AddAPIKeyForm.js index ccea02a5..b04a62b1 100644 --- a/packages/dashboard-v2/src/components/forms/AddAPIKeyForm.js +++ b/packages/dashboard-v2/src/components/forms/AddAPIKeyForm.js @@ -2,6 +2,7 @@ import * as Yup from "yup"; import { forwardRef, useImperativeHandle, useState } from "react"; import PropTypes from "prop-types"; import { Formik, Form } from "formik"; +import cn from "classnames"; import accountsService from "../../services/accountsService"; @@ -9,7 +10,6 @@ import { Alert } from "../Alert"; import { Button } from "../Button"; import { CopyButton } from "../CopyButton"; import { TextField } from "../Form/TextField"; -import { CircledProgressIcon, PlusIcon } from "../Icons"; const newAPIKeySchema = Yup.object().shape({ name: Yup.string(), @@ -22,7 +22,7 @@ const State = { }; export const APIKeyType = { - Public: "public", + Sponsor: "sponsor", General: "general", }; @@ -37,10 +37,10 @@ export const AddAPIKeyForm = forwardRef(({ onSuccess, type }, ref) => { return (
    {state === State.Success && ( - + Success!

    Please copy your new API key below. We'll never show it again!

    -
    +
    {generatedKey} @@ -62,8 +62,8 @@ export const AddAPIKeyForm = forwardRef(({ onSuccess, type }, ref) => { .post("user/apikeys", { json: { name, - public: type === APIKeyType.Public ? "true" : "false", - skylinks: type === APIKeyType.Public ? [] : null, + public: type === APIKeyType.Sponsor ? "true" : "false", + skylinks: type === APIKeyType.Sponsor ? [] : null, }, }) .json(); @@ -78,26 +78,20 @@ export const AddAPIKeyForm = forwardRef(({ onSuccess, type }, ref) => { }} > {({ errors, touched, isSubmitting }) => ( - -
    - -
    -
    - {isSubmitting ? ( - - ) : ( - - )} + + +
    +
    )} @@ -110,5 +104,5 @@ AddAPIKeyForm.displayName = "AddAPIKeyForm"; AddAPIKeyForm.propTypes = { onSuccess: PropTypes.func.isRequired, - type: PropTypes.oneOf([APIKeyType.Public, APIKeyType.General]).isRequired, + type: PropTypes.oneOf([APIKeyType.Sponsor, APIKeyType.General]).isRequired, }; diff --git a/packages/dashboard-v2/src/components/forms/AddSkylinkToAPIKeyForm.js b/packages/dashboard-v2/src/components/forms/AddSkylinkToSponsorKeyForm.js similarity index 94% rename from packages/dashboard-v2/src/components/forms/AddSkylinkToAPIKeyForm.js rename to packages/dashboard-v2/src/components/forms/AddSkylinkToSponsorKeyForm.js index 60ed905c..71742096 100644 --- a/packages/dashboard-v2/src/components/forms/AddSkylinkToAPIKeyForm.js +++ b/packages/dashboard-v2/src/components/forms/AddSkylinkToSponsorKeyForm.js @@ -19,7 +19,7 @@ const newSkylinkSchema = Yup.object().shape({ }), }); -export const AddSkylinkToAPIKeyForm = ({ addSkylink }) => ( +export const AddSkylinkToSponsorKeyForm = ({ addSkylink }) => ( ( ); -AddSkylinkToAPIKeyForm.propTypes = { +AddSkylinkToSponsorKeyForm.propTypes = { addSkylink: PropTypes.func.isRequired, }; diff --git a/packages/dashboard-v2/src/components/forms/AddPublicAPIKeyForm.js b/packages/dashboard-v2/src/components/forms/AddSponsorKeyForm.js similarity index 91% rename from packages/dashboard-v2/src/components/forms/AddPublicAPIKeyForm.js rename to packages/dashboard-v2/src/components/forms/AddSponsorKeyForm.js index c98daac9..236cdc9b 100644 --- a/packages/dashboard-v2/src/components/forms/AddPublicAPIKeyForm.js +++ b/packages/dashboard-v2/src/components/forms/AddSponsorKeyForm.js @@ -25,7 +25,7 @@ const skylinkValidator = (optional) => (value) => { } }; -const newPublicAPIKeySchema = Yup.object().shape({ +const newSponsorKeySchema = Yup.object().shape({ name: Yup.string(), skylinks: Yup.array().of(Yup.string().test("skylink", "Provide a valid Skylink", skylinkValidator(false))), nextSkylink: Yup.string().when("skylinks", { @@ -41,7 +41,7 @@ const State = { Failure: "FAILURE", }; -export const AddPublicAPIKeyForm = forwardRef(({ onSuccess }, ref) => { +export const AddSponsorKeyForm = forwardRef(({ onSuccess }, ref) => { const [state, setState] = useState(State.Pure); const [generatedKey, setGeneratedKey] = useState(null); @@ -52,10 +52,10 @@ export const AddPublicAPIKeyForm = forwardRef(({ onSuccess }, ref) => { return (
    {state === State.Success && ( - + Success!

    Please copy your new API key below. We'll never show it again!

    -
    +
    {generatedKey} @@ -72,7 +72,7 @@ export const AddPublicAPIKeyForm = forwardRef(({ onSuccess }, ref) => { skylinks: [], nextSkylink: "", }} - validationSchema={newPublicAPIKeySchema} + validationSchema={newSponsorKeySchema} onSubmit={async ({ name, skylinks, nextSkylink }, { resetForm }) => { try { const { key } = await accountsService @@ -80,7 +80,7 @@ export const AddPublicAPIKeyForm = forwardRef(({ onSuccess }, ref) => { json: { name, public: "true", - skylinks: [...skylinks, nextSkylink].filter(Boolean).map(parseSkylink), + skylinks: [...skylinks, nextSkylink].filter(Boolean).map((skylink) => parseSkylink(skylink)), }, }) .json(); @@ -101,14 +101,14 @@ export const AddPublicAPIKeyForm = forwardRef(({ onSuccess }, ref) => { type="text" id="name" name="name" - label="Public API Key Name" + label="Sponsor API Key Name" placeholder="my_applications_statistics" error={errors.name} touched={touched.name} />
    -
    Skylinks accessible with the new key
    +
    Skylinks sponsored by the new key
    { @@ -182,7 +182,7 @@ export const AddPublicAPIKeyForm = forwardRef(({ onSuccess }, ref) => { className={cn("px-2.5", { "cursor-wait": isSubmitting })} disabled={!isValid || isSubmitting} > - {isSubmitting ? "Generating" : "Generate"} your public key + {isSubmitting ? "Generating your sponsor key..." : "Generate your sponsor key"}
    @@ -192,8 +192,8 @@ export const AddPublicAPIKeyForm = forwardRef(({ onSuccess }, ref) => { ); }); -AddPublicAPIKeyForm.displayName = "AddAPIKeyForm"; +AddSponsorKeyForm.displayName = "AddSponsorKeyForm"; -AddPublicAPIKeyForm.propTypes = { +AddSponsorKeyForm.propTypes = { onSuccess: PropTypes.func.isRequired, }; diff --git a/packages/dashboard-v2/src/components/forms/SignUpForm.js b/packages/dashboard-v2/src/components/forms/SignUpForm.js index 6725c673..c6ebed3e 100644 --- a/packages/dashboard-v2/src/components/forms/SignUpForm.js +++ b/packages/dashboard-v2/src/components/forms/SignUpForm.js @@ -32,14 +32,16 @@ export const SignUpForm = ({ onSuccess, onFailure }) => ( validationSchema={registrationSchema} onSubmit={async ({ email, password }, { setErrors }) => { try { - await accountsService.post("user", { - json: { - email, - password, - }, - }); + const user = await accountsService + .post("user", { + json: { + email, + password, + }, + }) + .json(); - onSuccess(); + onSuccess(user); } catch (err) { let isFormErrorSet = false; diff --git a/packages/dashboard-v2/src/contexts/plans/PlansProvider.js b/packages/dashboard-v2/src/contexts/plans/PlansProvider.js index 135c9bcb..7c6579ad 100644 --- a/packages/dashboard-v2/src/contexts/plans/PlansProvider.js +++ b/packages/dashboard-v2/src/contexts/plans/PlansProvider.js @@ -19,7 +19,14 @@ const aggregatePlansAndLimits = (plans, limits, { includeFreePlan }) => { // Decorate each plan with its corresponding limits data, if available. if (limits?.length) { - return sortedPlans.map((plan) => ({ ...plan, limits: limits[plan.tier] || null })); + return limits.map((limitsDescriptor, index) => { + const asssociatedPlan = sortedPlans.find((plan) => plan.tier === index) || {}; + + return { + ...asssociatedPlan, + limits: limitsDescriptor || null, + }; + }); } // If we don't have the limits data yet, set just return the plans. @@ -40,10 +47,12 @@ export const PlansProvider = ({ children }) => { if (plansError || limitsError) { setLoading(false); setError(plansError || limitsError); - } else if (rawPlans) { + } else if (rawPlans || limits) { setLoading(false); setPlans( - aggregatePlansAndLimits(rawPlans, limits?.userLimits, { includeFreePlan: !settings.isSubscriptionRequired }) + aggregatePlansAndLimits(rawPlans || [], limits?.userLimits, { + includeFreePlan: !settings.isSubscriptionRequired, + }) ); } }, [rawPlans, limits, plansError, limitsError, settings.isSubscriptionRequired]); diff --git a/packages/dashboard-v2/src/layouts/AuthLayout.js b/packages/dashboard-v2/src/layouts/AuthLayout.js index 8141e606..323e4319 100644 --- a/packages/dashboard-v2/src/layouts/AuthLayout.js +++ b/packages/dashboard-v2/src/layouts/AuthLayout.js @@ -3,10 +3,13 @@ import styled from "styled-components"; import { UserProvider } from "../contexts/user"; +import skynetLogo from "../../static/images/logo-black-text.svg"; +import authBg from "../../static/images/auth-bg.svg"; + const Layout = styled.div.attrs({ className: "min-h-screen w-screen bg-black flex", })` - background-image: url(/images/auth-bg.svg); + background-image: url(${authBg}); background-repeat: no-repeat; background-position: center center; `; @@ -36,7 +39,7 @@ const AuthLayout =
    - Skynet + Skynet
    {children}
    diff --git a/packages/dashboard-v2/src/layouts/DashboardLayout.js b/packages/dashboard-v2/src/layouts/DashboardLayout.js index 633057eb..8ac7c393 100644 --- a/packages/dashboard-v2/src/layouts/DashboardLayout.js +++ b/packages/dashboard-v2/src/layouts/DashboardLayout.js @@ -7,10 +7,12 @@ import { Footer } from "../components/Footer"; import { UserProvider, useUser } from "../contexts/user"; import { FullScreenLoadingIndicator } from "../components/LoadingIndicator"; +import dashboardBg from "../../static/images/dashboard-bg.svg"; + const Wrapper = styled.div.attrs({ className: "min-h-screen overflow-hidden", })` - background-image: url(/images/dashboard-bg.svg); + background-image: url(${dashboardBg}); background-position: center -280px; background-repeat: no-repeat; `; diff --git a/packages/dashboard-v2/src/layouts/UserSettingsLayout.js b/packages/dashboard-v2/src/layouts/UserSettingsLayout.js index 81820021..f008ee40 100644 --- a/packages/dashboard-v2/src/layouts/UserSettingsLayout.js +++ b/packages/dashboard-v2/src/layouts/UserSettingsLayout.js @@ -16,8 +16,8 @@ const Sidebar = () => ( Export - - API Keys + + Developer settings diff --git a/packages/dashboard-v2/src/lib/config.js b/packages/dashboard-v2/src/lib/config.js new file mode 100644 index 00000000..9c3beb35 --- /dev/null +++ b/packages/dashboard-v2/src/lib/config.js @@ -0,0 +1 @@ +export const DATE_FORMAT = "MMM D, YYYY HH:MM"; diff --git a/packages/dashboard-v2/src/pages/auth/registration.js b/packages/dashboard-v2/src/pages/auth/registration.js index b58562fd..5764ad6a 100644 --- a/packages/dashboard-v2/src/pages/auth/registration.js +++ b/packages/dashboard-v2/src/pages/auth/registration.js @@ -1,5 +1,4 @@ -import { useEffect, useState } from "react"; -import { navigate } from "gatsby"; +import { useCallback, useState } from "react"; import bytes from "pretty-bytes"; import AuthLayout from "../../layouts/AuthLayout"; @@ -10,6 +9,7 @@ import { SignUpForm } from "../../components/forms/SignUpForm"; import { usePortalSettings } from "../../contexts/portal-settings"; import { PlansProvider, usePlans } from "../../contexts/plans"; import { Metadata } from "../../components/Metadata"; +import { useUser } from "../../contexts/user"; const FreePortalHeader = () => { const { plans } = usePlans(); @@ -47,14 +47,14 @@ const State = { const SignUpPage = () => { const [state, setState] = useState(State.Pure); const { settings } = usePortalSettings(); + const { mutate: refreshUserState } = useUser(); - useEffect(() => { - if (state === State.Success) { - const timer = setTimeout(() => navigate(settings.isSubscriptionRequired ? "/upgrade" : "/"), 3000); - - return () => clearTimeout(timer); - } - }, [state, settings.isSubscriptionRequired]); + const onUserCreated = useCallback( + (newUser) => { + refreshUserState(newUser); + }, + [refreshUserState] + ); return ( @@ -70,7 +70,7 @@ const SignUpPage = () => { {state !== State.Success && ( <> - setState(State.Success)} onFailure={() => setState(State.Failure)} /> + setState(State.Failure)} />

    Already have an account? Sign in @@ -78,14 +78,6 @@ const SignUpPage = () => { )} - {state === State.Success && ( -

    -

    Please check your inbox and confirm your email address.

    -

    You will be redirected to your dashboard shortly.

    - Click here to go there now. -
    - )} - {state === State.Failure && (

    Something went wrong, please try again later.

    )} diff --git a/packages/dashboard-v2/src/pages/files.js b/packages/dashboard-v2/src/pages/files.js index f4a47a1d..be856d4a 100644 --- a/packages/dashboard-v2/src/pages/files.js +++ b/packages/dashboard-v2/src/pages/files.js @@ -1,32 +1,19 @@ import * as React from "react"; -import { useSearchParam } from "react-use"; import DashboardLayout from "../layouts/DashboardLayout"; import { Panel } from "../components/Panel"; -import { Tab, TabPanel, Tabs } from "../components/Tabs"; import { Metadata } from "../components/Metadata"; import FileList from "../components/FileList/FileList"; const FilesPage = () => { - const defaultTab = useSearchParam("tab"); - return ( <> - My Files + Files - - - - - - - - - - + ); diff --git a/packages/dashboard-v2/src/pages/settings/api-keys.js b/packages/dashboard-v2/src/pages/settings/api-keys.js deleted file mode 100644 index 03486248..00000000 --- a/packages/dashboard-v2/src/pages/settings/api-keys.js +++ /dev/null @@ -1,110 +0,0 @@ -import useSWR from "swr"; -import { useCallback, useRef } from "react"; - -import UserSettingsLayout from "../../layouts/UserSettingsLayout"; - -import { AddAPIKeyForm, APIKeyType } from "../../components/forms/AddAPIKeyForm"; -import { APIKeyList } from "../../components/APIKeyList/APIKeyList"; -import { Alert } from "../../components/Alert"; -import { AddPublicAPIKeyForm } from "../../components/forms/AddPublicAPIKeyForm"; -import { Metadata } from "../../components/Metadata"; - -const APIKeysPage = () => { - const { data: apiKeys = [], mutate: reloadKeys, error } = useSWR("user/apikeys"); - const generalKeys = apiKeys.filter(({ public: isPublic }) => isPublic === "false"); - const publicKeys = apiKeys.filter(({ public: isPublic }) => isPublic === "true"); - - const publicFormRef = useRef(); - const generalFormRef = useRef(); - - const refreshState = useCallback( - (resetForms) => { - if (resetForms) { - publicFormRef.current?.reset(); - generalFormRef.current?.reset(); - } - reloadKeys(); - }, - [reloadKeys] - ); - - return ( - <> - - API Keys - -
    -
    -
    -

    API Keys

    -

    There are two types of API keys that you can generate for your account.

    -

    Make sure to use the appropriate type.

    -
    - -
    - -
    -
    Public keys
    -

    - Public keys provide read access to a selected list of skylinks. You can share them publicly. -

    - -
    - -
    - - {error ? ( - - An error occurred while loading your API keys. Please try again later. - - ) : ( -
    - {publicKeys?.length > 0 ? ( - refreshState(true)} /> - ) : ( - No public API keys found. - )} -
    - )} -
    -
    - -
    -
    General keys
    -

    - These keys provide full access to Accounts service and are equivalent to using a JWT token. -

    -

    - This type of API keys needs to be kept secret and should never be shared with anyone. -

    - -
    - -
    - - {error ? ( - - An error occurred while loading your API keys. Please try again later. - - ) : ( -
    - {generalKeys?.length > 0 ? ( - refreshState(true)} /> - ) : ( - No general API keys found. - )} -
    - )} -
    -
    -
    - -
    -
    - - ); -}; - -APIKeysPage.Layout = UserSettingsLayout; - -export default APIKeysPage; diff --git a/packages/dashboard-v2/src/pages/settings/developer-settings.js b/packages/dashboard-v2/src/pages/settings/developer-settings.js new file mode 100644 index 00000000..3f1917c1 --- /dev/null +++ b/packages/dashboard-v2/src/pages/settings/developer-settings.js @@ -0,0 +1,117 @@ +import useSWR from "swr"; +import { useCallback, useRef } from "react"; + +import UserSettingsLayout from "../../layouts/UserSettingsLayout"; + +import { AddAPIKeyForm, APIKeyType } from "../../components/forms/AddAPIKeyForm"; +import { APIKeyList } from "../../components/APIKeyList/APIKeyList"; +import { Alert } from "../../components/Alert"; +import { AddSponsorKeyForm } from "../../components/forms/AddSponsorKeyForm"; +import { Metadata } from "../../components/Metadata"; +import HighlightedLink from "../../components/HighlightedLink"; + +import apiKeysImg from "../../../static/images/api-keys.svg"; + +const DeveloperSettingsPage = () => { + const { data: allKeys = [], mutate: reloadKeys, error } = useSWR("user/apikeys"); + const apiKeys = allKeys.filter(({ public: isPublic }) => isPublic === "false"); + const sponsorKeys = allKeys.filter(({ public: isPublic }) => isPublic === "true"); + + const publicFormRef = useRef(); + const generalFormRef = useRef(); + + const refreshState = useCallback( + (resetForms) => { + if (resetForms) { + publicFormRef.current?.reset(); + generalFormRef.current?.reset(); + } + reloadKeys(); + }, + [reloadKeys] + ); + + return ( + <> + + Developer settings + +
    +
    +
    +

    Developer settings

    +

    API keys allow developers and applications to extend the functionality of your portal account.

    +

    Skynet uses two types of API keys, explained below.

    +
    + +
    + +
    +
    Sponsor keys
    +
    +

    + Sponsor keys allow users without an account on this portal to download skylinks covered by the API key. +

    +

    + Learn more about sponsoring content with Sponsor API Keys{" "} + + here + + . +

    {" "} + {/* TODO: missing documentation link */} +
    + +
    + {error ? ( + + An error occurred while loading your sponsor keys. Please try again later. + + ) : ( +
    + {sponsorKeys?.length > 0 ? ( + refreshState(true)} /> + ) : ( + No sponsor keys found. + )} +
    + )} +
    + +
    + +
    +
    API keys
    +

    + These keys allow uploading and downloading skyfiles, as well as reading and writing to the registry. +

    +
    + +
    + + {error ? ( + + An error occurred while loading your API keys. Please try again later. + + ) : ( +
    + {apiKeys?.length > 0 ? ( + refreshState(true)} /> + ) : ( + No API keys found. + )} +
    + )} +
    +
    +
    + +
    +
    + + ); +}; + +DeveloperSettingsPage.Layout = UserSettingsLayout; + +export default DeveloperSettingsPage; diff --git a/packages/dashboard-v2/src/pages/settings/export.js b/packages/dashboard-v2/src/pages/settings/export.js index 7a75bc0c..aeb8aed3 100644 --- a/packages/dashboard-v2/src/pages/settings/export.js +++ b/packages/dashboard-v2/src/pages/settings/export.js @@ -6,6 +6,8 @@ import { Switch } from "../../components/Switch"; import { Button } from "../../components/Button"; import { Metadata } from "../../components/Metadata"; +import exportImg from "../../../static/images/import-export.svg"; + const useExportOptions = () => { const [pinnedFiles, setPinnedFiles] = useState(false); const [uploadHistory, setUploadHistory] = useState(false); @@ -38,8 +40,8 @@ const ExportPage = () => {

    Export

    - Et quidem exercitus quid ex eo delectu rerum, quem modo ista sis aequitate. Probabo, inquit, modo dixi, - constituto. + Select the items you want to export. You can use this data to migrate your account to another Skynet + portal.


    @@ -65,7 +67,7 @@ const ExportPage = () => {
    - +
    diff --git a/packages/dashboard-v2/src/pages/settings/index.js b/packages/dashboard-v2/src/pages/settings/index.js index 23a815e2..f8449cdc 100644 --- a/packages/dashboard-v2/src/pages/settings/index.js +++ b/packages/dashboard-v2/src/pages/settings/index.js @@ -8,6 +8,10 @@ import { Modal } from "../../components/Modal/Modal"; import { AccountRemovalForm } from "../../components/forms/AccountRemovalForm"; import { Alert } from "../../components/Alert"; import { Metadata } from "../../components/Metadata"; +import HighlightedLink from "../../components/HighlightedLink"; +import { AvatarUploader } from "../../components/AvatarUploader/AvatarUploader"; +import { useMedia } from "react-use"; +import theme from "../../lib/theme"; const State = { Pure: "PURE", @@ -19,10 +23,16 @@ const AccountPage = () => { const { user, mutate: reloadUser } = useUser(); const [state, setState] = useState(State.Pure); const [removalInitiated, setRemovalInitiated] = useState(false); + const isLargeScreen = useMedia(`(min-width: ${theme.screens.xl})`); const prompt = () => setRemovalInitiated(true); const abort = () => setRemovalInitiated(false); + const onAccountRemoved = useCallback(async () => { + await reloadUser(null); + await navigate("/auth/login"); + }, [reloadUser]); + const onSettingsUpdated = useCallback( async (updatedState) => { try { @@ -45,14 +55,7 @@ const AccountPage = () => {
    -
    -

    Account

    -

    - Tum dicere exorsus est laborum et quasi involuta aperiri, altera prompta et expedita. Primum igitur, - inquit, modo ista sis aequitate. -

    -
    -
    +

    Account

    {state === State.Failure && ( There was an error processing your request. Please try again later. @@ -63,7 +66,23 @@ const AccountPage = () => {
    Delete account
    -

    This will completely delete your account. This process can't be undone.

    +
    +

    + This action will delete your account and cannot be undone. +

    +

    + Your uploaded files will remain accessible while any portal continues to{" "} + + pin + {" "} + them to Skynet. +

    +
    +
    + {isLargeScreen && } +
    {removalInitiated && ( - navigate("/auth/login")} /> + )}
    diff --git a/packages/dashboard-v2/src/pages/settings/notifications.js b/packages/dashboard-v2/src/pages/settings/notifications.js index 447e40c8..5467f3a5 100644 --- a/packages/dashboard-v2/src/pages/settings/notifications.js +++ b/packages/dashboard-v2/src/pages/settings/notifications.js @@ -1,11 +1,12 @@ import * as React from "react"; -import { StaticImage } from "gatsby-plugin-image"; import UserSettingsLayout from "../../layouts/UserSettingsLayout"; import { Switch } from "../../components/Switch"; import { Metadata } from "../../components/Metadata"; +import inboxImg from "../../../static/images/inbox.svg"; + const NotificationsPage = () => { return ( <> @@ -18,18 +19,13 @@ const NotificationsPage = () => {
    {/* TODO: saves on change */} - I agreee to get the latest news, updates and special offers delivered to my email inbox. + I agree to receive emails of the latest news, updates and offers.

    Statistics
    - {/* TODO: proper content :) */} -

    - Si sine causa, nollem me tamen laudandis maioribus meis corrupisti nec in malis. Si sine causa, mox - videro. -

    - +

    Check below to be notified by email when your usage approaches your plan's limits.

    • {/* TODO: saves on change */} @@ -37,13 +33,13 @@ const NotificationsPage = () => {
    • {/* TODO: saves on change */} - File limit + Files limit
    -
    - +
    +
    diff --git a/packages/dashboard-v2/src/services/skynetClient.js b/packages/dashboard-v2/src/services/skynetClient.js index 0549085c..a01e96f9 100644 --- a/packages/dashboard-v2/src/services/skynetClient.js +++ b/packages/dashboard-v2/src/services/skynetClient.js @@ -1,3 +1,3 @@ import { SkynetClient } from "skynet-js"; -export default new SkynetClient("https://skynetpro.net"); // TODO: proper API url +export default new SkynetClient(`https://${process.env.GATSBY_PORTAL_DOMAIN}`); diff --git a/packages/dashboard-v2/yarn.lock b/packages/dashboard-v2/yarn.lock index 39b088e4..0ac39653 100644 --- a/packages/dashboard-v2/yarn.lock +++ b/packages/dashboard-v2/yarn.lock @@ -6735,11 +6735,31 @@ dot-prop@^5.2.0: dependencies: is-obj "^2.0.0" +dotenv-cli@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/dotenv-cli/-/dotenv-cli-5.1.0.tgz#0d2942b089082da0157f9b26bd6c5c4dd51ef48e" + integrity sha512-NoEZAlKo9WVrG0b3i9mBxdD6INdDuGqdgR74t68t8084QcI077/1MnPerRW1odl+9uULhcdnQp2U0pYVppKHOA== + dependencies: + cross-spawn "^7.0.3" + dotenv "^16.0.0" + dotenv-expand "^8.0.1" + minimist "^1.2.5" + dotenv-expand@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0" integrity sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA== +dotenv-expand@^8.0.1: + version "8.0.3" + resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-8.0.3.tgz#29016757455bcc748469c83a19b36aaf2b83dd6e" + integrity sha512-SErOMvge0ZUyWd5B0NXMQlDkN+8r+HhVUsxgOO7IoPDOdDRD2JjExpN6y3KnFR66jsJMwSn1pqIivhU5rcJiNg== + +dotenv@^16.0.0: + version "16.0.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.0.tgz#c619001253be89ebb638d027b609c75c26e47411" + integrity sha512-qD9WU0MPM4SWLPJy/r2Be+2WgQj8plChsyrCNQzW/0WjvcJQiKQJ9mH3ZgB3fxbUUxgc/11ZJ0Fi5KiimWGz2Q== + dotenv@^8.0.0, dotenv@^8.6.0: version "8.6.0" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b" diff --git a/packages/dashboard/Dockerfile b/packages/dashboard/Dockerfile index 39707664..25f584b3 100644 --- a/packages/dashboard/Dockerfile +++ b/packages/dashboard/Dockerfile @@ -2,14 +2,19 @@ FROM node:16.14.2-alpine WORKDIR /usr/app -COPY package.json yarn.lock ./ +COPY packages/dashboard/package.json \ + packages/dashboard/yarn.lock \ + ./ ENV NEXT_TELEMETRY_DISABLED 1 RUN yarn --frozen-lockfile -COPY public ./public -COPY src ./src -COPY styles ./styles -COPY .eslintrc.json postcss.config.js tailwind.config.js ./ +COPY packages/dashboard/public ./public +COPY packages/dashboard/src ./src +COPY packages/dashboard/styles ./styles +COPY packages/dashboard/.eslintrc.json \ + packages/dashboard/postcss.config.js \ + packages/dashboard/tailwind.config.js \ + ./ CMD ["sh", "-c", "env | grep -E 'NEXT_PUBLIC|STRIPE|ACCOUNTS' > .env.local && yarn build && yarn start"] diff --git a/packages/dashboard/package.json b/packages/dashboard/package.json index a184c7b8..a10f5b26 100644 --- a/packages/dashboard/package.json +++ b/packages/dashboard/package.json @@ -10,25 +10,25 @@ "dependencies": { "@fontsource/sora": "4.5.5", "@fontsource/source-sans-pro": "4.5.6", - "@stripe/react-stripe-js": "1.7.1", + "@stripe/react-stripe-js": "1.7.2", "@stripe/stripe-js": "1.27.0", "classnames": "2.3.1", "copy-text-to-clipboard": "^3.0.1", - "dayjs": "1.11.0", - "express-jwt": "6.1.1", + "dayjs": "1.11.1", + "express-jwt": "6.1.2", "fast-levenshtein": "3.0.0", "formik": "2.2.9", "http-status-codes": "2.2.0", "ky": "0.30.0", - "next": "12.1.4", + "next": "12.1.5", "normalize.css": "8.0.1", "pretty-bytes": "6.0.0", "react": "17.0.2", "react-dom": "17.0.2", "react-toastify": "8.2.0", "skynet-js": "3.0.2", - "stripe": "8.216.0", - "swr": "1.2.2", + "stripe": "8.218.0", + "swr": "1.3.0", "yup": "0.32.11" }, "devDependencies": { @@ -36,9 +36,9 @@ "@tailwindcss/typography": "0.5.2", "autoprefixer": "10.4.4", "eslint": "8.13.0", - "eslint-config-next": "12.1.4", + "eslint-config-next": "12.1.5", "postcss": "8.4.12", "prettier": "2.6.2", - "tailwindcss": "3.0.23" + "tailwindcss": "3.0.24" } } diff --git a/packages/dashboard/yarn.lock b/packages/dashboard/yarn.lock index 94f7ef53..f4e7ce67 100644 --- a/packages/dashboard/yarn.lock +++ b/packages/dashboard/yarn.lock @@ -2,27 +2,6 @@ # yarn lockfile v1 -"@babel/code-frame@^7.0.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.14.5.tgz#23b08d740e83f49c5e59945fbf1b43e80bbf4edb" - integrity sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw== - dependencies: - "@babel/highlight" "^7.14.5" - -"@babel/helper-validator-identifier@^7.14.5": - version "7.15.7" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz#220df993bfe904a4a6b02ab4f3385a5ebf6e2389" - integrity sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w== - -"@babel/highlight@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9" - integrity sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg== - dependencies: - "@babel/helper-validator-identifier" "^7.14.5" - chalk "^2.0.0" - js-tokens "^4.0.0" - "@babel/runtime-corejs3@^7.10.2": version "7.16.3" resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.16.3.tgz#1e25de4fa994c57c18e5fdda6cc810dac70f5590" @@ -77,77 +56,77 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== -"@next/env@12.1.4": - version "12.1.4" - resolved "https://registry.yarnpkg.com/@next/env/-/env-12.1.4.tgz#5af629b43075281ecd7f87938802b7cf5b67e94b" - integrity sha512-7gQwotJDKnfMxxXd8xJ2vsX5AzyDxO3zou0+QOXX8/unypA6icw5+wf6A62yKZ6qQ4UZHHxS68pb6UV+wNneXg== +"@next/env@12.1.5": + version "12.1.5" + resolved "https://registry.yarnpkg.com/@next/env/-/env-12.1.5.tgz#a21ba6708022d630402ca2b340316e69a0296dfc" + integrity sha512-+34yUJslfJi7Lyx6ELuN8nWcOzi27izfYnZIC1Dqv7kmmfiBVxgzR3BXhlvEMTKC2IRJhXVs2FkMY+buQe3k7Q== -"@next/eslint-plugin-next@12.1.4": - version "12.1.4" - resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-12.1.4.tgz#9c52637af8eecab24dac3f2e5098376f6fc2dff4" - integrity sha512-BRy565KVK6Cdy8LHaHTiwctLqBu/RT84RLpESug70BDJzBlV8QBvODyx/j7wGhvYqp9kvstM05lyb6JaTkSCcQ== +"@next/eslint-plugin-next@12.1.5": + version "12.1.5" + resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-12.1.5.tgz#273885b35e6bbcd40ff1436d2a8d0ec03fb6f6ef" + integrity sha512-Cnb8ERC5bNKBFrnMH6203sp/b0Y78QRx1XsFu+86oBtDBmQmOFoHu7teQjHm69ER73XKK3aGaeoLiXacHoUFsg== dependencies: glob "7.1.7" -"@next/swc-android-arm-eabi@12.1.4": - version "12.1.4" - resolved "https://registry.yarnpkg.com/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.1.4.tgz#c3dae178b7c15ad627d2e9b8dfb38caecb5c4ac7" - integrity sha512-FJg/6a3s2YrUaqZ+/DJZzeZqfxbbWrynQMT1C5wlIEq9aDLXCFpPM/PiOyJh0ahxc0XPmi6uo38Poq+GJTuKWw== +"@next/swc-android-arm-eabi@12.1.5": + version "12.1.5" + resolved "https://registry.yarnpkg.com/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.1.5.tgz#36729ab3dfd7743e82cfe536b43254dcb146620c" + integrity sha512-SKnGTdYcoN04Y2DvE0/Y7/MjkA+ltsmbuH/y/hR7Ob7tsj+8ZdOYuk+YvW1B8dY20nDPHP58XgDTSm2nA8BzzA== -"@next/swc-android-arm64@12.1.4": - version "12.1.4" - resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-12.1.4.tgz#f320d60639e19ecffa1f9034829f2d95502a9a51" - integrity sha512-LXraazvQQFBgxIg3Htny6G5V5he9EK7oS4jWtMdTGIikmD/OGByOv8ZjLuVLZLtVm3UIvaAiGtlQSLecxJoJDw== +"@next/swc-android-arm64@12.1.5": + version "12.1.5" + resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-12.1.5.tgz#52578f552305c92d0b9b81d603c9643fb71e0835" + integrity sha512-YXiqgQ/9Rxg1dXp6brXbeQM1JDx9SwUY/36JiE+36FXqYEmDYbxld9qkX6GEzkc5rbwJ+RCitargnzEtwGW0mw== -"@next/swc-darwin-arm64@12.1.4": - version "12.1.4" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.1.4.tgz#fd578278312613eddcf3aee26910100509941b63" - integrity sha512-SSST/dBymecllZxcqTCcSTCu5o1NKk9I+xcvhn/O9nH6GWjgvGgGkNqLbCarCa0jJ1ukvlBA138FagyrmZ/4rQ== +"@next/swc-darwin-arm64@12.1.5": + version "12.1.5" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.1.5.tgz#3d5b53211484c72074f4975ba0ec2b1107db300e" + integrity sha512-y8mhldb/WFZ6lFeowkGfi0cO/lBdiBqDk4T4LZLvCpoQp4Or/NzUN6P5NzBQZ5/b4oUHM/wQICEM+1wKA4qIVw== -"@next/swc-darwin-x64@12.1.4": - version "12.1.4" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-12.1.4.tgz#ace5f80d8c8348efe194f6d7074c6213c52b3944" - integrity sha512-p1lwdX0TVjaoDXQVuAkjtxVBbCL/urgxiMCBwuPDO7TikpXtSRivi+mIzBj5q7ypgICFmIAOW3TyupXeoPRAnA== +"@next/swc-darwin-x64@12.1.5": + version "12.1.5" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-12.1.5.tgz#adcabb732d226453777c0d37d58eaff9328b66fd" + integrity sha512-wqJ3X7WQdTwSGi0kIDEmzw34QHISRIQ5uvC+VXmsIlCPFcMA+zM5723uh8NfuKGquDMiEMS31a83QgkuHMYbwQ== -"@next/swc-linux-arm-gnueabihf@12.1.4": - version "12.1.4" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.1.4.tgz#2bf2c83863635f19c71c226a2df936e001cce29c" - integrity sha512-67PZlgkCn3TDxacdVft0xqDCL7Io1/C4xbAs0+oSQ0xzp6OzN2RNpuKjHJrJgKd0DsE1XZ9sCP27Qv0591yfyg== +"@next/swc-linux-arm-gnueabihf@12.1.5": + version "12.1.5" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.1.5.tgz#82a7cde67482b756bc65fbebf1dfa8a782074e93" + integrity sha512-WnhdM5duONMvt2CncAl+9pim0wBxDS2lHoo7ub/o/i1bRbs11UTzosKzEXVaTDCUkCX2c32lIDi1WcN2ZPkcdw== -"@next/swc-linux-arm64-gnu@12.1.4": - version "12.1.4" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.1.4.tgz#d577190f641c9b4b463719dd6b8953b6ba9be8d9" - integrity sha512-OnOWixhhw7aU22TQdQLYrgpgFq0oA1wGgnjAiHJ+St7MLj82KTDyM9UcymAMbGYy6nG/TFOOHdTmRMtCRNOw0g== +"@next/swc-linux-arm64-gnu@12.1.5": + version "12.1.5" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.1.5.tgz#f82ca014504950aab751e81f467492e9be0bad5d" + integrity sha512-Jq2H68yQ4bLUhR/XQnbw3LDW0GMQn355qx6rU36BthDLeGue7YV7MqNPa8GKvrpPocEMW77nWx/1yI6w6J07gw== -"@next/swc-linux-arm64-musl@12.1.4": - version "12.1.4" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.1.4.tgz#e70ffe70393d8f9242deecdb282ce5a8fd588b14" - integrity sha512-UoRMzPZnsAavdWtVylYxH8DNC7Uy0i6RrvNwT4PyQVdfANBn2omsUkcH5lgS2O7oaz0nAYLk1vqyZDO7+tJotA== +"@next/swc-linux-arm64-musl@12.1.5": + version "12.1.5" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.1.5.tgz#f811ec9f4b12a978426c284c95ab2f515ddf7f9e" + integrity sha512-KgPjwdbhDqXI7ghNN8V/WAiLquc9Ebe8KBrNNEL0NQr+yd9CyKJ6KqjayVkmX+hbHzbyvbui/5wh/p3CZQ9xcQ== -"@next/swc-linux-x64-gnu@12.1.4": - version "12.1.4" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.1.4.tgz#91498a130387fb1961902f2bee55863f8e910cff" - integrity sha512-nM+MA/frxlTLUKLJKorctdI20/ugfHRjVEEkcLp/58LGG7slNaP1E5d5dRA1yX6ISjPcQAkywas5VlGCg+uTvA== +"@next/swc-linux-x64-gnu@12.1.5": + version "12.1.5" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.1.5.tgz#d44857257e6d20dc841998951d584ab1f25772c3" + integrity sha512-O2ErUTvCJ6DkNTSr9pbu1n3tcqykqE/ebty1rwClzIYdOgpB3T2MfEPP+K7GhUR87wmN/hlihO9ch7qpVFDGKw== -"@next/swc-linux-x64-musl@12.1.4": - version "12.1.4" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.1.4.tgz#78057b03c148c121553d41521ad38f6c732762ff" - integrity sha512-GoRHxkuW4u4yKw734B9SzxJwVdyEJosaZ62P7ifOwcujTxhgBt3y76V2nNUrsSuopcKI2ZTDjaa+2wd5zyeXbA== +"@next/swc-linux-x64-musl@12.1.5": + version "12.1.5" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.1.5.tgz#3cc523abadc9a2a6de680593aff06e71cc29ecef" + integrity sha512-1eIlZmlO/VRjxxzUBcVosf54AFU3ltAzHi+BJA+9U/lPxCYIsT+R4uO3QksRzRjKWhVQMRjEnlXyyq5SKJm7BA== -"@next/swc-win32-arm64-msvc@12.1.4": - version "12.1.4" - resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.1.4.tgz#05bbaabacac23b8edf6caa99eb86b17550a09051" - integrity sha512-6TQkQze0ievXwHJcVUrIULwCYVe3ccX6T0JgZ1SiMeXpHxISN7VJF/O8uSCw1JvXZYZ6ud0CJ7nfC5HXivgfPg== +"@next/swc-win32-arm64-msvc@12.1.5": + version "12.1.5" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.1.5.tgz#c62232d869f1f9b22e8f24e4e7f05307c20f30ca" + integrity sha512-oromsfokbEuVb0CBLLE7R9qX3KGXucZpsojLpzUh1QJjuy1QkrPJncwr8xmWQnwgtQ6ecMWXgXPB+qtvizT9Tw== -"@next/swc-win32-ia32-msvc@12.1.4": - version "12.1.4" - resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.1.4.tgz#8fd2fb48f04a2802e51fc320878bf6b411c1c866" - integrity sha512-CsbX/IXuZ5VSmWCpSetG2HD6VO5FTsO39WNp2IR2Ut/uom9XtLDJAZqjQEnbUTLGHuwDKFjrIO3LkhtROXLE/g== +"@next/swc-win32-ia32-msvc@12.1.5": + version "12.1.5" + resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.1.5.tgz#2bd9b28a9ba730d12a493e7d9d18e150fe89d496" + integrity sha512-a/51L5KzBpeZSW9LbekMo3I3Cwul+V+QKwbEIMA+Qwb2qrlcn1L9h3lt8cHqNTFt2y72ce6aTwDTw1lyi5oIRA== -"@next/swc-win32-x64-msvc@12.1.4": - version "12.1.4" - resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.1.4.tgz#a72ed44c9b1f850986a30fe36c59e01f8a79b5f3" - integrity sha512-JtYuWzKXKLDMgE/xTcFtCm1MiCIRaAc5XYZfYX3n/ZWSI1SJS/GMm+Su0SAHJgRFavJh6U/p998YwO/iGTIgqQ== +"@next/swc-win32-x64-msvc@12.1.5": + version "12.1.5" + resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.1.5.tgz#02f377e4d41eaaacf265e34bab9bacd8efc4a351" + integrity sha512-/SoXW1Ntpmpw3AXAzfDRaQidnd8kbZ2oSni8u5z0yw6t4RwJvmdZy1eOaAADRThWKV+2oU90++LSnXJIwBRWYQ== "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -175,10 +154,10 @@ resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.0.8.tgz#be3e914e84eacf16dbebd311c0d0b44aa1174c64" integrity sha512-ZK5v4bJwgXldAUA8r3q9YKfCwOqoHTK/ZqRjSeRXQrBXWouoPnS4MQtgC4AXGiiBuUu5wxrRgTlv0ktmM4P1Aw== -"@stripe/react-stripe-js@1.7.1": - version "1.7.1" - resolved "https://registry.yarnpkg.com/@stripe/react-stripe-js/-/react-stripe-js-1.7.1.tgz#6e1db8f4a0eaf2193b153173d4aa7c38b681310d" - integrity sha512-GiUPoMo0xVvmpRD6JR9JAhAZ0W3ZpnYZNi0KE+91+tzrSFVpChKZbeSsJ5InlZhHFk9NckJCt1wOYBTqNsvt3A== +"@stripe/react-stripe-js@1.7.2": + version "1.7.2" + resolved "https://registry.yarnpkg.com/@stripe/react-stripe-js/-/react-stripe-js-1.7.2.tgz#87cc5464378fb28bc7390702415cf70f13a46bcd" + integrity sha512-IAVg2nPUPoSwI//XDRCO7D8mGeK4+N3Xg63fYZHmlfEWAuFVcuaqJKTT67uzIdKYZhHZ/NMdZw/ttz+GOjP/rQ== dependencies: prop-types "^15.7.2" @@ -218,11 +197,6 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-16.10.1.tgz#f3647623199ca920960006b3dccf633ea905f243" integrity sha512-4/Z9DMPKFexZj/Gn3LylFgamNKHm4K3QDi0gz9B26Uk0c8izYf97B5fxfpspMNkWlFupblKM/nV8+NA9Ffvr+w== -"@types/parse-json@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" - integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== - "@typescript-eslint/parser@5.10.1": version "5.10.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.10.1.tgz#4ce9633cc33fc70bc13786cb793c1a76fe5ad6bd" @@ -311,13 +285,6 @@ ansi-regex@^5.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== -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.1.0: version "4.3.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" @@ -390,10 +357,10 @@ ast-types-flow@^0.0.7: resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad" integrity sha1-9wtzXGvKGlycItmCw+Oef+ujva0= -async@^1.5.0: - version "1.5.2" - resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" - integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo= +async@^3.2.2: + version "3.2.3" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.3.tgz#ac53dafd3f4720ee9e8a160628f18ea91df196c9" + integrity sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g== autoprefixer@10.4.4: version "10.4.4" @@ -513,16 +480,7 @@ caniuse-lite@^1.0.30001283, caniuse-lite@^1.0.30001317: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001319.tgz#eb4da4eb3ecdd409f7ba1907820061d56096e88f" integrity sha512-xjlIAFHucBRSMUo1kb5D4LYgcN1M45qdKP++lhqowDpwJwGkpIRTt5qQqnhxjj1vHcI7nrJxWhCC1ATrCEBTcw== -chalk@^2.0.0: - 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@^4.0.0, chalk@^4.1.2: +chalk@^4.0.0: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -555,13 +513,6 @@ clsx@^1.1.1: resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188" integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA== -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" @@ -569,11 +520,6 @@ color-convert@^2.0.1: 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, color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" @@ -594,17 +540,6 @@ core-js-pure@^3.19.0: resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.19.1.tgz#edffc1fc7634000a55ba05e95b3f0fe9587a5aa4" integrity sha512-Q0Knr8Es84vtv62ei6/6jXH/7izKmOrtrxH9WJTHLCMAVeU+8TF8z8Nr08CsH4Ot0oJKzBzJJL9SJBYIv7WlfQ== -cosmiconfig@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.1.tgz#714d756522cace867867ccb4474c5d01bbae5d6d" - integrity sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ== - dependencies: - "@types/parse-json" "^4.0.0" - import-fresh "^3.2.1" - parse-json "^5.0.0" - path-type "^4.0.0" - yaml "^1.10.0" - cross-spawn@^7.0.2: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -624,10 +559,10 @@ damerau-levenshtein@^1.0.7: resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.7.tgz#64368003512a1a6992593741a09a9d31a836f55d" integrity sha512-VvdQIPGdWP0SqFXghj79Wf/5LArmreyMsGLa6FG6iC4t3j7j5s71TrwWmT/4akbDQIqjfACkLZmjXhA7g2oUZw== -dayjs@1.11.0: - version "1.11.0" - resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.0.tgz#009bf7ef2e2ea2d5db2e6583d2d39a4b5061e805" - integrity sha512-JLC809s6Y948/FuCZPm5IX8rRhQwOiyMb2TfVVQEixG7P8Lm/gt5S7yoQZmC8x1UehI9Pb7sksEt4xx14m+7Ug== +dayjs@1.11.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.1.tgz#90b33a3dda3417258d48ad2771b415def6545eb0" + integrity sha512-ER7EjqVAMkRRsxNCC5YqJ9d9VQYuWdGt7aiH2qA5R5wt8ZmWaP2dLUSIK6y/kVzLMlmh1Tvu5xUf4M/wdGJ5KA== debug@^2.6.9: version "2.6.9" @@ -729,13 +664,6 @@ emoji-regex@^9.2.2: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== -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" - es-abstract@^1.19.0, es-abstract@^1.19.1: version "1.19.1" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.19.1.tgz#d4885796876916959de78edaa0df456627115ec3" @@ -776,22 +704,17 @@ escalade@^3.1.1: resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== -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= - escape-string-regexp@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== -eslint-config-next@12.1.4: - version "12.1.4" - resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-12.1.4.tgz#939ea2ff33034763300bf1e62482cea91212d274" - integrity sha512-Uj0jrVjoQbg9qerxRjSHoOOv3PEzoZxpb8G9LYct25fsflP8xIiUq0l4WEu2KSB5owuLv5hie7wSMqPEsHj+bQ== +eslint-config-next@12.1.5: + version "12.1.5" + resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-12.1.5.tgz#658cc61194a32dfd917a3db199351396ea5db1d1" + integrity sha512-P+DCt5ti63KhC0qNLzrAmPcwRGq8pYqgcf/NNr1E+WjCrMkWdCAXkIANTquo+kcO1adR2k1lTo5GCrNUtKy4hQ== dependencies: - "@next/eslint-plugin-next" "12.1.4" + "@next/eslint-plugin-next" "12.1.5" "@rushstack/eslint-patch" "1.0.8" "@typescript-eslint/parser" "5.10.1" eslint-import-resolver-node "0.3.4" @@ -997,12 +920,12 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== -express-jwt@6.1.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/express-jwt/-/express-jwt-6.1.1.tgz#2b157fb4fa33c2d367ee71c61b5aca762de39657" - integrity sha512-m8gkY04v5jtiFZn6bYQINYX/DVXq1DVb5nIW7H8l87qJ4BBvtQKFRpxyRE31odct7OPfHdT+B8678zJHhlMrpw== +express-jwt@6.1.2: + version "6.1.2" + resolved "https://registry.yarnpkg.com/express-jwt/-/express-jwt-6.1.2.tgz#4a6cc11d1dcff6f23126dd79ec5b2b441333e78b" + integrity sha512-l5dlf5lNM/1EODMsJGfHn1VnrhhsUYEetzrKFStJZLjFQXtR+HGdBiW+jUNZ+ISsFe+h7Wl/hQKjLrY2TX0Qkg== dependencies: - async "^1.5.0" + async "^3.2.2" express-unless "^1.0.0" jsonwebtoken "^8.1.0" lodash "^4.17.21" @@ -1213,11 +1136,6 @@ has-bigints@^1.0.1: resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== -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" @@ -1264,13 +1182,6 @@ ignore@^5.1.4, ignore@^5.2.0: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== -import-cwd@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-3.0.0.tgz#20845547718015126ea9b3676b7592fb8bd4cf92" - integrity sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg== - dependencies: - import-from "^3.0.0" - import-fresh@^3.0.0, import-fresh@^3.2.1: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" @@ -1279,13 +1190,6 @@ import-fresh@^3.0.0, import-fresh@^3.2.1: parent-module "^1.0.0" resolve-from "^4.0.0" -import-from@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/import-from/-/import-from-3.0.0.tgz#055cfec38cd5a27d8057ca51376d7d3bf0891966" - integrity sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ== - dependencies: - resolve-from "^5.0.0" - imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" @@ -1313,11 +1217,6 @@ internal-slot@^1.0.3: has "^1.0.3" side-channel "^1.0.4" -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-bigint@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" @@ -1434,7 +1333,7 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= -"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: +"js-tokens@^3.0.0 || ^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== @@ -1446,11 +1345,6 @@ js-yaml@^4.1.0: dependencies: argparse "^2.0.1" -json-parse-even-better-errors@^2.3.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" - integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== - 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" @@ -1534,15 +1428,10 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" -lilconfig@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.3.tgz#68f3005e921dafbd2a2afb48379986aa6d2579fd" - integrity sha512-EHKqr/+ZvdKCifpNrJCKxBTgk5XupZA3y/aCPY9mxfgBzmgh93Mt/WqjjQ38oMxXuvDokaKiM3lAgvSH2sjtHg== - -lines-and-columns@^1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" - integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= +lilconfig@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.5.tgz#19e57fd06ccc3848fd1891655b5a447092225b25" + integrity sha512-xaYmXZtTHPAw5m+xLN8ab9C+3a8YmV3asNSPOATITbtwrfbwaLJj8h66H1WMIpALCkqsIzK3h7oQ+PdX+LQ9Eg== locate-path@^2.0.0: version "2.0.0" @@ -1686,28 +1575,28 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= -next@12.1.4: - version "12.1.4" - resolved "https://registry.yarnpkg.com/next/-/next-12.1.4.tgz#597a9bdec7aec778b442c4f6d41afd2c64a54b23" - integrity sha512-DA4g97BM4Z0nKtDvCTm58RxdvoQyYzeg0AeVbh0N4Y/D8ELrNu47lQeEgRGF8hV4eQ+Sal90zxrJQQG/mPQ8CQ== +next@12.1.5: + version "12.1.5" + resolved "https://registry.yarnpkg.com/next/-/next-12.1.5.tgz#7a07687579ddce61ee519493e1c178d83abac063" + integrity sha512-YGHDpyfgCfnT5GZObsKepmRnne7Kzp7nGrac07dikhutWQug7hHg85/+sPJ4ZW5Q2pDkb+n0FnmLkmd44htIJQ== dependencies: - "@next/env" "12.1.4" + "@next/env" "12.1.5" caniuse-lite "^1.0.30001283" postcss "8.4.5" styled-jsx "5.0.1" optionalDependencies: - "@next/swc-android-arm-eabi" "12.1.4" - "@next/swc-android-arm64" "12.1.4" - "@next/swc-darwin-arm64" "12.1.4" - "@next/swc-darwin-x64" "12.1.4" - "@next/swc-linux-arm-gnueabihf" "12.1.4" - "@next/swc-linux-arm64-gnu" "12.1.4" - "@next/swc-linux-arm64-musl" "12.1.4" - "@next/swc-linux-x64-gnu" "12.1.4" - "@next/swc-linux-x64-musl" "12.1.4" - "@next/swc-win32-arm64-msvc" "12.1.4" - "@next/swc-win32-ia32-msvc" "12.1.4" - "@next/swc-win32-x64-msvc" "12.1.4" + "@next/swc-android-arm-eabi" "12.1.5" + "@next/swc-android-arm64" "12.1.5" + "@next/swc-darwin-arm64" "12.1.5" + "@next/swc-darwin-x64" "12.1.5" + "@next/swc-linux-arm-gnueabihf" "12.1.5" + "@next/swc-linux-arm64-gnu" "12.1.5" + "@next/swc-linux-arm64-musl" "12.1.5" + "@next/swc-linux-x64-gnu" "12.1.5" + "@next/swc-linux-x64-musl" "12.1.5" + "@next/swc-win32-arm64-msvc" "12.1.5" + "@next/swc-win32-ia32-msvc" "12.1.5" + "@next/swc-win32-x64-msvc" "12.1.5" node-releases@^2.0.2: version "2.0.2" @@ -1734,10 +1623,10 @@ object-assign@^4.1.1: resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= -object-hash@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.2.0.tgz#5ad518581eefc443bd763472b8ff2e9c2c0d54a5" - integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw== +object-hash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9" + integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw== object-inspect@^1.11.0, object-inspect@^1.9.0: version "1.11.0" @@ -1839,16 +1728,6 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" -parse-json@^5.0.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" - integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== - dependencies: - "@babel/code-frame" "^7.0.0" - error-ex "^1.3.1" - json-parse-even-better-errors "^2.3.0" - lines-and-columns "^1.1.6" - path-browserify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" @@ -1896,13 +1775,12 @@ postcss-js@^4.0.0: dependencies: camelcase-css "^2.0.1" -postcss-load-config@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-3.1.0.tgz#d39c47091c4aec37f50272373a6a648ef5e97829" - integrity sha512-ipM8Ds01ZUophjDTQYSVP70slFSYg3T0/zyfII5vzhN6V57YSxMgG5syXuwi5VtS8wSf3iL30v0uBdoIVx4Q0g== +postcss-load-config@^3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-3.1.4.tgz#1ab2571faf84bb078877e1d07905eabe9ebda855" + integrity sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg== dependencies: - import-cwd "^3.0.0" - lilconfig "^2.0.3" + lilconfig "^2.0.5" yaml "^1.10.2" postcss-nested@5.0.6: @@ -1912,10 +1790,10 @@ postcss-nested@5.0.6: dependencies: postcss-selector-parser "^6.0.6" -postcss-selector-parser@^6.0.6, postcss-selector-parser@^6.0.9: - version "6.0.9" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.9.tgz#ee71c3b9ff63d9cd130838876c13a2ec1a992b2f" - integrity sha512-UO3SgnZOVTwu4kyLR22UQ1xZh086RyNZppb7lLAKBFK8a32ttG5i87Y/P3+2bRSjZNyJ1B7hfFNo273tKe9YxQ== +postcss-selector-parser@^6.0.10, postcss-selector-parser@^6.0.6: + version "6.0.10" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz#79b61e2c0d1bfc2602d549e11d0876256f8df88d" + integrity sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w== dependencies: cssesc "^3.0.0" util-deprecate "^1.0.2" @@ -1925,7 +1803,7 @@ postcss-value-parser@^4.2.0: resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== -postcss@8.4.12, postcss@^8.4.6: +postcss@8.4.12, postcss@^8.4.12: version "8.4.12" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.12.tgz#1e7de78733b28970fa4743f7da6f3763648b1905" integrity sha512-lg6eITwYe9v6Hr5CncVbK70SoioNQIq81nsaG86ev5hAidQvmOeETBqs7jm43K2F5/Ley3ytDtriImV6TpNiSg== @@ -2075,11 +1953,6 @@ resolve-from@^4.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== -resolve-from@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" - integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== - resolve@^1.13.1, resolve@^1.17.0, resolve@^1.20.0, resolve@^1.22.0: version "1.22.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" @@ -2248,10 +2121,10 @@ strip-json-comments@^3.1.0, strip-json-comments@^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== -stripe@8.216.0: - version "8.216.0" - resolved "https://registry.yarnpkg.com/stripe/-/stripe-8.216.0.tgz#23c047498526d13a238c3aca7b4dc8cbbd522e46" - integrity sha512-LY8cNGizEnklIa4T82l6mZW0HS4cfzo1hNuhT+ZR9PBkmYcSUbg3ilUBVF0FCd4RP+NA44VEVfoSTTZ1Gg5+rQ== +stripe@8.218.0: + version "8.218.0" + resolved "https://registry.yarnpkg.com/stripe/-/stripe-8.218.0.tgz#c63e751acd1ab4ef833a65e3d7713f8565d71d81" + integrity sha512-cH0CAep/x+N39dnfVKJxRpgPF5ggMR26Ckn0VkHH1rtFUspJzAEu4yTApSEszD2mirMK1gD42+oihJ1uJmbnUw== dependencies: "@types/node" ">=8.1.0" qs "^6.10.3" @@ -2261,13 +2134,6 @@ styled-jsx@5.0.1: resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.0.1.tgz#78fecbbad2bf95ce6cd981a08918ce4696f5fc80" integrity sha512-+PIZ/6Uk40mphiQJJI1202b+/dYeTVd9ZnMPR80pgiWbjIwvN2zIp4r9et0BgqBuShh48I0gttPlAXA7WVvBxw== -supports-color@^5.3.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.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" @@ -2280,34 +2146,34 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -swr@1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/swr/-/swr-1.2.2.tgz#6cae09928d30593a7980d80f85823e57468fac5d" - integrity sha512-ky0BskS/V47GpW8d6RU7CPsr6J8cr7mQD6+do5eky3bM0IyJaoi3vO8UhvrzJaObuTlGhPl2szodeB2dUd76Xw== +swr@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/swr/-/swr-1.3.0.tgz#c6531866a35b4db37b38b72c45a63171faf9f4e8" + integrity sha512-dkghQrOl2ORX9HYrMDtPa7LTVHJjCTeZoB1dqTbnnEDlSvN8JEKpYIYurDfvbQFUUS8Cg8PceFVZNkW0KNNYPw== -tailwindcss@3.0.23: - version "3.0.23" - resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.0.23.tgz#c620521d53a289650872a66adfcb4129d2200d10" - integrity sha512-+OZOV9ubyQ6oI2BXEhzw4HrqvgcARY38xv3zKcjnWtMIZstEsXdI9xftd1iB7+RbOnj2HOEzkA0OyB5BaSxPQA== +tailwindcss@3.0.24: + version "3.0.24" + resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.0.24.tgz#22e31e801a44a78a1d9a81ecc52e13b69d85704d" + integrity sha512-H3uMmZNWzG6aqmg9q07ZIRNIawoiEcNFKDfL+YzOPuPsXuDXxJxB9icqzLgdzKNwjG3SAro2h9SYav8ewXNgig== dependencies: arg "^5.0.1" - chalk "^4.1.2" chokidar "^3.5.3" color-name "^1.1.4" - cosmiconfig "^7.0.1" detective "^5.2.0" didyoumean "^1.2.2" dlv "^1.1.3" fast-glob "^3.2.11" glob-parent "^6.0.2" is-glob "^4.0.3" + lilconfig "^2.0.5" normalize-path "^3.0.0" - object-hash "^2.2.0" - postcss "^8.4.6" + object-hash "^3.0.0" + picocolors "^1.0.0" + postcss "^8.4.12" postcss-js "^4.0.0" - postcss-load-config "^3.1.0" + postcss-load-config "^3.1.4" postcss-nested "5.0.6" - postcss-selector-parser "^6.0.9" + postcss-selector-parser "^6.0.10" postcss-value-parser "^4.2.0" quick-lru "^5.1.1" resolve "^1.22.0" @@ -2456,7 +2322,7 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== -yaml@^1.10.0, yaml@^1.10.2: +yaml@^1.10.2: version "1.10.2" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== diff --git a/packages/health-check/package.json b/packages/health-check/package.json index 9c6e2025..117ced46 100644 --- a/packages/health-check/package.json +++ b/packages/health-check/package.json @@ -13,7 +13,7 @@ "http-status-codes": "^2.2.0", "lodash": "^4.17.21", "lowdb": "^1.0.0", - "skynet-js": "^4.0.19-beta", + "skynet-js": "^4.1.0", "write-file-atomic": "^4.0.1", "yargs": "^17.4.1" }, diff --git a/packages/health-check/yarn.lock b/packages/health-check/yarn.lock index 4343388e..15cf315c 100644 --- a/packages/health-check/yarn.lock +++ b/packages/health-check/yarn.lock @@ -515,6 +515,19 @@ dependencies: "@sinonjs/commons" "^1.7.0" +"@skynetlabs/tus-js-client@^2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@skynetlabs/tus-js-client/-/tus-js-client-2.3.0.tgz#a14fd4197e2bc4ce8be724967a0e4c17d937cb64" + integrity sha512-piGvPlJh+Bu3Qf08bDlc/TnFLXE81KnFoPgvnsddNwTSLyyspxPFxJmHO5ki6SYyOl3HmUtGPoix+r2M2UpFEA== + dependencies: + buffer-from "^0.1.1" + combine-errors "^3.0.3" + is-stream "^2.0.0" + js-base64 "^2.6.1" + lodash.throttle "^4.1.1" + proper-lockfile "^2.0.1" + url-parse "^1.4.3" + "@szmarczak/http-timer@^4.0.5": version "4.0.6" resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz#b4a914bb62e7c272d4e5989fe4440f812ab1d807" @@ -736,17 +749,24 @@ array-flatten@1.1.1: resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= +async-mutex@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/async-mutex/-/async-mutex-0.3.2.tgz#1485eda5bda1b0ec7c8df1ac2e815757ad1831df" + integrity sha512-HuTK7E7MT7jZEh1P9GtRW9+aTWiDWWi9InbZ5hjxrnRa39KS4BW04+xLBhYNS2aXhHUIKZSw3gj4Pn1pj+qGAA== + dependencies: + tslib "^2.3.1" + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= -axios@^0.24.0: - version "0.24.0" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.24.0.tgz#804e6fa1e4b9c5288501dd9dff56a7a0940d20d6" - integrity sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA== +axios@^0.26.0: + version "0.26.1" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.26.1.tgz#1ede41c51fcf51bbbd6fd43669caaa4f0495aaa9" + integrity sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA== dependencies: - follow-redirects "^1.14.4" + follow-redirects "^1.14.8" babel-jest@^27.5.1: version "27.5.1" @@ -1407,10 +1427,10 @@ find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" -follow-redirects@^1.14.4: - version "1.14.8" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.8.tgz#016996fb9a11a100566398b1c6839337d7bfa8fc" - integrity sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA== +follow-redirects@^1.14.8: + version "1.14.9" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.9.tgz#dd4ea157de7bfaf9ea9b3fbd85aa16951f78d8d7" + integrity sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w== form-data@^3.0.0: version "3.0.1" @@ -2410,10 +2430,10 @@ mime@1.6.0: resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== -mime@^2.5.2: - version "2.5.2" - resolved "https://registry.yarnpkg.com/mime/-/mime-2.5.2.tgz#6e3dc6cc2b9510643830e5f19d5cb753da5eeabe" - integrity sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg== +mime@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-3.0.0.tgz#b374550dca3a0c18443b0c950a6a58f1931cf7a7" + integrity sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A== mimic-fn@^2.1.0: version "2.1.0" @@ -2896,24 +2916,25 @@ sjcl@^1.0.8: resolved "https://registry.yarnpkg.com/sjcl/-/sjcl-1.0.8.tgz#f2ec8d7dc1f0f21b069b8914a41a8f236b0e252a" integrity sha512-LzIjEQ0S0DpIgnxMEayM1rq9aGwGRG4OnZhCdjx7glTaJtf4zRfpg87ImfjSJjoW9vKpagd82McDOwbRT5kQKQ== -skynet-js@^4.0.19-beta: - version "4.0.19-beta" - resolved "https://registry.yarnpkg.com/skynet-js/-/skynet-js-4.0.19-beta.tgz#d4c640898c79cf69e45aa1c3c1ed5c80aa1aeced" - integrity sha512-d8/q3E3OjUxgCCAW28gNFvbahj0ks8ym122XTopbRyvAZKk9+/Z4ians9v8Tov36Z4k/un+Ilw/0i6DtM8c8Dw== +skynet-js@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/skynet-js/-/skynet-js-4.1.0.tgz#eccb84d04e9f42aa4f86ecb24fb4d59ed21e44cc" + integrity sha512-VmUjJ9QnLpfuQA2j7vzFh8JvukjQlX4QLGw1HY3VyslFPj92vPpyO8gqjPfzgbkR05TXL7CbdqZoLZr/RBDZPw== dependencies: - axios "^0.24.0" + "@skynetlabs/tus-js-client" "^2.3.0" + async-mutex "^0.3.2" + axios "^0.26.0" base32-decode "^1.0.0" base32-encode "^1.1.1" base64-js "^1.3.1" blakejs "^1.1.0" buffer "^6.0.1" - mime "^2.5.2" + mime "^3.0.0" path-browserify "^1.0.1" post-me "^0.4.5" randombytes "^2.1.0" sjcl "^1.0.8" skynet-mysky-utils "^0.3.0" - tus-js-client "^2.2.0" tweetnacl "^1.0.3" url-join "^4.0.1" url-parse "^1.5.1" @@ -3120,18 +3141,10 @@ tr46@^2.1.0: dependencies: punycode "^2.1.1" -tus-js-client@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/tus-js-client/-/tus-js-client-2.3.0.tgz#5d76145476cea46a4e7c045a0054637cddf8dc39" - integrity sha512-I4cSwm6N5qxqCmBqenvutwSHe9ntf81lLrtf6BmLpG2v4wTl89atCQKqGgqvkodE6Lx+iKIjMbaXmfvStTg01g== - dependencies: - buffer-from "^0.1.1" - combine-errors "^3.0.3" - is-stream "^2.0.0" - js-base64 "^2.6.1" - lodash.throttle "^4.1.1" - proper-lockfile "^2.0.1" - url-parse "^1.4.3" +tslib@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" + integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== tweetnacl@^1.0.3: version "1.0.3" diff --git a/packages/website/package.json b/packages/website/package.json index 83b3db88..9c56bb25 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -12,45 +12,45 @@ "classnames": "2.3.1", "copy-text-to-clipboard": "3.0.1", "crypto-browserify": "3.12.0", - "framer-motion": "6.2.8", - "gatsby": "4.11.2", + "framer-motion": "6.3.0", + "gatsby": "4.12.1", "gatsby-background-image": "1.6.0", - "gatsby-plugin-image": "2.11.1", - "gatsby-plugin-manifest": "4.11.1", - "gatsby-plugin-postcss": "5.10.0", - "gatsby-plugin-react-helmet": "5.10.0", - "gatsby-plugin-robots-txt": "1.7.0", - "gatsby-plugin-sharp": "4.10.2", - "gatsby-plugin-sitemap": "5.11.1", + "gatsby-plugin-image": "2.12.1", + "gatsby-plugin-manifest": "4.12.1", + "gatsby-plugin-postcss": "5.12.1", + "gatsby-plugin-react-helmet": "5.12.1", + "gatsby-plugin-robots-txt": "1.7.1", + "gatsby-plugin-sharp": "4.12.1", + "gatsby-plugin-sitemap": "5.12.1", "gatsby-plugin-svgr": "3.0.0-beta.0", - "gatsby-source-filesystem": "4.10.1", - "gatsby-transformer-sharp": "4.10.0", - "gatsby-transformer-yaml": "4.11.0", + "gatsby-source-filesystem": "4.12.1", + "gatsby-transformer-sharp": "4.12.1", + "gatsby-transformer-yaml": "4.12.1", "gbimage-bridge": "0.2.1", "http-status-codes": "2.2.0", "ms": "2.1.3", - "nanoid": "3.3.2", + "nanoid": "3.3.3", "normalize.css": "8.0.1", "path-browserify": "1.0.1", - "polished": "4.2.1", + "polished": "4.2.2", "postcss": "8.4.12", "prop-types": "15.8.1", "react": "17.0.2", "react-dom": "17.0.2", - "react-dropzone": "12.0.4", + "react-dropzone": "12.0.5", "react-helmet": "6.1.0", "react-use": "17.3.2", - "skynet-js": "4.0.26-beta", + "skynet-js": "4.1.0", "stream-browserify": "3.0.0", - "swr": "1.2.2" + "swr": "1.3.0" }, "devDependencies": { "@tailwindcss/typography": "0.5.2", "autoprefixer": "10.4.4", "cross-env": "7.0.3", - "cypress": "9.5.2", + "cypress": "9.5.4", "prettier": "2.6.2", - "tailwindcss": "3.0.23" + "tailwindcss": "3.0.24" }, "keywords": [ "gatsby" diff --git a/packages/website/yarn.lock b/packages/website/yarn.lock index 8ffd53a6..67a2b94d 100644 --- a/packages/website/yarn.lock +++ b/packages/website/yarn.lock @@ -2594,10 +2594,10 @@ resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== -"@types/sharp@^0.29.5": - version "0.29.5" - resolved "https://registry.yarnpkg.com/@types/sharp/-/sharp-0.29.5.tgz#9c7032d30d138ad16dde6326beaff2af757b91b3" - integrity sha512-3TC+S3H5RwnJmLYMHrcdfNjz/CaApKmujjY9b6PU/pE6n0qfooi99YqXGWoW8frU9EWYj/XTI35Pzxa+ThAZ5Q== +"@types/sharp@^0.30.0": + version "0.30.2" + resolved "https://registry.yarnpkg.com/@types/sharp/-/sharp-0.30.2.tgz#df5ff34140b3bad165482e6f3d26b08e42a0503a" + integrity sha512-uLCBwjDg/BTcQit0dpNGvkIjvH3wsb8zpaJePCjvONBBSfaKHoxXBIuq1MT8DMQEfk2fKYnpC9QExCgFhkGkMQ== dependencies: "@types/node" "*" @@ -3339,23 +3339,23 @@ babel-plugin-polyfill-regenerator@^0.3.0: dependencies: "@babel/helper-define-polyfill-provider" "^0.3.1" -babel-plugin-remove-graphql-queries@^4.11.1: - version "4.11.1" - resolved "https://registry.yarnpkg.com/babel-plugin-remove-graphql-queries/-/babel-plugin-remove-graphql-queries-4.11.1.tgz#6f107865e8c1a83807c4b48b2262f5e0e0ba537e" - integrity sha512-Bqbeow4Xf+Vm4YhAucRGJjf9pNAXakSndYiLKfvef/W6mdtBh00SM8FMaX0U3rtR7ZUXV63RmIyOybVQ6SWCyg== +babel-plugin-remove-graphql-queries@^4.12.1: + version "4.12.1" + resolved "https://registry.yarnpkg.com/babel-plugin-remove-graphql-queries/-/babel-plugin-remove-graphql-queries-4.12.1.tgz#08e7531ed3c61aaa3c2f083ddce8040844e611d4" + integrity sha512-z4Z0VkDpmoIW3cihPYEb+HJMgwa+RF77LnpgAC6y6ozS76ci3ENqfIry/vvdD6auys5TG3xYZ0eHpdPobXzhfA== dependencies: "@babel/runtime" "^7.15.4" - gatsby-core-utils "^3.11.1" + gatsby-core-utils "^3.12.1" babel-plugin-transform-react-remove-prop-types@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz#f2edaf9b4c6a5fbe5c1d678bfb531078c1555f3a" integrity sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA== -babel-preset-gatsby@^2.11.1: - version "2.11.1" - resolved "https://registry.yarnpkg.com/babel-preset-gatsby/-/babel-preset-gatsby-2.11.1.tgz#860d8d9903df38c314fa6f0cfdb197d02555c4e4" - integrity sha512-NGUNAIb3hzD1Mt97q5T3gSSuVuaqnYFSm7AvgByDa3Mk2ohF5Ni86sCLVPRIntIzJvgU5OWY4Qz+6rrI1SwprQ== +babel-preset-gatsby@^2.12.1: + version "2.12.1" + resolved "https://registry.yarnpkg.com/babel-preset-gatsby/-/babel-preset-gatsby-2.12.1.tgz#33d904dc54d5395e049fb346015eba1dbd62bfbf" + integrity sha512-ozpDqxxQa32gZVeXO07S0jLJvfewzMLAytP6QHJvVlHEcDnfo7sTo/r3ZNm+2SzeHP51eTDuTFo46WWQnY5kMw== dependencies: "@babel/plugin-proposal-class-properties" "^7.14.0" "@babel/plugin-proposal-nullish-coalescing-operator" "^7.14.5" @@ -3370,8 +3370,8 @@ babel-preset-gatsby@^2.11.1: babel-plugin-dynamic-import-node "^2.3.3" babel-plugin-macros "^2.8.0" babel-plugin-transform-react-remove-prop-types "^0.4.24" - gatsby-core-utils "^3.11.1" - gatsby-legacy-polyfills "^2.11.0" + gatsby-core-utils "^3.12.1" + gatsby-legacy-polyfills "^2.12.1" backo2@^1.0.2, backo2@~1.0.2: version "1.0.2" @@ -3566,7 +3566,7 @@ braces@^2.3.1: split-string "^3.0.2" to-regex "^3.0.1" -braces@^3.0.1, braces@~3.0.2: +braces@^3.0.2, 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== @@ -4340,10 +4340,10 @@ create-ecdh@^4.0.0: bn.js "^4.1.0" elliptic "^6.5.3" -create-gatsby@^2.11.2: - version "2.11.2" - resolved "https://registry.yarnpkg.com/create-gatsby/-/create-gatsby-2.11.2.tgz#b932bb16f024c929c4597225771275d54f3541bc" - integrity sha512-EHlULRVoiXoLM400sLYNtFRy5pemp2WoNKR6vjUlFnLBqn+BGe+TJAmKfwqHYFheXMozKqY2bW0ekuDj2x8zAg== +create-gatsby@^2.12.1: + version "2.12.1" + resolved "https://registry.yarnpkg.com/create-gatsby/-/create-gatsby-2.12.1.tgz#da5ab2b6dde54d62ec853b699c5aceafb0f166a2" + integrity sha512-dOsEy9feLJVFVzFFnA6xJL9OhfYcKewaGMqI9uUaUdifIehBjb5jdeWi+cNy49j2FQLMm38jfZ2SNSQjEK2yOw== dependencies: "@babel/runtime" "^7.15.4" @@ -4613,10 +4613,10 @@ custom-error-instance@2.1.1: resolved "https://registry.yarnpkg.com/custom-error-instance/-/custom-error-instance-2.1.1.tgz#3cf6391487a6629a6247eb0ca0ce00081b7e361a" integrity sha1-PPY5FIemYppiR+sMoM4ACBt+Nho= -cypress@9.5.2: - version "9.5.2" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-9.5.2.tgz#8fb6ee4a890fbc35620800810bf6fb11995927bd" - integrity sha512-gYiQYvJozMzDOriUV1rCt6CeRM/pRK4nhwGJj3nJQyX2BoUdTCVwp30xDMKc771HiNVhBtgj5o5/iBdVDVXQUg== +cypress@9.5.4: + version "9.5.4" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-9.5.4.tgz#49d9272f62eba12f2314faf29c2a865610e87550" + integrity sha512-6AyJAD8phe7IMvOL4oBsI9puRNOWxZjl8z1lgixJMcgJ85JJmyKeP6uqNA0dI1z14lmJ7Qklf2MOgP/xdAqJ/Q== dependencies: "@cypress/request" "^2.88.10" "@cypress/xvfb" "^1.2.4" @@ -4650,7 +4650,7 @@ cypress@9.5.2: listr2 "^3.8.3" lodash "^4.17.21" log-symbols "^4.0.0" - minimist "^1.2.5" + minimist "^1.2.6" ospath "^1.2.2" pretty-bytes "^5.6.0" proxy-from-env "1.0.0" @@ -4710,10 +4710,10 @@ debug@^3.0.0, debug@^3.1.0, debug@^3.2.6, debug@^3.2.7: dependencies: ms "^2.1.1" -debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@~4.3.1: - version "4.3.3" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" - integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== +debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@~4.3.1: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== dependencies: ms "2.1.2" @@ -5384,15 +5384,15 @@ eslint-plugin-jsx-a11y@^6.5.1: language-tags "^1.0.5" minimatch "^3.0.4" -eslint-plugin-react-hooks@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.3.0.tgz#318dbf312e06fab1c835a4abef00121751ac1172" - integrity sha512-XslZy0LnMn+84NEG9jSGR6eGqaZB3133L8xewQo3fQagbQuGt7a63gf+P1NGKZavEYEC3UXaWEAA/AqDkuN6xA== +eslint-plugin-react-hooks@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.4.0.tgz#71c39e528764c848d8253e1aa2c7024ed505f6c4" + integrity sha512-U3RVIfdzJaeKDQKEJbz5p3NW8/L80PCATJAfuojwbaEL+gBjfGdhUcGde+WGUW46Q5sr/NgxevsIiDtNXrvZaQ== -eslint-plugin-react@^7.29.2: - version "7.29.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.29.2.tgz#2d4da69d30d0a736efd30890dc6826f3e91f3f7c" - integrity sha512-ypEBTKOy5liFQXZWMchJ3LN0JX1uPI6n7MN7OPHKacqXAxq5gYC30TdO7wqGYQyxD1OrzpobdHC3hDmlRWDg9w== +eslint-plugin-react@^7.29.4: + version "7.29.4" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.29.4.tgz#4717de5227f55f3801a5fd51a16a4fa22b5914d2" + integrity sha512-CVCXajliVh509PcZYRFyu/BoUEz452+jtQJq2b3Bae4v3xBUWPLCmtmBM+ZinG4MzwmxJgJ2M5rMqhqLVn7MtQ== dependencies: array-includes "^3.1.4" array.prototype.flatmap "^1.2.5" @@ -6068,10 +6068,10 @@ fragment-cache@^0.2.1: dependencies: map-cache "^0.2.2" -framer-motion@6.2.8: - version "6.2.8" - resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-6.2.8.tgz#02abb529191af7e2df444185fe27e932215b715d" - integrity sha512-4PtBWFJ6NqR350zYVt9AsFDtISTqsdqna79FvSYPfYDXuuqFmiKtZdkTnYPslnsOMedTW0pEvaQ7eqjD+sA+HA== +framer-motion@6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-6.3.0.tgz#0e50ef04b4fa070fca7d04bc32fb1d64027b7ea7" + integrity sha512-Nm6l2cemuFeSC1fmq9R32sCQs1eplOuZ3r14/PxRDewpE3NUr+ul5ulGRRzk8K0Aa5p76Tedi3sfCUaTPa5fRg== dependencies: framesync "6.0.1" hey-listen "^1.0.8" @@ -6158,10 +6158,10 @@ gatsby-background-image@1.6.0: short-uuid "^4.2.0" sort-media-queries "^0.2.2" -gatsby-cli@^4.11.2: - version "4.11.2" - resolved "https://registry.yarnpkg.com/gatsby-cli/-/gatsby-cli-4.11.2.tgz#0bd5c218f378edb0e674f7ba7a903be202fe3620" - integrity sha512-MypoVvMwWcDEtf5JTm1UTdGeOavRjnNRKfuUqvbhvb+q1vQ2xIFhu/pK9sdOlQfL6v6Fl8xwO2FuOfz+i53z3w== +gatsby-cli@^4.12.1: + version "4.12.1" + resolved "https://registry.yarnpkg.com/gatsby-cli/-/gatsby-cli-4.12.1.tgz#157d6dbe783102248af2c938816a58401afeb64b" + integrity sha512-vlSqri0p9HpLfACFtUCJhxQArzxSvdcUkrN4Jlw8RgeJYxcJyb8VPPDJHJT3rMGRKZFeBaAeqMbqx/eK4K5F1w== dependencies: "@babel/code-frame" "^7.14.0" "@babel/core" "^7.15.5" @@ -6179,13 +6179,13 @@ gatsby-cli@^4.11.2: common-tags "^1.8.2" configstore "^5.0.1" convert-hrtime "^3.0.0" - create-gatsby "^2.11.2" + create-gatsby "^2.12.1" envinfo "^7.8.1" execa "^5.1.1" fs-exists-cached "^1.0.0" fs-extra "^10.0.0" - gatsby-core-utils "^3.11.1" - gatsby-telemetry "^3.11.1" + gatsby-core-utils "^3.12.1" + gatsby-telemetry "^3.12.1" hosted-git-info "^3.0.8" is-valid-path "^0.1.1" joi "^17.4.2" @@ -6209,10 +6209,10 @@ gatsby-cli@^4.11.2: yoga-layout-prebuilt "^1.10.0" yurnalist "^2.1.0" -gatsby-core-utils@^3.10.1, gatsby-core-utils@^3.11.1, gatsby-core-utils@^3.8.2: - version "3.11.1" - resolved "https://registry.yarnpkg.com/gatsby-core-utils/-/gatsby-core-utils-3.11.1.tgz#ea87c1d3aa45c26c9ea32b8e8b029afe6a56f8c7" - integrity sha512-Op9/uihtcsDLlZDfRsGJ1ya2mFx2YH9Zmx93bawElZ0YpIzKjCkNTp+I5i5UANxvs5I+Fljl0WHQRudMWg+fWA== +gatsby-core-utils@^3.12.1, gatsby-core-utils@^3.8.2: + version "3.12.1" + resolved "https://registry.yarnpkg.com/gatsby-core-utils/-/gatsby-core-utils-3.12.1.tgz#590ec08de7168b086b7d49128732b9ada95b985f" + integrity sha512-jBG1MfR6t2MZNIl8LQ3Cwc92F6uFNcEC091IK+qKVy9FNT0+WzcKQ6Olip6u1NSvCatfrg1FqrH0K78a6lmnLQ== dependencies: "@babel/runtime" "^7.15.4" ci-info "2.0.0" @@ -6222,7 +6222,7 @@ gatsby-core-utils@^3.10.1, gatsby-core-utils@^3.11.1, gatsby-core-utils@^3.8.2: fs-extra "^10.0.0" got "^11.8.3" import-from "^4.0.0" - lmdb "^2.2.4" + lmdb "^2.2.6" lock "^1.1.0" node-object-hash "^2.3.10" proper-lockfile "^4.1.2" @@ -6230,49 +6230,49 @@ gatsby-core-utils@^3.10.1, gatsby-core-utils@^3.11.1, gatsby-core-utils@^3.8.2: tmp "^0.2.1" xdg-basedir "^4.0.0" -gatsby-graphiql-explorer@^2.11.0: - version "2.11.0" - resolved "https://registry.yarnpkg.com/gatsby-graphiql-explorer/-/gatsby-graphiql-explorer-2.11.0.tgz#7e886846482ad72bd49f515e7faa658a94342803" - integrity sha512-nMNXlF/pleO/rH66t00SdXdKq3vV0/Su5EEQY7xg3yRc38ueC2UkZq10nrJiVoc05RO8Txo5o2gpoC2DP07lFg== +gatsby-graphiql-explorer@^2.12.1: + version "2.12.1" + resolved "https://registry.yarnpkg.com/gatsby-graphiql-explorer/-/gatsby-graphiql-explorer-2.12.1.tgz#4fad5b9a3ccbcc4871f70222e8ac1ca880ff844d" + integrity sha512-H5phTjIGUiUZxN3C0hogH66lB+qC9HO9O4m4RpHZ3JyxVIvPemGSNmgovhL7+LydS34UY5rbT0UBFwaxrHMZpQ== dependencies: "@babel/runtime" "^7.15.4" -gatsby-legacy-polyfills@^2.11.0: - version "2.11.0" - resolved "https://registry.yarnpkg.com/gatsby-legacy-polyfills/-/gatsby-legacy-polyfills-2.11.0.tgz#8b2afc4d97f44eb5767fe9b49f55ff675055ffd2" - integrity sha512-ulkRNCitwFjwUM4f2ufljH0WjELm6QEIOGRryNRt9LKJEB9QGmdm+KUAWIv7xrFUqKq1Pn6is64wcfXDw21zSA== +gatsby-legacy-polyfills@^2.12.1: + version "2.12.1" + resolved "https://registry.yarnpkg.com/gatsby-legacy-polyfills/-/gatsby-legacy-polyfills-2.12.1.tgz#62ad6432b3c17f7f5640786ed00dba88da60ab22" + integrity sha512-x2Njk0GsBKsiVBDZHI7nVWDNBPQeonQsElzFEDoSJpW47j9H8PPJDeOUZ+u5q76rtxuQQo/VXl/eD817qRBxAA== dependencies: "@babel/runtime" "^7.15.4" core-js-compat "3.9.0" -gatsby-link@^4.11.1: - version "4.11.1" - resolved "https://registry.yarnpkg.com/gatsby-link/-/gatsby-link-4.11.1.tgz#f8bfee4c7f3bf0ede255bddf87d0f13c64ed39f2" - integrity sha512-wOhdgsnzHr4iYWo3iKadw8jj5PmIu1wbi6LUftwQzFOFvkBaJvC/br1ju8W0nbwSjWG474hTZRon43xDQX9bIw== +gatsby-link@^4.12.1: + version "4.12.1" + resolved "https://registry.yarnpkg.com/gatsby-link/-/gatsby-link-4.12.1.tgz#131448e9c51e0c9a65e2a342603d496e85cea2da" + integrity sha512-ILWYNqyTlEt2bOVWgzwmbijwC+Ow4CZVbnWOyaQ/jvu5z3ZGL0z5tGGD+sjZAHc8anOMWn/JWhL0BKGVaxjMGQ== dependencies: "@babel/runtime" "^7.15.4" "@types/reach__router" "^1.3.10" - gatsby-page-utils "^2.11.1" + gatsby-page-utils "^2.12.1" prop-types "^15.7.2" -gatsby-page-utils@^2.11.1: - version "2.11.1" - resolved "https://registry.yarnpkg.com/gatsby-page-utils/-/gatsby-page-utils-2.11.1.tgz#dd10f99184b64528ae76f2b654b8ed1b23cb9c39" - integrity sha512-K1Mbk4CKYZwpJcE4zk4JAff7ZBNFXI0fC8lZwLbDAzVcqYUaouqqqnoU7WeB8HHUqDQi05CXItx1bbZFDGIymw== +gatsby-page-utils@^2.12.1: + version "2.12.1" + resolved "https://registry.yarnpkg.com/gatsby-page-utils/-/gatsby-page-utils-2.12.1.tgz#ee5dbd21b8e22003e5b0c7e4d86ed3b89773c0ac" + integrity sha512-2NPfVHRoHYcqUZrGVAvHN28uqI/PTGE/DrpE79YR/blbnloEzwzpAGNbBjWitgcR0st5q5NrATJQ/Imu3M7ApA== dependencies: "@babel/runtime" "^7.15.4" bluebird "^3.7.2" chokidar "^3.5.2" fs-exists-cached "^1.0.0" - gatsby-core-utils "^3.11.1" + gatsby-core-utils "^3.12.1" glob "^7.2.0" lodash "^4.17.21" - micromatch "^4.0.4" + micromatch "^4.0.5" -gatsby-parcel-config@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/gatsby-parcel-config/-/gatsby-parcel-config-0.2.0.tgz#0b1795d17c825bd293c372fa0acfa987aa91111e" - integrity sha512-BbsSm5O0R7IvCRLNSk3lBpkU8RtSOn8s7Ifa7bHF63PzTG1SUpBjwMF6301tCbvdSXWrP7n9dsfaXS6ex/TElQ== +gatsby-parcel-config@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/gatsby-parcel-config/-/gatsby-parcel-config-0.3.1.tgz#424c7b3d65b60e2e454cd9d61d4c5de752aad576" + integrity sha512-Wpz6DSKiWeqVyZUNmO7EHy0h9ISG+HfUD8v2g0kN4ZcZjJtSiWvGym1+6Swgjo9bQvy59qa7bO4hKGA9gHvMVg== dependencies: "@gatsbyjs/parcel-namer-relative-to-cwd" "0.0.2" "@parcel/bundler-default" "^2.3.2" @@ -6292,109 +6292,109 @@ gatsby-parcel-config@^0.2.0: "@parcel/transformer-raw" "^2.3.2" "@parcel/transformer-react-refresh-wrap" "^2.3.2" -gatsby-plugin-image@2.11.1: - version "2.11.1" - resolved "https://registry.yarnpkg.com/gatsby-plugin-image/-/gatsby-plugin-image-2.11.1.tgz#1e800b65e8c18cc524c5855b9dbdb907745fdb0c" - integrity sha512-4tfDdcczBVOL6ELKNWuXQ9h1V/5DhBMIVHmr6FPwm8xgL8ARqfQMXX2mzUjpNiu7WDiMlm9cWrTQQaZAARhAwg== +gatsby-plugin-image@2.12.1: + version "2.12.1" + resolved "https://registry.yarnpkg.com/gatsby-plugin-image/-/gatsby-plugin-image-2.12.1.tgz#302324125a1e018ff669772495f4b291bd00a832" + integrity sha512-Azofblt5ZSk2NqCOrBI1WijcJw6dVHCKz85lz6J7qB3Fvy+YVjs/vbUODlXUwi3926Q5m7C2zdH0MQrk4T0DDQ== dependencies: "@babel/code-frame" "^7.14.0" "@babel/parser" "^7.15.5" "@babel/runtime" "^7.15.4" "@babel/traverse" "^7.15.4" babel-jsx-utils "^1.1.0" - babel-plugin-remove-graphql-queries "^4.11.1" + babel-plugin-remove-graphql-queries "^4.12.1" camelcase "^5.3.1" chokidar "^3.5.2" common-tags "^1.8.2" fs-extra "^10.0.0" - gatsby-core-utils "^3.11.1" + gatsby-core-utils "^3.12.1" objectFitPolyfill "^2.3.5" - prop-types "^15.7.2" + prop-types "^15.8.1" -gatsby-plugin-manifest@4.11.1: - version "4.11.1" - resolved "https://registry.yarnpkg.com/gatsby-plugin-manifest/-/gatsby-plugin-manifest-4.11.1.tgz#fb3061c2e989acb2634719c1e8645fd1388f4b3b" - integrity sha512-m5cdi6KBc8+zmnlIfEh92sXpFEUfjuCrjM5BKc8e6v0jxJS0CqVrZOyT12mT2yq9H12UrkzFx1qaI8e2/IJiGA== +gatsby-plugin-manifest@4.12.1: + version "4.12.1" + resolved "https://registry.yarnpkg.com/gatsby-plugin-manifest/-/gatsby-plugin-manifest-4.12.1.tgz#25342272c03fdce95901eb42a3ece09f12e4be4a" + integrity sha512-AZ0vtO/+khtpum4VUX/Gj/oRer6ckcEfVU/DNd3QlpG7ucHSzHKSwSLP6F5kevCHo7DS4hCMV8RixPlcv0ePWA== dependencies: "@babel/runtime" "^7.15.4" - gatsby-core-utils "^3.11.1" - gatsby-plugin-utils "^3.5.1" + gatsby-core-utils "^3.12.1" + gatsby-plugin-utils "^3.6.1" semver "^7.3.5" - sharp "^0.30.1" + sharp "^0.30.3" -gatsby-plugin-page-creator@^4.11.1: - version "4.11.1" - resolved "https://registry.yarnpkg.com/gatsby-plugin-page-creator/-/gatsby-plugin-page-creator-4.11.1.tgz#750f4b773684777cec6caa9266787427ed2630a6" - integrity sha512-6XET4qYqu2yVwUU6sO44wSR62zQZdq7BoMvN9OhKpUDBZYLfve9CwufkhZZnQvq+axNZZMUmKa/RqbBXiE6/yA== +gatsby-plugin-page-creator@^4.12.1: + version "4.12.1" + resolved "https://registry.yarnpkg.com/gatsby-plugin-page-creator/-/gatsby-plugin-page-creator-4.12.1.tgz#36460f5308df8b04bb2b0649150cb87e3eb15c8f" + integrity sha512-McVYpXWgneo1+3+8KGrGATgNwAYtZXbFKL8Q18lwH+bt5f2NbYP23g+xGitxT62zvhhzs0AjuEJa7BoTEmFTMQ== dependencies: "@babel/runtime" "^7.15.4" "@babel/traverse" "^7.15.4" "@sindresorhus/slugify" "^1.1.2" chokidar "^3.5.2" fs-exists-cached "^1.0.0" - gatsby-core-utils "^3.11.1" - gatsby-page-utils "^2.11.1" - gatsby-plugin-utils "^3.5.1" - gatsby-telemetry "^3.11.1" - globby "^11.0.4" + gatsby-core-utils "^3.12.1" + gatsby-page-utils "^2.12.1" + gatsby-plugin-utils "^3.6.1" + gatsby-telemetry "^3.12.1" + globby "^11.1.0" lodash "^4.17.21" -gatsby-plugin-postcss@5.10.0: - version "5.10.0" - resolved "https://registry.yarnpkg.com/gatsby-plugin-postcss/-/gatsby-plugin-postcss-5.10.0.tgz#e241f1671e66f7b660826f39fd26591aae652716" - integrity sha512-s1zzysu1kKIqR+CfQeQsG0CCdj2S7tjc4BhCY2a3V4cl7ORJtMx1HGKDUzE9gV/EXRTmr9lhE9Gl+2v8fRouvA== +gatsby-plugin-postcss@5.12.1: + version "5.12.1" + resolved "https://registry.yarnpkg.com/gatsby-plugin-postcss/-/gatsby-plugin-postcss-5.12.1.tgz#8be5cf7bbfbb27967cad2c5da8dbe1b8c7e576b8" + integrity sha512-gNfMl/ZWCZpnCsy+IPzRPzqaPPStbUr/LG8eikGbyoZj1gSjom/a7Hi3z29sWx/vEd4dAHGRPS+n7bjf3iOnNw== dependencies: "@babel/runtime" "^7.15.4" postcss-loader "^4.3.0" -gatsby-plugin-react-helmet@5.10.0: - version "5.10.0" - resolved "https://registry.yarnpkg.com/gatsby-plugin-react-helmet/-/gatsby-plugin-react-helmet-5.10.0.tgz#d6491d35d4b4e3bb36631c1a93f4e53913f42cbb" - integrity sha512-QcypYLqnwKoD84f9c6Yfajs/sLfVmxPSOPWwHaK+3NG1IjmmQrL42qn2CP6gs29WznGzrThGeGiwIVdA5x31JA== +gatsby-plugin-react-helmet@5.12.1: + version "5.12.1" + resolved "https://registry.yarnpkg.com/gatsby-plugin-react-helmet/-/gatsby-plugin-react-helmet-5.12.1.tgz#98205abb73cd7a522f2891b3a42514ae2b41ca57" + integrity sha512-lW9uRpkccSj0NC41dunFM4AoDuQockgpWHcvLivzGWMnWYtGWPNci7zy8+NUL1+6CchQqWTr0LZEeGYgpHym+w== dependencies: "@babel/runtime" "^7.15.4" -gatsby-plugin-robots-txt@1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/gatsby-plugin-robots-txt/-/gatsby-plugin-robots-txt-1.7.0.tgz#58ac310c9fb7e58162d6e21802884b342837b451" - integrity sha512-Y1D8FBeXNtECoCd0g0jIkhKpSvzFzeh2xpt1xTvGluRP6xmqJq7iB3DPEv7xqGlZAcfzaSxw/j5++Y+3WLva8A== +gatsby-plugin-robots-txt@1.7.1: + version "1.7.1" + resolved "https://registry.yarnpkg.com/gatsby-plugin-robots-txt/-/gatsby-plugin-robots-txt-1.7.1.tgz#f956729e34f6269cc314352e9ef1cf7b4c515a68" + integrity sha512-ZdZm8/4b7Whf+W5kf+DqjZwz/+DY+IB7xp227+m2f2rgGUsz8yVCz4RitiN5+EInGFZFry0v+IbrUKCXTpIZYg== dependencies: "@babel/runtime" "^7.16.7" generate-robotstxt "^8.0.3" -gatsby-plugin-sharp@4.10.2: - version "4.10.2" - resolved "https://registry.yarnpkg.com/gatsby-plugin-sharp/-/gatsby-plugin-sharp-4.10.2.tgz#253a49c452a7409ceece4e541e4770e61a306bcc" - integrity sha512-MWzPTYnu7HZ0kctHtkLbZOe6ZGUqSsNATO3lWlSBIFpeimxaPF5iHBiu1CX/ofz4pwt7VamtIzAV28VB6sjONw== +gatsby-plugin-sharp@4.12.1: + version "4.12.1" + resolved "https://registry.yarnpkg.com/gatsby-plugin-sharp/-/gatsby-plugin-sharp-4.12.1.tgz#f97cb29074f4a07b469b9ddb1058a3574928d4eb" + integrity sha512-P6noUl5LyASwYtCRSo1rjchk/ytfJvSFTLwzgXr1TiQHgZh06SUIqR8v3UqT90EDERNd1GeEBsQjRfWkrV2nbg== dependencies: "@babel/runtime" "^7.15.4" async "^3.2.3" bluebird "^3.7.2" - debug "^4.3.3" + debug "^4.3.4" filenamify "^4.3.0" fs-extra "^10.0.0" - gatsby-core-utils "^3.10.1" - gatsby-plugin-utils "^3.4.2" - gatsby-telemetry "^3.10.1" + gatsby-core-utils "^3.12.1" + gatsby-plugin-utils "^3.6.1" + gatsby-telemetry "^3.12.1" got "^11.8.3" lodash "^4.17.21" - mini-svg-data-uri "^1.4.3" + mini-svg-data-uri "^1.4.4" potrace "^2.1.8" - probe-image-size "^7.0.0" + probe-image-size "^7.2.3" progress "^2.0.3" semver "^7.3.5" - sharp "^0.30.1" + sharp "^0.30.3" svgo "1.3.2" uuid "3.4.0" -gatsby-plugin-sitemap@5.11.1: - version "5.11.1" - resolved "https://registry.yarnpkg.com/gatsby-plugin-sitemap/-/gatsby-plugin-sitemap-5.11.1.tgz#863397fe9dd5aab89bda8db09ef9b877c960150e" - integrity sha512-tt92KLUDS+eCrqSA5oYieDGjXLyUDXfYKEwLhYKXk7KlMMjporFJWVrc4Ba8WD04bUWVnzc2rqr19/zQI0ZIpQ== +gatsby-plugin-sitemap@5.12.1: + version "5.12.1" + resolved "https://registry.yarnpkg.com/gatsby-plugin-sitemap/-/gatsby-plugin-sitemap-5.12.1.tgz#782a926a558d31663d4a8e66b9466c86df278568" + integrity sha512-Njdx91OF4xiFqHSSA3Yrnzxm4qu4xHyrhkiwQniFncoIGMI6IXqk9aDoxo3E1jZDWGPsI/2gzdcd8dBNQq9juA== dependencies: "@babel/runtime" "^7.15.4" common-tags "^1.8.2" - minimatch "^3.0.4" + minimatch "^3.1.2" sitemap "^7.0.0" gatsby-plugin-svgr@3.0.0-beta.0: @@ -6402,10 +6402,10 @@ gatsby-plugin-svgr@3.0.0-beta.0: resolved "https://registry.yarnpkg.com/gatsby-plugin-svgr/-/gatsby-plugin-svgr-3.0.0-beta.0.tgz#7e5315f51dae2663a447899322ea1487cef93dd6" integrity sha512-oALTh6VwO6l3khgC/vGr706aqt38EkXwdr6iXVei/auOKGxpCLEuDCQVal1a4SpYXdjHjRsEyab6bxaHL2lzsA== -gatsby-plugin-typescript@^4.11.1: - version "4.11.1" - resolved "https://registry.yarnpkg.com/gatsby-plugin-typescript/-/gatsby-plugin-typescript-4.11.1.tgz#dcd96ded685f8c4a73ae5524faab342f9c9e3c1d" - integrity sha512-6ef2wRhPqcLPyekEAU3xcoqI59r+mDnCzn/O+8hRgwJyx/2dwvF8brusetXoqdTk4Vyhk44p8dog8+gCGATckw== +gatsby-plugin-typescript@^4.12.1: + version "4.12.1" + resolved "https://registry.yarnpkg.com/gatsby-plugin-typescript/-/gatsby-plugin-typescript-4.12.1.tgz#8fe51b57f1fca5027f5dd558b73364a4c34a0a38" + integrity sha512-7ZzGTL+hNGGmiIk4j4QSZYyYsy4i9EW/zgK/IJwmpSBNzoagI/Pz64ntNWpxZstfgzkuIYZfvuvj3Ao9mKF5aw== dependencies: "@babel/core" "^7.15.5" "@babel/plugin-proposal-nullish-coalescing-operator" "^7.14.5" @@ -6413,48 +6413,48 @@ gatsby-plugin-typescript@^4.11.1: "@babel/plugin-proposal-optional-chaining" "^7.14.5" "@babel/preset-typescript" "^7.15.0" "@babel/runtime" "^7.15.4" - babel-plugin-remove-graphql-queries "^4.11.1" + babel-plugin-remove-graphql-queries "^4.12.1" -gatsby-plugin-utils@^3.4.2, gatsby-plugin-utils@^3.5.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/gatsby-plugin-utils/-/gatsby-plugin-utils-3.5.1.tgz#9aed9deec0f4ee82bfc7390f735b9455ca4f8494" - integrity sha512-RZXUvwQjTnkukMfAGr+DCz/qZj7g6REljTmQS43MaovWO4Yf4YGvs+1Leays7J0XmqN2I3SIZGBgt4tgKCsNVQ== +gatsby-plugin-utils@^3.6.1: + version "3.6.1" + resolved "https://registry.yarnpkg.com/gatsby-plugin-utils/-/gatsby-plugin-utils-3.6.1.tgz#31d742e1aded08439ad42959880821e1fc9740cd" + integrity sha512-Ebk98v4mxaDWjGFl6VBeNv1zjeJ7UCQ29UTabzY2BpztvUCBHfLVQdMmuaAgzPRn+A3SFVOGpcl++CF0IEl+7A== dependencies: "@babel/runtime" "^7.15.4" fs-extra "^10.0.0" - gatsby-core-utils "^3.11.1" - gatsby-sharp "^0.5.0" + gatsby-core-utils "^3.12.1" + gatsby-sharp "^0.6.1" graphql-compose "^9.0.7" import-from "^4.0.0" joi "^17.4.2" mime "^3.0.0" -gatsby-react-router-scroll@^5.11.0: - version "5.11.0" - resolved "https://registry.yarnpkg.com/gatsby-react-router-scroll/-/gatsby-react-router-scroll-5.11.0.tgz#866b89366146d8df3852ed699d12be1e9fce4acc" - integrity sha512-g/lyG0X73cpI9DdYvCv5rZiV8LqHjn6q1l8Vfm/jBS7wtv8XxNR4BxUqkbMeHRcvZcX5bXku6FFSFUAOd9c3QQ== +gatsby-react-router-scroll@^5.12.1: + version "5.12.1" + resolved "https://registry.yarnpkg.com/gatsby-react-router-scroll/-/gatsby-react-router-scroll-5.12.1.tgz#10fdf43c1179ae53e7726c6c8d894139e7862e8f" + integrity sha512-zZCTiicALh6eSsQAgIhSCmQm6Dl6fY6eaKmOXGMMbVtUKmiGxikh2MFN6S5J5JU9MV/piSheVqYkouyTDGXbuw== dependencies: "@babel/runtime" "^7.15.4" - prop-types "^15.7.2" + prop-types "^15.8.1" -gatsby-sharp@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/gatsby-sharp/-/gatsby-sharp-0.5.0.tgz#879d3c462eefa917cb3a50c6ec891951d9740f56" - integrity sha512-9wZS0ADZsKTCsU66sxIP/tCHgFaREyoYm53tepgtp/YSVWNrurx9/0kGf8XsFFY9OecrqIRNuk1cWe7XKCpbQA== +gatsby-sharp@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/gatsby-sharp/-/gatsby-sharp-0.6.1.tgz#48805690cb020111722cc25445b4b662446da6d0" + integrity sha512-KhBFE72QLlrAgeMWNoBV2LDp0nZ9ZOw1pY5wIohb/ktDFRUi9K5nwVCJvDJonfPn100mxtDqnZVckXirtcHVzQ== dependencies: - "@types/sharp" "^0.29.5" - sharp "^0.30.1" + "@types/sharp" "^0.30.0" + sharp "^0.30.3" -gatsby-source-filesystem@4.10.1: - version "4.10.1" - resolved "https://registry.yarnpkg.com/gatsby-source-filesystem/-/gatsby-source-filesystem-4.10.1.tgz#c513fadb3cedb138ff28ba351ffd264524a0c28d" - integrity sha512-qdOWS234l6QyEN0M8tfdGQF530pK9nSiaT1JfSzZV7Bl9psX9SdsuOtfZ2AV0QVt1BQB7C53E/BNGaxMLCcnUg== +gatsby-source-filesystem@4.12.1: + version "4.12.1" + resolved "https://registry.yarnpkg.com/gatsby-source-filesystem/-/gatsby-source-filesystem-4.12.1.tgz#86f45cc047b0b9ca06b5e4ef5c48e69fa3ec2206" + integrity sha512-lbtKa7oR2Q+8Qa8gZych/JaRBeoIW/dk4rfy13DOrSgUJK9gZFkpLuFQ471Z0JiHitDPswienBW60HjYvymOCw== dependencies: "@babel/runtime" "^7.15.4" chokidar "^3.5.2" file-type "^16.5.3" fs-extra "^10.0.0" - gatsby-core-utils "^3.10.1" + gatsby-core-utils "^3.12.1" got "^9.6.0" md5-file "^5.0.0" mime "^2.5.2" @@ -6463,10 +6463,10 @@ gatsby-source-filesystem@4.10.1: valid-url "^1.0.9" xstate "^4.26.1" -gatsby-telemetry@^3.10.1, gatsby-telemetry@^3.11.1: - version "3.11.1" - resolved "https://registry.yarnpkg.com/gatsby-telemetry/-/gatsby-telemetry-3.11.1.tgz#87caed899143276056e9af20ab38c15ad9dcdf52" - integrity sha512-TPNKTpuYFyULOuRvhpXUtj8h2E7bvrTYsRC/aKeHoWqEchwwbzPwBSJd+3ZFjsxLHIXAa5sTAlR2wd9SYBgOlA== +gatsby-telemetry@^3.12.1: + version "3.12.1" + resolved "https://registry.yarnpkg.com/gatsby-telemetry/-/gatsby-telemetry-3.12.1.tgz#a3508a45d95f2c3457db7dbe2628560d00c43beb" + integrity sha512-sAL2T9GdYpceGlFP6CymVDoy0UEhRvrJApv/mu7sU6F0gu8g8rOLvRxVYE3Y2D9RdfCzkuLIonzmscmVIduyOg== dependencies: "@babel/code-frame" "^7.14.0" "@babel/runtime" "^7.15.4" @@ -6476,48 +6476,48 @@ gatsby-telemetry@^3.10.1, gatsby-telemetry@^3.11.1: boxen "^4.2.0" configstore "^5.0.1" fs-extra "^10.0.0" - gatsby-core-utils "^3.11.1" + gatsby-core-utils "^3.12.1" git-up "^4.0.5" is-docker "^2.2.1" lodash "^4.17.21" node-fetch "^2.6.7" -gatsby-transformer-sharp@4.10.0: - version "4.10.0" - resolved "https://registry.yarnpkg.com/gatsby-transformer-sharp/-/gatsby-transformer-sharp-4.10.0.tgz#9db793a219d0fa85b7d33cb37b32aba63df4c58a" - integrity sha512-Gp9eRkGQOrkoD+yJgK2ZdXuVbet/opxdEnuTZ6BhLEVhfTwOnMEaui6ZqO0cKJ7/NYlptO38p+C5cyizC0FRYA== +gatsby-transformer-sharp@4.12.1: + version "4.12.1" + resolved "https://registry.yarnpkg.com/gatsby-transformer-sharp/-/gatsby-transformer-sharp-4.12.1.tgz#e26dc3540e97fc1c802518967c1e8d8a4ed96fae" + integrity sha512-14AGG10Jf7ZBWxJDN2jSupAsBofoGU+p7+QJRzDrKdJrzp9v/yO/1xPB+r7UxtlW0l8cqPT6UyCITvJbWTDaww== dependencies: "@babel/runtime" "^7.15.4" bluebird "^3.7.2" common-tags "^1.8.2" fs-extra "^10.0.0" potrace "^2.1.8" - probe-image-size "^7.0.0" + probe-image-size "^7.2.3" semver "^7.3.5" - sharp "^0.30.1" + sharp "^0.30.3" -gatsby-transformer-yaml@4.11.0: - version "4.11.0" - resolved "https://registry.yarnpkg.com/gatsby-transformer-yaml/-/gatsby-transformer-yaml-4.11.0.tgz#900bf446ce7aece9253f46244519ccb5bd8cf7f2" - integrity sha512-GTCkULgOxbyRbovO9VHi0P+7iv/fEQG3uBeKiJyvMRUDD4bIQ9uIdT7hZ1RPwctu9dpt9T/X7kx+CShRzmELYw== +gatsby-transformer-yaml@4.12.1: + version "4.12.1" + resolved "https://registry.yarnpkg.com/gatsby-transformer-yaml/-/gatsby-transformer-yaml-4.12.1.tgz#0c372c25304d3305b3dcdc2b39c08e059d8f3d74" + integrity sha512-mpliK4YDMDBUe+g6JO6B8qhQffbBLoJI9q/4ja4YSbY/Jaj2tv/tkhepuwxOMuUwmQcBRu+OQ5J0RNiCI1aUcQ== dependencies: "@babel/runtime" "^7.15.4" js-yaml "^3.14.1" lodash "^4.17.21" unist-util-select "^1.5.0" -gatsby-worker@^1.11.0: - version "1.11.0" - resolved "https://registry.yarnpkg.com/gatsby-worker/-/gatsby-worker-1.11.0.tgz#bf8c3b9374390260b8335d7cfccbc332d0716727" - integrity sha512-uJ5bNrifIrS20o0SYkmb379logfRKO35cqYxd2R0uNf9kWGaQOda0SZfm7Uw+Vdx7cO9Ra8p1ArijbHm7ZArCA== +gatsby-worker@^1.12.1: + version "1.12.1" + resolved "https://registry.yarnpkg.com/gatsby-worker/-/gatsby-worker-1.12.1.tgz#553a0fd5ad796567fc3ab965490270ff7b0ae680" + integrity sha512-9slhXsK1/N4nJK+Yia84PL/zvNqV/bqD820W4R2f5jh5gEnVYrY2TcnG6A+UDbY7orhS0CLf1mMW9WKd6u6CUA== dependencies: "@babel/core" "^7.15.5" "@babel/runtime" "^7.15.4" -gatsby@4.11.2: - version "4.11.2" - resolved "https://registry.yarnpkg.com/gatsby/-/gatsby-4.11.2.tgz#fba8843316992e2f0eafc5ae3ee6fb37ce7aa953" - integrity sha512-kJ2gHQzO3efV1BaynGfxi0divFOorUqxKom/QdIEnaj9VMkNRnrpNxUNsT4ljq+d9BTlq/0AAIpGNQZKC1ZgFg== +gatsby@4.12.1: + version "4.12.1" + resolved "https://registry.yarnpkg.com/gatsby/-/gatsby-4.12.1.tgz#b9a72bca167662a05d7154e5e6091f31aa333efd" + integrity sha512-/QteQShPAW1dRmG9wrjHmdfQEQxh6WfOi9jnJXAxljAx8UlRt0JFntxMc9gWGUJD6fXYKmf13Jan9izuNDQxNQ== dependencies: "@babel/code-frame" "^7.14.0" "@babel/core" "^7.15.5" @@ -6544,8 +6544,8 @@ gatsby@4.11.2: babel-plugin-add-module-exports "^1.0.4" babel-plugin-dynamic-import-node "^2.3.3" babel-plugin-lodash "^3.3.4" - babel-plugin-remove-graphql-queries "^4.11.1" - babel-preset-gatsby "^2.11.1" + babel-plugin-remove-graphql-queries "^4.12.1" + babel-preset-gatsby "^2.12.1" better-opn "^2.1.1" bluebird "^3.7.2" body-parser "^1.19.0" @@ -6574,8 +6574,8 @@ gatsby@4.11.2: eslint-plugin-graphql "^4.0.0" eslint-plugin-import "^2.25.4" eslint-plugin-jsx-a11y "^6.5.1" - eslint-plugin-react "^7.29.2" - eslint-plugin-react-hooks "^4.3.0" + eslint-plugin-react "^7.29.4" + eslint-plugin-react-hooks "^4.4.0" eslint-webpack-plugin "^2.6.0" event-source-polyfill "^1.0.25" execa "^5.1.1" @@ -6587,19 +6587,19 @@ gatsby@4.11.2: find-cache-dir "^3.3.2" fs-exists-cached "1.0.0" fs-extra "^10.0.0" - gatsby-cli "^4.11.2" - gatsby-core-utils "^3.11.1" - gatsby-graphiql-explorer "^2.11.0" - gatsby-legacy-polyfills "^2.11.0" - gatsby-link "^4.11.1" - gatsby-page-utils "^2.11.1" - gatsby-parcel-config "^0.2.0" - gatsby-plugin-page-creator "^4.11.1" - gatsby-plugin-typescript "^4.11.1" - gatsby-plugin-utils "^3.5.1" - gatsby-react-router-scroll "^5.11.0" - gatsby-telemetry "^3.11.1" - gatsby-worker "^1.11.0" + gatsby-cli "^4.12.1" + gatsby-core-utils "^3.12.1" + gatsby-graphiql-explorer "^2.12.1" + gatsby-legacy-polyfills "^2.12.1" + gatsby-link "^4.12.1" + gatsby-page-utils "^2.12.1" + gatsby-parcel-config "^0.3.1" + gatsby-plugin-page-creator "^4.12.1" + gatsby-plugin-typescript "^4.12.1" + gatsby-plugin-utils "^3.6.1" + gatsby-react-router-scroll "^5.12.1" + gatsby-telemetry "^3.12.1" + gatsby-worker "^1.12.1" glob "^7.2.0" globby "^11.1.0" got "^11.8.2" @@ -6614,7 +6614,7 @@ gatsby@4.11.2: joi "^17.4.2" json-loader "^0.5.7" latest-version "5.1.0" - lmdb "^2.2.3" + lmdb "~2.2.3" lodash "^4.17.21" md5-file "^5.0.0" meant "^1.0.3" @@ -6672,7 +6672,7 @@ gatsby@4.11.2: xstate "^4.26.0" yaml-loader "^0.6.0" optionalDependencies: - gatsby-sharp "^0.5.0" + gatsby-sharp "^0.6.1" gauge@~2.7.3: version "2.7.4" @@ -6894,7 +6894,7 @@ globby@11.0.3: merge2 "^1.3.0" slash "^3.0.0" -globby@^11.0.3, globby@^11.0.4, globby@^11.1.0: +globby@^11.0.3, globby@^11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== @@ -8072,10 +8072,10 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" -lilconfig@^2.0.3, lilconfig@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.4.tgz#f4507d043d7058b380b6a8f5cb7bcd4b34cee082" - integrity sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA== +lilconfig@^2.0.3, lilconfig@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.5.tgz#19e57fd06ccc3848fd1891655b5a447092225b25" + integrity sha512-xaYmXZtTHPAw5m+xLN8ab9C+3a8YmV3asNSPOATITbtwrfbwaLJj8h66H1WMIpALCkqsIzK3h7oQ+PdX+LQ9Eg== lines-and-columns@^1.1.6: version "1.2.4" @@ -8096,6 +8096,36 @@ listr2@^3.8.3: through "^2.3.8" wrap-ansi "^7.0.0" +lmdb-darwin-arm64@2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/lmdb-darwin-arm64/-/lmdb-darwin-arm64-2.3.2.tgz#edcd324e4693abfcd02e7c5ba44a7f0e209f7588" + integrity sha512-20lWWUPGKnSZRFY8FBm+vZEFx/5Deh0joz6cqJ8/0SuO/ejqRCppSsNqAxPqW87KUNR5rNfhaA2oRekMeb0cwQ== + +lmdb-darwin-x64@2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/lmdb-darwin-x64/-/lmdb-darwin-x64-2.3.2.tgz#6561f37d0461c3128b92fb80996c54e6243af939" + integrity sha512-BsBnOfgK1B11Dh4RgcgBTmkmsPv3mjBPKsA4W4E+18SW9K2aRi86CAMPXqjfY/OJDUe1pSrpVf1A83b8N/C9rg== + +lmdb-linux-arm64@2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/lmdb-linux-arm64/-/lmdb-linux-arm64-2.3.2.tgz#feb4a52a0030feb9520543c78919c30250a85107" + integrity sha512-DIibLZHpwwlIsP9cBRmw0xqDy6wZH+CDAnOTI+eihQ5PdWjTs+kaQs5O/x8l6/8fwCB0TPYKWTqfdUbvd/F7AA== + +lmdb-linux-arm@2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/lmdb-linux-arm/-/lmdb-linux-arm-2.3.2.tgz#d8ee33547cc0f671efcf63ca381306df51f972f5" + integrity sha512-ofxfxVQqMbaC2Ygjzk8k6xgS5Dg/3cANeLcEx14T35GoU5pQKlLAWjypptyLQEeOboEmEOpZmHMoD7sWu/zakQ== + +lmdb-linux-x64@2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/lmdb-linux-x64/-/lmdb-linux-x64-2.3.2.tgz#1c66a012199ab5235d3fcec8c3fdad573cf3eff4" + integrity sha512-HBUd013RRQ2KpiyBqqqSPSEwPpVUpTJZdTZGDVQFQZuxqyJumt4Wye3uh6ZgEiBtxzSelt4xvAeNjYPH0dcZSQ== + +lmdb-win32-x64@2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/lmdb-win32-x64/-/lmdb-win32-x64-2.3.2.tgz#e65f79bfbb9f09ebfa53d3b03959bc581ebda55a" + integrity sha512-/hir5oU+GYm7/B6QirrpyOmIuzCKiIbWoKIJI2ebXeJlrs6Jj7UY9caPBYVkCzd79QzJnB7hIlX/F6Jx6gcUmg== + lmdb@2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/lmdb/-/lmdb-2.2.4.tgz#6494d5a1d1db152e0be759edcfa06893e4cbdb53" @@ -8107,7 +8137,26 @@ lmdb@2.2.4: ordered-binary "^1.2.4" weak-lru-cache "^1.2.2" -lmdb@^2.0.2, lmdb@^2.2.3, lmdb@^2.2.4: +lmdb@^2.0.2, lmdb@^2.2.6: + version "2.3.3" + resolved "https://registry.yarnpkg.com/lmdb/-/lmdb-2.3.3.tgz#ee0c53a5f5c2509c566b891803b3322e5a59095f" + integrity sha512-CrooSvHOzd+jPXCXpiffu2+5m90Fe6L/cw90fg+4sCWNrw3W7/ad20CGuTkMVU7mAuwXEAJAfnUwvHN2pS9Rqg== + dependencies: + msgpackr "^1.5.4" + nan "^2.14.2" + node-addon-api "^4.3.0" + node-gyp-build-optional-packages "^4.3.2" + ordered-binary "^1.2.4" + weak-lru-cache "^1.2.2" + optionalDependencies: + lmdb-darwin-arm64 "2.3.2" + lmdb-darwin-x64 "2.3.2" + lmdb-linux-arm "2.3.2" + lmdb-linux-arm64 "2.3.2" + lmdb-linux-x64 "2.3.2" + lmdb-win32-x64 "2.3.2" + +lmdb@~2.2.3: version "2.2.6" resolved "https://registry.yarnpkg.com/lmdb/-/lmdb-2.2.6.tgz#a52ef533812b8abcbe0033fc9d74d215e7dfc0a0" integrity sha512-UmQV0oZZcV3EN6rjcAjIiuWcc3MYZGWQ0GUYz46Ron5fuTa/dUow7WSQa6leFkvZIKVUdECBWVw96tckfEzUFQ== @@ -8579,13 +8628,13 @@ micromatch@^3.1.10: snapdragon "^0.8.1" to-regex "^3.0.2" -micromatch@^4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" - integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== +micromatch@^4.0.4, micromatch@^4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== dependencies: - braces "^3.0.1" - picomatch "^2.2.3" + braces "^3.0.2" + picomatch "^2.3.1" miller-rabin@^4.0.0: version "4.0.1" @@ -8668,10 +8717,10 @@ mini-css-extract-plugin@1.6.2: schema-utils "^3.0.0" webpack-sources "^1.1.0" -mini-svg-data-uri@^1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/mini-svg-data-uri/-/mini-svg-data-uri-1.4.3.tgz#43177b2e93766ba338931a3e2a84a3dfd3a222b8" - integrity sha512-gSfqpMRC8IxghvMcxzzmMnWpXAChSA+vy4cia33RgerMS8Fex95akUyQZPbxJJmeBGiGmK7n/1OpUX8ksRjIdA== +mini-svg-data-uri@^1.4.4: + version "1.4.4" + resolved "https://registry.yarnpkg.com/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz#8ab0aabcdf8c29ad5693ca595af19dd2ead09939" + integrity sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg== minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: version "1.0.1" @@ -8706,7 +8755,7 @@ minimist-options@4.1.0: is-plain-obj "^1.1.0" kind-of "^6.0.3" -minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5: +minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5, minimist@^1.2.6: version "1.2.6" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== @@ -8809,10 +8858,10 @@ nano-css@^5.3.1: stacktrace-js "^2.0.2" stylis "^4.0.6" -nanoid@3.3.2, nanoid@^3.3.1: - version "3.3.2" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.2.tgz#c89622fafb4381cd221421c69ec58547a1eec557" - integrity sha512-CuHBogktKwpm5g2sRgv83jEy2ijFzBwMoYA60orPDR7ynsLijJDqgsi4RDGj3OJpy3Ieb+LYwiRmIOGyytgITA== +nanoid@3.3.3, nanoid@^3.3.1: + version "3.3.3" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25" + integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w== nanomatch@^1.2.9: version "1.2.13" @@ -8919,6 +8968,11 @@ node-fetch@^2.6.1, node-fetch@^2.6.6, node-fetch@^2.6.7: dependencies: whatwg-url "^5.0.0" +node-gyp-build-optional-packages@^4.3.2: + version "4.3.2" + resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-4.3.2.tgz#82de9bdf9b1ad042457533afb2f67469dc2264bb" + integrity sha512-P5Ep3ISdmwcCkZIaBaQamQtWAG0facC89phWZgi5Z3hBU//J6S48OIvyZWSPPf6yQMklLZiqoosWAZUj7N+esA== + node-gyp-build@^4.2.3, node-gyp-build@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.3.0.tgz#9f256b03e5826150be39c764bf51e993946d71a3" @@ -9051,10 +9105,10 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" -object-hash@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.2.0.tgz#5ad518581eefc443bd763472b8ff2e9c2c0d54a5" - integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw== +object-hash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9" + integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw== object-inspect@^1.11.0, object-inspect@^1.9.0: version "1.12.0" @@ -9509,7 +9563,7 @@ picocolors@^1.0.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== -picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3: +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== @@ -9555,10 +9609,10 @@ pngjs@^3.0.0, pngjs@^3.3.3: resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.4.0.tgz#99ca7d725965fb655814eaf65f38f12bbdbf555f" integrity sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w== -polished@4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/polished/-/polished-4.2.1.tgz#e38cdf4244b3bea63f77b0f8ab2335e22a66bd08" - integrity sha512-vRkUnHBwVX7kIeCzCghcLCWoDenV+sV7lkItnmTc7bb6Uzbe8ogU1FxqEW8+dXCxUX8YW8vusQ0HTk2yES7bfQ== +polished@4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/polished/-/polished-4.2.2.tgz#2529bb7c3198945373c52e34618c8fe7b1aa84d1" + integrity sha512-Sz2Lkdxz6F2Pgnpi9U5Ng/WdWAUZxmHrNPoVlm3aAemxoy2Qy7LGjQg4uf8qKelDAUW94F4np3iH2YPf2qefcQ== dependencies: "@babel/runtime" "^7.17.8" @@ -9639,12 +9693,12 @@ postcss-js@^4.0.0: dependencies: camelcase-css "^2.0.1" -postcss-load-config@^3.1.0: - version "3.1.3" - resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-3.1.3.tgz#21935b2c43b9a86e6581a576ca7ee1bde2bd1d23" - integrity sha512-5EYgaM9auHGtO//ljHH+v/aC/TQ5LHXtL7bQajNAUBKUVKiYE8rYpFms7+V26D9FncaGe2zwCoPQsFKb5zF/Hw== +postcss-load-config@^3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-3.1.4.tgz#1ab2571faf84bb078877e1d07905eabe9ebda855" + integrity sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg== dependencies: - lilconfig "^2.0.4" + lilconfig "^2.0.5" yaml "^1.10.2" postcss-loader@^4.3.0: @@ -9838,10 +9892,10 @@ postcss-reduce-transforms@^5.1.0: dependencies: postcss-value-parser "^4.2.0" -postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4, postcss-selector-parser@^6.0.5, postcss-selector-parser@^6.0.6, postcss-selector-parser@^6.0.9: - version "6.0.9" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.9.tgz#ee71c3b9ff63d9cd130838876c13a2ec1a992b2f" - integrity sha512-UO3SgnZOVTwu4kyLR22UQ1xZh086RyNZppb7lLAKBFK8a32ttG5i87Y/P3+2bRSjZNyJ1B7hfFNo273tKe9YxQ== +postcss-selector-parser@^6.0.10, postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4, postcss-selector-parser@^6.0.5, postcss-selector-parser@^6.0.6, postcss-selector-parser@^6.0.9: + version "6.0.10" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz#79b61e2c0d1bfc2602d549e11d0876256f8df88d" + integrity sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w== dependencies: cssesc "^3.0.0" util-deprecate "^1.0.2" @@ -9866,7 +9920,7 @@ postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0: resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== -postcss@8.4.12, postcss@^8.2.15, postcss@^8.2.9, postcss@^8.3.11, postcss@^8.4.6: +postcss@8.4.12, postcss@^8.2.15, postcss@^8.2.9, postcss@^8.3.11, postcss@^8.4.12: version "8.4.12" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.12.tgz#1e7de78733b28970fa4743f7da6f3763648b1905" integrity sha512-lg6eITwYe9v6Hr5CncVbK70SoioNQIq81nsaG86ev5hAidQvmOeETBqs7jm43K2F5/Ley3ytDtriImV6TpNiSg== @@ -9929,7 +9983,7 @@ pretty-error@^2.1.2: lodash "^4.17.20" renderkid "^2.0.4" -probe-image-size@^7.0.0: +probe-image-size@^7.2.3: version "7.2.3" resolved "https://registry.yarnpkg.com/probe-image-size/-/probe-image-size-7.2.3.tgz#d49c64be540ec8edea538f6f585f65a9b3ab4309" integrity sha512-HubhG4Rb2UH8YtV4ba0Vp5bQ7L78RTONYu/ujmCu5nBI8wGv24s4E9xSKBi0N1MowRpxk76pFCpJtW0KPzOK0w== @@ -10209,10 +10263,10 @@ react-dom@17.0.2: object-assign "^4.1.1" scheduler "^0.20.2" -react-dropzone@12.0.4: - version "12.0.4" - resolved "https://registry.yarnpkg.com/react-dropzone/-/react-dropzone-12.0.4.tgz#b88eeaa2c7118f7fd042404682b17a1d466f2fcf" - integrity sha512-fcqHEYe1MzAghU6/Hz86lHDlBNsA+lO48nAcm7/wA+kIzwS6uuJbUG33tBZjksj7GAZ1iUQ6NHwjUURPmSGang== +react-dropzone@12.0.5: + version "12.0.5" + resolved "https://registry.yarnpkg.com/react-dropzone/-/react-dropzone-12.0.5.tgz#f9b557484a8afd6267f670f96a770ddd3948838b" + integrity sha512-zUjZigD0VJ91CSm9T1h7ErxFReBLaa9sjS2dUL0+inb0RROZpSJTNDHPY1rrBES5V2NXhF8v0kghmaHc81BMFg== dependencies: attr-accept "^2.2.2" file-selector "^0.4.0" @@ -10857,10 +10911,10 @@ shallow-compare@^1.2.2: resolved "https://registry.yarnpkg.com/shallow-compare/-/shallow-compare-1.2.2.tgz#fa4794627bf455a47c4f56881d8a6132d581ffdb" integrity sha512-LUMFi+RppPlrHzbqmFnINTrazo0lPNwhcgzuAXVVcfy/mqPDrQmHAyz5bvV0gDAuRFrk804V0HpQ6u9sZ0tBeg== -sharp@^0.30.1: - version "0.30.2" - resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.30.2.tgz#95b309b2740424702dc19b62a62595dd34a458b1" - integrity sha512-mrMeKI5ECTdYhslPlA2TbBtU3nZXMEBcQwI6qYXjPlu1LpW4HBZLFm6xshMI1HpIdEEJ3UcYp5AKifLT/fEHZQ== +sharp@^0.30.3: + version "0.30.3" + resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.30.3.tgz#315a1817423a4d1cde5119a21c99c234a7a6fb37" + integrity sha512-rjpfJFK58ZOFSG8sxYSo3/JQb4ej095HjXp9X7gVu7gEn1aqSG8TCW29h/Rr31+PXrFADo1H/vKfw0uhMQWFtg== dependencies: color "^4.2.1" detect-libc "^2.0.1" @@ -10963,10 +11017,10 @@ sjcl@^1.0.8: resolved "https://registry.yarnpkg.com/sjcl/-/sjcl-1.0.8.tgz#f2ec8d7dc1f0f21b069b8914a41a8f236b0e252a" integrity sha512-LzIjEQ0S0DpIgnxMEayM1rq9aGwGRG4OnZhCdjx7glTaJtf4zRfpg87ImfjSJjoW9vKpagd82McDOwbRT5kQKQ== -skynet-js@4.0.26-beta: - version "4.0.26-beta" - resolved "https://registry.yarnpkg.com/skynet-js/-/skynet-js-4.0.26-beta.tgz#5b6e924a0efa5fd6ee2c00760e1d4ce92d1ba0a9" - integrity sha512-YPqjNyqL6AhS9jMLyJ5PoilDZ7f2YFrqqhXUnzLBrjmWxICxcDeRu2GJh9MGCJUZ2Cv35IlG1ch4eiqFbs1wqA== +skynet-js@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/skynet-js/-/skynet-js-4.1.0.tgz#eccb84d04e9f42aa4f86ecb24fb4d59ed21e44cc" + integrity sha512-VmUjJ9QnLpfuQA2j7vzFh8JvukjQlX4QLGw1HY3VyslFPj92vPpyO8gqjPfzgbkR05TXL7CbdqZoLZr/RBDZPw== dependencies: "@skynetlabs/tus-js-client" "^2.3.0" async-mutex "^0.3.2" @@ -11583,10 +11637,10 @@ svgo@^2.5.0, svgo@^2.7.0: picocolors "^1.0.0" stable "^0.1.8" -swr@1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/swr/-/swr-1.2.2.tgz#6cae09928d30593a7980d80f85823e57468fac5d" - integrity sha512-ky0BskS/V47GpW8d6RU7CPsr6J8cr7mQD6+do5eky3bM0IyJaoi3vO8UhvrzJaObuTlGhPl2szodeB2dUd76Xw== +swr@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/swr/-/swr-1.3.0.tgz#c6531866a35b4db37b38b72c45a63171faf9f4e8" + integrity sha512-dkghQrOl2ORX9HYrMDtPa7LTVHJjCTeZoB1dqTbnnEDlSvN8JEKpYIYurDfvbQFUUS8Cg8PceFVZNkW0KNNYPw== symbol-observable@^1.0.4: version "1.2.0" @@ -11612,29 +11666,29 @@ table@^6.0.9: string-width "^4.2.3" strip-ansi "^6.0.1" -tailwindcss@3.0.23: - version "3.0.23" - resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.0.23.tgz#c620521d53a289650872a66adfcb4129d2200d10" - integrity sha512-+OZOV9ubyQ6oI2BXEhzw4HrqvgcARY38xv3zKcjnWtMIZstEsXdI9xftd1iB7+RbOnj2HOEzkA0OyB5BaSxPQA== +tailwindcss@3.0.24: + version "3.0.24" + resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.0.24.tgz#22e31e801a44a78a1d9a81ecc52e13b69d85704d" + integrity sha512-H3uMmZNWzG6aqmg9q07ZIRNIawoiEcNFKDfL+YzOPuPsXuDXxJxB9icqzLgdzKNwjG3SAro2h9SYav8ewXNgig== dependencies: arg "^5.0.1" - chalk "^4.1.2" chokidar "^3.5.3" color-name "^1.1.4" - cosmiconfig "^7.0.1" detective "^5.2.0" didyoumean "^1.2.2" dlv "^1.1.3" fast-glob "^3.2.11" glob-parent "^6.0.2" is-glob "^4.0.3" + lilconfig "^2.0.5" normalize-path "^3.0.0" - object-hash "^2.2.0" - postcss "^8.4.6" + object-hash "^3.0.0" + picocolors "^1.0.0" + postcss "^8.4.12" postcss-js "^4.0.0" - postcss-load-config "^3.1.0" + postcss-load-config "^3.1.4" postcss-nested "5.0.6" - postcss-selector-parser "^6.0.9" + postcss-selector-parser "^6.0.10" postcss-value-parser "^4.2.0" quick-lru "^5.1.1" resolve "^1.22.0" diff --git a/setup-scripts/README.md b/setup-scripts/README.md index 1d032795..d5237e09 100644 --- a/setup-scripts/README.md +++ b/setup-scripts/README.md @@ -2,7 +2,7 @@ > :warning: This documentation is outdated and should be used for reference only. Portal setup documentation is located at -https://docs.siasky.net/webportal-management/overview. +https://portal-docs.skynetlabs.com/. This directory contains a setup guide and scripts that will install and configure some basic requirements for running a Skynet Portal. The assumption is