diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 05669038..beb0a9e2 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -3,48 +3,48 @@ updates:
- package-ecosystem: npm
directory: "/packages/dashboard"
schedule:
- interval: weekly
+ interval: monthly
- package-ecosystem: npm
directory: "/packages/dnslink-api"
schedule:
- interval: weekly
+ interval: monthly
- package-ecosystem: npm
directory: "/packages/handshake-api"
schedule:
- interval: weekly
+ interval: monthly
- package-ecosystem: npm
directory: "/packages/health-check"
schedule:
- interval: weekly
+ interval: monthly
- package-ecosystem: npm
directory: "/packages/website"
schedule:
- interval: weekly
+ interval: monthly
- package-ecosystem: docker
directory: "/docker/nginx"
schedule:
- interval: weekly
+ interval: monthly
- package-ecosystem: docker
directory: "/docker/sia"
schedule:
- interval: weekly
+ interval: monthly
- package-ecosystem: docker
directory: "/packages/dashboard"
schedule:
- interval: weekly
+ interval: monthly
- package-ecosystem: docker
directory: "/packages/dnslink-api"
schedule:
- interval: weekly
+ interval: monthly
- package-ecosystem: docker
directory: "/packages/handshake-api"
schedule:
- interval: weekly
+ interval: monthly
- package-ecosystem: docker
directory: "/packages/health-check"
schedule:
- interval: weekly
+ interval: monthly
- package-ecosystem: docker
directory: "/packages/website"
schedule:
- interval: weekly
+ interval: monthly
diff --git a/.github/workflows/lint-dockerfiles.yml b/.github/workflows/lint-dockerfiles.yml
index 467dc117..afdd6558 100644
--- a/.github/workflows/lint-dockerfiles.yml
+++ b/.github/workflows/lint-dockerfiles.yml
@@ -14,6 +14,7 @@ jobs:
matrix:
dockerfile:
- docker/nginx/Dockerfile
+ - docker/nginx/testing/Dockerfile
- docker/sia/Dockerfile
- packages/dashboard/Dockerfile
- packages/dashboard-v2/Dockerfile
diff --git a/.github/workflows/nginx-lua-unit-tests.yml b/.github/workflows/nginx-lua-unit-tests.yml
index 6c9fba83..5af9c101 100644
--- a/.github/workflows/nginx-lua-unit-tests.yml
+++ b/.github/workflows/nginx-lua-unit-tests.yml
@@ -7,7 +7,11 @@ on:
push:
branches:
- master
+ paths:
+ - docker/nginx/libs/**
pull_request:
+ paths:
+ - docker/nginx/libs/**
jobs:
test:
diff --git a/README.md b/README.md
index 337f9791..e2c1714f 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
## Latest Setup Documentation
Latest Skynet Webportal setup documentation and the setup process Skynet Labs
-supports is located at https://docs.siasky.net/webportal-management/overview.
+supports is located at https://portal-docs.skynetlabs.com/.
Some scripts and setup documentation contained in this repository
(`skynet-webportal`) may be outdated and generally should not be used.
diff --git a/changelog/items/bugs-fixed/fix-context.md b/changelog/items/bugs-fixed/fix-context.md
new file mode 100644
index 00000000..e94c8e6e
--- /dev/null
+++ b/changelog/items/bugs-fixed/fix-context.md
@@ -0,0 +1,2 @@
+- Fix `dashboard-v2` Dockerfile context in `docker-compose.accounts.yml` to
+ avoid Ansible deploy (docker compose build) `permission denied` issues.
\ No newline at end of file
diff --git a/docker-compose.abuse-scanner.yml b/docker-compose.abuse-scanner.yml
index 4edb6556..6080ebcd 100644
--- a/docker-compose.abuse-scanner.yml
+++ b/docker-compose.abuse-scanner.yml
@@ -10,7 +10,7 @@ services:
abuse-scanner:
# uncomment "build" and comment out "image" to build from sources
# build: https://github.com/SkynetLabs/abuse-scanner.git#main
- image: skynetlabs/abuse-scanner
+ image: skynetlabs/abuse-scanner:0.1.0
container_name: abuse-scanner
restart: unless-stopped
logging: *default-logging
diff --git a/docker-compose.accounts.yml b/docker-compose.accounts.yml
index 3c2c46a9..221badf8 100644
--- a/docker-compose.accounts.yml
+++ b/docker-compose.accounts.yml
@@ -55,9 +55,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
diff --git a/docker-compose.blocker.yml b/docker-compose.blocker.yml
index edcb45c0..29d29e34 100644
--- a/docker-compose.blocker.yml
+++ b/docker-compose.blocker.yml
@@ -15,7 +15,7 @@ services:
blocker:
# uncomment "build" and comment out "image" to build from sources
# build: https://github.com/SkynetLabs/blocker.git#main
- image: skynetlabs/blocker
+ image: skynetlabs/blocker:0.1.0
container_name: blocker
restart: unless-stopped
logging: *default-logging
diff --git a/docker-compose.malware-scanner.yml b/docker-compose.malware-scanner.yml
index fba60f98..ee3b6700 100644
--- a/docker-compose.malware-scanner.yml
+++ b/docker-compose.malware-scanner.yml
@@ -28,7 +28,7 @@ services:
malware-scanner:
# uncomment "build" and comment out "image" to build from sources
# build: https://github.com/SkynetLabs/malware-scanner.git#main
- image: skynetlabs/malware-scanner
+ image: skynetlabs/malware-scanner:0.1.0
container_name: malware-scanner
restart: unless-stopped
logging: *default-logging
diff --git a/docker-compose.mongodb.yml b/docker-compose.mongodb.yml
index 610d5308..e8eb9aca 100644
--- a/docker-compose.mongodb.yml
+++ b/docker-compose.mongodb.yml
@@ -15,7 +15,7 @@ services:
mongo:
image: mongo:4.4.1
- command: --keyFile=/data/mgkey --replSet=${SKYNET_DB_REPLICASET:-skynet}
+ command: --keyFile=/data/mgkey --replSet=${SKYNET_DB_REPLICASET:-skynet} --setParameter ShardingTaskExecutorPoolMinSize=10
container_name: mongo
restart: unless-stopped
logging: *default-logging
diff --git a/docker-compose.yml b/docker-compose.yml
index 5838f136..6450a10f 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -54,9 +54,11 @@ services:
- ./docker/data/certbot:/etc/letsencrypt
nginx:
- build:
- context: ./docker/nginx
- dockerfile: Dockerfile
+ # uncomment "build" and comment out "image" to build from sources
+ # build:
+ # context: https://github.com/SkynetLabs/skynet-webportal.git#master
+ # dockerfile: ./docker/nginx/Dockerfile
+ image: skynetlabs/nginx
container_name: nginx
restart: unless-stopped
logging: *default-logging
@@ -69,6 +71,10 @@ services:
- ./docker/data/nginx/skynet:/data/nginx/skynet:ro
- ./docker/data/sia/apipassword:/data/sia/apipassword:ro
- ./docker/data/certbot:/etc/letsencrypt
+ - ./docker/nginx/libs:/etc/nginx/libs
+ - ./docker/nginx/conf.d:/etc/nginx/conf.d
+ - ./docker/nginx/conf.d.templates:/etc/nginx/templates
+ - ./docker/nginx/nginx.conf:/usr/local/openresty/nginx/conf/nginx.conf
networks:
shared:
ipv4_address: 10.10.10.30
diff --git a/docker/nginx/Dockerfile b/docker/nginx/Dockerfile
index eca1e3d8..f35c0aff 100644
--- a/docker/nginx/Dockerfile
+++ b/docker/nginx/Dockerfile
@@ -2,25 +2,20 @@ FROM openresty/openresty:1.19.9.1-focal
WORKDIR /
-RUN luarocks install lua-resty-http && \
- luarocks install hasher && \
- openssl req -new -newkey rsa:2048 -days 3650 -nodes -x509 \
- -subj '/CN=local-certificate' \
- -keyout /etc/ssl/local-certificate.key \
- -out /etc/ssl/local-certificate.crt
+RUN apt-get update && apt-get --no-install-recommends -y install bc=1.07.1-2build1 && \
+ apt-get clean && rm -rf /var/lib/apt/lists/* && \
+ luarocks install lua-resty-http && \
+ luarocks install hasher
-COPY mo ./
-COPY libs /etc/nginx/libs
-COPY conf.d /etc/nginx/conf.d
-COPY conf.d.templates /etc/nginx/conf.d.templates
-COPY nginx.conf /usr/local/openresty/nginx/conf/nginx.conf
+# reload nginx every 6 hours (for reloading certificates)
+ENV NGINX_ENTRYPOINT_RELOAD_EVERY_X_HOURS 6
-CMD [ "bash", "-c", \
- "./mo < /etc/nginx/conf.d.templates/server.account.conf > /etc/nginx/conf.d/server.account.conf ; \
- ./mo < /etc/nginx/conf.d.templates/server.api.conf > /etc/nginx/conf.d/server.api.conf; \
- ./mo < /etc/nginx/conf.d.templates/server.dnslink.conf > /etc/nginx/conf.d/server.dnslink.conf; \
- ./mo < /etc/nginx/conf.d.templates/server.hns.conf > /etc/nginx/conf.d/server.hns.conf; \
- ./mo < /etc/nginx/conf.d.templates/server.skylink.conf > /etc/nginx/conf.d/server.skylink.conf ; \
- while :; do sleep 6h & wait ${!}; /usr/local/openresty/bin/openresty -s reload; done & \
- /usr/local/openresty/bin/openresty '-g daemon off;'" \
- ]
+# copy entrypoint and entrypoint scripts
+COPY docker/nginx/docker-entrypoint.sh /
+COPY docker/nginx/docker-entrypoint.d /docker-entrypoint.d
+
+ENTRYPOINT ["/docker-entrypoint.sh"]
+
+STOPSIGNAL SIGQUIT
+
+CMD ["nginx", "-g", "daemon off;"]
diff --git a/docker/nginx/conf.d.templates/server.account.conf b/docker/nginx/conf.d.templates/server.account.conf
deleted file mode 100644
index af3b7c4d..00000000
--- a/docker/nginx/conf.d.templates/server.account.conf
+++ /dev/null
@@ -1,45 +0,0 @@
-{{#ACCOUNTS_ENABLED}}
- {{#PORTAL_DOMAIN}}
- server {
- server_name account.{{PORTAL_DOMAIN}}; # example: account.siasky.net
-
- include /etc/nginx/conf.d/server/server.http;
- }
-
- server {
- server_name account.{{PORTAL_DOMAIN}}; # example: account.siasky.net
-
- set_by_lua_block $skynet_portal_domain { return "{{PORTAL_DOMAIN}}" }
- set_by_lua_block $skynet_server_domain {
- -- fall back to portal domain if server domain is not defined
- if "{{SERVER_DOMAIN}}" == "" then
- return "{{PORTAL_DOMAIN}}"
- end
- return "{{SERVER_DOMAIN}}"
- }
-
- include /etc/nginx/conf.d/server/server.account;
- }
- {{/PORTAL_DOMAIN}}
-
- {{#SERVER_DOMAIN}}
- server {
- server_name account.{{SERVER_DOMAIN}}; # example: account.eu-ger-1.siasky.net
-
- include /etc/nginx/conf.d/server/server.http;
-
- set_by_lua_block $server_alias { return string.match("{{SERVER_DOMAIN}}", "^([^.]+)") }
- }
-
- server {
- server_name account.{{SERVER_DOMAIN}}; # example: account.eu-ger-1.siasky.net
-
- set_by_lua_block $skynet_portal_domain { return "{{SERVER_DOMAIN}}" }
- set_by_lua_block $skynet_server_domain { return "{{SERVER_DOMAIN}}" }
-
- include /etc/nginx/conf.d/server/server.account;
-
- set_by_lua_block $server_alias { return string.match("{{SERVER_DOMAIN}}", "^([^.]+)") }
- }
- {{/SERVER_DOMAIN}}
-{{/ACCOUNTS_ENABLED}}
diff --git a/docker/nginx/conf.d.templates/server.account.conf.template b/docker/nginx/conf.d.templates/server.account.conf.template
new file mode 100644
index 00000000..8507c407
--- /dev/null
+++ b/docker/nginx/conf.d.templates/server.account.conf.template
@@ -0,0 +1,44 @@
+server {
+ server_name account.${PORTAL_DOMAIN}; # example: account.siasky.net
+
+ include /etc/nginx/conf.d/server/server.http;
+}
+
+server {
+ server_name account.${PORTAL_DOMAIN}; # example: account.siasky.net
+
+ set_by_lua_block $skynet_portal_domain { return "${PORTAL_DOMAIN}" }
+ set_by_lua_block $skynet_server_domain {
+ -- fall back to portal domain if server domain is not defined
+ if "${SERVER_DOMAIN}" == "" then
+ return "${PORTAL_DOMAIN}"
+ end
+ return "${SERVER_DOMAIN}"
+ }
+
+ include /etc/nginx/conf.d/server/server.account;
+}
+
+server {
+ server_name account.${SERVER_DOMAIN}; # example: account.eu-ger-1.siasky.net
+
+ include /etc/nginx/conf.d/server/server.http;
+
+ set_by_lua_block $server_alias { return string.match("${SERVER_DOMAIN}", "^([^.]+)") }
+}
+
+server {
+ server_name account.${SERVER_DOMAIN}; # example: account.eu-ger-1.siasky.net
+
+ set_by_lua_block $skynet_portal_domain {
+ -- when accessing portal directly through server domain, portal domain should be set to server domain
+ -- motivation: skynet-js uses Skynet-Portal-Api header (that is set to $skynet_portal_domain) to detect current
+ -- portal address and it should be server domain when accessing specific server by its domain address
+ return "${SERVER_DOMAIN}"
+ }
+ set_by_lua_block $skynet_server_domain { return "${SERVER_DOMAIN}" }
+
+ include /etc/nginx/conf.d/server/server.account;
+
+ set_by_lua_block $server_alias { return string.match("${SERVER_DOMAIN}", "^([^.]+)") }
+}
diff --git a/docker/nginx/conf.d.templates/server.api.conf b/docker/nginx/conf.d.templates/server.api.conf
deleted file mode 100644
index 591212ba..00000000
--- a/docker/nginx/conf.d.templates/server.api.conf
+++ /dev/null
@@ -1,43 +0,0 @@
-{{#PORTAL_DOMAIN}}
-server {
- server_name {{PORTAL_DOMAIN}}; # example: siasky.net
-
- include /etc/nginx/conf.d/server/server.http;
-}
-
-server {
- server_name {{PORTAL_DOMAIN}}; # example: siasky.net
-
- set_by_lua_block $skynet_portal_domain { return "{{PORTAL_DOMAIN}}" }
- set_by_lua_block $skynet_server_domain {
- -- fall back to portal domain if server domain is not defined
- if "{{SERVER_DOMAIN}}" == "" then
- return "{{PORTAL_DOMAIN}}"
- end
- return "{{SERVER_DOMAIN}}"
- }
-
- include /etc/nginx/conf.d/server/server.api;
-}
-{{/PORTAL_DOMAIN}}
-
-{{#SERVER_DOMAIN}}
-server {
- server_name {{SERVER_DOMAIN}}; # example: eu-ger-1.siasky.net
-
- include /etc/nginx/conf.d/server/server.http;
-
- set_by_lua_block $server_alias { return string.match("{{SERVER_DOMAIN}}", "^([^.]+)") }
-}
-
-server {
- server_name {{SERVER_DOMAIN}}; # example: eu-ger-1.siasky.net
-
- set_by_lua_block $skynet_portal_domain { return "{{SERVER_DOMAIN}}" }
- set_by_lua_block $skynet_server_domain { return "{{SERVER_DOMAIN}}" }
-
- include /etc/nginx/conf.d/server/server.api;
-
- set_by_lua_block $server_alias { return string.match("{{SERVER_DOMAIN}}", "^([^.]+)") }
-}
-{{/SERVER_DOMAIN}}
diff --git a/docker/nginx/conf.d.templates/server.api.conf.template b/docker/nginx/conf.d.templates/server.api.conf.template
new file mode 100644
index 00000000..5f742127
--- /dev/null
+++ b/docker/nginx/conf.d.templates/server.api.conf.template
@@ -0,0 +1,44 @@
+server {
+ server_name ${PORTAL_DOMAIN}; # example: siasky.net
+
+ include /etc/nginx/conf.d/server/server.http;
+}
+
+server {
+ server_name ${PORTAL_DOMAIN}; # example: siasky.net
+
+ set_by_lua_block $skynet_portal_domain { return "${PORTAL_DOMAIN}" }
+ set_by_lua_block $skynet_server_domain {
+ -- fall back to portal domain if server domain is not defined
+ if "${SERVER_DOMAIN}" == "" then
+ return "${PORTAL_DOMAIN}"
+ end
+ return "${SERVER_DOMAIN}"
+ }
+
+ include /etc/nginx/conf.d/server/server.api;
+}
+
+server {
+ server_name ${SERVER_DOMAIN}; # example: eu-ger-1.siasky.net
+
+ include /etc/nginx/conf.d/server/server.http;
+
+ set_by_lua_block $server_alias { return string.match("${SERVER_DOMAIN}", "^([^.]+)") }
+}
+
+server {
+ server_name ${SERVER_DOMAIN}; # example: eu-ger-1.siasky.net
+
+ set_by_lua_block $skynet_portal_domain {
+ -- when accessing portal directly through server domain, portal domain should be set to server domain
+ -- motivation: skynet-js uses Skynet-Portal-Api header (that is set to $skynet_portal_domain) to detect current
+ -- portal address and it should be server domain when accessing specific server by its domain address
+ return "${SERVER_DOMAIN}"
+ }
+ set_by_lua_block $skynet_server_domain { return "${SERVER_DOMAIN}" }
+
+ include /etc/nginx/conf.d/server/server.api;
+
+ set_by_lua_block $server_alias { return string.match("${SERVER_DOMAIN}", "^([^.]+)") }
+}
diff --git a/docker/nginx/conf.d.templates/server.dnslink.conf b/docker/nginx/conf.d.templates/server.dnslink.conf.template
similarity index 71%
rename from docker/nginx/conf.d.templates/server.dnslink.conf
rename to docker/nginx/conf.d.templates/server.dnslink.conf.template
index d42ee245..95c623b6 100644
--- a/docker/nginx/conf.d.templates/server.dnslink.conf
+++ b/docker/nginx/conf.d.templates/server.dnslink.conf.template
@@ -12,13 +12,13 @@ server {
ssl_certificate /etc/ssl/local-certificate.crt;
ssl_certificate_key /etc/ssl/local-certificate.key;
- set_by_lua_block $skynet_portal_domain { return "{{PORTAL_DOMAIN}}" }
+ set_by_lua_block $skynet_portal_domain { return "${PORTAL_DOMAIN}" }
set_by_lua_block $skynet_server_domain {
-- fall back to portal domain if server domain is not defined
- if "{{SERVER_DOMAIN}}" == "" then
- return "{{PORTAL_DOMAIN}}"
+ if "${SERVER_DOMAIN}" == "" then
+ return "${PORTAL_DOMAIN}"
end
- return "{{SERVER_DOMAIN}}"
+ return "${SERVER_DOMAIN}"
}
include /etc/nginx/conf.d/server/server.dnslink;
diff --git a/docker/nginx/conf.d.templates/server.hns.conf b/docker/nginx/conf.d.templates/server.hns.conf
deleted file mode 100644
index 0e4f21f3..00000000
--- a/docker/nginx/conf.d.templates/server.hns.conf
+++ /dev/null
@@ -1,45 +0,0 @@
-{{#PORTAL_DOMAIN}}
-server {
- server_name *.hns.{{PORTAL_DOMAIN}}; # example: *.hns.siasky.net
-
- include /etc/nginx/conf.d/server/server.http;
-}
-
-server {
- server_name *.hns.{{PORTAL_DOMAIN}}; # example: *.hns.siasky.net
-
- set_by_lua_block $skynet_portal_domain { return "{{PORTAL_DOMAIN}}" }
- set_by_lua_block $skynet_server_domain {
- -- fall back to portal domain if server domain is not defined
- if "{{SERVER_DOMAIN}}" == "" then
- return "{{PORTAL_DOMAIN}}"
- end
- return "{{SERVER_DOMAIN}}"
- }
-
- proxy_set_header Host {{PORTAL_DOMAIN}};
- include /etc/nginx/conf.d/server/server.hns;
-}
-{{/PORTAL_DOMAIN}}
-
-{{#SERVER_DOMAIN}}
-server {
- server_name *.hns.{{SERVER_DOMAIN}}; # example: *.hns.eu-ger-1.siasky.net
-
- include /etc/nginx/conf.d/server/server.http;
-
- set_by_lua_block $server_alias { return string.match("{{SERVER_DOMAIN}}", "^([^.]+)") }
-}
-
-server {
- server_name *.hns.{{SERVER_DOMAIN}}; # example: *.hns.eu-ger-1.siasky.net
-
- set_by_lua_block $skynet_portal_domain { return "{{SERVER_DOMAIN}}" }
- set_by_lua_block $skynet_server_domain { return "{{SERVER_DOMAIN}}" }
-
- proxy_set_header Host {{SERVER_DOMAIN}};
- include /etc/nginx/conf.d/server/server.hns;
-
- set_by_lua_block $server_alias { return string.match("{{SERVER_DOMAIN}}", "^([^.]+)") }
-}
-{{/SERVER_DOMAIN}}
diff --git a/docker/nginx/conf.d.templates/server.hns.conf.template b/docker/nginx/conf.d.templates/server.hns.conf.template
new file mode 100644
index 00000000..f7bb43fb
--- /dev/null
+++ b/docker/nginx/conf.d.templates/server.hns.conf.template
@@ -0,0 +1,46 @@
+server {
+ server_name *.hns.${PORTAL_DOMAIN}; # example: *.hns.siasky.net
+
+ include /etc/nginx/conf.d/server/server.http;
+}
+
+server {
+ server_name *.hns.${PORTAL_DOMAIN}; # example: *.hns.siasky.net
+
+ set_by_lua_block $skynet_portal_domain { return "${PORTAL_DOMAIN}" }
+ set_by_lua_block $skynet_server_domain {
+ -- fall back to portal domain if server domain is not defined
+ if "${SERVER_DOMAIN}" == "" then
+ return "${PORTAL_DOMAIN}"
+ end
+ return "${SERVER_DOMAIN}"
+ }
+
+ proxy_set_header Host ${PORTAL_DOMAIN};
+ include /etc/nginx/conf.d/server/server.hns;
+}
+
+server {
+ server_name *.hns.${SERVER_DOMAIN}; # example: *.hns.eu-ger-1.siasky.net
+
+ include /etc/nginx/conf.d/server/server.http;
+
+ set_by_lua_block $server_alias { return string.match("${SERVER_DOMAIN}", "^([^.]+)") }
+}
+
+server {
+ server_name *.hns.${SERVER_DOMAIN}; # example: *.hns.eu-ger-1.siasky.net
+
+ set_by_lua_block $skynet_portal_domain {
+ -- when accessing portal directly through server domain, portal domain should be set to server domain
+ -- motivation: skynet-js uses Skynet-Portal-Api header (that is set to $skynet_portal_domain) to detect current
+ -- portal address and it should be server domain when accessing specific server by its domain address
+ return "${SERVER_DOMAIN}"
+ }
+ set_by_lua_block $skynet_server_domain { return "${SERVER_DOMAIN}" }
+
+ proxy_set_header Host ${SERVER_DOMAIN};
+ include /etc/nginx/conf.d/server/server.hns;
+
+ set_by_lua_block $server_alias { return string.match("${SERVER_DOMAIN}", "^([^.]+)") }
+}
diff --git a/docker/nginx/conf.d.templates/server.skylink.conf b/docker/nginx/conf.d.templates/server.skylink.conf
deleted file mode 100644
index a97e240c..00000000
--- a/docker/nginx/conf.d.templates/server.skylink.conf
+++ /dev/null
@@ -1,43 +0,0 @@
-{{#PORTAL_DOMAIN}}
-server {
- server_name *.{{PORTAL_DOMAIN}}; # example: *.siasky.net
-
- include /etc/nginx/conf.d/server/server.http;
-}
-
-server {
- server_name *.{{PORTAL_DOMAIN}}; # example: *.siasky.net
-
- set_by_lua_block $skynet_portal_domain { return "{{PORTAL_DOMAIN}}" }
- set_by_lua_block $skynet_server_domain {
- -- fall back to portal domain if server domain is not defined
- if "{{SERVER_DOMAIN}}" == "" then
- return "{{PORTAL_DOMAIN}}"
- end
- return "{{SERVER_DOMAIN}}"
- }
-
- include /etc/nginx/conf.d/server/server.skylink;
-}
-{{/PORTAL_DOMAIN}}
-
-{{#SERVER_DOMAIN}}
-server {
- server_name *.{{SERVER_DOMAIN}}; # example: *.eu-ger-1.siasky.net
-
- include /etc/nginx/conf.d/server/server.http;
-
- set_by_lua_block $server_alias { return string.match("{{SERVER_DOMAIN}}", "^([^.]+)") }
-}
-
-server {
- server_name *.{{SERVER_DOMAIN}}; # example: *.eu-ger-1.siasky.net
-
- set_by_lua_block $skynet_portal_domain { return "{{SERVER_DOMAIN}}" }
- set_by_lua_block $skynet_server_domain { return "{{SERVER_DOMAIN}}" }
-
- include /etc/nginx/conf.d/server/server.skylink;
-
- set_by_lua_block $server_alias { return string.match("{{SERVER_DOMAIN}}", "^([^.]+)") }
-}
-{{/SERVER_DOMAIN}}
diff --git a/docker/nginx/conf.d.templates/server.skylink.conf.template b/docker/nginx/conf.d.templates/server.skylink.conf.template
new file mode 100644
index 00000000..0d337abb
--- /dev/null
+++ b/docker/nginx/conf.d.templates/server.skylink.conf.template
@@ -0,0 +1,44 @@
+server {
+ server_name *.${PORTAL_DOMAIN}; # example: *.siasky.net
+
+ include /etc/nginx/conf.d/server/server.http;
+}
+
+server {
+ server_name *.${PORTAL_DOMAIN}; # example: *.siasky.net
+
+ set_by_lua_block $skynet_portal_domain { return "${PORTAL_DOMAIN}" }
+ set_by_lua_block $skynet_server_domain {
+ -- fall back to portal domain if server domain is not defined
+ if "${SERVER_DOMAIN}" == "" then
+ return "${PORTAL_DOMAIN}"
+ end
+ return "${SERVER_DOMAIN}"
+ }
+
+ include /etc/nginx/conf.d/server/server.skylink;
+}
+
+server {
+ server_name *.${SERVER_DOMAIN}; # example: *.eu-ger-1.siasky.net
+
+ include /etc/nginx/conf.d/server/server.http;
+
+ set_by_lua_block $server_alias { return string.match("${SERVER_DOMAIN}", "^([^.]+)") }
+}
+
+server {
+ server_name *.${SERVER_DOMAIN}; # example: *.eu-ger-1.siasky.net
+
+ set_by_lua_block $skynet_portal_domain {
+ -- when accessing portal directly through server domain, portal domain should be set to server domain
+ -- motivation: skynet-js uses Skynet-Portal-Api header (that is set to $skynet_portal_domain) to detect current
+ -- portal address and it should be server domain when accessing specific server by its domain address
+ return "${SERVER_DOMAIN}"
+ }
+ set_by_lua_block $skynet_server_domain { return "${SERVER_DOMAIN}" }
+
+ include /etc/nginx/conf.d/server/server.skylink;
+
+ set_by_lua_block $server_alias { return string.match("${SERVER_DOMAIN}", "^([^.]+)") }
+}
diff --git a/docker/nginx/conf.d/server/server.account b/docker/nginx/conf.d/server/server.account
index 127ba4bf..9d444296 100644
--- a/docker/nginx/conf.d/server/server.account
+++ b/docker/nginx/conf.d/server/server.account
@@ -3,6 +3,11 @@ listen 443 ssl http2;
include /etc/nginx/conf.d/include/ssl-settings;
include /etc/nginx/conf.d/include/init-optional-variables;
+# Uncomment to launch new Dashboard under /v2 path
+# location /v2 {
+# proxy_pass http://dashboard-v2:9000;
+# }
+
location / {
proxy_pass http://dashboard:3000;
}
diff --git a/docker/nginx/conf.d/server/server.api b/docker/nginx/conf.d/server/server.api
index 0243ab90..48e7a638 100644
--- a/docker/nginx/conf.d/server/server.api
+++ b/docker/nginx/conf.d/server/server.api
@@ -235,7 +235,12 @@ location /skynet/skyfile {
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())
+ skynet_tracker.track_upload(
+ ngx.header["Skynet-Skylink"],
+ ngx.status,
+ skynet_account.get_auth_headers(),
+ ngx.var.remote_addr
+ )
end
if skynet_modules.is_enabled("s") then
@@ -315,7 +320,12 @@ location /skynet/tus {
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())
+ skynet_tracker.track_upload(
+ ngx.header["Skynet-Skylink"],
+ ngx.status,
+ skynet_account.get_auth_headers(),
+ ngx.var.remote_addr
+ )
end
if skynet_modules.is_enabled("s") then
@@ -346,7 +356,12 @@ location /skynet/pin {
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())
+ skynet_tracker.track_upload(
+ ngx.header["Skynet-Skylink"],
+ ngx.status,
+ skynet_account.get_auth_headers(),
+ ngx.var.remote_addr
+ )
end
if skynet_modules.is_enabled("s") then
diff --git a/docker/nginx/docker-entrypoint.d/20-envsubst-on-templates.sh b/docker/nginx/docker-entrypoint.d/20-envsubst-on-templates.sh
new file mode 100755
index 00000000..be9c5f8e
--- /dev/null
+++ b/docker/nginx/docker-entrypoint.d/20-envsubst-on-templates.sh
@@ -0,0 +1,59 @@
+#!/bin/sh
+
+# https://github.com/nginxinc/docker-nginx/blob/master/entrypoint/20-envsubst-on-templates.sh
+# https://github.com/nginxinc/docker-nginx/blob/master/LICENSE
+
+# Copyright (C) 2011-2016 Nginx, Inc.
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+set -e
+
+ME=$(basename $0)
+
+auto_envsubst() {
+ local template_dir="${NGINX_ENVSUBST_TEMPLATE_DIR:-/etc/nginx/templates}"
+ local suffix="${NGINX_ENVSUBST_TEMPLATE_SUFFIX:-.template}"
+ local output_dir="${NGINX_ENVSUBST_OUTPUT_DIR:-/etc/nginx/conf.d}"
+
+ local template defined_envs relative_path output_path subdir
+ defined_envs=$(printf '${%s} ' $(env | cut -d= -f1))
+ [ -d "$template_dir" ] || return 0
+ if [ ! -w "$output_dir" ]; then
+ echo >&3 "$ME: ERROR: $template_dir exists, but $output_dir is not writable"
+ return 0
+ fi
+ find "$template_dir" -follow -type f -name "*$suffix" -print | while read -r template; do
+ relative_path="${template#$template_dir/}"
+ output_path="$output_dir/${relative_path%$suffix}"
+ subdir=$(dirname "$relative_path")
+ # create a subdirectory where the template file exists
+ mkdir -p "$output_dir/$subdir"
+ echo >&3 "$ME: Running envsubst on $template to $output_path"
+ envsubst "$defined_envs" < "$template" > "$output_path"
+ done
+}
+
+auto_envsubst
+
+exit 0
diff --git a/docker/nginx/docker-entrypoint.d/40-reload-every-x-hours.sh b/docker/nginx/docker-entrypoint.d/40-reload-every-x-hours.sh
new file mode 100755
index 00000000..cdd7d17e
--- /dev/null
+++ b/docker/nginx/docker-entrypoint.d/40-reload-every-x-hours.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+# source: https://github.com/nginxinc/docker-nginx/pull/509
+
+set -e
+
+ME=$(basename $0)
+
+[ "${NGINX_ENTRYPOINT_RELOAD_EVERY_X_HOURS:-}" ] || exit 0
+if [ $(echo "$NGINX_ENTRYPOINT_RELOAD_EVERY_X_HOURS > 0" | bc) = 0 ]; then
+ echo >&3 "$ME: Error. Provide integer or floating point number greater that 0. See 'man sleep'."
+ exit 1
+fi
+
+start_background_reload() {
+ echo >&3 "$ME: Reloading Nginx every $NGINX_ENTRYPOINT_RELOAD_EVERY_X_HOURS hour(s)"
+ while :; do sleep ${NGINX_ENTRYPOINT_RELOAD_EVERY_X_HOURS}h; echo >&3 "$ME: Reloading Nginx ..." && nginx -s reload; done &
+}
+
+start_background_reload
diff --git a/docker/nginx/docker-entrypoint.d/50-generate-local-certificate.sh b/docker/nginx/docker-entrypoint.d/50-generate-local-certificate.sh
new file mode 100755
index 00000000..f5ecada1
--- /dev/null
+++ b/docker/nginx/docker-entrypoint.d/50-generate-local-certificate.sh
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+# Generate locally signed ssl certificate to be used on routes
+# that do not require certificate issued by trusted CA
+
+set -e
+
+ME=$(basename $0)
+
+generate_local_certificate() {
+ echo >&3 "$ME: Generating locally signed ssl certificate"
+ openssl req -new -newkey rsa:2048 -days 3650 -nodes -x509 \
+ -subj '/CN=local-certificate' \
+ -keyout /etc/ssl/local-certificate.key \
+ -out /etc/ssl/local-certificate.crt
+}
+
+generate_local_certificate
diff --git a/docker/nginx/docker-entrypoint.sh b/docker/nginx/docker-entrypoint.sh
new file mode 100755
index 00000000..d45a6d9a
--- /dev/null
+++ b/docker/nginx/docker-entrypoint.sh
@@ -0,0 +1,65 @@
+#!/bin/sh
+# vim:sw=4:ts=4:et
+
+# https://github.com/nginxinc/docker-nginx/blob/master/entrypoint/docker-entrypoint.sh
+# https://github.com/nginxinc/docker-nginx/blob/master/LICENSE
+
+# Copyright (C) 2011-2016 Nginx, Inc.
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+set -e
+
+if [ -z "${NGINX_ENTRYPOINT_QUIET_LOGS:-}" ]; then
+ exec 3>&1
+else
+ exec 3>/dev/null
+fi
+
+if [ "$1" = "nginx" -o "$1" = "nginx-debug" ]; then
+ if /usr/bin/find "/docker-entrypoint.d/" -mindepth 1 -maxdepth 1 -type f -print -quit 2>/dev/null | read v; then
+ echo >&3 "$0: /docker-entrypoint.d/ is not empty, will attempt to perform configuration"
+
+ echo >&3 "$0: Looking for shell scripts in /docker-entrypoint.d/"
+ find "/docker-entrypoint.d/" -follow -type f -print | sort -V | while read -r f; do
+ case "$f" in
+ *.sh)
+ if [ -x "$f" ]; then
+ echo >&3 "$0: Launching $f";
+ "$f"
+ else
+ # warn on shell scripts without exec bit
+ echo >&3 "$0: Ignoring $f, not executable";
+ fi
+ ;;
+ *) echo >&3 "$0: Ignoring $f";;
+ esac
+ done
+
+ echo >&3 "$0: Configuration complete; ready for start up"
+ else
+ echo >&3 "$0: No files found in /docker-entrypoint.d/, skipping configuration"
+ fi
+fi
+
+exec "$@"
diff --git a/docker/nginx/libs/skynet/account.lua b/docker/nginx/libs/skynet/account.lua
index 56c99897..709d8130 100644
--- a/docker/nginx/libs/skynet/account.lua
+++ b/docker/nginx/libs/skynet/account.lua
@@ -76,9 +76,16 @@ function _M.get_account_limits()
if ngx.var.account_limits == "" then
local httpc = require("resty.http").new()
+ local uri = "http://10.10.10.70:3000/user/limits"
+
+ -- include skylink if it is available in the context of request
+ -- todo: this should not rely on skylink variable to be defined
+ if ngx.var.skylink ~= nil and ngx.var.skylink ~= "" then
+ uri = uri .. "/" .. ngx.var.skylink
+ end
-- 10.10.10.70 points to accounts service (alias not available when using resty-http)
- local res, err = httpc:request_uri("http://10.10.10.70:3000/user/limits?unit=byte", {
+ local res, err = httpc:request_uri(uri .. "?unit=byte", {
headers = auth_headers,
})
diff --git a/docker/nginx/libs/skynet/tracker.lua b/docker/nginx/libs/skynet/tracker.lua
index 78fa6995..37413215 100644
--- a/docker/nginx/libs/skynet/tracker.lua
+++ b/docker/nginx/libs/skynet/tracker.lua
@@ -30,15 +30,24 @@ function _M.track_download(skylink, status_code, auth_headers, body_bytes_sent)
end
end
-function _M.track_upload_timer(premature, skylink, auth_headers)
+function _M.track_upload_timer(premature, skylink, auth_headers, uploader_ip)
if premature then return end
local httpc = require("resty.http").new()
+ -- set correct content type header and include auth headers
+ local headers = {
+ ["Content-Type"] = "application/x-www-form-urlencoded",
+ }
+ for key, value in pairs(auth_headers) do
+ headers[key] = value
+ 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/track/upload/" .. skylink, {
method = "POST",
- headers = auth_headers,
+ headers = headers,
+ body = "ip=" .. uploader_ip,
})
if err or (res and res.status ~= 204) then
@@ -47,12 +56,11 @@ function _M.track_upload_timer(premature, skylink, auth_headers)
end
end
-function _M.track_upload(skylink, status_code, auth_headers)
- local has_auth_headers = not utils.is_table_empty(auth_headers)
+function _M.track_upload(skylink, status_code, auth_headers, uploader_ip)
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_upload_timer, skylink, auth_headers)
+ if skylink and status_success then
+ local ok, err = ngx.timer.at(0, _M.track_upload_timer, skylink, auth_headers, uploader_ip)
if not ok then ngx.log(ngx.ERR, "Failed to create timer: ", err) end
end
end
diff --git a/docker/nginx/libs/skynet/tracker.spec.lua b/docker/nginx/libs/skynet/tracker.spec.lua
index 41dab843..98d587d8 100644
--- a/docker/nginx/libs/skynet/tracker.spec.lua
+++ b/docker/nginx/libs/skynet/tracker.spec.lua
@@ -5,6 +5,7 @@ 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" }
+local valid_ip = "12.34.56.78"
describe("track_download", function()
local valid_body_bytes_sent = 12345
@@ -200,20 +201,21 @@ describe("track_upload", function()
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)
+ skynet_tracker.track_upload(valid_skylink, valid_status_code, valid_auth_headers, valid_ip)
assert.stub(ngx.timer.at).was_called_with(
0,
skynet_tracker.track_upload_timer,
valid_skylink,
- valid_auth_headers
+ valid_auth_headers,
+ valid_ip
)
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)
+ skynet_tracker.track_upload(nil, valid_status_code, valid_auth_headers, valid_ip)
assert.stub(ngx.timer.at).was_not_called()
end)
@@ -222,34 +224,41 @@ describe("track_upload", 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)
+ skynet_tracker.track_upload(valid_skylink, 401, valid_auth_headers, valid_ip)
+ skynet_tracker.track_upload(valid_skylink, 403, valid_auth_headers, valid_ip)
+ skynet_tracker.track_upload(valid_skylink, 490, valid_auth_headers, valid_ip)
+ skynet_tracker.track_upload(valid_skylink, 500, valid_auth_headers, valid_ip)
+ skynet_tracker.track_upload(valid_skylink, 502, valid_auth_headers, valid_ip)
assert.stub(ngx.timer.at).was_not_called()
end)
- it("should not schedule a timer if auth headers are empty", function()
+ 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, {})
+ skynet_tracker.track_upload(valid_skylink, valid_status_code, {}, valid_ip)
- assert.stub(ngx.timer.at).was_not_called()
+ assert.stub(ngx.timer.at).was_called_with(
+ 0,
+ skynet_tracker.track_upload_timer,
+ valid_skylink,
+ {},
+ valid_ip
+ )
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)
+ skynet_tracker.track_upload(valid_skylink, valid_status_code, valid_auth_headers, valid_ip)
assert.stub(ngx.timer.at).was_called_with(
0,
skynet_tracker.track_upload_timer,
valid_skylink,
- valid_auth_headers
+ valid_auth_headers,
+ valid_ip
)
assert.stub(ngx.log).was_called_with(ngx.ERR, "Failed to create timer: ", "such a failure")
@@ -279,7 +288,8 @@ describe("track_upload", function()
skynet_tracker.track_upload_timer(
true,
valid_skylink,
- valid_auth_headers
+ valid_auth_headers,
+ valid_ip
)
assert.stub(request_uri).was_not_called()
@@ -297,11 +307,19 @@ describe("track_upload", function()
skynet_tracker.track_upload_timer(
false,
valid_skylink,
- valid_auth_headers
+ valid_auth_headers,
+ valid_ip
)
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(request_uri).was_called_with(httpc, uri, {
+ method = "POST",
+ headers = {
+ ["Content-Type"] = "application/x-www-form-urlencoded",
+ ["Skynet-Api-Key"] = "foo",
+ },
+ body = "ip=" .. valid_ip
+ })
assert.stub(ngx.log).was_not_called()
end)
@@ -316,11 +334,19 @@ describe("track_upload", function()
skynet_tracker.track_upload_timer(
false,
valid_skylink,
- valid_auth_headers
+ valid_auth_headers,
+ valid_ip
)
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(request_uri).was_called_with(httpc, uri, {
+ method = "POST",
+ headers = {
+ ["Content-Type"] = "application/x-www-form-urlencoded",
+ ["Skynet-Api-Key"] = "foo",
+ },
+ body = "ip=" .. valid_ip
+ })
assert.stub(ngx.log).was_called_with(
ngx.ERR,
"Failed accounts service request /track/upload/" .. valid_skylink .. ": ",
@@ -339,11 +365,19 @@ describe("track_upload", function()
skynet_tracker.track_upload_timer(
false,
valid_skylink,
- valid_auth_headers
+ valid_auth_headers,
+ valid_ip
)
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(request_uri).was_called_with(httpc, uri, {
+ method = "POST",
+ headers = {
+ ["Content-Type"] = "application/x-www-form-urlencoded",
+ ["Skynet-Api-Key"] = "foo",
+ },
+ body = "ip=" .. valid_ip
+ })
assert.stub(ngx.log).was_called_with(
ngx.ERR,
"Failed accounts service request /track/upload/" .. valid_skylink .. ": ",
diff --git a/docker/nginx/mo b/docker/nginx/mo
deleted file mode 100755
index ba8e48d1..00000000
--- a/docker/nginx/mo
+++ /dev/null
@@ -1,1106 +0,0 @@
-#!/usr/bin/env bash
-#
-#/ Mo is a mustache template rendering software written in bash. It inserts
-#/ environment variables into templates.
-#/
-#/ Simply put, mo will change {{VARIABLE}} into the value of that
-#/ environment variable. You can use {{#VARIABLE}}content{{/VARIABLE}} to
-#/ conditionally display content or iterate over the values of an array.
-#/
-#/ Learn more about mustache templates at https://mustache.github.io/
-#/
-#/ Simple usage:
-#/
-#/ mo [OPTIONS] filenames...
-#/
-#/ Options:
-#/
-#/ -u, --fail-not-set
-#/ Fail upon expansion of an unset variable.
-#/ -x, --fail-on-function
-#/ Fail when a function returns a non-zero status code.
-#/ -e, --false
-#/ Treat the string "false" as empty for conditionals.
-#/ -h, --help
-#/ This message.
-#/ -s=FILE, --source=FILE
-#/ Load FILE into the environment before processing templates.
-#/ Can be used multiple times.
-#
-# Mo is under a MIT style licence with an additional non-advertising clause.
-# See LICENSE.md for the full text.
-#
-# This is open source! Please feel free to contribute.
-#
-# https://github.com/tests-always-included/mo
-
-
-# Public: Template parser function. Writes templates to stdout.
-#
-# $0 - Name of the mo file, used for getting the help message.
-# $@ - Filenames to parse.
-#
-# Options:
-#
-# --allow-function-arguments
-#
-# Permit functions in templates to be called with additional arguments. This
-# puts template data directly in to the path of an eval statement. Use with
-# caution. Not listed in the help because it only makes sense when mo is
-# sourced.
-#
-# -u, --fail-not-set
-#
-# Fail upon expansion of an unset variable. Default behavior is to silently
-# ignore and expand into empty string.
-#
-# -x, --fail-on-function
-#
-# Fail when a function used by a template returns an error status code.
-# Alternately, ou may set the MO_FAIL_ON_FUNCTION environment variable to a
-# non-empty value to enable this behavior.
-#
-# -e, --false
-#
-# Treat "false" as an empty value. You may set the MO_FALSE_IS_EMPTY
-# environment variable instead to a non-empty value to enable this behavior.
-#
-# -h, --help
-#
-# Display a help message.
-#
-# -s=FILE, --source=FILE
-#
-# Source a file into the environment before processing template files.
-# This can be used multiple times.
-#
-# --
-#
-# Used to indicate the end of options. You may optionally use this when
-# filenames may start with two hyphens.
-#
-# Mo uses the following environment variables:
-#
-# MO_ALLOW_FUNCTION_ARGUMENTS - When set to a non-empty value, this allows
-# functions referenced in templates to receive additional
-# options and arguments. This puts the content from the
-# template directly into an eval statement. Use with extreme
-# care.
-# MO_FUNCTION_ARGS - Arguments passed to the function
-# MO_FAIL_ON_FUNCTION - If a function returns a non-zero status code, abort
-# with an error.
-# MO_FAIL_ON_UNSET - When set to a non-empty value, expansion of an unset env
-# variable will be aborted with an error.
-# MO_FALSE_IS_EMPTY - When set to a non-empty value, the string "false" will be
-# treated as an empty value for the purposes of conditionals.
-# MO_ORIGINAL_COMMAND - Used to find the `mo` program in order to generate a
-# help message.
-#
-# Returns nothing.
-mo() (
- # This function executes in a subshell so IFS is reset.
- # Namespace this variable so we don't conflict with desired values.
- local moContent f2source files doubleHyphens
-
- IFS=$' \n\t'
- files=()
- doubleHyphens=false
-
- if [[ $# -gt 0 ]]; then
- for arg in "$@"; do
- if $doubleHyphens; then
- #: After we encounter two hyphens together, all the rest
- #: of the arguments are files.
- files=("${files[@]}" "$arg")
- else
- case "$arg" in
- -h|--h|--he|--hel|--help|-\?)
- moUsage "$0"
- exit 0
- ;;
-
- --allow-function-arguments)
- # shellcheck disable=SC2030
- MO_ALLOW_FUNCTION_ARGUMENTS=true
- ;;
-
- -u | --fail-not-set)
- # shellcheck disable=SC2030
- MO_FAIL_ON_UNSET=true
- ;;
-
- -x | --fail-on-function)
- # shellcheck disable=SC2030
- MO_FAIL_ON_FUNCTION=true
- ;;
-
- -e | --false)
- # shellcheck disable=SC2030
- MO_FALSE_IS_EMPTY=true
- ;;
-
- -s=* | --source=*)
- if [[ "$arg" == --source=* ]]; then
- f2source="${arg#--source=}"
- else
- f2source="${arg#-s=}"
- fi
-
- if [[ -f "$f2source" ]]; then
- # shellcheck disable=SC1090
- . "$f2source"
- else
- echo "No such file: $f2source" >&2
- exit 1
- fi
- ;;
-
- --)
- #: Set a flag indicating we've encountered double hyphens
- doubleHyphens=true
- ;;
-
- *)
- #: Every arg that is not a flag or a option should be a file
- files=(${files[@]+"${files[@]}"} "$arg")
- ;;
- esac
- fi
- done
- fi
-
- moGetContent moContent "${files[@]}" || return 1
- moParse "$moContent" "" true
-)
-
-
-# Internal: Call a function.
-#
-# $1 - Variable for output
-# $2 - Function to call
-# $3 - Content to pass
-# $4 - Additional arguments as a single string
-#
-# This can be dangerous, especially if you are using tags like
-# {{someFunction ; rm -rf / }}
-#
-# Returns nothing.
-moCallFunction() {
- local moArgs moContent moFunctionArgs moFunctionResult
-
- moArgs=()
- moTrimWhitespace moFunctionArgs "$4"
-
- # shellcheck disable=SC2031
- if [[ -n "${MO_ALLOW_FUNCTION_ARGUMENTS-}" ]]; then
- # Intentionally bad behavior
- # shellcheck disable=SC2206
- moArgs=($4)
- fi
-
- moContent=$(echo -n "$3" | MO_FUNCTION_ARGS="$moFunctionArgs" eval "$2" "${moArgs[@]}") || {
- moFunctionResult=$?
- # shellcheck disable=SC2031
- if [[ -n "${MO_FAIL_ON_FUNCTION-}" && "$moFunctionResult" != 0 ]]; then
- echo "Function '$2' with args (${moArgs[*]+"${moArgs[@]}"}) failed with status code $moFunctionResult"
- exit "$moFunctionResult"
- fi
- }
-
- # shellcheck disable=SC2031
- local "$1" && moIndirect "$1" "$moContent"
-}
-
-
-# Internal: Scan content until the right end tag is found. Creates an array
-# with the following members:
-#
-# [0] = Content before end tag
-# [1] = End tag (complete tag)
-# [2] = Content after end tag
-#
-# Everything using this function uses the "standalone tags" logic.
-#
-# $1 - Name of variable for the array
-# $2 - Content
-# $3 - Name of end tag
-# $4 - If -z, do standalone tag processing before finishing
-#
-# Returns nothing.
-moFindEndTag() {
- local content remaining scanned standaloneBytes tag
-
- #: Find open tags
- scanned=""
- moSplit content "$2" '{{' '}}'
-
- while [[ "${#content[@]}" -gt 1 ]]; do
- moTrimWhitespace tag "${content[1]}"
-
- #: Restore content[1] before we start using it
- content[1]='{{'"${content[1]}"'}}'
-
- case $tag in
- '#'* | '^'*)
- #: Start another block
- scanned="${scanned}${content[0]}${content[1]}"
- moTrimWhitespace tag "${tag:1}"
- moFindEndTag content "${content[2]}" "$tag" "loop"
- scanned="${scanned}${content[0]}${content[1]}"
- remaining=${content[2]}
- ;;
-
- '/'*)
- #: End a block - could be ours
- moTrimWhitespace tag "${tag:1}"
- scanned="$scanned${content[0]}"
-
- if [[ "$tag" == "$3" ]]; then
- #: Found our end tag
- if [[ -z "${4-}" ]] && moIsStandalone standaloneBytes "$scanned" "${content[2]}" true; then
- #: This is also a standalone tag - clean up whitespace
- #: and move those whitespace bytes to the "tag" element
- # shellcheck disable=SC2206
- standaloneBytes=( $standaloneBytes )
- content[1]="${scanned:${standaloneBytes[0]}}${content[1]}${content[2]:0:${standaloneBytes[1]}}"
- scanned="${scanned:0:${standaloneBytes[0]}}"
- content[2]="${content[2]:${standaloneBytes[1]}}"
- fi
-
- local "$1" && moIndirectArray "$1" "$scanned" "${content[1]}" "${content[2]}"
- return 0
- fi
-
- scanned="$scanned${content[1]}"
- remaining=${content[2]}
- ;;
-
- *)
- #: Ignore all other tags
- scanned="${scanned}${content[0]}${content[1]}"
- remaining=${content[2]}
- ;;
- esac
-
- moSplit content "$remaining" '{{' '}}'
- done
-
- #: Did not find our closing tag
- scanned="$scanned${content[0]}"
- local "$1" && moIndirectArray "$1" "${scanned}" "" ""
-}
-
-
-# Internal: Find the first index of a substring. If not found, sets the
-# index to -1.
-#
-# $1 - Destination variable for the index
-# $2 - Haystack
-# $3 - Needle
-#
-# Returns nothing.
-moFindString() {
- local pos string
-
- string=${2%%$3*}
- [[ "$string" == "$2" ]] && pos=-1 || pos=${#string}
- local "$1" && moIndirect "$1" "$pos"
-}
-
-
-# Internal: Generate a dotted name based on current context and target name.
-#
-# $1 - Target variable to store results
-# $2 - Context name
-# $3 - Desired variable name
-#
-# Returns nothing.
-moFullTagName() {
- if [[ -z "${2-}" ]] || [[ "$2" == *.* ]]; then
- local "$1" && moIndirect "$1" "$3"
- else
- local "$1" && moIndirect "$1" "${2}.${3}"
- fi
-}
-
-
-# Internal: Fetches the content to parse into a variable. Can be a list of
-# partials for files or the content from stdin.
-#
-# $1 - Variable name to assign this content back as
-# $2-@ - File names (optional)
-#
-# Returns nothing.
-moGetContent() {
- local moContent moFilename moTarget
-
- moTarget=$1
- shift
- if [[ "${#@}" -gt 0 ]]; then
- moContent=""
-
- for moFilename in "$@"; do
- #: This is so relative paths work from inside template files
- moContent="$moContent"'{{>'"$moFilename"'}}'
- done
- else
- moLoadFile moContent || return 1
- fi
-
- local "$moTarget" && moIndirect "$moTarget" "$moContent"
-}
-
-
-# Internal: Indent a string, placing the indent at the beginning of every
-# line that has any content.
-#
-# $1 - Name of destination variable to get an array of lines
-# $2 - The indent string
-# $3 - The string to reindent
-#
-# Returns nothing.
-moIndentLines() {
- local content fragment len posN posR result trimmed
-
- result=""
-
- #: Remove the period from the end of the string.
- len=$((${#3} - 1))
- content=${3:0:$len}
-
- if [[ -z "${2-}" ]]; then
- local "$1" && moIndirect "$1" "$content"
-
- return 0
- fi
-
- moFindString posN "$content" $'\n'
- moFindString posR "$content" $'\r'
-
- while [[ "$posN" -gt -1 ]] || [[ "$posR" -gt -1 ]]; do
- if [[ "$posN" -gt -1 ]]; then
- fragment="${content:0:$posN + 1}"
- content=${content:$posN + 1}
- else
- fragment="${content:0:$posR + 1}"
- content=${content:$posR + 1}
- fi
-
- moTrimChars trimmed "$fragment" false true " " $'\t' $'\n' $'\r'
-
- if [[ -n "$trimmed" ]]; then
- fragment="$2$fragment"
- fi
-
- result="$result$fragment"
-
- moFindString posN "$content" $'\n'
- moFindString posR "$content" $'\r'
-
- # If the content ends in a newline, do not indent.
- if [[ "$posN" -eq ${#content} ]]; then
- # Special clause for \r\n
- if [[ "$posR" -eq "$((posN - 1))" ]]; then
- posR=-1
- fi
-
- posN=-1
- fi
-
- if [[ "$posR" -eq ${#content} ]]; then
- posR=-1
- fi
- done
-
- moTrimChars trimmed "$content" false true " " $'\t'
-
- if [[ -n "$trimmed" ]]; then
- content="$2$content"
- fi
-
- result="$result$content"
-
- local "$1" && moIndirect "$1" "$result"
-}
-
-
-# Internal: Send a variable up to the parent of the caller of this function.
-#
-# $1 - Variable name
-# $2 - Value
-#
-# Examples
-#
-# callFunc () {
-# local "$1" && moIndirect "$1" "the value"
-# }
-# callFunc dest
-# echo "$dest" # writes "the value"
-#
-# Returns nothing.
-moIndirect() {
- unset -v "$1"
- printf -v "$1" '%s' "$2"
-}
-
-
-# Internal: Send an array as a variable up to caller of a function
-#
-# $1 - Variable name
-# $2-@ - Array elements
-#
-# Examples
-#
-# callFunc () {
-# local myArray=(one two three)
-# local "$1" && moIndirectArray "$1" "${myArray[@]}"
-# }
-# callFunc dest
-# echo "${dest[@]}" # writes "one two three"
-#
-# Returns nothing.
-moIndirectArray() {
- unset -v "$1"
-
- # IFS must be set to a string containing space or unset in order for
- # the array slicing to work regardless of the current IFS setting on
- # bash 3. This is detailed further at
- # https://github.com/fidian/gg-core/pull/7
- eval "$(printf "IFS= %s=(\"\${@:2}\") IFS=%q" "$1" "$IFS")"
-}
-
-
-# Internal: Determine if a given environment variable exists and if it is
-# an array.
-#
-# $1 - Name of environment variable
-#
-# Be extremely careful. Even if strict mode is enabled, it is not honored
-# in newer versions of Bash. Any errors that crop up here will not be
-# caught automatically.
-#
-# Examples
-#
-# var=(abc)
-# if moIsArray var; then
-# echo "This is an array"
-# echo "Make sure you don't accidentally use \$var"
-# fi
-#
-# Returns 0 if the name is not empty, 1 otherwise.
-moIsArray() {
- # Namespace this variable so we don't conflict with what we're testing.
- local moTestResult
-
- moTestResult=$(declare -p "$1" 2>/dev/null) || return 1
- [[ "${moTestResult:0:10}" == "declare -a" ]] && return 0
- [[ "${moTestResult:0:10}" == "declare -A" ]] && return 0
-
- return 1
-}
-
-
-# Internal: Determine if the given name is a defined function.
-#
-# $1 - Function name to check
-#
-# Be extremely careful. Even if strict mode is enabled, it is not honored
-# in newer versions of Bash. Any errors that crop up here will not be
-# caught automatically.
-#
-# Examples
-#
-# moo () {
-# echo "This is a function"
-# }
-# if moIsFunction moo; then
-# echo "moo is a defined function"
-# fi
-#
-# Returns 0 if the name is a function, 1 otherwise.
-moIsFunction() {
- local functionList functionName
-
- functionList=$(declare -F)
- # shellcheck disable=SC2206
- functionList=( ${functionList//declare -f /} )
-
- for functionName in "${functionList[@]}"; do
- if [[ "$functionName" == "$1" ]]; then
- return 0
- fi
- done
-
- return 1
-}
-
-
-# Internal: Determine if the tag is a standalone tag based on whitespace
-# before and after the tag.
-#
-# Passes back a string containing two numbers in the format "BEFORE AFTER"
-# like "27 10". It indicates the number of bytes remaining in the "before"
-# string (27) and the number of bytes to trim in the "after" string (10).
-# Useful for string manipulation:
-#
-# $1 - Variable to set for passing data back
-# $2 - Content before the tag
-# $3 - Content after the tag
-# $4 - true/false: is this the beginning of the content?
-#
-# Examples
-#
-# moIsStandalone RESULT "$before" "$after" false || return 0
-# RESULT_ARRAY=( $RESULT )
-# echo "${before:0:${RESULT_ARRAY[0]}}...${after:${RESULT_ARRAY[1]}}"
-#
-# Returns nothing.
-moIsStandalone() {
- local afterTrimmed beforeTrimmed char
-
- moTrimChars beforeTrimmed "$2" false true " " $'\t'
- moTrimChars afterTrimmed "$3" true false " " $'\t'
- char=$((${#beforeTrimmed} - 1))
- char=${beforeTrimmed:$char}
-
- # If the content before didn't end in a newline
- if [[ "$char" != $'\n' ]] && [[ "$char" != $'\r' ]]; then
- # and there was content or this didn't start the file
- if [[ -n "$char" ]] || ! $4; then
- # then this is not a standalone tag.
- return 1
- fi
- fi
-
- char=${afterTrimmed:0:1}
-
- # If the content after doesn't start with a newline and it is something
- if [[ "$char" != $'\n' ]] && [[ "$char" != $'\r' ]] && [[ -n "$char" ]]; then
- # then this is not a standalone tag.
- return 2
- fi
-
- if [[ "$char" == $'\r' ]] && [[ "${afterTrimmed:1:1}" == $'\n' ]]; then
- char="$char"$'\n'
- fi
-
- local "$1" && moIndirect "$1" "$((${#beforeTrimmed})) $((${#3} + ${#char} - ${#afterTrimmed}))"
-}
-
-
-# Internal: Join / implode an array
-#
-# $1 - Variable name to receive the joined content
-# $2 - Joiner
-# $3-$* - Elements to join
-#
-# Returns nothing.
-moJoin() {
- local joiner part result target
-
- target=$1
- joiner=$2
- result=$3
- shift 3
-
- for part in "$@"; do
- result="$result$joiner$part"
- done
-
- local "$target" && moIndirect "$target" "$result"
-}
-
-
-# Internal: Read a file into a variable.
-#
-# $1 - Variable name to receive the file's content
-# $2 - Filename to load - if empty, defaults to /dev/stdin
-#
-# Returns nothing.
-moLoadFile() {
- local content len
-
- # The subshell removes any trailing newlines. We forcibly add
- # a dot to the content to preserve all newlines.
- # As a future optimization, it would be worth considering removing
- # cat and replacing this with a read loop.
-
- content=$(cat -- "${2:-/dev/stdin}" && echo '.') || return 1
- len=$((${#content} - 1))
- content=${content:0:$len} # Remove last dot
-
- local "$1" && moIndirect "$1" "$content"
-}
-
-
-# Internal: Process a chunk of content some number of times. Writes output
-# to stdout.
-#
-# $1 - Content to parse repeatedly
-# $2 - Tag prefix (context name)
-# $3-@ - Names to insert into the parsed content
-#
-# Returns nothing.
-moLoop() {
- local content context contextBase
-
- content=$1
- contextBase=$2
- shift 2
-
- while [[ "${#@}" -gt 0 ]]; do
- moFullTagName context "$contextBase" "$1"
- moParse "$content" "$context" false
- shift
- done
-}
-
-
-# Internal: Parse a block of text, writing the result to stdout.
-#
-# $1 - Block of text to change
-# $2 - Current name (the variable NAME for what {{.}} means)
-# $3 - true when no content before this, false otherwise
-#
-# Returns nothing.
-moParse() {
- # Keep naming variables mo* here to not overwrite needed variables
- # used in the string replacements
- local moArgs moBlock moContent moCurrent moIsBeginning moNextIsBeginning moTag
-
- moCurrent=$2
- moIsBeginning=$3
-
- # Find open tags
- moSplit moContent "$1" '{{' '}}'
-
- while [[ "${#moContent[@]}" -gt 1 ]]; do
- moTrimWhitespace moTag "${moContent[1]}"
- moNextIsBeginning=false
-
- case $moTag in
- '#'*)
- # Loop, if/then, or pass content through function
- # Sets context
- moStandaloneAllowed moContent "${moContent[@]}" "$moIsBeginning"
- moTrimWhitespace moTag "${moTag:1}"
-
- # Split arguments from the tag name. Arguments are passed to
- # functions.
- moArgs=$moTag
- moTag=${moTag%% *}
- moTag=${moTag%%$'\t'*}
- moArgs=${moArgs:${#moTag}}
- moFindEndTag moBlock "$moContent" "$moTag"
- moFullTagName moTag "$moCurrent" "$moTag"
-
- if moTest "$moTag"; then
- # Show / loop / pass through function
- if moIsFunction "$moTag"; then
- moCallFunction moContent "$moTag" "${moBlock[0]}" "$moArgs"
- moParse "$moContent" "$moCurrent" false
- moContent="${moBlock[2]}"
- elif moIsArray "$moTag"; then
- eval "moLoop \"\${moBlock[0]}\" \"$moTag\" \"\${!${moTag}[@]}\""
- else
- moParse "${moBlock[0]}" "$moCurrent" true
- fi
- fi
-
- moContent="${moBlock[2]}"
- ;;
-
- '>'*)
- # Load partial - get name of file relative to cwd
- moPartial moContent "${moContent[@]}" "$moIsBeginning" "$moCurrent"
- moNextIsBeginning=${moContent[1]}
- moContent=${moContent[0]}
- ;;
-
- '/'*)
- # Closing tag - If hit in this loop, we simply ignore
- # Matching tags are found in moFindEndTag
- moStandaloneAllowed moContent "${moContent[@]}" "$moIsBeginning"
- ;;
-
- '^'*)
- # Display section if named thing does not exist
- moStandaloneAllowed moContent "${moContent[@]}" "$moIsBeginning"
- moTrimWhitespace moTag "${moTag:1}"
- moFindEndTag moBlock "$moContent" "$moTag"
- moFullTagName moTag "$moCurrent" "$moTag"
-
- if ! moTest "$moTag"; then
- moParse "${moBlock[0]}" "$moCurrent" false "$moCurrent"
- fi
-
- moContent="${moBlock[2]}"
- ;;
-
- '!'*)
- # Comment - ignore the tag content entirely
- # Trim spaces/tabs before the comment
- moStandaloneAllowed moContent "${moContent[@]}" "$moIsBeginning"
- ;;
-
- .)
- # Current content (environment variable or function)
- moStandaloneDenied moContent "${moContent[@]}"
- moShow "$moCurrent" "$moCurrent"
- ;;
-
- '=')
- # Change delimiters
- # Any two non-whitespace sequences separated by whitespace.
- # This tag is ignored.
- moStandaloneAllowed moContent "${moContent[@]}" "$moIsBeginning"
- ;;
-
- '{'*)
- # Unescaped - split on }}} not }}
- moStandaloneDenied moContent "${moContent[@]}"
- moContent="${moTag:1}"'}}'"$moContent"
- moSplit moContent "$moContent" '}}}'
- moTrimWhitespace moTag "${moContent[0]}"
- moArgs=$moTag
- moTag=${moTag%% *}
- moTag=${moTag%%$'\t'*}
- moArgs=${moArgs:${#moTag}}
- moFullTagName moTag "$moCurrent" "$moTag"
- moContent=${moContent[1]}
-
- # Now show the value
- # Quote moArgs here, do not quote it later.
- moShow "$moTag" "$moCurrent" "$moArgs"
- ;;
-
- '&'*)
- # Unescaped
- moStandaloneDenied moContent "${moContent[@]}"
- moTrimWhitespace moTag "${moTag:1}"
- moFullTagName moTag "$moCurrent" "$moTag"
- moShow "$moTag" "$moCurrent"
- ;;
-
- *)
- # Normal environment variable or function call
- moStandaloneDenied moContent "${moContent[@]}"
- moArgs=$moTag
- moTag=${moTag%% *}
- moTag=${moTag%%$'\t'*}
- moArgs=${moArgs:${#moTag}}
- moFullTagName moTag "$moCurrent" "$moTag"
-
- # Quote moArgs here, do not quote it later.
- moShow "$moTag" "$moCurrent" "$moArgs"
- ;;
- esac
-
- moIsBeginning=$moNextIsBeginning
- moSplit moContent "$moContent" '{{' '}}'
- done
-
- echo -n "${moContent[0]}"
-}
-
-
-# Internal: Process a partial.
-#
-# Indentation should be applied to the entire partial.
-#
-# This sends back the "is beginning" flag because the newline after a
-# standalone partial is consumed. That newline is very important in the middle
-# of content. We send back this flag to reset the processing loop's
-# `moIsBeginning` variable, so the software thinks we are back at the
-# beginning of a file and standalone processing continues to work.
-#
-# Prefix all variables.
-#
-# $1 - Name of destination variable. Element [0] is the content, [1] is the
-# true/false flag indicating if we are at the beginning of content.
-# $2 - Content before the tag that was not yet written
-# $3 - Tag content
-# $4 - Content after the tag
-# $5 - true/false: is this the beginning of the content?
-# $6 - Current context name
-#
-# Returns nothing.
-moPartial() {
- # Namespace variables here to prevent conflicts.
- local moContent moFilename moIndent moIsBeginning moPartial moStandalone moUnindented
-
- if moIsStandalone moStandalone "$2" "$4" "$5"; then
- # shellcheck disable=SC2206
- moStandalone=( $moStandalone )
- echo -n "${2:0:${moStandalone[0]}}"
- moIndent=${2:${moStandalone[0]}}
- moContent=${4:${moStandalone[1]}}
- moIsBeginning=true
- else
- moIndent=""
- echo -n "$2"
- moContent=$4
- moIsBeginning=$5
- fi
-
- moTrimWhitespace moFilename "${3:1}"
-
- # Execute in subshell to preserve current cwd and environment
- (
- # It would be nice to remove `dirname` and use a function instead,
- # but that's difficult when you're only given filenames.
- cd "$(dirname -- "$moFilename")" || exit 1
- moUnindented="$(
- moLoadFile moPartial "${moFilename##*/}" || exit 1
- moParse "${moPartial}" "$6" true
-
- # Fix bash handling of subshells and keep trailing whitespace.
- # This is removed in moIndentLines.
- echo -n "."
- )" || exit 1
- moIndentLines moPartial "$moIndent" "$moUnindented"
- echo -n "$moPartial"
- ) || exit 1
-
- # If this is a standalone tag, the trailing newline after the tag is
- # removed and the contents of the partial are added, which typically
- # contain a newline. We need to send a signal back to the processing
- # loop that the moIsBeginning flag needs to be turned on again.
- #
- # [0] is the content, [1] is that flag.
- local "$1" && moIndirectArray "$1" "$moContent" "$moIsBeginning"
-}
-
-
-# Internal: Show an environment variable or the output of a function to
-# stdout.
-#
-# Limit/prefix any variables used.
-#
-# $1 - Name of environment variable or function
-# $2 - Current context
-# $3 - Arguments string if $1 is a function
-#
-# Returns nothing.
-moShow() {
- # Namespace these variables
- local moJoined moNameParts moContent
-
- if moIsFunction "$1"; then
- moCallFunction moContent "$1" "" "$3"
- moParse "$moContent" "$2" false
- return 0
- fi
-
- moSplit moNameParts "$1" "."
-
- if [[ -z "${moNameParts[1]-}" ]]; then
- if moIsArray "$1"; then
- eval moJoin moJoined "," "\${$1[@]}"
- echo -n "$moJoined"
- else
- # shellcheck disable=SC2031
- if moTestVarSet "$1"; then
- echo -n "${!1}"
- elif [[ -n "${MO_FAIL_ON_UNSET-}" ]]; then
- echo "Env variable not set: $1" >&2
- exit 1
- fi
- fi
- else
- # Further subindexes are disallowed
- eval "echo -n \"\${${moNameParts[0]}[${moNameParts[1]%%.*}]}\""
- fi
-}
-
-
-# Internal: Split a larger string into an array.
-#
-# $1 - Destination variable
-# $2 - String to split
-# $3 - Starting delimiter
-# $4 - Ending delimiter (optional)
-#
-# Returns nothing.
-moSplit() {
- local pos result
-
- result=( "$2" )
- moFindString pos "${result[0]}" "$3"
-
- if [[ "$pos" -ne -1 ]]; then
- # The first delimiter was found
- result[1]=${result[0]:$pos + ${#3}}
- result[0]=${result[0]:0:$pos}
-
- if [[ -n "${4-}" ]]; then
- moFindString pos "${result[1]}" "$4"
-
- if [[ "$pos" -ne -1 ]]; then
- # The second delimiter was found
- result[2]="${result[1]:$pos + ${#4}}"
- result[1]="${result[1]:0:$pos}"
- fi
- fi
- fi
-
- local "$1" && moIndirectArray "$1" "${result[@]}"
-}
-
-
-# Internal: Handle the content for a standalone tag. This means removing
-# whitespace (not newlines) before a tag and whitespace and a newline after
-# a tag. That is, assuming, that the line is otherwise empty.
-#
-# $1 - Name of destination "content" variable.
-# $2 - Content before the tag that was not yet written
-# $3 - Tag content (not used)
-# $4 - Content after the tag
-# $5 - true/false: is this the beginning of the content?
-#
-# Returns nothing.
-moStandaloneAllowed() {
- local bytes
-
- if moIsStandalone bytes "$2" "$4" "$5"; then
- # shellcheck disable=SC2206
- bytes=( $bytes )
- echo -n "${2:0:${bytes[0]}}"
- local "$1" && moIndirect "$1" "${4:${bytes[1]}}"
- else
- echo -n "$2"
- local "$1" && moIndirect "$1" "$4"
- fi
-}
-
-
-# Internal: Handle the content for a tag that is never "standalone". No
-# adjustments are made for newlines and whitespace.
-#
-# $1 - Name of destination "content" variable.
-# $2 - Content before the tag that was not yet written
-# $3 - Tag content (not used)
-# $4 - Content after the tag
-#
-# Returns nothing.
-moStandaloneDenied() {
- echo -n "$2"
- local "$1" && moIndirect "$1" "$4"
-}
-
-
-# Internal: Determines if the named thing is a function or if it is a
-# non-empty environment variable. When MO_FALSE_IS_EMPTY is set to a
-# non-empty value, then "false" is also treated is an empty value.
-#
-# Do not use variables without prefixes here if possible as this needs to
-# check if any name exists in the environment
-#
-# $1 - Name of environment variable or function
-# $2 - Current value (our context)
-# MO_FALSE_IS_EMPTY - When set to a non-empty value, this will say the
-# string value "false" is empty.
-#
-# Returns 0 if the name is not empty, 1 otherwise. When MO_FALSE_IS_EMPTY
-# is set, this returns 1 if the name is "false".
-moTest() {
- # Test for functions
- moIsFunction "$1" && return 0
-
- if moIsArray "$1"; then
- # Arrays must have at least 1 element
- eval "[[ \"\${#${1}[@]}\" -gt 0 ]]" && return 0
- else
- # If MO_FALSE_IS_EMPTY is set, then return 1 if the value of
- # the variable is "false".
- # shellcheck disable=SC2031
- [[ -n "${MO_FALSE_IS_EMPTY-}" ]] && [[ "${!1-}" == "false" ]] && return 1
-
- # Environment variables must not be empty
- [[ -n "${!1-}" ]] && return 0
- fi
-
- return 1
-}
-
-# Internal: Determine if a variable is assigned, even if it is assigned an empty
-# value.
-#
-# $1 - Variable name to check.
-#
-# Returns true (0) if the variable is set, 1 if the variable is unset.
-moTestVarSet() {
- [[ "${!1-a}" == "${!1-b}" ]]
-}
-
-
-# Internal: Trim the leading whitespace only.
-#
-# $1 - Name of destination variable
-# $2 - The string
-# $3 - true/false - trim front?
-# $4 - true/false - trim end?
-# $5-@ - Characters to trim
-#
-# Returns nothing.
-moTrimChars() {
- local back current front last target varName
-
- target=$1
- current=$2
- front=$3
- back=$4
- last=""
- shift 4 # Remove target, string, trim front flag, trim end flag
-
- while [[ "$current" != "$last" ]]; do
- last=$current
-
- for varName in "$@"; do
- $front && current="${current/#$varName}"
- $back && current="${current/%$varName}"
- done
- done
-
- local "$target" && moIndirect "$target" "$current"
-}
-
-
-# Internal: Trim leading and trailing whitespace from a string.
-#
-# $1 - Name of variable to store trimmed string
-# $2 - The string
-#
-# Returns nothing.
-moTrimWhitespace() {
- local result
-
- moTrimChars result "$2" true true $'\r' $'\n' $'\t' " "
- local "$1" && moIndirect "$1" "$result"
-}
-
-
-# Internal: Displays the usage for mo. Pulls this from the file that
-# contained the `mo` function. Can only work when the right filename
-# comes is the one argument, and that only happens when `mo` is called
-# with `$0` set to this file.
-#
-# $1 - Filename that has the help message
-#
-# Returns nothing.
-moUsage() {
- grep '^#/' "${MO_ORIGINAL_COMMAND}" | cut -c 4-
- echo ""
- echo "MO_VERSION=$MO_VERSION"
-}
-
-
-# Save the original command's path for usage later
-MO_ORIGINAL_COMMAND="$(cd "${BASH_SOURCE[0]%/*}" || exit 1; pwd)/${BASH_SOURCE[0]##*/}"
-MO_VERSION="2.2.0"
-
-# If sourced, load all functions.
-# If executed, perform the actions as expected.
-if [[ "$0" == "${BASH_SOURCE[0]}" ]] || [[ -z "${BASH_SOURCE[0]}" ]]; then
- mo "$@"
-fi
diff --git a/docker/nginx/nginx.conf b/docker/nginx/nginx.conf
index 3517a6bc..de55d276 100644
--- a/docker/nginx/nginx.conf
+++ b/docker/nginx/nginx.conf
@@ -19,6 +19,9 @@
user root;
worker_processes auto;
+# Enables the use of JIT for regular expressions to speed-up their processing.
+pcre_jit on;
+
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
diff --git a/docker/nginx/testing/Dockerfile b/docker/nginx/testing/Dockerfile
index ae2cd78f..ed7740b6 100644
--- a/docker/nginx/testing/Dockerfile
+++ b/docker/nginx/testing/Dockerfile
@@ -8,4 +8,4 @@ RUN luarocks install lua-resty-http && \
COPY rbusted /etc/nginx/
-CMD /etc/nginx/rbusted --verbose --pattern=spec /usr/local/openresty/site/lualib
+CMD ["/etc/nginx/rbusted", "--verbose", "--pattern=spec", "/usr/local/openresty/site/lualib"]
diff --git a/packages/dashboard-v2/Dockerfile b/packages/dashboard-v2/Dockerfile
index 70790cfa..86fe89bc 100644
--- a/packages/dashboard-v2/Dockerfile
+++ b/packages/dashboard-v2/Dockerfile
@@ -2,13 +2,17 @@ FROM node:16.14.2-alpine
WORKDIR /usr/app
-COPY package.json yarn.lock ./
+COPY package.json \
+ yarn.lock \
+ ./
RUN yarn --frozen-lockfile
COPY static ./static
COPY src ./src
-COPY gatsby*.js ./
-COPY postcss.config.js tailwind.config.js ./
+COPY gatsby*.js \
+ postcss.config.js \
+ tailwind.config.js \
+ ./
CMD ["sh", "-c", "yarn build && yarn serve --host 0.0.0.0 -p 9000"]
diff --git a/packages/dashboard-v2/gatsby-config.js b/packages/dashboard-v2/gatsby-config.js
index 0e269557..d087fa65 100644
--- a/packages/dashboard-v2/gatsby-config.js
+++ b/packages/dashboard-v2/gatsby-config.js
@@ -11,6 +11,7 @@ module.exports = {
title: `Account Dashboard`,
siteUrl: `https://account.${GATSBY_PORTAL_DOMAIN}`,
},
+ pathPrefix: "/v2",
trailingSlash: "never",
plugins: [
"gatsby-plugin-image",
diff --git a/packages/dashboard-v2/package.json b/packages/dashboard-v2/package.json
index 694a6129..80070815 100644
--- a/packages/dashboard-v2/package.json
+++ b/packages/dashboard-v2/package.json
@@ -11,8 +11,8 @@
"develop": "gatsby develop",
"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 .",
diff --git a/packages/dashboard-v2/src/components/AvatarUploader/AvatarUploader.js b/packages/dashboard-v2/src/components/AvatarUploader/AvatarUploader.js
index 2a3e400d..f97ca2d5 100644
--- a/packages/dashboard-v2/src/components/AvatarUploader/AvatarUploader.js
+++ b/packages/dashboard-v2/src/components/AvatarUploader/AvatarUploader.js
@@ -3,14 +3,14 @@ import { useEffect, useState } from "react";
import { useUser } from "../../contexts/user";
// 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 (
diff --git a/packages/dashboard-v2/src/components/CurrentUsage/CurrentUsage.js b/packages/dashboard-v2/src/components/CurrentUsage/CurrentUsage.js
index 3947638d..f9dbbc36 100644
--- a/packages/dashboard-v2/src/components/CurrentUsage/CurrentUsage.js
+++ b/packages/dashboard-v2/src/components/CurrentUsage/CurrentUsage.js
@@ -1,6 +1,7 @@
import { useEffect, useMemo, useState } from "react";
import fileSize from "pretty-bytes";
import { Link } from "gatsby";
+import cn from "classnames";
import useSWR from "swr";
import { useUser } from "../../contexts/user";
@@ -62,7 +63,9 @@ const ErrorMessage = () => (
);
export default function CurrentUsage() {
+ const { activePlan, plans } = useActivePlan();
const { usage, error, loading } = useUsageData();
+ const nextPlan = useMemo(() => plans.find(({ tier }) => tier > activePlan?.tier), [plans, activePlan]);
const storageUsage = size(usage.storageUsed);
const storageLimit = size(usage.storageLimit);
const filesUsedLabel = useMemo(() => ({ value: usage.filesUsed, unit: "files" }), [usage.filesUsed]);
@@ -89,7 +92,7 @@ export default function CurrentUsage() {
{storageLimit.text}