Merge branch 'master' into portal-latest

This commit is contained in:
Matthew Sevey 2022-04-22 11:36:38 -04:00
commit 3364282875
No known key found for this signature in database
GPG Key ID: 9ADDD344F13057F6
94 changed files with 2643 additions and 2428 deletions

View File

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

View File

@ -14,6 +14,7 @@ jobs:
matrix:
dockerfile:
- docker/nginx/Dockerfile
- docker/nginx/testing/Dockerfile
- docker/sia/Dockerfile
- packages/dashboard/Dockerfile
- packages/dashboard-v2/Dockerfile

View File

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

4
.gitignore vendored
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,22 @@
FROM golang:1.16.7
LABEL maintainer="SkynetLabs <devs@siasky.net>"
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"]

View File

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

View File

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

View File

@ -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}", "^([^.]+)") }
}

View File

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

View File

@ -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}", "^([^.]+)") }
}

View File

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

View File

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

View File

@ -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}", "^([^.]+)") }
}

View File

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

View File

@ -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}", "^([^.]+)") }
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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,
})

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -0,0 +1,6 @@
exclude = {
"/usr/local/openresty", -- internal openresty libraries
"rbusted", -- busted executable
"basexx", -- external library https://github.com/aiq/basexx
}
includeuntestedfiles = true

View File

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

View File

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

8
docker/nginx/testing/rbusted Executable file
View File

@ -0,0 +1,8 @@
#!/usr/bin/env resty
setmetatable(_G, nil)
pcall(require, "luarocks.loader")
-- Busted command-line runner
require "busted.runner"({ standalone = false })

View File

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

View File

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

View File

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

View File

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

View File

@ -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 (
<li
@ -66,6 +66,7 @@ export const APIKey = ({ apiKey, onRemoved, onEdited, onRemovalError }) => {
<span className="col-span-2 sm:col-span-1 flex items-center">
<span className="flex flex-col">
<span className={cn("truncate", { "text-palette-300": !name })}>{name || "unnamed key"}</span>
{isSponsorKey && (
<button
onClick={promptEdit}
className={cn("text-xs hover:underline decoration-dotted", {
@ -75,12 +76,13 @@ export const APIKey = ({ apiKey, onRemoved, onEdited, onRemovalError }) => {
>
{skylinksPhrase}
</button>
)}
</span>
</span>
<span className="col-span-2 my-4 border-t border-t-palette-200/50 sm:hidden" />
<span className="text-palette-400">{dayjs(createdAt).format("MMM DD, YYYY")}</span>
<span className="flex items-center justify-end">
{isPublic && (
{isSponsorKey && (
<button
title="Add or remove skylinks"
aria-label="Add or remove skylinks"
@ -119,7 +121,7 @@ export const APIKey = ({ apiKey, onRemoved, onEdited, onRemovalError }) => {
)}
{editInitiated && (
<Modal onClose={closeEditModal} className="flex flex-col gap-4 text-center sm:px-8 sm:py-6">
<h4>Covered skylinks</h4>
<h4>Sponsored skylinks</h4>
{skylinks?.length > 0 ? (
<ul className="text-xs flex flex-col gap-2">
{skylinks.map((skylink) => (
@ -143,7 +145,7 @@ export const APIKey = ({ apiKey, onRemoved, onEdited, onRemovalError }) => {
<div className="flex flex-col gap-4">
{error && <Alert $variant="error">{error}</Alert>}
<AddSkylinkToAPIKeyForm addSkylink={addSkylink} />
<AddSkylinkToSponsorKeyForm addSkylink={addSkylink} />
</div>
<div className="flex gap-4 justify-center mt-4">
<Button onClick={closeEditModal}>Close</Button>

View File

@ -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) => {
>
<img src={imageUrl} className="w-[160px]" alt="" />
</div>
{/* TODO: uncomment when avatar uploads work
<div className="flex justify-center">
<button
className="flex items-center gap-4 hover:underline decoration-1 decoration-dashed underline-offset-2 decoration-gray-400"
@ -28,8 +29,8 @@ export const AvatarUploader = (props) => {
>
<SimpleUploadIcon size={20} className="shrink-0" /> Upload profile picture
</button>
{/* TODO: actual uploading */}
</div>
*/}
</div>
);
};

View File

@ -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 (
<div>
<div className="flex flex-col h-full">
<h4>{activePlan.name}</h4>
<div className="text-palette-400">
{activePlan.price === 0 && <p>100GB without paying a dime! 🎉</p>}
<div className="text-palette-400 justify-between flex flex-col grow">
{activePlan.price === 0 && activePlan.limits && (
<p>{prettyBytes(activePlan.limits.storageLimit, { binary: true })} without paying a dime! 🎉</p>
)}
{activePlan.price !== 0 &&
(user.subscriptionCancelAtPeriodEnd ? (
<p>Your subscription expires {dayjs(user.subscribedUntil).fromNow()}</p>
) : (
<p className="first-letter:uppercase">{dayjs(user.subscribedUntil).fromNow(true)} until the next payment</p>
))}
<LatestPayment user={user} />
{user.subscriptionStatus && <LatestPayment user={user} />}
<SuggestedPlan plans={plans} activePlan={activePlan} />
</div>
</div>

View File

@ -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() {
<span>{storageLimit.text}</span>
</div>
<UsageGraph>
<GraphBar value={usage.storageUsed} limit={usage.storageLimit} label={storageUsage} />
<GraphBar value={usage.storageUsed} limit={usage.storageLimit} label={storageUsage} className="normal-case" />
<GraphBar value={usage.filesUsed} limit={usage.filesLimit} label={filesUsedLabel} />
</UsageGraph>
<div className="flex place-content-between">
@ -97,7 +100,10 @@ export default function CurrentUsage() {
<span className="inline-flex place-content-between w-[37%]">
<Link
to="/upgrade"
className="text-primary underline-offset-3 decoration-dotted hover:text-primary-light hover:underline"
className={cn(
"text-primary underline-offset-3 decoration-dotted hover:text-primary-light hover:underline",
{ invisible: !nextPlan }
)}
>
UPGRADE
</Link>{" "}

View File

@ -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 (
<div className="relative flex items-center">
<div className={`relative flex items-center ${className}`}>
<Bar $percentage={percentage}>
<BarTip />
</Bar>

View File

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

View File

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

View File

@ -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 = () => (
<PageContainer className="font-content text-palette-300 py-4">
<p>© Skynet Labs Inc. All rights reserved.</p>
<p>
Made by <FooterLink href="https://skynetlabs.com">Skynet Labs</FooterLink>. Open-sourced{" "}
<FooterLink href="https://github.com/SkynetLabs/skynet-webportal">on Github</FooterLink>.
</p>
</PageContainer>
);

View File

@ -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",
})``;

View File

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

View File

@ -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 (
<Panel title="Latest activity">
<Tabs>
<Tab id="uploads" title="Uploads" />
<Tab id="downloads" title="Downloads" />
<TabPanel tabId="uploads" className="pt-4">
<Panel title="Latest uploads">
<ActivityTable type="uploads" />
</TabPanel>
<TabPanel tabId="downloads" className="pt-4">
<ActivityTable type="downloads" />
</TabPanel>
</Tabs>
</Panel>
);
}

View File

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

View File

@ -0,0 +1 @@
export * from "./Tooltip";

View File

@ -118,7 +118,7 @@ const Uploader = ({ mode }) => {
</div>
{uploads.length > 0 && (
<div className="flex flex-col space-y-4 py-10">
<div className="flex flex-col space-y-4 pt-6 pb-10">
{uploads.map((upload) => (
<UploaderItem key={upload.id} onUploadStateChange={onUploadStateChange} upload={upload} />
))}

View File

@ -34,8 +34,9 @@ export const AccountRemovalForm = ({ abort, onSuccess }) => {
<Form className="flex flex-col gap-4">
<div>
<h4>Delete account</h4>
<p>This will completely delete your account.</p>
<p>
This will completely delete your account. <strong>This process can't be undone.</strong>
<strong>This process cannot be undone.</strong>
</p>
</div>

View File

@ -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 (
<div ref={ref} className="flex flex-col gap-4">
{state === State.Success && (
<Alert $variant="success" className="text-center">
<Alert $variant="success">
<strong>Success!</strong>
<p>Please copy your new API key below. We'll never show it again!</p>
<div className="flex items-center gap-2 mt-4 justify-center">
<div className="flex items-center gap-2 mt-4">
<code className="p-2 rounded border border-palette-200 text-xs selection:bg-primary/30 truncate">
{generatedKey}
</code>
@ -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 }) => (
<Form className="grid grid-cols-[1fr_min-content] w-full gap-y-2 gap-x-4 items-start">
<div className="flex items-center">
<Form className="flex flex-col gap-4">
<TextField
type="text"
id="name"
name="name"
label="New API Key Name"
label="New API Key Label"
placeholder="my_applications_statistics"
error={errors.name}
touched={touched.name}
/>
</div>
<div className="flex mt-5 justify-center">
{isSubmitting ? (
<CircledProgressIcon size={38} className="text-palette-300 animate-[spin_3s_linear_infinite]" />
) : (
<Button type="submit" className="px-2.5" aria-label="Create general API key">
<PlusIcon size={14} />
<div className="flex justify-center">
<Button type="submit" disabled={isSubmitting} className={cn({ "cursor-wait": isSubmitting })}>
{isSubmitting ? "Generating your API key..." : "Generate your API key"}
</Button>
)}
</div>
</Form>
)}
@ -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,
};

View File

@ -19,7 +19,7 @@ const newSkylinkSchema = Yup.object().shape({
}),
});
export const AddSkylinkToAPIKeyForm = ({ addSkylink }) => (
export const AddSkylinkToSponsorKeyForm = ({ addSkylink }) => (
<Formik
initialValues={{
skylink: "",
@ -58,6 +58,6 @@ export const AddSkylinkToAPIKeyForm = ({ addSkylink }) => (
</Formik>
);
AddSkylinkToAPIKeyForm.propTypes = {
AddSkylinkToSponsorKeyForm.propTypes = {
addSkylink: PropTypes.func.isRequired,
};

View File

@ -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 (
<div ref={ref} className="flex flex-col gap-4">
{state === State.Success && (
<Alert $variant="success" className="text-center">
<Alert $variant="success">
<strong>Success!</strong>
<p>Please copy your new API key below. We'll never show it again!</p>
<div className="flex items-center gap-2 mt-4 justify-center">
<div className="flex items-center gap-2 mt-4">
<code className="p-2 rounded border border-palette-200 text-xs selection:bg-primary/30 truncate">
{generatedKey}
</code>
@ -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}
/>
</div>
<div>
<h6 className="text-palette-300 mb-2">Skylinks accessible with the new key</h6>
<h6 className="text-palette-300 mb-2">Skylinks sponsored by the new key</h6>
<FieldArray
name="skylinks"
render={({ push, remove }) => {
@ -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"}
</Button>
</div>
</Form>
@ -192,8 +192,8 @@ export const AddPublicAPIKeyForm = forwardRef(({ onSuccess }, ref) => {
);
});
AddPublicAPIKeyForm.displayName = "AddAPIKeyForm";
AddSponsorKeyForm.displayName = "AddSponsorKeyForm";
AddPublicAPIKeyForm.propTypes = {
AddSponsorKeyForm.propTypes = {
onSuccess: PropTypes.func.isRequired,
};

View File

@ -32,14 +32,16 @@ export const SignUpForm = ({ onSuccess, onFailure }) => (
validationSchema={registrationSchema}
onSubmit={async ({ email, password }, { setErrors }) => {
try {
await accountsService.post("user", {
const user = await accountsService
.post("user", {
json: {
email,
password,
},
});
})
.json();
onSuccess();
onSuccess(user);
} catch (err) {
let isFormErrorSet = false;

View File

@ -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]);

View File

@ -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 =
<Content>
<div className="bg-white px-8 py-10 md:py-32 lg:px-16 xl:px-28 min-h-screen">
<div className="mb-4 md:mb-16">
<img src="/images/logo-black-text.svg" alt="Skynet" className="-ml-2" />
<img src={skynetLogo} alt="Skynet" className="-ml-2" />
</div>
{children}
</div>

View File

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

View File

@ -16,8 +16,8 @@ const Sidebar = () => (
<SidebarLink activeClassName="!border-l-primary" to="/settings/export">
Export
</SidebarLink>
<SidebarLink activeClassName="!border-l-primary" to="/settings/api-keys">
API Keys
<SidebarLink activeClassName="!border-l-primary" to="/settings/developer-settings">
Developer settings
</SidebarLink>
</nav>
</aside>

View File

@ -0,0 +1 @@
export const DATE_FORMAT = "MMM D, YYYY HH:MM";

View File

@ -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 (
<PlansProvider>
@ -70,7 +70,7 @@ const SignUpPage = () => {
{state !== State.Success && (
<>
<SignUpForm onSuccess={() => setState(State.Success)} onFailure={() => setState(State.Failure)} />
<SignUpForm onSuccess={onUserCreated} onFailure={() => setState(State.Failure)} />
<p className="text-sm text-center mt-8">
Already have an account? <HighlightedLink to="/auth/login">Sign in</HighlightedLink>
@ -78,14 +78,6 @@ const SignUpPage = () => {
</>
)}
{state === State.Success && (
<div>
<p className="text-primary font-semibold">Please check your inbox and confirm your email address.</p>
<p>You will be redirected to your dashboard shortly.</p>
<HighlightedLink to="/">Click here to go there now.</HighlightedLink>
</div>
)}
{state === State.Failure && (
<p className="text-error text-center">Something went wrong, please try again later.</p>
)}

View File

@ -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 (
<>
<Metadata>
<title>My Files</title>
<title>Files</title>
</Metadata>
<Panel title="Files">
<Tabs defaultTab={defaultTab || "uploads"}>
<Tab id="uploads" title="Uploads" />
<Tab id="downloads" title="Downloads" />
<TabPanel tabId="uploads" className="pt-4">
<FileList type="uploads" />
</TabPanel>
<TabPanel tabId="downloads" className="pt-4">
<FileList type="downloads" />
</TabPanel>
</Tabs>
</Panel>
</>
);

View File

@ -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 (
<>
<Metadata>
<title>API Keys</title>
</Metadata>
<div className="flex flex-col xl:flex-row">
<div className="flex flex-col gap-10 lg:shrink-0 lg:max-w-[576px] xl:max-w-[524px]">
<div>
<h4>API Keys</h4>
<p className="leading-relaxed">There are two types of API keys that you can generate for your account.</p>
<p>Make sure to use the appropriate type.</p>
</div>
<hr />
<section className="flex flex-col gap-2">
<h5>Public keys</h5>
<p className="text-palette-500">
Public keys provide read access to a selected list of skylinks. You can share them publicly.
</p>
<div className="mt-4">
<AddPublicAPIKeyForm ref={publicFormRef} onSuccess={refreshState} />
</div>
{error ? (
<Alert $variant="error" className="mt-4">
An error occurred while loading your API keys. Please try again later.
</Alert>
) : (
<div className="mt-4">
{publicKeys?.length > 0 ? (
<APIKeyList title="Your public keys" keys={publicKeys} reloadKeys={() => refreshState(true)} />
) : (
<Alert $variant="info">No public API keys found.</Alert>
)}
</div>
)}
</section>
<hr />
<section className="flex flex-col gap-2">
<h5>General keys</h5>
<p className="text-palette-500">
These keys provide full access to <b>Accounts</b> service and are equivalent to using a JWT token.
</p>
<p className="underline">
This type of API keys needs to be kept secret and should never be shared with anyone.
</p>
<div className="mt-4">
<AddAPIKeyForm ref={generalFormRef} onSuccess={refreshState} type={APIKeyType.General} />
</div>
{error ? (
<Alert $variant="error" className="mt-4">
An error occurred while loading your API keys. Please try again later.
</Alert>
) : (
<div className="mt-4">
{generalKeys?.length > 0 ? (
<APIKeyList title="Your general keys" keys={generalKeys} reloadKeys={() => refreshState(true)} />
) : (
<Alert $variant="info">No general API keys found.</Alert>
)}
</div>
)}
</section>
</div>
<div className="hidden xl:block w-full text-right pt-16 pr-5">
<img src="/images/api-keys.svg" alt="" className="inline-block h-[150px]" />
</div>
</div>
</>
);
};
APIKeysPage.Layout = UserSettingsLayout;
export default APIKeysPage;

View File

@ -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 (
<>
<Metadata>
<title>Developer settings</title>
</Metadata>
<div className="flex flex-col xl:flex-row">
<div className="flex flex-col gap-10 lg:shrink-0 lg:max-w-[576px] xl:max-w-[524px] leading-relaxed">
<div>
<h4>Developer settings</h4>
<p>API keys allow developers and applications to extend the functionality of your portal account.</p>
<p>Skynet uses two types of API keys, explained below.</p>
</div>
<hr />
<section className="flex flex-col gap-2">
<h5>Sponsor keys</h5>
<div className="text-palette-500"></div>
<p>
Sponsor keys allow users without an account on this portal to download skylinks covered by the API key.
</p>
<p>
Learn more about sponsoring content with Sponsor API Keys{" "}
<HighlightedLink as="a" href="#">
here
</HighlightedLink>
.
</p>{" "}
{/* TODO: missing documentation link */}
<div className="mt-4">
<AddSponsorKeyForm ref={publicFormRef} onSuccess={refreshState} />
</div>
{error ? (
<Alert $variant="error" className="mt-4">
An error occurred while loading your sponsor keys. Please try again later.
</Alert>
) : (
<div className="mt-4">
{sponsorKeys?.length > 0 ? (
<APIKeyList title="Your public keys" keys={sponsorKeys} reloadKeys={() => refreshState(true)} />
) : (
<Alert $variant="info">No sponsor keys found.</Alert>
)}
</div>
)}
</section>
<hr />
<section className="flex flex-col gap-2">
<h5>API keys</h5>
<p className="text-palette-500">
These keys allow uploading and downloading skyfiles, as well as reading and writing to the registry.
</p>
<div className="mt-4">
<AddAPIKeyForm ref={generalFormRef} onSuccess={refreshState} type={APIKeyType.General} />
</div>
{error ? (
<Alert $variant="error" className="mt-4">
An error occurred while loading your API keys. Please try again later.
</Alert>
) : (
<div className="mt-4">
{apiKeys?.length > 0 ? (
<APIKeyList title="Your API keys" keys={apiKeys} reloadKeys={() => refreshState(true)} />
) : (
<Alert $variant="info">No API keys found.</Alert>
)}
</div>
)}
</section>
</div>
<div className="hidden xl:block w-full text-right pt-16 pr-5">
<img src={apiKeysImg} alt="" className="inline-block h-[150px]" />
</div>
</div>
</>
);
};
DeveloperSettingsPage.Layout = UserSettingsLayout;
export default DeveloperSettingsPage;

View File

@ -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 = () => {
<section>
<h4>Export</h4>
<p>
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.
</p>
</section>
<hr />
@ -65,7 +67,7 @@ const ExportPage = () => {
</section>
</div>
<div className="hidden xl:block w-full text-right pt-20 pr-6">
<img src="/images/import-export.svg" alt="" className="inline-block w-[200px]" />
<img src={exportImg} alt="" className="inline-block w-[200px]" />
</div>
</div>
</>

View File

@ -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 = () => {
</Metadata>
<div className="flex flex-col xl:flex-row">
<div className="flex flex-col gap-10 lg:shrink-0 lg:max-w-[576px] xl:max-w-[524px]">
<section>
<h4>Account</h4>
<p>
Tum dicere exorsus est laborum et quasi involuta aperiri, altera prompta et expedita. Primum igitur,
inquit, modo ista sis aequitate.
</p>
</section>
<hr />
<section className="flex flex-col gap-8">
{state === State.Failure && (
<Alert $variant="error">There was an error processing your request. Please try again later.</Alert>
@ -63,7 +66,23 @@ const AccountPage = () => {
<hr />
<section>
<h6 className="text-palette-400">Delete account</h6>
<p>This will completely delete your account. This process can't be undone.</p>
<div className="my-4">
<p>
This action will delete your account and <strong>cannot be undone</strong>.
</p>
<p>
Your uploaded files will remain accessible while any portal continues to{" "}
<HighlightedLink
as="a"
href="https://support.skynetlabs.com/key-concepts/faqs#what-is-pinning"
target="_blank"
rel="noreferrer"
>
pin
</HighlightedLink>{" "}
them to Skynet.
</p>
</div>
<button
type="button"
onClick={prompt}
@ -73,9 +92,12 @@ const AccountPage = () => {
</button>
</section>
</div>
<div className="flex w-full justify-start xl:justify-end">
{isLargeScreen && <AvatarUploader className="flex flex-col gap-4" />}
</div>
{removalInitiated && (
<Modal onClose={abort} className="text-center">
<AccountRemovalForm abort={abort} onSuccess={() => navigate("/auth/login")} />
<AccountRemovalForm abort={abort} onSuccess={onAccountRemoved} />
</Modal>
)}
</div>

View File

@ -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 = () => {
<section>
{/* TODO: saves on change */}
<Switch onChange={console.info.bind(console)} labelClassName="!items-start flex-col md:flex-row">
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.
</Switch>
</section>
<hr />
<section>
<h6 className="text-palette-300">Statistics</h6>
{/* TODO: proper content :) */}
<p>
Si sine causa, nollem me tamen laudandis maioribus meis corrupisti nec in malis. Si sine causa, mox
videro.
</p>
<p>Check below to be notified by email when your usage approaches your plan's limits.</p>
<ul className="mt-7 flex flex-col gap-2">
<li>
{/* TODO: saves on change */}
@ -37,13 +33,13 @@ const NotificationsPage = () => {
</li>
<li>
{/* TODO: saves on change */}
<Switch onChange={console.info.bind(console)}>File limit</Switch>
<Switch onChange={console.info.bind(console)}>Files limit</Switch>
</li>
</ul>
</section>
</div>
<div className="hidden xl:block text-right w-full pr-14 pt-20">
<StaticImage src="../../../static/images/inbox.svg" alt="" placeholder="none" />
<div className="hidden xl:block text-right w-full pl-12 pt-20">
<img src={inboxImg} alt="" className="w-[200px]" />
</div>
</div>
</>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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