Merge branch 'master' into portal-latest
This commit is contained in:
commit
ca322e3126
|
@ -20,18 +20,6 @@ updates:
|
||||||
directory: "/packages/website"
|
directory: "/packages/website"
|
||||||
schedule:
|
schedule:
|
||||||
interval: weekly
|
interval: weekly
|
||||||
- package-ecosystem: docker
|
|
||||||
directory: "/docker/accounts"
|
|
||||||
schedule:
|
|
||||||
interval: weekly
|
|
||||||
- package-ecosystem: docker
|
|
||||||
directory: "/docker/caddy"
|
|
||||||
schedule:
|
|
||||||
interval: weekly
|
|
||||||
- package-ecosystem: docker
|
|
||||||
directory: "/docker/handshake"
|
|
||||||
schedule:
|
|
||||||
interval: weekly
|
|
||||||
- package-ecosystem: docker
|
- package-ecosystem: docker
|
||||||
directory: "/docker/nginx"
|
directory: "/docker/nginx"
|
||||||
schedule:
|
schedule:
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
name: Build Storybook - packages/dashboard-v2
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
paths:
|
||||||
|
- "packages/dashboard-v2/**"
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- "packages/dashboard-v2/**"
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: packages/dashboard-v2
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
publish:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: 16.x
|
||||||
|
- run: yarn install
|
||||||
|
- run: yarn build-storybook
|
||||||
|
- name: "Deploy to Skynet"
|
||||||
|
uses: skynetlabs/deploy-to-skynet-action@v2
|
||||||
|
with:
|
||||||
|
upload-dir: packages/dashboard-v2/storybook-build
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
@ -36,8 +36,10 @@ jobs:
|
||||||
working-directory: packages/website
|
working-directory: packages/website
|
||||||
install: false
|
install: false
|
||||||
record: true
|
record: true
|
||||||
start: yarn serve
|
start: yarn develop
|
||||||
wait-on: "http://127.0.0.1:9000"
|
wait-on: http://localhost:8000
|
||||||
|
wait-on-timeout: 120
|
||||||
|
config: baseUrl=http://localhost:8000
|
||||||
|
|
||||||
- name: "Deploy to Skynet"
|
- name: "Deploy to Skynet"
|
||||||
uses: skynetlabs/deploy-to-skynet-action@v2
|
uses: skynetlabs/deploy-to-skynet-action@v2
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
name: Dockerfile Lint
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
hadolint:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
dockerfile:
|
||||||
|
- docker/nginx/Dockerfile
|
||||||
|
- docker/sia/Dockerfile
|
||||||
|
- packages/dashboard/Dockerfile
|
||||||
|
- packages/dashboard-v2/Dockerfile
|
||||||
|
- packages/dnslink-api/Dockerfile
|
||||||
|
- packages/handshake-api/Dockerfile
|
||||||
|
- packages/health-check/Dockerfile
|
||||||
|
- packages/website/Dockerfile
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: hadolint/hadolint-action@v2.0.0
|
||||||
|
with:
|
||||||
|
dockerfile: ${{ matrix.dockerfile }}
|
|
@ -0,0 +1,24 @@
|
||||||
|
name: Lint - packages/dashboard-v2
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- packages/dashboard-v2/**
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: packages/dashboard-v2
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: 16.x
|
||||||
|
|
||||||
|
- run: yarn
|
||||||
|
- run: yarn prettier --check
|
||||||
|
- run: yarn lint
|
|
@ -4,8 +4,15 @@
|
||||||
name: Nginx Lua Unit Tests
|
name: Nginx Lua Unit Tests
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- "master"
|
||||||
|
paths:
|
||||||
|
- ".github/workflows/nginx-lua-unit-tests.yml"
|
||||||
|
- "docker/nginx/libs/**.lua"
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
|
- ".github/workflows/nginx-lua-unit-tests.yml"
|
||||||
- "docker/nginx/libs/**.lua"
|
- "docker/nginx/libs/**.lua"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
@ -25,9 +32,22 @@ jobs:
|
||||||
hererocks env --lua=5.1 -rlatest
|
hererocks env --lua=5.1 -rlatest
|
||||||
source env/bin/activate
|
source env/bin/activate
|
||||||
luarocks install busted
|
luarocks install busted
|
||||||
|
luarocks install luacov
|
||||||
luarocks install hasher
|
luarocks install hasher
|
||||||
|
luarocks install luacheck
|
||||||
|
|
||||||
|
- name: Lint code
|
||||||
|
run: |
|
||||||
|
source env/bin/activate
|
||||||
|
luacheck docker/nginx/libs --std ngx_lua+busted
|
||||||
|
|
||||||
- name: Unit Tests
|
- name: Unit Tests
|
||||||
run: |
|
run: |
|
||||||
source env/bin/activate
|
source env/bin/activate
|
||||||
busted --verbose --pattern=spec --directory=docker/nginx/libs .
|
busted --verbose --coverage --pattern=spec --directory=docker/nginx/libs .
|
||||||
|
cd docker/nginx/libs && luacov
|
||||||
|
|
||||||
|
- uses: codecov/codecov-action@v2
|
||||||
|
with:
|
||||||
|
directory: docker/nginx/libs
|
||||||
|
flags: nginx-lua
|
||||||
|
|
40
CHANGELOG.md
40
CHANGELOG.md
|
@ -10,6 +10,46 @@ Version History
|
||||||
|
|
||||||
Latest:
|
Latest:
|
||||||
|
|
||||||
|
## Mar 8, 2022:
|
||||||
|
### v0.1.4
|
||||||
|
**Key Updates**
|
||||||
|
- expose generic skylink serving endpoint on domain aliases
|
||||||
|
- Add abuse scanner service, activated by adding `u` to `PORTAL_MODULES`
|
||||||
|
- Add malware scanner service, activated by adding `s` to `PORTAL_MODULES`
|
||||||
|
- Remove ORY Kratos, ORY Oathkeeper, CockroachDB.
|
||||||
|
- Add `/serverload` endpoint for CPU usage and free disk space
|
||||||
|
|
||||||
|
**Bugs Fixed**
|
||||||
|
- Add missing servers and blocklist command to the manual blocklist script.
|
||||||
|
- fixed a bug when accessing file from skylink via subdomain with a filename that had escaped characters
|
||||||
|
- Fix `blocklist-skylink.sh` script that didn't removed blocked skylink from
|
||||||
|
nginx cache.
|
||||||
|
- fixed uploaded directory name (was "undefined" before)
|
||||||
|
- fixed empty directory upload progress (size was not calculated for directories)
|
||||||
|
|
||||||
|
**Other**
|
||||||
|
- add new critical health check that scans config and makes sure that all relevant configurations are set
|
||||||
|
- Add abuse report configuration
|
||||||
|
- Remove hardcoded Airtable default values from blocklist script. Portal
|
||||||
|
operators need to define their own values in portal common config (LastPass).
|
||||||
|
- Add health check for the blocker container
|
||||||
|
- Drop `Skynet-Requested-Skylink` header
|
||||||
|
- Dump disk space usage when health-checker script disables portal due to
|
||||||
|
critical free disk space.
|
||||||
|
- Enable the accounting module for skyd
|
||||||
|
- Add link to supported setup process in Gitbook.
|
||||||
|
- Set `min_free` parameter on the `proxy_cache_path` directive to `100g`
|
||||||
|
- Parameterize MongoDB replicaset in `docker-compose.mongodb.yml` via
|
||||||
|
`SKYNET_DB_REPLICASET` from `.env` file.
|
||||||
|
- Hot reload Nginx after pruning cache files.
|
||||||
|
- Added script to prune nginx cache.
|
||||||
|
- Remove hardcoded server list from `blocklist-skylink.sh` so it removes server
|
||||||
|
list duplication and can also be called from Ansible.
|
||||||
|
- Remove outdated portal setup documentation and point to developer docs.
|
||||||
|
- Block skylinks in batches to improve performance.
|
||||||
|
- Add trimming Airtable skylinks from Takedown Request table.
|
||||||
|
- Update handshake to use v3.0.1
|
||||||
|
|
||||||
## Oct 18, 2021:
|
## Oct 18, 2021:
|
||||||
### v0.1.3
|
### v0.1.3
|
||||||
**Key Updates**
|
**Key Updates**
|
||||||
|
|
|
@ -1,3 +1,43 @@
|
||||||
|
## Mar 8, 2022:
|
||||||
|
### v0.1.4
|
||||||
|
**Key Updates**
|
||||||
|
- expose generic skylink serving endpoint on domain aliases
|
||||||
|
- Add abuse scanner service, activated by adding `u` to `PORTAL_MODULES`
|
||||||
|
- Add malware scanner service, activated by adding `s` to `PORTAL_MODULES`
|
||||||
|
- Remove ORY Kratos, ORY Oathkeeper, CockroachDB.
|
||||||
|
- Add `/serverload` endpoint for CPU usage and free disk space
|
||||||
|
|
||||||
|
**Bugs Fixed**
|
||||||
|
- Add missing servers and blocklist command to the manual blocklist script.
|
||||||
|
- fixed a bug when accessing file from skylink via subdomain with a filename that had escaped characters
|
||||||
|
- Fix `blocklist-skylink.sh` script that didn't removed blocked skylink from
|
||||||
|
nginx cache.
|
||||||
|
- fixed uploaded directory name (was "undefined" before)
|
||||||
|
- fixed empty directory upload progress (size was not calculated for directories)
|
||||||
|
|
||||||
|
**Other**
|
||||||
|
- add new critical health check that scans config and makes sure that all relevant configurations are set
|
||||||
|
- Add abuse report configuration
|
||||||
|
- Remove hardcoded Airtable default values from blocklist script. Portal
|
||||||
|
operators need to define their own values in portal common config (LastPass).
|
||||||
|
- Add health check for the blocker container
|
||||||
|
- Drop `Skynet-Requested-Skylink` header
|
||||||
|
- Dump disk space usage when health-checker script disables portal due to
|
||||||
|
critical free disk space.
|
||||||
|
- Enable the accounting module for skyd
|
||||||
|
- Add link to supported setup process in Gitbook.
|
||||||
|
- Set `min_free` parameter on the `proxy_cache_path` directive to `100g`
|
||||||
|
- Parameterize MongoDB replicaset in `docker-compose.mongodb.yml` via
|
||||||
|
`SKYNET_DB_REPLICASET` from `.env` file.
|
||||||
|
- Hot reload Nginx after pruning cache files.
|
||||||
|
- Added script to prune nginx cache.
|
||||||
|
- Remove hardcoded server list from `blocklist-skylink.sh` so it removes server
|
||||||
|
list duplication and can also be called from Ansible.
|
||||||
|
- Remove outdated portal setup documentation and point to developer docs.
|
||||||
|
- Block skylinks in batches to improve performance.
|
||||||
|
- Add trimming Airtable skylinks from Takedown Request table.
|
||||||
|
- Update handshake to use v3.0.1
|
||||||
|
|
||||||
## Oct 18, 2021:
|
## Oct 18, 2021:
|
||||||
### v0.1.3
|
### v0.1.3
|
||||||
**Key Updates**
|
**Key Updates**
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
- Add missing servers and blocklist command to the manual blocklist script.
|
|
|
@ -1 +0,0 @@
|
||||||
- fixed a bug when accessing file from skylink via subdomain with a filename that had escaped characters
|
|
|
@ -1,2 +0,0 @@
|
||||||
- Fix `blocklist-skylink.sh` script that didn't removed blocked skylink from
|
|
||||||
nginx cache.
|
|
|
@ -1,2 +0,0 @@
|
||||||
- fixed uploaded directory name (was "undefined" before)
|
|
||||||
- fixed empty directory upload progress (size was not calculated for directories)
|
|
|
@ -1 +0,0 @@
|
||||||
- expose generic skylink serving endpoint on domain aliases
|
|
|
@ -1 +0,0 @@
|
||||||
- Add abuse scanner service, activated by adding `u` to `PORTAL_MODULES`
|
|
|
@ -1 +0,0 @@
|
||||||
- Add malware scanner service, activated by adding `s` to `PORTAL_MODULES`
|
|
|
@ -1 +0,0 @@
|
||||||
- Remove ORY Kratos, ORY Oathkeeper, CockroachDB.
|
|
|
@ -1 +0,0 @@
|
||||||
- Add `/serverload` endpoint for CPU usage and free disk space
|
|
|
@ -1 +0,0 @@
|
||||||
- add new critical health check that scans config and makes sure that all relevant configurations are set
|
|
|
@ -1 +0,0 @@
|
||||||
- Add abuse report configuration
|
|
|
@ -1,2 +0,0 @@
|
||||||
- Remove hardcoded Airtable default values from blocklist script. Portal
|
|
||||||
operators need to define their own values in portal common config (LastPass).
|
|
|
@ -1 +0,0 @@
|
||||||
- Add health check for the blocker container
|
|
|
@ -1 +0,0 @@
|
||||||
- Drop `Skynet-Requested-Skylink` header
|
|
|
@ -1,2 +0,0 @@
|
||||||
- Dump disk space usage when health-checker script disables portal due to
|
|
||||||
critical free disk space.
|
|
|
@ -1 +0,0 @@
|
||||||
- Enable the accounting module for skyd
|
|
|
@ -1 +0,0 @@
|
||||||
- Add link to supported setup process in Gitbook.
|
|
|
@ -1 +0,0 @@
|
||||||
- Set `min_free` parameter on the `proxy_cache_path` directive to `100g`
|
|
|
@ -1,2 +0,0 @@
|
||||||
- Parameterize MongoDB replicaset in `docker-compose.mongodb.yml` via
|
|
||||||
`SKYNET_DB_REPLICASET` from `.env` file.
|
|
|
@ -1 +0,0 @@
|
||||||
- Hot reload Nginx after pruning cache files.
|
|
|
@ -1 +0,0 @@
|
||||||
- Added script to prune nginx cache.
|
|
|
@ -1,2 +0,0 @@
|
||||||
- Remove hardcoded server list from `blocklist-skylink.sh` so it removes server
|
|
||||||
list duplication and can also be called from Ansible.
|
|
|
@ -1 +0,0 @@
|
||||||
- Remove outdated portal setup documentation and point to developer docs.
|
|
|
@ -1 +0,0 @@
|
||||||
- Block skylinks in batches to improve performance.
|
|
|
@ -1 +0,0 @@
|
||||||
- Add trimming Airtable skylinks from Takedown Request table.
|
|
|
@ -1 +0,0 @@
|
||||||
- Update handshake to use v3.0.1
|
|
4
dc
4
dc
|
@ -41,9 +41,9 @@ for i in $(seq 1 ${#PORTAL_MODULES}); do
|
||||||
COMPOSE_FILES+=" -f docker-compose.mongodb.yml"
|
COMPOSE_FILES+=" -f docker-compose.mongodb.yml"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# abuse module - alias "u"
|
# abuse-scanner module - alias "u"
|
||||||
if [[ ${PORTAL_MODULES:i-1:1} == "u" ]]; then
|
if [[ ${PORTAL_MODULES:i-1:1} == "u" ]]; then
|
||||||
COMPOSE_FILES+=" -f docker-compose.mongodb.yml -f docker-compose.blocker.yml -f docker-compose.abuse.yml"
|
COMPOSE_FILES+=" -f docker-compose.mongodb.yml -f docker-compose.blocker.yml -f docker-compose.abuse-scanner.yml"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|
|
@ -7,11 +7,11 @@ x-logging: &default-logging
|
||||||
max-file: "3"
|
max-file: "3"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
abuse:
|
abuse-scanner:
|
||||||
build:
|
# uncomment "build" and comment out "image" to build from sources
|
||||||
context: ./docker/abuse
|
# build: https://github.com/SkynetLabs/abuse-scanner.git#main
|
||||||
dockerfile: Dockerfile
|
image: skynetlabs/abuse-scanner
|
||||||
container_name: abuse
|
container_name: abuse-scanner
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
logging: *default-logging
|
logging: *default-logging
|
||||||
env_file:
|
env_file:
|
|
@ -20,11 +20,9 @@ services:
|
||||||
- ACCOUNTS_LIMIT_ACCESS=${ACCOUNTS_LIMIT_ACCESS:-authenticated} # default to authenticated access only
|
- ACCOUNTS_LIMIT_ACCESS=${ACCOUNTS_LIMIT_ACCESS:-authenticated} # default to authenticated access only
|
||||||
|
|
||||||
accounts:
|
accounts:
|
||||||
build:
|
# uncomment "build" and comment out "image" to build from sources
|
||||||
context: ./docker/accounts
|
# build: https://github.com/SkynetLabs/skynet-accounts.git#main
|
||||||
dockerfile: Dockerfile
|
image: skynetlabs/skynet-accounts
|
||||||
args:
|
|
||||||
branch: main
|
|
||||||
container_name: accounts
|
container_name: accounts
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
logging: *default-logging
|
logging: *default-logging
|
||||||
|
@ -66,8 +64,7 @@ services:
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
environment:
|
environment:
|
||||||
- NEXT_PUBLIC_SKYNET_PORTAL_API=${SKYNET_PORTAL_API}
|
- NEXT_PUBLIC_PORTAL_DOMAIN=${PORTAL_DOMAIN}
|
||||||
- NEXT_PUBLIC_SKYNET_DASHBOARD_URL=${SKYNET_DASHBOARD_URL}
|
|
||||||
- NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=${STRIPE_PUBLISHABLE_KEY}
|
- NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=${STRIPE_PUBLISHABLE_KEY}
|
||||||
volumes:
|
volumes:
|
||||||
- ./docker/data/dashboard/.next:/usr/app/.next
|
- ./docker/data/dashboard/.next:/usr/app/.next
|
||||||
|
@ -78,3 +75,25 @@ services:
|
||||||
- 3000
|
- 3000
|
||||||
depends_on:
|
depends_on:
|
||||||
- mongo
|
- mongo
|
||||||
|
|
||||||
|
dashboard-v2:
|
||||||
|
build:
|
||||||
|
context: ./packages/dashboard-v2
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: dashboard-v2
|
||||||
|
restart: unless-stopped
|
||||||
|
logging: *default-logging
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
environment:
|
||||||
|
- GATSBY_PORTAL_DOMAIN=${PORTAL_DOMAIN}
|
||||||
|
volumes:
|
||||||
|
- ./docker/data/dashboard-v2/.cache:/usr/app/.cache
|
||||||
|
- ./docker/data/dashboard-v2/public:/usr/app/public
|
||||||
|
networks:
|
||||||
|
shared:
|
||||||
|
ipv4_address: 10.10.10.86
|
||||||
|
expose:
|
||||||
|
- 9000
|
||||||
|
depends_on:
|
||||||
|
- mongo
|
||||||
|
|
|
@ -13,9 +13,9 @@ services:
|
||||||
- BLOCKER_PORT=4000
|
- BLOCKER_PORT=4000
|
||||||
|
|
||||||
blocker:
|
blocker:
|
||||||
build:
|
# uncomment "build" and comment out "image" to build from sources
|
||||||
context: ./docker/blocker
|
# build: https://github.com/SkynetLabs/blocker.git#main
|
||||||
dockerfile: Dockerfile
|
image: skynetlabs/blocker
|
||||||
container_name: blocker
|
container_name: blocker
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
logging: *default-logging
|
logging: *default-logging
|
||||||
|
|
|
@ -10,7 +10,7 @@ services:
|
||||||
sia:
|
sia:
|
||||||
environment:
|
environment:
|
||||||
- JAEGER_DISABLED=${JAEGER_DISABLED:-false} # Enable/Disable tracing
|
- JAEGER_DISABLED=${JAEGER_DISABLED:-false} # Enable/Disable tracing
|
||||||
- JAEGER_SERVICE_NAME=${PORTAL_NAME:-Skyd} # change to e.g. eu-ger-1
|
- JAEGER_SERVICE_NAME=${SERVER_DOMAIN:-Skyd} # change to e.g. eu-ger-1
|
||||||
# Configuration
|
# Configuration
|
||||||
# See https://github.com/jaegertracing/jaeger-client-go#environment-variables
|
# See https://github.com/jaegertracing/jaeger-client-go#environment-variables
|
||||||
# for all options.
|
# for all options.
|
||||||
|
@ -21,7 +21,7 @@ services:
|
||||||
- JAEGER_REPORTER_LOG_SPANS=false
|
- JAEGER_REPORTER_LOG_SPANS=false
|
||||||
|
|
||||||
jaeger-agent:
|
jaeger-agent:
|
||||||
image: jaegertracing/jaeger-agent
|
image: jaegertracing/jaeger-agent:1.32.0
|
||||||
command:
|
command:
|
||||||
[
|
[
|
||||||
"--reporter.grpc.host-port=jaeger-collector:14250",
|
"--reporter.grpc.host-port=jaeger-collector:14250",
|
||||||
|
@ -43,7 +43,7 @@ services:
|
||||||
- jaeger-collector
|
- jaeger-collector
|
||||||
|
|
||||||
jaeger-collector:
|
jaeger-collector:
|
||||||
image: jaegertracing/jaeger-collector
|
image: jaegertracing/jaeger-collector:1.32.0
|
||||||
entrypoint: /wait_to_start.sh
|
entrypoint: /wait_to_start.sh
|
||||||
container_name: jaeger-collector
|
container_name: jaeger-collector
|
||||||
restart: on-failure
|
restart: on-failure
|
||||||
|
@ -68,7 +68,7 @@ services:
|
||||||
- elasticsearch
|
- elasticsearch
|
||||||
|
|
||||||
jaeger-query:
|
jaeger-query:
|
||||||
image: jaegertracing/jaeger-query
|
image: jaegertracing/jaeger-query:1.32.0
|
||||||
entrypoint: /wait_to_start.sh
|
entrypoint: /wait_to_start.sh
|
||||||
container_name: jaeger-query
|
container_name: jaeger-query
|
||||||
restart: on-failure
|
restart: on-failure
|
||||||
|
|
|
@ -26,19 +26,17 @@ services:
|
||||||
ipv4_address: 10.10.10.100
|
ipv4_address: 10.10.10.100
|
||||||
|
|
||||||
malware-scanner:
|
malware-scanner:
|
||||||
build:
|
# uncomment "build" and comment out "image" to build from sources
|
||||||
context: ./docker/malware-scanner
|
# build: https://github.com/SkynetLabs/malware-scanner.git#main
|
||||||
dockerfile: Dockerfile
|
image: skynetlabs/malware-scanner
|
||||||
args:
|
|
||||||
branch: main
|
|
||||||
container_name: malware-scanner
|
container_name: malware-scanner
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
logging: *default-logging
|
logging: *default-logging
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
environment:
|
environment:
|
||||||
- CLAMAV_IP=${CLAMAV_IP:-10.10.10.100}
|
- CLAMAV_IP=10.10.10.100
|
||||||
- CLAMAV_PORT=${CLAMAV_PORT:-3310}
|
- CLAMAV_PORT=3310
|
||||||
- BLOCKER_IP=10.10.10.110
|
- BLOCKER_IP=10.10.10.110
|
||||||
- BLOCKER_PORT=4000
|
- BLOCKER_PORT=4000
|
||||||
expose:
|
expose:
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
version: "3.7"
|
|
||||||
|
|
||||||
services:
|
|
||||||
nginx:
|
|
||||||
build:
|
|
||||||
context: ./docker/nginx
|
|
||||||
dockerfile: Dockerfile.bionic
|
|
||||||
args:
|
|
||||||
RESTY_ADD_PACKAGE_BUILDDEPS: git
|
|
||||||
RESTY_EVAL_PRE_CONFIGURE: git clone https://github.com/fdintino/nginx-upload-module /tmp/nginx-upload-module
|
|
||||||
RESTY_CONFIG_OPTIONS_MORE: --add-module=/tmp/nginx-upload-module
|
|
||||||
RESTY_EVAL_POST_MAKE: /usr/local/openresty/luajit/bin/luarocks install luasocket
|
|
|
@ -25,11 +25,10 @@ services:
|
||||||
logging: *default-logging
|
logging: *default-logging
|
||||||
environment:
|
environment:
|
||||||
- SIA_MODULES=gctwra
|
- SIA_MODULES=gctwra
|
||||||
- SKYD_DISK_CACHE_ENABLED=${SKYD_DISK_CACHE_ENABLED:-false}
|
- SKYD_DISK_CACHE_ENABLED=${SKYD_DISK_CACHE_ENABLED:-true}
|
||||||
- SKYD_DISK_CACHE_SIZE=${SKYD_DISK_CACHE_SIZE:-53690000000} # 50GB
|
- SKYD_DISK_CACHE_SIZE=${SKYD_DISK_CACHE_SIZE:-53690000000} # 50GB
|
||||||
- SKYD_DISK_CACHE_MIN_HITS=${SKYD_DISK_CACHE_MIN_HITS:-3}
|
- SKYD_DISK_CACHE_MIN_HITS=${SKYD_DISK_CACHE_MIN_HITS:-3}
|
||||||
- SKYD_DISK_CACHE_HIT_PERIOD=${SKYD_DISK_CACHE_HIT_PERIOD:-3600} # 1h
|
- SKYD_DISK_CACHE_HIT_PERIOD=${SKYD_DISK_CACHE_HIT_PERIOD:-3600} # 1h
|
||||||
|
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
volumes:
|
volumes:
|
||||||
|
@ -40,21 +39,19 @@ services:
|
||||||
expose:
|
expose:
|
||||||
- 9980
|
- 9980
|
||||||
|
|
||||||
caddy:
|
certbot:
|
||||||
build:
|
image: certbot/dns-route53:v1.25.0
|
||||||
context: ./docker/caddy
|
entrypoint: sh /entrypoint.sh
|
||||||
dockerfile: Dockerfile
|
container_name: certbot
|
||||||
container_name: caddy
|
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
logging: *default-logging
|
logging: *default-logging
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
|
environment:
|
||||||
|
- CERTBOT_ARGS=--dns-route53
|
||||||
volumes:
|
volumes:
|
||||||
- ./docker/data/caddy/data:/data
|
- ./docker/certbot/entrypoint.sh:/entrypoint.sh
|
||||||
- ./docker/data/caddy/config:/config
|
- ./docker/data/certbot:/etc/letsencrypt
|
||||||
networks:
|
|
||||||
shared:
|
|
||||||
ipv4_address: 10.10.10.20
|
|
||||||
|
|
||||||
nginx:
|
nginx:
|
||||||
build:
|
build:
|
||||||
|
@ -71,7 +68,7 @@ services:
|
||||||
- ./docker/data/nginx/logs:/usr/local/openresty/nginx/logs
|
- ./docker/data/nginx/logs:/usr/local/openresty/nginx/logs
|
||||||
- ./docker/data/nginx/skynet:/data/nginx/skynet:ro
|
- ./docker/data/nginx/skynet:/data/nginx/skynet:ro
|
||||||
- ./docker/data/sia/apipassword:/data/sia/apipassword:ro
|
- ./docker/data/sia/apipassword:/data/sia/apipassword:ro
|
||||||
- ./docker/data/caddy/data:/data/caddy:ro
|
- ./docker/data/certbot:/etc/letsencrypt
|
||||||
networks:
|
networks:
|
||||||
shared:
|
shared:
|
||||||
ipv4_address: 10.10.10.30
|
ipv4_address: 10.10.10.30
|
||||||
|
@ -80,15 +77,16 @@ services:
|
||||||
- "80:80"
|
- "80:80"
|
||||||
depends_on:
|
depends_on:
|
||||||
- sia
|
- sia
|
||||||
- caddy
|
|
||||||
- handshake-api
|
- handshake-api
|
||||||
- dnslink-api
|
- dnslink-api
|
||||||
- website
|
- website
|
||||||
|
|
||||||
website:
|
website:
|
||||||
build:
|
# uncomment "build" and comment out "image" to build from sources
|
||||||
context: ./packages/website
|
# build:
|
||||||
dockerfile: Dockerfile
|
# context: https://github.com/SkynetLabs/skynet-webportal.git#master
|
||||||
|
# dockerfile: ./packages/website/Dockerfile
|
||||||
|
image: skynetlabs/website
|
||||||
container_name: website
|
container_name: website
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
logging: *default-logging
|
logging: *default-logging
|
||||||
|
@ -101,9 +99,7 @@ services:
|
||||||
- 9000
|
- 9000
|
||||||
|
|
||||||
handshake:
|
handshake:
|
||||||
build:
|
image: skynetlabs/hsd:3.0.1
|
||||||
context: ./docker/handshake
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
command: --chain-migrate=2 --wallet-migrate=1
|
command: --chain-migrate=2 --wallet-migrate=1
|
||||||
container_name: handshake
|
container_name: handshake
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
@ -124,9 +120,11 @@ services:
|
||||||
- 12037
|
- 12037
|
||||||
|
|
||||||
handshake-api:
|
handshake-api:
|
||||||
build:
|
# uncomment "build" and comment out "image" to build from sources
|
||||||
context: ./packages/handshake-api
|
# build:
|
||||||
dockerfile: Dockerfile
|
# context: https://github.com/SkynetLabs/skynet-webportal.git#master
|
||||||
|
# dockerfile: ./packages/handshake-api/Dockerfile
|
||||||
|
image: skynetlabs/handshake-api
|
||||||
container_name: handshake-api
|
container_name: handshake-api
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
logging: *default-logging
|
logging: *default-logging
|
||||||
|
@ -146,9 +144,11 @@ services:
|
||||||
- handshake
|
- handshake
|
||||||
|
|
||||||
dnslink-api:
|
dnslink-api:
|
||||||
build:
|
# uncomment "build" and comment out "image" to build from sources
|
||||||
context: ./packages/dnslink-api
|
# build:
|
||||||
dockerfile: Dockerfile
|
# context: https://github.com/SkynetLabs/skynet-webportal.git#master
|
||||||
|
# dockerfile: ./packages/dnslink-api/Dockerfile
|
||||||
|
image: skynetlabs/dnslink-api
|
||||||
container_name: dnslink-api
|
container_name: dnslink-api
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
logging: *default-logging
|
logging: *default-logging
|
||||||
|
@ -159,9 +159,11 @@ services:
|
||||||
- 3100
|
- 3100
|
||||||
|
|
||||||
health-check:
|
health-check:
|
||||||
build:
|
# uncomment "build" and comment out "image" to build from sources
|
||||||
context: ./packages/health-check
|
# build:
|
||||||
dockerfile: Dockerfile
|
# context: https://github.com/SkynetLabs/skynet-webportal.git#master
|
||||||
|
# dockerfile: ./packages/health-check/Dockerfile
|
||||||
|
image: skynetlabs/health-check
|
||||||
container_name: health-check
|
container_name: health-check
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
logging: *default-logging
|
logging: *default-logging
|
||||||
|
@ -177,5 +179,3 @@ services:
|
||||||
- STATE_DIR=/usr/app/state
|
- STATE_DIR=/usr/app/state
|
||||||
expose:
|
expose:
|
||||||
- 3100
|
- 3100
|
||||||
depends_on:
|
|
||||||
- caddy
|
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
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/abuse-scanner.git && \
|
|
||||||
cd abuse-scanner && \
|
|
||||||
go mod download && \
|
|
||||||
make release
|
|
||||||
|
|
||||||
ENTRYPOINT ["abuse-scanner"]
|
|
|
@ -1,22 +0,0 @@
|
||||||
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"]
|
|
|
@ -1,16 +0,0 @@
|
||||||
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/blocker.git && \
|
|
||||||
cd blocker && \
|
|
||||||
go mod download && \
|
|
||||||
make release
|
|
||||||
|
|
||||||
ENTRYPOINT ["blocker"]
|
|
|
@ -1,18 +0,0 @@
|
||||||
FROM caddy:2.4.6-builder AS caddy-builder
|
|
||||||
|
|
||||||
# available dns resolvers: https://github.com/caddy-dns
|
|
||||||
RUN xcaddy build --with github.com/caddy-dns/route53
|
|
||||||
|
|
||||||
FROM caddy:2.4.6-alpine
|
|
||||||
|
|
||||||
COPY --from=caddy-builder /usr/bin/caddy /usr/bin/caddy
|
|
||||||
|
|
||||||
# bash required for mo to work (mo is mustache templating engine - https://github.com/tests-always-included/mo)
|
|
||||||
RUN apk add --no-cache bash
|
|
||||||
|
|
||||||
COPY caddy.json.template mo /etc/caddy/
|
|
||||||
|
|
||||||
CMD [ "sh", "-c", \
|
|
||||||
"/etc/caddy/mo < /etc/caddy/caddy.json.template > /etc/caddy/caddy.json ; \
|
|
||||||
caddy run --config /etc/caddy/caddy.json" \
|
|
||||||
]
|
|
|
@ -1,39 +0,0 @@
|
||||||
{
|
|
||||||
"apps": {
|
|
||||||
"tls": {
|
|
||||||
"certificates": {
|
|
||||||
"automate": [
|
|
||||||
{{#PORTAL_DOMAIN}}
|
|
||||||
"{{PORTAL_DOMAIN}}", "*.{{PORTAL_DOMAIN}}", "*.hns.{{PORTAL_DOMAIN}}"
|
|
||||||
{{/PORTAL_DOMAIN}}
|
|
||||||
|
|
||||||
{{#PORTAL_DOMAIN}}{{#SERVER_DOMAIN}},{{/SERVER_DOMAIN}}{{/PORTAL_DOMAIN}}
|
|
||||||
|
|
||||||
{{#SERVER_DOMAIN}}
|
|
||||||
"{{SERVER_DOMAIN}}", "*.{{SERVER_DOMAIN}}", "*.hns.{{SERVER_DOMAIN}}"
|
|
||||||
{{/SERVER_DOMAIN}}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"automation": {
|
|
||||||
"policies": [
|
|
||||||
{
|
|
||||||
"issuers": [
|
|
||||||
{
|
|
||||||
"module": "acme",
|
|
||||||
"email": "{{EMAIL_ADDRESS}}",
|
|
||||||
"challenges": {
|
|
||||||
"dns": {
|
|
||||||
"provider": {
|
|
||||||
"name": "route53"
|
|
||||||
},
|
|
||||||
"ttl": "30m"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
1106
docker/caddy/mo
1106
docker/caddy/mo
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,55 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Portal domain requires 3 domain certificates:
|
||||||
|
# - exact portal domain, ie. example.com
|
||||||
|
# - wildcard subdomain on portal domain, ie. *.example.com
|
||||||
|
# used for skylinks served from portal subdomain
|
||||||
|
# - wildcard subdomain on hns portal domain subdomain, ie. *.hns.example.com
|
||||||
|
# used for resolving handshake domains
|
||||||
|
DOMAINS=${PORTAL_DOMAIN},*.${PORTAL_DOMAIN},*.hns.${PORTAL_DOMAIN}
|
||||||
|
|
||||||
|
# Add server domain when it is not empty and different from portal domain
|
||||||
|
if [ ! -z "${SERVER_DOMAIN}" ] && [ "${PORTAL_DOMAIN}" != "${SERVER_DOMAIN}" ]; then
|
||||||
|
# In case where server domain is not covered by portal domain's
|
||||||
|
# wildcard certificate, add server domain name to domains list.
|
||||||
|
# - server-001.example.com is covered by *.example.com
|
||||||
|
# - server-001.servers.example.com or server-001.example-severs.com
|
||||||
|
# are not covered by any already requested wildcard certificates
|
||||||
|
#
|
||||||
|
# The condition checks whether server domain does not match portal domain
|
||||||
|
# with exactly one level of subdomain (portal domain wildcard cert):
|
||||||
|
# (start) [anything but the dot] + [dot] + [portal domain] (end)
|
||||||
|
if ! printf "${SERVER_DOMAIN}" | grep -q -E "^[^\.]+\.${PORTAL_DOMAIN}$"; then
|
||||||
|
DOMAINS=${DOMAINS},${SERVER_DOMAIN}
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Server domain requires the same set of domain certificates as portal domain.
|
||||||
|
# Exact server domain case is handled above.
|
||||||
|
DOMAINS=${DOMAINS},*.${SERVER_DOMAIN},*.hns.${SERVER_DOMAIN}
|
||||||
|
fi
|
||||||
|
|
||||||
|
# The "wait" will prevent an exit from the script while background tasks are
|
||||||
|
# still active, so we are adding the line below as a method to prevent orphaning
|
||||||
|
# the background child processe. The trap fires when docker terminates the container.
|
||||||
|
trap exit TERM
|
||||||
|
|
||||||
|
while :; do
|
||||||
|
# Execute certbot and generate or maintain certificates for given domain string.
|
||||||
|
# --non-interactive: we are running this as an automation so we cannot be prompted
|
||||||
|
# --agree-tos: required flag marking agreement with letsencrypt tos
|
||||||
|
# --cert-name: output directory name
|
||||||
|
# --email: required for generating certificates, used for communication with CA
|
||||||
|
# --domains: comma separated list of domains (will generate one bundled SAN cert)
|
||||||
|
# Use CERTBOT_ARGS env variable to pass any additional arguments, ie --dns-route53
|
||||||
|
certbot certonly \
|
||||||
|
--non-interactive --agree-tos --cert-name skynet-portal \
|
||||||
|
--email ${EMAIL_ADDRESS} --domains ${DOMAINS} ${CERTBOT_ARGS}
|
||||||
|
|
||||||
|
# Run a background sleep process that counts down given time
|
||||||
|
# Certbot docs advise running maintenance process every 12 hours
|
||||||
|
sleep 12h &
|
||||||
|
|
||||||
|
# Await execution until sleep process is finished (it's a background process)
|
||||||
|
# Syntax explanation: ${!} expands to a pid of last ran process
|
||||||
|
wait ${!}
|
||||||
|
done
|
|
@ -1,12 +0,0 @@
|
||||||
FROM node:16.13.2-alpine
|
|
||||||
|
|
||||||
WORKDIR /opt/hsd
|
|
||||||
|
|
||||||
RUN apk update && apk add bash unbound-dev gmp-dev g++ gcc make python2 git
|
|
||||||
RUN git clone https://github.com/handshake-org/hsd.git /opt/hsd && \
|
|
||||||
cd /opt/hsd && git checkout v3.0.1 && cd -
|
|
||||||
RUN npm install --production
|
|
||||||
|
|
||||||
ENV PATH="${PATH}:/opt/hsd/bin:/opt/hsd/node_modules/.bin"
|
|
||||||
|
|
||||||
ENTRYPOINT ["hsd"]
|
|
|
@ -1,23 +0,0 @@
|
||||||
FROM golang:1.17.3
|
|
||||||
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/malware-scanner.git && \
|
|
||||||
cd malware-scanner && \
|
|
||||||
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 CLAMAV_IP=127.0.0.1
|
|
||||||
ENV CLAMAV_PORT=3310
|
|
||||||
|
|
||||||
ENTRYPOINT ["malware-scanner"]
|
|
|
@ -1,4 +1,6 @@
|
||||||
FROM openresty/openresty:1.19.9.1-bionic
|
FROM openresty/openresty:1.19.9.1-focal
|
||||||
|
|
||||||
|
WORKDIR /
|
||||||
|
|
||||||
RUN luarocks install lua-resty-http && \
|
RUN luarocks install lua-resty-http && \
|
||||||
luarocks install hasher && \
|
luarocks install hasher && \
|
||||||
|
@ -16,7 +18,9 @@ COPY nginx.conf /usr/local/openresty/nginx/conf/nginx.conf
|
||||||
CMD [ "bash", "-c", \
|
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.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.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.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 ; \
|
./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;'" \
|
/usr/local/openresty/bin/openresty '-g daemon off;'" \
|
||||||
]
|
]
|
||||||
|
|
|
@ -9,8 +9,14 @@
|
||||||
server {
|
server {
|
||||||
server_name account.{{PORTAL_DOMAIN}}; # example: account.siasky.net
|
server_name account.{{PORTAL_DOMAIN}}; # example: account.siasky.net
|
||||||
|
|
||||||
ssl_certificate /data/caddy/caddy/certificates/acme-v02.api.letsencrypt.org-directory/wildcard_.{{PORTAL_DOMAIN}}/wildcard_.{{PORTAL_DOMAIN}}.crt;
|
set_by_lua_block $skynet_portal_domain { return "{{PORTAL_DOMAIN}}" }
|
||||||
ssl_certificate_key /data/caddy/caddy/certificates/acme-v02.api.letsencrypt.org-directory/wildcard_.{{PORTAL_DOMAIN}}/wildcard_.{{PORTAL_DOMAIN}}.key;
|
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;
|
include /etc/nginx/conf.d/server/server.account;
|
||||||
}
|
}
|
||||||
|
@ -28,8 +34,8 @@
|
||||||
server {
|
server {
|
||||||
server_name account.{{SERVER_DOMAIN}}; # example: account.eu-ger-1.siasky.net
|
server_name account.{{SERVER_DOMAIN}}; # example: account.eu-ger-1.siasky.net
|
||||||
|
|
||||||
ssl_certificate /data/caddy/caddy/certificates/acme-v02.api.letsencrypt.org-directory/wildcard_.{{SERVER_DOMAIN}}/wildcard_.{{SERVER_DOMAIN}}.crt;
|
set_by_lua_block $skynet_portal_domain { return "{{SERVER_DOMAIN}}" }
|
||||||
ssl_certificate_key /data/caddy/caddy/certificates/acme-v02.api.letsencrypt.org-directory/wildcard_.{{SERVER_DOMAIN}}/wildcard_.{{SERVER_DOMAIN}}.key;
|
set_by_lua_block $skynet_server_domain { return "{{SERVER_DOMAIN}}" }
|
||||||
|
|
||||||
include /etc/nginx/conf.d/server/server.account;
|
include /etc/nginx/conf.d/server/server.account;
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,14 @@ server {
|
||||||
server {
|
server {
|
||||||
server_name {{PORTAL_DOMAIN}}; # example: siasky.net
|
server_name {{PORTAL_DOMAIN}}; # example: siasky.net
|
||||||
|
|
||||||
ssl_certificate /data/caddy/caddy/certificates/acme-v02.api.letsencrypt.org-directory/{{PORTAL_DOMAIN}}/{{PORTAL_DOMAIN}}.crt;
|
set_by_lua_block $skynet_portal_domain { return "{{PORTAL_DOMAIN}}" }
|
||||||
ssl_certificate_key /data/caddy/caddy/certificates/acme-v02.api.letsencrypt.org-directory/{{PORTAL_DOMAIN}}/{{PORTAL_DOMAIN}}.key;
|
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;
|
include /etc/nginx/conf.d/server/server.api;
|
||||||
}
|
}
|
||||||
|
@ -27,8 +33,8 @@ server {
|
||||||
server {
|
server {
|
||||||
server_name {{SERVER_DOMAIN}}; # example: eu-ger-1.siasky.net
|
server_name {{SERVER_DOMAIN}}; # example: eu-ger-1.siasky.net
|
||||||
|
|
||||||
ssl_certificate /data/caddy/caddy/certificates/acme-v02.api.letsencrypt.org-directory/{{SERVER_DOMAIN}}/{{SERVER_DOMAIN}}.crt;
|
set_by_lua_block $skynet_portal_domain { return "{{SERVER_DOMAIN}}" }
|
||||||
ssl_certificate_key /data/caddy/caddy/certificates/acme-v02.api.letsencrypt.org-directory/{{SERVER_DOMAIN}}/{{SERVER_DOMAIN}}.key;
|
set_by_lua_block $skynet_server_domain { return "{{SERVER_DOMAIN}}" }
|
||||||
|
|
||||||
include /etc/nginx/conf.d/server/server.api;
|
include /etc/nginx/conf.d/server/server.api;
|
||||||
|
|
||||||
|
|
|
@ -2,17 +2,24 @@ lua_shared_dict dnslink 10m;
|
||||||
|
|
||||||
server {
|
server {
|
||||||
listen 80 default_server;
|
listen 80 default_server;
|
||||||
listen [::]:80 default_server;
|
|
||||||
|
|
||||||
include /etc/nginx/conf.d/server/server.dnslink;
|
include /etc/nginx/conf.d/server/server.dnslink;
|
||||||
}
|
}
|
||||||
|
|
||||||
server {
|
server {
|
||||||
listen 443 default_server;
|
listen 443 default_server;
|
||||||
listen [::]:443 default_server;
|
|
||||||
|
|
||||||
ssl_certificate /etc/ssl/local-certificate.crt;
|
ssl_certificate /etc/ssl/local-certificate.crt;
|
||||||
ssl_certificate_key /etc/ssl/local-certificate.key;
|
ssl_certificate_key /etc/ssl/local-certificate.key;
|
||||||
|
|
||||||
|
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.dnslink;
|
include /etc/nginx/conf.d/server/server.dnslink;
|
||||||
}
|
}
|
|
@ -8,8 +8,14 @@ server {
|
||||||
server {
|
server {
|
||||||
server_name *.hns.{{PORTAL_DOMAIN}}; # example: *.hns.siasky.net
|
server_name *.hns.{{PORTAL_DOMAIN}}; # example: *.hns.siasky.net
|
||||||
|
|
||||||
ssl_certificate /data/caddy/caddy/certificates/acme-v02.api.letsencrypt.org-directory/wildcard_.hns.{{PORTAL_DOMAIN}}/wildcard_.hns.{{PORTAL_DOMAIN}}.crt;
|
set_by_lua_block $skynet_portal_domain { return "{{PORTAL_DOMAIN}}" }
|
||||||
ssl_certificate_key /data/caddy/caddy/certificates/acme-v02.api.letsencrypt.org-directory/wildcard_.hns.{{PORTAL_DOMAIN}}/wildcard_.hns.{{PORTAL_DOMAIN}}.key;
|
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}};
|
proxy_set_header Host {{PORTAL_DOMAIN}};
|
||||||
include /etc/nginx/conf.d/server/server.hns;
|
include /etc/nginx/conf.d/server/server.hns;
|
||||||
|
@ -28,8 +34,8 @@ server {
|
||||||
server {
|
server {
|
||||||
server_name *.hns.{{SERVER_DOMAIN}}; # example: *.hns.eu-ger-1.siasky.net
|
server_name *.hns.{{SERVER_DOMAIN}}; # example: *.hns.eu-ger-1.siasky.net
|
||||||
|
|
||||||
ssl_certificate /data/caddy/caddy/certificates/acme-v02.api.letsencrypt.org-directory/wildcard_.hns.{{SERVER_DOMAIN}}/wildcard_.hns.{{SERVER_DOMAIN}}.crt;
|
set_by_lua_block $skynet_portal_domain { return "{{SERVER_DOMAIN}}" }
|
||||||
ssl_certificate_key /data/caddy/caddy/certificates/acme-v02.api.letsencrypt.org-directory/wildcard_.hns.{{SERVER_DOMAIN}}/wildcard_.hns.{{SERVER_DOMAIN}}.key;
|
set_by_lua_block $skynet_server_domain { return "{{SERVER_DOMAIN}}" }
|
||||||
|
|
||||||
proxy_set_header Host {{SERVER_DOMAIN}};
|
proxy_set_header Host {{SERVER_DOMAIN}};
|
||||||
include /etc/nginx/conf.d/server/server.hns;
|
include /etc/nginx/conf.d/server/server.hns;
|
||||||
|
|
|
@ -8,8 +8,14 @@ server {
|
||||||
server {
|
server {
|
||||||
server_name *.{{PORTAL_DOMAIN}}; # example: *.siasky.net
|
server_name *.{{PORTAL_DOMAIN}}; # example: *.siasky.net
|
||||||
|
|
||||||
ssl_certificate /data/caddy/caddy/certificates/acme-v02.api.letsencrypt.org-directory/wildcard_.{{PORTAL_DOMAIN}}/wildcard_.{{PORTAL_DOMAIN}}.crt;
|
set_by_lua_block $skynet_portal_domain { return "{{PORTAL_DOMAIN}}" }
|
||||||
ssl_certificate_key /data/caddy/caddy/certificates/acme-v02.api.letsencrypt.org-directory/wildcard_.{{PORTAL_DOMAIN}}/wildcard_.{{PORTAL_DOMAIN}}.key;
|
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;
|
include /etc/nginx/conf.d/server/server.skylink;
|
||||||
}
|
}
|
||||||
|
@ -27,8 +33,8 @@ server {
|
||||||
server {
|
server {
|
||||||
server_name *.{{SERVER_DOMAIN}}; # example: *.eu-ger-1.siasky.net
|
server_name *.{{SERVER_DOMAIN}}; # example: *.eu-ger-1.siasky.net
|
||||||
|
|
||||||
ssl_certificate /data/caddy/caddy/certificates/acme-v02.api.letsencrypt.org-directory/wildcard_.{{SERVER_DOMAIN}}/wildcard_.{{SERVER_DOMAIN}}.crt;
|
set_by_lua_block $skynet_portal_domain { return "{{SERVER_DOMAIN}}" }
|
||||||
ssl_certificate_key /data/caddy/caddy/certificates/acme-v02.api.letsencrypt.org-directory/wildcard_.{{SERVER_DOMAIN}}/wildcard_.{{SERVER_DOMAIN}}.key;
|
set_by_lua_block $skynet_server_domain { return "{{SERVER_DOMAIN}}" }
|
||||||
|
|
||||||
include /etc/nginx/conf.d/server/server.skylink;
|
include /etc/nginx/conf.d/server/server.skylink;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
more_set_headers 'Access-Control-Allow-Origin: $http_origin';
|
more_set_headers 'Access-Control-Allow-Origin: $http_origin';
|
||||||
more_set_headers 'Access-Control-Allow-Credentials: true';
|
more_set_headers 'Access-Control-Allow-Credentials: true';
|
||||||
more_set_headers 'Access-Control-Allow-Methods: GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE';
|
more_set_headers 'Access-Control-Allow-Methods: GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE';
|
||||||
more_set_headers 'Access-Control-Allow-Headers: DNT,User-Agent,X-Requested-With,If-Modified-Since,If-None-Match,Cache-Control,Content-Type,Range,X-HTTP-Method-Override,upload-offset,upload-metadata,upload-length,tus-version,tus-resumable,tus-extension,tus-max-size,upload-concat,location';
|
more_set_headers 'Access-Control-Allow-Headers: DNT,User-Agent,X-Requested-With,If-Modified-Since,If-None-Match,Cache-Control,Content-Type,Range,X-HTTP-Method-Override,upload-offset,upload-metadata,upload-length,tus-version,tus-resumable,tus-extension,tus-max-size,upload-concat,location,Skynet-API-Key';
|
||||||
more_set_headers 'Access-Control-Expose-Headers: Content-Length,Content-Range,ETag,Skynet-File-Metadata,Skynet-Skylink,Skynet-Proof,Skynet-Portal-Api,Skynet-Server-Api,upload-offset,upload-metadata,upload-length,tus-version,tus-resumable,tus-extension,tus-max-size,upload-concat,location';
|
more_set_headers 'Access-Control-Expose-Headers: Content-Length,Content-Range,ETag,Accept-Ranges,Skynet-File-Metadata,Skynet-Skylink,Skynet-Proof,Skynet-Portal-Api,Skynet-Server-Api,upload-offset,upload-metadata,upload-length,tus-version,tus-resumable,tus-extension,tus-max-size,upload-concat,location';
|
||||||
|
|
|
@ -13,6 +13,3 @@ set $skylink "";
|
||||||
|
|
||||||
# cached account limits (json string) - applies only if accounts are enabled
|
# cached account limits (json string) - applies only if accounts are enabled
|
||||||
set $account_limits "";
|
set $account_limits "";
|
||||||
|
|
||||||
# set this internal flag to true if current request should not be limited in any way
|
|
||||||
set $internal_no_limits "false";
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
include /etc/nginx/conf.d/include/proxy-buffer;
|
|
||||||
include /etc/nginx/conf.d/include/proxy-pass-internal;
|
include /etc/nginx/conf.d/include/proxy-pass-internal;
|
||||||
include /etc/nginx/conf.d/include/portal-access-check;
|
include /etc/nginx/conf.d/include/portal-access-check;
|
||||||
|
|
||||||
|
@ -81,8 +80,8 @@ proxy_pass https://127.0.0.1/$skylink$path$is_args$args;
|
||||||
|
|
||||||
# in case siad returns location header, we need to replace the skylink with the domain name
|
# in case siad returns location header, we need to replace the skylink with the domain name
|
||||||
header_filter_by_lua_block {
|
header_filter_by_lua_block {
|
||||||
ngx.header["Skynet-Portal-Api"] = os.getenv("SKYNET_PORTAL_API")
|
ngx.header["Skynet-Portal-Api"] = ngx.var.scheme .. "://" .. ngx.var.skynet_portal_domain
|
||||||
ngx.header["Skynet-Server-Api"] = os.getenv("SKYNET_SERVER_API")
|
ngx.header["Skynet-Server-Api"] = ngx.var.scheme .. "://" .. ngx.var.skynet_server_domain
|
||||||
|
|
||||||
if ngx.header.location then
|
if ngx.header.location then
|
||||||
-- match location redirect part after the skylink
|
-- match location redirect part after the skylink
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
include /etc/nginx/conf.d/include/cors;
|
include /etc/nginx/conf.d/include/cors;
|
||||||
include /etc/nginx/conf.d/include/proxy-buffer;
|
|
||||||
include /etc/nginx/conf.d/include/proxy-cache-downloads;
|
|
||||||
include /etc/nginx/conf.d/include/track-download;
|
include /etc/nginx/conf.d/include/track-download;
|
||||||
|
|
||||||
limit_conn downloads_by_ip 100; # ddos protection: max 100 downloads at a time
|
limit_conn downloads_by_ip 100; # ddos protection: max 100 downloads at a time
|
||||||
|
@ -9,59 +7,10 @@ limit_conn downloads_by_ip 100; # ddos protection: max 100 downloads at a time
|
||||||
# this is important because we want only one format in cache keys and logs
|
# this is important because we want only one format in cache keys and logs
|
||||||
set_by_lua_block $skylink { return require("skynet.skylink").parse(ngx.var.skylink) }
|
set_by_lua_block $skylink { return require("skynet.skylink").parse(ngx.var.skylink) }
|
||||||
|
|
||||||
# $skylink_v1 and $skylink_v2 variables default to the same value but in case the requested skylink was:
|
|
||||||
# a) skylink v1 - it would not matter, no additional logic is executed
|
|
||||||
# b) skylink v2 - in a lua block below we will resolve the skylink v2 into skylink v1 and update
|
|
||||||
# $skylink_v1 variable so then the proxy request to skyd can be cached in nginx (proxy_cache_key
|
|
||||||
# in proxy-cache-downloads includes $skylink_v1 as a part of the cache key)
|
|
||||||
set $skylink_v1 $skylink;
|
|
||||||
set $skylink_v2 $skylink;
|
|
||||||
|
|
||||||
# variable for Skynet-Proof header that we need to inject
|
|
||||||
# into a response if the request was for skylink v2
|
|
||||||
set $skynet_proof '';
|
|
||||||
|
|
||||||
# default download rate to unlimited
|
# default download rate to unlimited
|
||||||
set $limit_rate 0;
|
set $limit_rate 0;
|
||||||
|
|
||||||
access_by_lua_block {
|
access_by_lua_block {
|
||||||
local httpc = require("resty.http").new()
|
|
||||||
|
|
||||||
-- detect whether requested skylink is v2
|
|
||||||
local isBase32v2 = string.len(ngx.var.skylink) == 55 and string.sub(ngx.var.skylink, 0, 2) == "04"
|
|
||||||
local isBase64v2 = string.len(ngx.var.skylink) == 46 and string.sub(ngx.var.skylink, 0, 2) == "AQ"
|
|
||||||
|
|
||||||
if isBase32v2 or isBase64v2 then
|
|
||||||
-- 10.10.10.10 points to sia service (alias not available when using resty-http)
|
|
||||||
local res, err = httpc:request_uri("http://10.10.10.10:9980/skynet/resolve/" .. ngx.var.skylink_v2, {
|
|
||||||
headers = { ["User-Agent"] = "Sia-Agent" }
|
|
||||||
})
|
|
||||||
|
|
||||||
-- print error and exit with 500 or exit with response if status is not 200
|
|
||||||
if err or (res and res.status ~= ngx.HTTP_OK) then
|
|
||||||
ngx.status = (err and ngx.HTTP_INTERNAL_SERVER_ERROR) or res.status
|
|
||||||
ngx.header["content-type"] = "text/plain"
|
|
||||||
ngx.say(err or res.body)
|
|
||||||
return ngx.exit(ngx.status)
|
|
||||||
end
|
|
||||||
|
|
||||||
local json = require('cjson')
|
|
||||||
local resolve = json.decode(res.body)
|
|
||||||
ngx.var.skylink_v1 = resolve.skylink
|
|
||||||
ngx.var.skynet_proof = res.headers["Skynet-Proof"]
|
|
||||||
end
|
|
||||||
|
|
||||||
-- check if skylink v1 is present on blocklist (compare hashes)
|
|
||||||
if require("skynet.blocklist").is_blocked(ngx.var.skylink_v1) then
|
|
||||||
return require("skynet.blocklist").exit_illegal()
|
|
||||||
end
|
|
||||||
|
|
||||||
-- if skylink is found on nocache list then set internal nocache variable
|
|
||||||
-- to tell nginx that it should not try and cache this file (too large)
|
|
||||||
if ngx.shared.nocache:get(ngx.var.skylink_v1) then
|
|
||||||
ngx.var.nocache = "1"
|
|
||||||
end
|
|
||||||
|
|
||||||
if require("skynet.account").accounts_enabled() then
|
if require("skynet.account").accounts_enabled() then
|
||||||
-- check if portal is in authenticated only mode
|
-- check if portal is in authenticated only mode
|
||||||
if require("skynet.account").is_access_unauthorized() then
|
if require("skynet.account").is_access_unauthorized() then
|
||||||
|
@ -81,33 +30,10 @@ access_by_lua_block {
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
header_filter_by_lua_block {
|
|
||||||
ngx.header["Skynet-Portal-Api"] = os.getenv("SKYNET_PORTAL_API")
|
|
||||||
ngx.header["Skynet-Server-Api"] = os.getenv("SKYNET_SERVER_API")
|
|
||||||
|
|
||||||
-- not empty skynet_proof means this is a skylink v2 request
|
|
||||||
-- so we should replace the Skynet-Proof header with the one
|
|
||||||
-- we got from /skynet/resolve/ endpoint, otherwise we would
|
|
||||||
-- be serving cached empty v1 skylink Skynet-Proof header
|
|
||||||
if ngx.var.skynet_proof and ngx.var.skynet_proof ~= "" then
|
|
||||||
ngx.header["Skynet-Proof"] = ngx.var.skynet_proof
|
|
||||||
end
|
|
||||||
|
|
||||||
-- add skylink to nocache list if it exceeds 1GB (1e+9 bytes) threshold
|
|
||||||
-- (content length can be nil for already cached files - we can ignore them)
|
|
||||||
if ngx.header["Content-Length"] and tonumber(ngx.header["Content-Length"]) > 1e+9 then
|
|
||||||
ngx.shared.nocache:set(ngx.var.skylink_v1, ngx.header["Content-Length"])
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
limit_rate_after 512k;
|
limit_rate_after 512k;
|
||||||
limit_rate $limit_rate;
|
limit_rate $limit_rate;
|
||||||
|
|
||||||
proxy_read_timeout 600;
|
proxy_read_timeout 600;
|
||||||
proxy_set_header User-Agent: Sia-Agent;
|
proxy_set_header User-Agent: Sia-Agent;
|
||||||
|
|
||||||
# in case the requested skylink was v2 and we already resolved it to skylink v1, we are going to pass resolved
|
proxy_pass http://sia:9980/skynet/skylink/$skylink$path$is_args$args;
|
||||||
# skylink v1 to skyd to save that extra skylink v2 lookup in skyd but in turn, in case skyd returns a redirect
|
|
||||||
# we need to rewrite the skylink v1 to skylink v2 in the location header with proxy_redirect
|
|
||||||
proxy_redirect $skylink_v1 $skylink_v2;
|
|
||||||
proxy_pass http://sia:9980/skynet/skylink/$skylink_v1$path$is_args$args;
|
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
# if you are expecting large headers (ie. Skynet-Skyfile-Metadata), tune these values to your needs
|
|
||||||
# read more: https://www.getpagespeed.com/server-setup/nginx/tuning-proxy_buffer_size-in-nginx
|
|
||||||
proxy_buffer_size 4096k;
|
|
||||||
proxy_buffers 64 256k;
|
|
||||||
proxy_busy_buffers_size 4096k; # at least as high as proxy_buffer_size
|
|
|
@ -1,14 +0,0 @@
|
||||||
proxy_cache skynet; # cache name
|
|
||||||
proxy_cache_key $skylink_v1$path$arg_format$arg_attachment$arg_start$arg_end$http_range; # unique cache key
|
|
||||||
proxy_cache_min_uses 3; # cache after 3 uses
|
|
||||||
proxy_cache_valid 200 206 307 308 48h; # keep 200, 206, 307 and 308 responses valid for up to 2 days
|
|
||||||
add_header X-Proxy-Cache $upstream_cache_status; # add response header to indicate cache hits and misses
|
|
||||||
|
|
||||||
# bypass - this will bypass cache hit on request (status BYPASS)
|
|
||||||
# but still stores file in cache if cache conditions are met
|
|
||||||
proxy_cache_bypass $cookie_nocache $arg_nocache;
|
|
||||||
|
|
||||||
# no cache - this will ignore cache on request (status MISS)
|
|
||||||
# and does not store file in cache under no condition
|
|
||||||
set_if_empty $nocache "0";
|
|
||||||
proxy_no_cache $nocache;
|
|
|
@ -1,7 +1,10 @@
|
||||||
# https://ssl-config.mozilla.org/#server=nginx&version=1.17.7&config=intermediate&openssl=1.1.1d&hsts=false&ocsp=false&guideline=5.6
|
# https://ssl-config.mozilla.org/#server=nginx&version=1.17.7&config=intermediate&openssl=1.1.1d&hsts=false&ocsp=false&guideline=5.6
|
||||||
|
|
||||||
|
ssl_certificate /etc/letsencrypt/live/skynet-portal/fullchain.pem;
|
||||||
|
ssl_certificate_key /etc/letsencrypt/live/skynet-portal/privkey.pem;
|
||||||
|
|
||||||
ssl_session_timeout 1d;
|
ssl_session_timeout 1d;
|
||||||
ssl_session_cache shared:MozSSL:10m; # about 40000 sessions
|
ssl_session_cache shared:MozSSL:10m; # about 40000 sessions
|
||||||
ssl_session_tickets off;
|
ssl_session_tickets off;
|
||||||
|
|
||||||
# curl https://ssl-config.mozilla.org/ffdhe2048.txt > /path/to/dhparam
|
# curl https://ssl-config.mozilla.org/ffdhe2048.txt > /path/to/dhparam
|
||||||
|
@ -11,3 +14,13 @@ ssl_dhparam /etc/nginx/conf.d/dhparam.pem;
|
||||||
ssl_protocols TLSv1.2 TLSv1.3;
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
|
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
|
||||||
ssl_prefer_server_ciphers off;
|
ssl_prefer_server_ciphers off;
|
||||||
|
|
||||||
|
# HSTS (ngx_http_headers_module is required) (63072000 seconds)
|
||||||
|
add_header Strict-Transport-Security "max-age=63072000" always;
|
||||||
|
|
||||||
|
# OCSP stapling
|
||||||
|
ssl_stapling on;
|
||||||
|
ssl_stapling_verify on;
|
||||||
|
|
||||||
|
# verify chain of trust of OCSP response using Root CA and Intermediate certs
|
||||||
|
ssl_trusted_certificate /etc/letsencrypt/live/skynet-portal/chain.pem;
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
# register the download in accounts service (cookies should contain jwt)
|
|
||||||
log_by_lua_block {
|
log_by_lua_block {
|
||||||
-- this block runs only when accounts are enabled
|
local skynet_account = require("skynet.account")
|
||||||
if require("skynet.account").accounts_enabled() then
|
|
||||||
local function track(premature, skylink, status, body_bytes_sent, jwt)
|
-- 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
|
if premature then return end
|
||||||
|
|
||||||
local httpc = require("resty.http").new()
|
local httpc = require("resty.http").new()
|
||||||
|
@ -11,16 +12,18 @@ log_by_lua_block {
|
||||||
-- 10.10.10.70 points to accounts service (alias not available when using resty-http)
|
-- 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, {
|
local res, err = httpc:request_uri("http://10.10.10.70:3000/track/download/" .. skylink .. "?" .. query, {
|
||||||
method = "POST",
|
method = "POST",
|
||||||
headers = { ["Cookie"] = "skynet-jwt=" .. jwt },
|
headers = auth_headers,
|
||||||
})
|
})
|
||||||
|
|
||||||
if err or (res and res.status ~= ngx.HTTP_NO_CONTENT) then
|
if err or (res and res.status ~= ngx.HTTP_NO_CONTENT) then
|
||||||
ngx.log(ngx.ERR, "Failed accounts service request /track/download/" .. skylink .. ": ", err or ("[HTTP " .. res.status .. "] " .. res.body))
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
if ngx.header["Skynet-Skylink"] and ngx.var.skynet_jwt ~= "" and ngx.status >= ngx.HTTP_OK and ngx.status < ngx.HTTP_SPECIAL_RESPONSE then
|
if ngx.header["Skynet-Skylink"] and ngx.status >= ngx.HTTP_OK and ngx.status < ngx.HTTP_SPECIAL_RESPONSE then
|
||||||
local ok, err = ngx.timer.at(0, track, ngx.header["Skynet-Skylink"], ngx.status, ngx.var.body_bytes_sent, ngx.var.skynet_jwt)
|
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
|
if err then ngx.log(ngx.ERR, "Failed to create timer: ", err) end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -38,7 +41,8 @@ log_by_lua_block {
|
||||||
})
|
})
|
||||||
|
|
||||||
if err or (res and res.status ~= ngx.HTTP_OK) then
|
if err or (res and res.status ~= ngx.HTTP_OK) then
|
||||||
ngx.log(ngx.ERR, "Failed malware-scanner request /scan/" .. skylink .. ": ", err or ("[HTTP " .. res.status .. "] " .. res.body))
|
local error_response = err or ("[HTTP " .. res.status .. "] " .. res.body)
|
||||||
|
ngx.log(ngx.ERR, "Failed malware-scanner request /scan/" .. skylink .. ": ", error_response)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
# register the registry access in accounts service (cookies should contain jwt)
|
|
||||||
log_by_lua_block {
|
log_by_lua_block {
|
||||||
-- this block runs only when accounts are enabled
|
local skynet_account = require("skynet.account")
|
||||||
if require("skynet.account").accounts_enabled() then
|
|
||||||
local function track(premature, request_method, jwt)
|
-- 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
|
if premature then return end
|
||||||
|
|
||||||
local httpc = require("resty.http").new()
|
local httpc = require("resty.http").new()
|
||||||
|
@ -14,16 +15,18 @@ log_by_lua_block {
|
||||||
-- 10.10.10.70 points to accounts service (alias not available when using resty-http)
|
-- 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, {
|
local res, err = httpc:request_uri("http://10.10.10.70:3000/track/registry/" .. registry_action, {
|
||||||
method = "POST",
|
method = "POST",
|
||||||
headers = { ["Cookie"] = "skynet-jwt=" .. jwt },
|
headers = auth_headers,
|
||||||
})
|
})
|
||||||
|
|
||||||
if err or (res and res.status ~= ngx.HTTP_NO_CONTENT) then
|
if err or (res and res.status ~= ngx.HTTP_NO_CONTENT) then
|
||||||
ngx.log(ngx.ERR, "Failed accounts service request /track/registry/" .. registry_action .. ": ", err or ("[HTTP " .. res.status .. "] " .. res.body))
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
if ngx.var.skynet_jwt ~= "" and (ngx.status == ngx.HTTP_OK or ngx.status == ngx.HTTP_NOT_FOUND) then
|
if ngx.status == ngx.HTTP_OK or ngx.status == ngx.HTTP_NOT_FOUND then
|
||||||
local ok, err = ngx.timer.at(0, track, ngx.req.get_method(), ngx.var.skynet_jwt)
|
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
|
if err then ngx.log(ngx.ERR, "Failed to create timer: ", err) end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
# register the upload in accounts service (cookies should contain jwt)
|
|
||||||
log_by_lua_block {
|
log_by_lua_block {
|
||||||
-- this block runs only when accounts are enabled
|
local skynet_account = require("skynet.account")
|
||||||
if require("skynet.account").accounts_enabled() then
|
|
||||||
local function track(premature, skylink, jwt)
|
-- 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
|
if premature then return end
|
||||||
|
|
||||||
local httpc = require("resty.http").new()
|
local httpc = require("resty.http").new()
|
||||||
|
@ -10,17 +11,19 @@ log_by_lua_block {
|
||||||
-- 10.10.10.70 points to accounts service (alias not available when using resty-http)
|
-- 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, {
|
local res, err = httpc:request_uri("http://10.10.10.70:3000/track/upload/" .. skylink, {
|
||||||
method = "POST",
|
method = "POST",
|
||||||
headers = { ["Cookie"] = "skynet-jwt=" .. jwt },
|
headers = auth_headers,
|
||||||
})
|
})
|
||||||
|
|
||||||
if err or (res and res.status ~= ngx.HTTP_NO_CONTENT) then
|
if err or (res and res.status ~= ngx.HTTP_NO_CONTENT) then
|
||||||
ngx.log(ngx.ERR, "Failed accounts service request /track/upload/" .. skylink .. ": ", err or ("[HTTP " .. res.status .. "] " .. res.body))
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
-- report all skylinks (header empty if request failed) but only if jwt is preset (user is authenticated)
|
-- report all skylinks (header empty if request failed) but only if jwt is preset (user is authenticated)
|
||||||
if ngx.header["Skynet-Skylink"] and ngx.var.skynet_jwt ~= "" then
|
if ngx.header["Skynet-Skylink"] then
|
||||||
local ok, err = ngx.timer.at(0, track, ngx.header["Skynet-Skylink"], ngx.var.skynet_jwt)
|
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
|
if err then ngx.log(ngx.ERR, "Failed to create timer: ", err) end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -38,7 +41,8 @@ log_by_lua_block {
|
||||||
})
|
})
|
||||||
|
|
||||||
if err or (res and res.status ~= ngx.HTTP_OK) then
|
if err or (res and res.status ~= ngx.HTTP_OK) then
|
||||||
ngx.log(ngx.ERR, "Failed malware-scanner request /scan/" .. skylink .. ": ", err or ("[HTTP " .. res.status .. "] " .. res.body))
|
local error_response = err or ("[HTTP " .. res.status .. "] " .. res.body)
|
||||||
|
ngx.log(ngx.ERR, "Failed malware-scanner request /scan/" .. skylink .. ": ", error_response)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
server {
|
|
||||||
# local server - do not expose this port externally
|
|
||||||
listen 8000;
|
|
||||||
listen [::]:8000;
|
|
||||||
|
|
||||||
# secure traffic by limiting to only local networks
|
|
||||||
include /etc/nginx/conf.d/include/local-network-only;
|
|
||||||
|
|
||||||
include /etc/nginx/conf.d/server/server.local;
|
|
||||||
}
|
|
|
@ -1,5 +1,4 @@
|
||||||
listen 443 ssl http2;
|
listen 443 ssl http2;
|
||||||
listen [::]:443 ssl http2;
|
|
||||||
|
|
||||||
include /etc/nginx/conf.d/include/ssl-settings;
|
include /etc/nginx/conf.d/include/ssl-settings;
|
||||||
include /etc/nginx/conf.d/include/init-optional-variables;
|
include /etc/nginx/conf.d/include/init-optional-variables;
|
||||||
|
@ -36,9 +35,23 @@ location /api/register {
|
||||||
proxy_pass http://accounts:3000;
|
proxy_pass http://accounts:3000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
location /api/user/pubkey/register {
|
||||||
|
include /etc/nginx/conf.d/include/cors;
|
||||||
|
|
||||||
|
rewrite /api/(.*) /$1 break;
|
||||||
|
proxy_pass http://accounts:3000;
|
||||||
|
}
|
||||||
|
|
||||||
location /api/login {
|
location /api/login {
|
||||||
include /etc/nginx/conf.d/include/cors;
|
include /etc/nginx/conf.d/include/cors;
|
||||||
|
|
||||||
rewrite /api/(.*) /$1 break;
|
rewrite /api/(.*) /$1 break;
|
||||||
proxy_pass http://accounts:3000;
|
proxy_pass http://accounts:3000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
location /api/logout {
|
||||||
|
include /etc/nginx/conf.d/include/cors;
|
||||||
|
|
||||||
|
rewrite /api/(.*) /$1 break;
|
||||||
|
proxy_pass http://accounts:3000;
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
listen 443 ssl http2;
|
listen 443 ssl http2;
|
||||||
listen [::]:443 ssl http2;
|
|
||||||
|
|
||||||
include /etc/nginx/conf.d/include/ssl-settings;
|
include /etc/nginx/conf.d/include/ssl-settings;
|
||||||
include /etc/nginx/conf.d/include/init-optional-variables;
|
include /etc/nginx/conf.d/include/init-optional-variables;
|
||||||
|
@ -22,40 +21,42 @@ client_max_body_size 128k;
|
||||||
rewrite ^/portals /skynet/portals permanent;
|
rewrite ^/portals /skynet/portals permanent;
|
||||||
rewrite ^/stats /skynet/stats permanent;
|
rewrite ^/stats /skynet/stats permanent;
|
||||||
rewrite ^/skynet/blacklist /skynet/blocklist permanent;
|
rewrite ^/skynet/blacklist /skynet/blocklist permanent;
|
||||||
|
rewrite ^/docs(?:/(.*))?$ https://sdk.skynetlabs.com/$1 permanent;
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
include /etc/nginx/conf.d/include/cors;
|
include /etc/nginx/conf.d/include/cors;
|
||||||
|
|
||||||
set $skylink "0404dsjvti046fsua4ktor9grrpe76erq9jot9cvopbhsvsu76r4r30";
|
|
||||||
set $path $uri;
|
|
||||||
set $internal_no_limits "true";
|
|
||||||
|
|
||||||
include /etc/nginx/conf.d/include/location-skylink;
|
|
||||||
|
|
||||||
proxy_intercept_errors on;
|
|
||||||
error_page 400 404 490 500 502 503 504 =200 @fallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
location @fallback {
|
|
||||||
proxy_pass http://website:9000;
|
proxy_pass http://website:9000;
|
||||||
}
|
}
|
||||||
|
|
||||||
location /docs {
|
|
||||||
proxy_pass https://skynetlabs.github.io/skynet-docs;
|
|
||||||
}
|
|
||||||
|
|
||||||
location /skynet/blocklist {
|
location /skynet/blocklist {
|
||||||
include /etc/nginx/conf.d/include/cors;
|
include /etc/nginx/conf.d/include/cors;
|
||||||
|
|
||||||
|
add_header X-Proxy-Cache $upstream_cache_status;
|
||||||
|
|
||||||
proxy_cache skynet;
|
proxy_cache skynet;
|
||||||
proxy_cache_valid any 1m; # cache blocklist for 1 minute
|
proxy_cache_valid any 1m; # cache blocklist for 1 minute
|
||||||
proxy_set_header User-Agent: Sia-Agent;
|
proxy_set_header User-Agent: Sia-Agent;
|
||||||
proxy_pass http://sia:9980/skynet/blocklist;
|
proxy_pass http://sia:9980/skynet/blocklist;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
location /skynet/portal/blocklist {
|
||||||
|
include /etc/nginx/conf.d/include/cors;
|
||||||
|
|
||||||
|
add_header X-Proxy-Cache $upstream_cache_status;
|
||||||
|
|
||||||
|
proxy_cache skynet;
|
||||||
|
proxy_cache_valid 200 204 15m; # cache portal blocklist for 15 minutes
|
||||||
|
|
||||||
|
# 10.10.10.110 points to blocker service
|
||||||
|
proxy_pass http://10.10.10.110:4000/blocklist;
|
||||||
|
}
|
||||||
|
|
||||||
location /skynet/portals {
|
location /skynet/portals {
|
||||||
include /etc/nginx/conf.d/include/cors;
|
include /etc/nginx/conf.d/include/cors;
|
||||||
|
|
||||||
|
add_header X-Proxy-Cache $upstream_cache_status;
|
||||||
|
|
||||||
proxy_cache skynet;
|
proxy_cache skynet;
|
||||||
proxy_cache_valid any 1m; # cache portals for 1 minute
|
proxy_cache_valid any 1m; # cache portals for 1 minute
|
||||||
proxy_set_header User-Agent: Sia-Agent;
|
proxy_set_header User-Agent: Sia-Agent;
|
||||||
|
@ -65,6 +66,8 @@ location /skynet/portals {
|
||||||
location /skynet/stats {
|
location /skynet/stats {
|
||||||
include /etc/nginx/conf.d/include/cors;
|
include /etc/nginx/conf.d/include/cors;
|
||||||
|
|
||||||
|
add_header X-Proxy-Cache $upstream_cache_status;
|
||||||
|
|
||||||
proxy_cache skynet;
|
proxy_cache skynet;
|
||||||
proxy_cache_valid any 1m; # cache stats for 1 minute
|
proxy_cache_valid any 1m; # cache stats for 1 minute
|
||||||
proxy_set_header User-Agent: Sia-Agent;
|
proxy_set_header User-Agent: Sia-Agent;
|
||||||
|
@ -90,6 +93,8 @@ location /serverload {
|
||||||
location /skynet/health {
|
location /skynet/health {
|
||||||
include /etc/nginx/conf.d/include/cors;
|
include /etc/nginx/conf.d/include/cors;
|
||||||
|
|
||||||
|
add_header X-Proxy-Cache $upstream_cache_status;
|
||||||
|
|
||||||
proxy_cache skynet;
|
proxy_cache skynet;
|
||||||
proxy_cache_key $request_uri; # use whole request uri (uri + args) as cache key
|
proxy_cache_key $request_uri; # use whole request uri (uri + args) as cache key
|
||||||
proxy_cache_valid any 1m; # cache responses for 1 minute
|
proxy_cache_valid any 1m; # cache responses for 1 minute
|
||||||
|
@ -169,24 +174,25 @@ location /skynet/registry/subscription {
|
||||||
set $notificationdelay "0";
|
set $notificationdelay "0";
|
||||||
|
|
||||||
rewrite_by_lua_block {
|
rewrite_by_lua_block {
|
||||||
-- this block runs only when accounts are enabled
|
local skynet_account = require("skynet.account")
|
||||||
if os.getenv("PORTAL_MODULES"):match("a") then
|
|
||||||
local httpc = require("resty.http").new()
|
|
||||||
|
|
||||||
-- fetch account limits and set download bandwidth and registry delays accordingly
|
if skynet_account.accounts_enabled() then
|
||||||
local res, err = httpc:request_uri("http://10.10.10.70:3000/user/limits", {
|
-- check if portal is in authenticated only mode
|
||||||
headers = { ["Cookie"] = "skynet-jwt=" .. ngx.var.skynet_jwt }
|
if skynet_account.is_access_unauthorized() then
|
||||||
})
|
return skynet_account.exit_access_unauthorized()
|
||||||
|
|
||||||
-- fail gracefully in case /user/limits failed
|
|
||||||
if err or (res and res.status ~= ngx.HTTP_OK) then
|
|
||||||
ngx.log(ngx.ERR, "Failed accounts service request /user/limits: ", err or ("[HTTP " .. res.status .. "] " .. res.body))
|
|
||||||
elseif res and res.status == ngx.HTTP_OK then
|
|
||||||
local json = require('cjson')
|
|
||||||
local limits = json.decode(res.body)
|
|
||||||
ngx.var.bandwidthlimit = limits.download
|
|
||||||
ngx.var.notificationdelay = limits.registry
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- check if portal is in subscription only mode
|
||||||
|
if skynet_account.is_access_forbidden() then
|
||||||
|
return skynet_account.exit_access_forbidden()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- get account limits of currently authenticated user
|
||||||
|
local limits = skynet_account.get_account_limits()
|
||||||
|
|
||||||
|
-- apply bandwidth limit and notification delay
|
||||||
|
ngx.var.bandwidthlimit = limits.download
|
||||||
|
ngx.var.notificationdelay = limits.registry
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,8 +241,8 @@ location /skynet/tus {
|
||||||
limit_conn upload_conn 5;
|
limit_conn upload_conn 5;
|
||||||
limit_conn upload_conn_rl 1;
|
limit_conn upload_conn_rl 1;
|
||||||
|
|
||||||
# TUS chunks size is 40M + leaving 10M of breathing room
|
# Do not limit body size in nginx, skyd will reject early on too large upload
|
||||||
client_max_body_size 50M;
|
client_max_body_size 0;
|
||||||
|
|
||||||
# Those timeouts need to be elevated since skyd can stall reading
|
# Those timeouts need to be elevated since skyd can stall reading
|
||||||
# data for a while when overloaded which would terminate connection
|
# data for a while when overloaded which would terminate connection
|
||||||
|
@ -248,26 +254,27 @@ location /skynet/tus {
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
|
||||||
# rewrite proxy request to use correct host uri from env variable (required to return correct location header)
|
# rewrite proxy request to use correct host uri from env variable (required to return correct location header)
|
||||||
set_by_lua $SKYNET_SERVER_API 'return os.getenv("SKYNET_SERVER_API")';
|
proxy_redirect $scheme://$host $scheme://$skynet_server_domain;
|
||||||
proxy_redirect $scheme://$host $SKYNET_SERVER_API;
|
|
||||||
|
|
||||||
# proxy /skynet/tus requests to siad endpoint with all arguments
|
# proxy /skynet/tus requests to siad endpoint with all arguments
|
||||||
proxy_pass http://sia:9980;
|
proxy_pass http://sia:9980;
|
||||||
|
|
||||||
access_by_lua_block {
|
access_by_lua_block {
|
||||||
if require("skynet.account").accounts_enabled() then
|
local skynet_account = require("skynet.account")
|
||||||
|
|
||||||
|
if skynet_account.accounts_enabled() then
|
||||||
-- check if portal is in authenticated only mode
|
-- check if portal is in authenticated only mode
|
||||||
if require("skynet.account").is_access_unauthorized() then
|
if skynet_account.is_access_unauthorized() then
|
||||||
return require("skynet.account").exit_access_unauthorized()
|
return skynet_account.exit_access_unauthorized()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- check if portal is in subscription only mode
|
-- check if portal is in subscription only mode
|
||||||
if require("skynet.account").is_access_forbidden() then
|
if skynet_account.is_access_forbidden() then
|
||||||
return require("skynet.account").exit_access_forbidden()
|
return skynet_account.exit_access_forbidden()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- get account limits of currently authenticated user
|
-- get account limits of currently authenticated user
|
||||||
local limits = require("skynet.account").get_account_limits()
|
local limits = skynet_account.get_account_limits()
|
||||||
|
|
||||||
-- apply upload size limits
|
-- apply upload size limits
|
||||||
ngx.req.set_header("SkynetMaxUploadSize", limits.maxUploadSize)
|
ngx.req.set_header("SkynetMaxUploadSize", limits.maxUploadSize)
|
||||||
|
@ -276,8 +283,8 @@ location /skynet/tus {
|
||||||
|
|
||||||
# extract skylink from base64 encoded upload metadata and assign to a proper header
|
# extract skylink from base64 encoded upload metadata and assign to a proper header
|
||||||
header_filter_by_lua_block {
|
header_filter_by_lua_block {
|
||||||
ngx.header["Skynet-Portal-Api"] = os.getenv("SKYNET_PORTAL_API")
|
ngx.header["Skynet-Portal-Api"] = ngx.var.scheme .. "://" .. ngx.var.skynet_portal_domain
|
||||||
ngx.header["Skynet-Server-Api"] = os.getenv("SKYNET_SERVER_API")
|
ngx.header["Skynet-Server-Api"] = ngx.var.scheme .. "://" .. ngx.var.skynet_server_domain
|
||||||
|
|
||||||
if ngx.header["Upload-Metadata"] then
|
if ngx.header["Upload-Metadata"] then
|
||||||
local encodedSkylink = string.match(ngx.header["Upload-Metadata"], "Skylink ([^,?]+)")
|
local encodedSkylink = string.match(ngx.header["Upload-Metadata"], "Skylink ([^,?]+)")
|
||||||
|
@ -311,8 +318,8 @@ location /skynet/metadata {
|
||||||
include /etc/nginx/conf.d/include/portal-access-check;
|
include /etc/nginx/conf.d/include/portal-access-check;
|
||||||
|
|
||||||
header_filter_by_lua_block {
|
header_filter_by_lua_block {
|
||||||
ngx.header["Skynet-Portal-Api"] = os.getenv("SKYNET_PORTAL_API")
|
ngx.header["Skynet-Portal-Api"] = ngx.var.scheme .. "://" .. ngx.var.skynet_portal_domain
|
||||||
ngx.header["Skynet-Server-Api"] = os.getenv("SKYNET_SERVER_API")
|
ngx.header["Skynet-Server-Api"] = ngx.var.scheme .. "://" .. ngx.var.skynet_server_domain
|
||||||
}
|
}
|
||||||
|
|
||||||
proxy_set_header User-Agent: Sia-Agent;
|
proxy_set_header User-Agent: Sia-Agent;
|
||||||
|
@ -324,8 +331,8 @@ location /skynet/resolve {
|
||||||
include /etc/nginx/conf.d/include/portal-access-check;
|
include /etc/nginx/conf.d/include/portal-access-check;
|
||||||
|
|
||||||
header_filter_by_lua_block {
|
header_filter_by_lua_block {
|
||||||
ngx.header["Skynet-Portal-Api"] = os.getenv("SKYNET_PORTAL_API")
|
ngx.header["Skynet-Portal-Api"] = ngx.var.scheme .. "://" .. ngx.var.skynet_portal_domain
|
||||||
ngx.header["Skynet-Server-Api"] = os.getenv("SKYNET_SERVER_API")
|
ngx.header["Skynet-Server-Api"] = ngx.var.scheme .. "://" .. ngx.var.skynet_server_domain
|
||||||
}
|
}
|
||||||
|
|
||||||
proxy_set_header User-Agent: Sia-Agent;
|
proxy_set_header User-Agent: Sia-Agent;
|
||||||
|
@ -348,6 +355,44 @@ location ~ "^/file/(([a-zA-Z0-9-_]{46}|[a-z0-9]{55})(/.*)?)$" {
|
||||||
include /etc/nginx/conf.d/include/location-skylink;
|
include /etc/nginx/conf.d/include/location-skylink;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
# default download rate to unlimited
|
||||||
|
set $limit_rate 0;
|
||||||
|
|
||||||
|
access_by_lua_block {
|
||||||
|
local skynet_account = require("skynet.account")
|
||||||
|
|
||||||
|
if skynet_account.accounts_enabled() then
|
||||||
|
-- check if portal is in authenticated only mode
|
||||||
|
if skynet_account.is_access_unauthorized() then
|
||||||
|
return skynet_account.exit_access_unauthorized()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- check if portal is in subscription only mode
|
||||||
|
if skynet_account.is_access_forbidden() then
|
||||||
|
return skynet_account.exit_access_forbidden()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- get account limits of currently authenticated user
|
||||||
|
local limits = skynet_account.get_account_limits()
|
||||||
|
|
||||||
|
-- apply download speed limit
|
||||||
|
ngx.var.limit_rate = limits.download
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
limit_rate_after 512k;
|
||||||
|
limit_rate $limit_rate;
|
||||||
|
|
||||||
|
proxy_set_header User-Agent: Sia-Agent;
|
||||||
|
proxy_pass http://sia:9980;
|
||||||
|
}
|
||||||
|
|
||||||
location /__internal/do/not/use/accounts {
|
location /__internal/do/not/use/accounts {
|
||||||
include /etc/nginx/conf.d/include/cors;
|
include /etc/nginx/conf.d/include/cors;
|
||||||
|
|
||||||
|
@ -357,14 +402,20 @@ location /__internal/do/not/use/accounts {
|
||||||
|
|
||||||
content_by_lua_block {
|
content_by_lua_block {
|
||||||
local json = require('cjson')
|
local json = require('cjson')
|
||||||
local accounts_enabled = require("skynet.account").accounts_enabled()
|
local skynet_account = require("skynet.account")
|
||||||
local is_auth_required = require("skynet.account").is_auth_required()
|
|
||||||
local is_authenticated = accounts_enabled and require("skynet.account").is_authenticated()
|
local accounts_enabled = skynet_account.accounts_enabled()
|
||||||
|
local is_auth_required = skynet_account.is_auth_required()
|
||||||
|
local is_subscription_required = skynet_account.is_subscription_required()
|
||||||
|
local is_authenticated = skynet_account.is_authenticated()
|
||||||
|
local has_subscription = skynet_account.has_subscription()
|
||||||
|
|
||||||
ngx.say(json.encode{
|
ngx.say(json.encode{
|
||||||
enabled = accounts_enabled,
|
enabled = accounts_enabled,
|
||||||
auth_required = is_auth_required,
|
auth_required = is_auth_required,
|
||||||
|
subscription_required = is_subscription_required,
|
||||||
authenticated = is_authenticated,
|
authenticated = is_authenticated,
|
||||||
|
subscription = has_subscription,
|
||||||
})
|
})
|
||||||
return ngx.exit(ngx.HTTP_OK)
|
return ngx.exit(ngx.HTTP_OK)
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,8 +38,6 @@ location / {
|
||||||
end
|
end
|
||||||
|
|
||||||
ngx.var.skylink = require("skynet.skylink").parse(ngx.var.skylink)
|
ngx.var.skylink = require("skynet.skylink").parse(ngx.var.skylink)
|
||||||
ngx.var.skylink_v1 = ngx.var.skylink
|
|
||||||
ngx.var.skylink_v2 = ngx.var.skylink
|
|
||||||
}
|
}
|
||||||
|
|
||||||
include /etc/nginx/conf.d/include/location-skylink;
|
include /etc/nginx/conf.d/include/location-skylink;
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
listen 443 ssl http2;
|
listen 443 ssl http2;
|
||||||
listen [::]:443 ssl http2;
|
|
||||||
|
|
||||||
include /etc/nginx/conf.d/include/ssl-settings;
|
include /etc/nginx/conf.d/include/ssl-settings;
|
||||||
include /etc/nginx/conf.d/include/init-optional-variables;
|
include /etc/nginx/conf.d/include/init-optional-variables;
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
listen 80;
|
listen 80;
|
||||||
listen [::]:80;
|
|
||||||
|
|
||||||
include /etc/nginx/conf.d/include/init-optional-variables;
|
include /etc/nginx/conf.d/include/init-optional-variables;
|
||||||
|
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
include /etc/nginx/conf.d/include/init-optional-variables;
|
|
||||||
|
|
||||||
location /skynet/blocklist {
|
|
||||||
client_max_body_size 10m; # increase max body size to account for large lists
|
|
||||||
client_body_buffer_size 10m; # force whole body to memory so we can read it
|
|
||||||
|
|
||||||
content_by_lua_block {
|
|
||||||
local httpc = require("resty.http").new()
|
|
||||||
|
|
||||||
ngx.req.read_body() -- ensure the post body data is read before using get_body_data
|
|
||||||
|
|
||||||
-- proxy blocklist update request
|
|
||||||
-- 10.10.10.10 points to sia service (alias not available when using resty-http)
|
|
||||||
local res, err = httpc:request_uri("http://10.10.10.10:9980/skynet/blocklist", {
|
|
||||||
method = "POST",
|
|
||||||
body = ngx.req.get_body_data(),
|
|
||||||
headers = {
|
|
||||||
["Content-Type"] = "application/x-www-form-urlencoded",
|
|
||||||
["Authorization"] = require("skynet.utils").authorization_header(),
|
|
||||||
["User-Agent"] = "Sia-Agent",
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
-- print error and exit with 500 or exit with response if status is not 204
|
|
||||||
if err or (res and res.status ~= ngx.HTTP_NO_CONTENT) then
|
|
||||||
ngx.status = (err and ngx.HTTP_INTERNAL_SERVER_ERROR) or res.status
|
|
||||||
ngx.header["content-type"] = "text/plain"
|
|
||||||
ngx.say(err or res.body)
|
|
||||||
return ngx.exit(ngx.status)
|
|
||||||
end
|
|
||||||
|
|
||||||
require("skynet.blocklist").reload()
|
|
||||||
|
|
||||||
ngx.status = ngx.HTTP_NO_CONTENT
|
|
||||||
return ngx.exit(ngx.status)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +1,4 @@
|
||||||
listen 443 ssl http2;
|
listen 443 ssl http2;
|
||||||
listen [::]:443 ssl http2;
|
|
||||||
|
|
||||||
include /etc/nginx/conf.d/include/ssl-settings;
|
include /etc/nginx/conf.d/include/ssl-settings;
|
||||||
include /etc/nginx/conf.d/include/init-optional-variables;
|
include /etc/nginx/conf.d/include/init-optional-variables;
|
||||||
|
|
|
@ -1,13 +1,46 @@
|
||||||
local _M = {}
|
local _M = {}
|
||||||
|
|
||||||
|
-- constant tier ids
|
||||||
|
local tier_id_anonymous = 0
|
||||||
|
local tier_id_free = 1
|
||||||
|
|
||||||
-- fallback - remember to keep those updated
|
-- fallback - remember to keep those updated
|
||||||
local anon_limits = { ["tierName"] = "anonymous", ["upload"] = 655360, ["download"] = 655360, ["maxUploadSize"] = 1073741824, ["registry"] = 250 }
|
local anon_limits = {
|
||||||
|
["tierID"] = tier_id_anonymous,
|
||||||
|
["tierName"] = "anonymous",
|
||||||
|
["upload"] = 655360,
|
||||||
|
["download"] = 655360,
|
||||||
|
["maxUploadSize"] = 1073741824,
|
||||||
|
["registry"] = 250
|
||||||
|
}
|
||||||
|
|
||||||
-- no limits applied
|
-- get all non empty authentication headers from request, we want to return
|
||||||
local no_limits = { ["tierName"] = "internal", ["upload"] = 0, ["download"] = 0, ["maxUploadSize"] = 0, ["registry"] = 0 }
|
-- all of them and let accounts service deal with validation and prioritisation
|
||||||
|
function _M.get_auth_headers()
|
||||||
|
local utils = require("utils")
|
||||||
|
local request_headers = ngx.req.get_headers()
|
||||||
|
local headers = {}
|
||||||
|
|
||||||
-- free tier name
|
-- try to extract skynet-jwt cookie from cookie header
|
||||||
local free_tier = "free"
|
local skynet_jwt_cookie = utils.extract_cookie(request_headers["Cookie"], "skynet[-]jwt")
|
||||||
|
|
||||||
|
-- if skynet-jwt cookie is present, pass it as is
|
||||||
|
if skynet_jwt_cookie then
|
||||||
|
headers["Cookie"] = skynet_jwt_cookie
|
||||||
|
end
|
||||||
|
|
||||||
|
-- if authorization header is set, pass it as is
|
||||||
|
if request_headers["Authorization"] then
|
||||||
|
headers["Authorization"] = request_headers["Authorization"]
|
||||||
|
end
|
||||||
|
|
||||||
|
-- if skynet api key header is set, pass it as is
|
||||||
|
if request_headers["Skynet-Api-Key"] then
|
||||||
|
headers["Skynet-Api-Key"] = request_headers["Skynet-Api-Key"]
|
||||||
|
end
|
||||||
|
|
||||||
|
return headers
|
||||||
|
end
|
||||||
|
|
||||||
-- handle request exit when access to portal should be restricted to authenticated users only
|
-- handle request exit when access to portal should be restricted to authenticated users only
|
||||||
function _M.exit_access_unauthorized(message)
|
function _M.exit_access_unauthorized(message)
|
||||||
|
@ -31,12 +64,11 @@ end
|
||||||
|
|
||||||
function _M.get_account_limits()
|
function _M.get_account_limits()
|
||||||
local cjson = require('cjson')
|
local cjson = require('cjson')
|
||||||
|
local utils = require('utils')
|
||||||
|
local auth_headers = _M.get_auth_headers()
|
||||||
|
|
||||||
if ngx.var.internal_no_limits == "true" then
|
-- simple case of anonymous request - none of available auth headers exist
|
||||||
return no_limits
|
if utils.is_table_empty(auth_headers) then
|
||||||
end
|
|
||||||
|
|
||||||
if ngx.var.skynet_jwt == "" then
|
|
||||||
return anon_limits
|
return anon_limits
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -44,13 +76,14 @@ function _M.get_account_limits()
|
||||||
local httpc = require("resty.http").new()
|
local httpc = require("resty.http").new()
|
||||||
|
|
||||||
-- 10.10.10.70 points to accounts service (alias not available when using resty-http)
|
-- 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", {
|
local res, err = httpc:request_uri("http://10.10.10.70:3000/user/limits?unit=byte", {
|
||||||
headers = { ["Cookie"] = "skynet-jwt=" .. ngx.var.skynet_jwt }
|
headers = auth_headers,
|
||||||
})
|
})
|
||||||
|
|
||||||
-- fail gracefully in case /user/limits failed
|
-- fail gracefully in case /user/limits failed
|
||||||
if err or (res and res.status ~= ngx.HTTP_OK) then
|
if err or (res and res.status ~= ngx.HTTP_OK) then
|
||||||
ngx.log(ngx.ERR, "Failed accounts service request /user/limits: ", err or ("[HTTP " .. res.status .. "] " .. res.body))
|
local error_response = err or ("[HTTP " .. res.status .. "] " .. res.body)
|
||||||
|
ngx.log(ngx.ERR, "Failed accounts service request /user/limits?unit=byte: ", error_response)
|
||||||
ngx.var.account_limits = cjson.encode(anon_limits)
|
ngx.var.account_limits = cjson.encode(anon_limits)
|
||||||
elseif res and res.status == ngx.HTTP_OK then
|
elseif res and res.status == ngx.HTTP_OK then
|
||||||
ngx.var.account_limits = res.body
|
ngx.var.account_limits = res.body
|
||||||
|
@ -62,27 +95,31 @@ end
|
||||||
|
|
||||||
-- detect whether current user is authenticated
|
-- detect whether current user is authenticated
|
||||||
function _M.is_authenticated()
|
function _M.is_authenticated()
|
||||||
|
if not _M.accounts_enabled() then return false end
|
||||||
|
|
||||||
local limits = _M.get_account_limits()
|
local limits = _M.get_account_limits()
|
||||||
|
|
||||||
return limits.tierName ~= anon_limits.tierName
|
return limits.tierID > tier_id_anonymous
|
||||||
end
|
end
|
||||||
|
|
||||||
-- detect whether current user has active subscription
|
-- detect whether current user has active subscription
|
||||||
function _M.is_subscription_account()
|
function _M.has_subscription()
|
||||||
local limits = _M.get_account_limits()
|
local limits = _M.get_account_limits()
|
||||||
|
|
||||||
return limits.tierName ~= anon_limits.tierName and limits.tierName ~= free_tier
|
return limits.tierID > tier_id_free
|
||||||
end
|
end
|
||||||
|
|
||||||
function _M.is_auth_required()
|
function _M.is_auth_required()
|
||||||
return os.getenv("ACCOUNTS_LIMIT_ACCESS") == "authenticated"
|
-- authentication is required if mode is set to "authenticated"
|
||||||
|
-- or "subscription" (require active subscription to a premium plan)
|
||||||
|
return os.getenv("ACCOUNTS_LIMIT_ACCESS") == "authenticated" or _M.is_subscription_required()
|
||||||
end
|
end
|
||||||
|
|
||||||
function _M.is_subscription_required()
|
function _M.is_subscription_required()
|
||||||
return os.getenv("ACCOUNTS_LIMIT_ACCESS") == "subscription"
|
return os.getenv("ACCOUNTS_LIMIT_ACCESS") == "subscription"
|
||||||
end
|
end
|
||||||
|
|
||||||
function is_access_always_allowed()
|
local is_access_always_allowed = function ()
|
||||||
-- options requests do not attach cookies - should always be available
|
-- options requests do not attach cookies - should always be available
|
||||||
-- requests should not be limited based on accounts if accounts are not enabled
|
-- requests should not be limited based on accounts if accounts are not enabled
|
||||||
return ngx.req.get_method() == "OPTIONS" or not _M.accounts_enabled()
|
return ngx.req.get_method() == "OPTIONS" or not _M.accounts_enabled()
|
||||||
|
@ -101,7 +138,7 @@ function _M.is_access_forbidden()
|
||||||
if is_access_always_allowed() then return false end
|
if is_access_always_allowed() then return false end
|
||||||
|
|
||||||
-- check if active subscription is required and request is from user without it
|
-- check if active subscription is required and request is from user without it
|
||||||
return _M.is_subscription_required() and not _M.is_subscription_account()
|
return _M.is_subscription_required() and not _M.has_subscription()
|
||||||
end
|
end
|
||||||
|
|
||||||
return _M
|
return _M
|
||||||
|
|
|
@ -1,66 +0,0 @@
|
||||||
local _M = {}
|
|
||||||
|
|
||||||
function _M.reload()
|
|
||||||
local httpc = require("resty.http").new()
|
|
||||||
|
|
||||||
-- fetch blocklist records (all blocked skylink hashes)
|
|
||||||
-- 10.10.10.10 points to sia service (alias not available when using resty-http)
|
|
||||||
local res, err = httpc:request_uri("http://10.10.10.10:9980/skynet/blocklist", {
|
|
||||||
headers = {
|
|
||||||
["User-Agent"] = "Sia-Agent",
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
-- fail whole request in case this request failed, we want to make sure
|
|
||||||
-- the blocklist is pre cached before serving first skylink
|
|
||||||
if err or (res and res.status ~= ngx.HTTP_OK) then
|
|
||||||
ngx.log(ngx.ERR, "Failed skyd service request /skynet/blocklist: ", err or ("[HTTP " .. res.status .. "] " .. res.body))
|
|
||||||
ngx.status = (err and ngx.HTTP_INTERNAL_SERVER_ERROR) or res.status
|
|
||||||
ngx.header["content-type"] = "text/plain"
|
|
||||||
ngx.say(err or res.body)
|
|
||||||
return ngx.exit(ngx.status)
|
|
||||||
elseif res and res.status == ngx.HTTP_OK then
|
|
||||||
local json = require('cjson')
|
|
||||||
local data = json.decode(res.body)
|
|
||||||
|
|
||||||
-- mark all existing entries as expired
|
|
||||||
ngx.shared.blocklist:flush_all()
|
|
||||||
|
|
||||||
-- check if blocklist is table (it is null when empty)
|
|
||||||
if type(data.blocklist) == "table" then
|
|
||||||
-- set all cache entries one by one (resets expiration)
|
|
||||||
for i, hash in ipairs(data.blocklist) do
|
|
||||||
ngx.shared.blocklist:set(hash, true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- ensure that init flag is persisted
|
|
||||||
ngx.shared.blocklist:set("__init", true)
|
|
||||||
|
|
||||||
-- remove all leftover expired entries
|
|
||||||
ngx.shared.blocklist:flush_expired()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function _M.is_blocked(skylink)
|
|
||||||
-- make sure that blocklist has been preloaded
|
|
||||||
if not ngx.shared.blocklist:get("__init") then _M.reload() end
|
|
||||||
|
|
||||||
-- hash skylink before comparing it with blocklist
|
|
||||||
local hash = require("skynet.skylink").hash(skylink)
|
|
||||||
|
|
||||||
-- we need to use get_stale because we are expiring previous
|
|
||||||
-- entries when the blocklist is reloading and we still want
|
|
||||||
-- to block them until the reloading is finished
|
|
||||||
return ngx.shared.blocklist:get_stale(hash) == true
|
|
||||||
end
|
|
||||||
|
|
||||||
-- exit with 416 illegal content status code
|
|
||||||
function _M.exit_illegal()
|
|
||||||
ngx.status = ngx.HTTP_ILLEGAL
|
|
||||||
ngx.header["content-type"] = "text/plain"
|
|
||||||
ngx.say("Unavailable For Legal Reasons")
|
|
||||||
return ngx.exit(ngx.status)
|
|
||||||
end
|
|
||||||
|
|
||||||
return _M
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
local _M = {}
|
||||||
|
|
||||||
|
-- utility function for checking if table is empty
|
||||||
|
function _M.is_table_empty(check)
|
||||||
|
-- bind next to local variable to achieve ultimate efficiency
|
||||||
|
-- https://stackoverflow.com/a/1252776
|
||||||
|
local next = next
|
||||||
|
|
||||||
|
return next(check) == nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- extract full cookie name and value by its name from cookie string
|
||||||
|
-- note: name matcher argument is a pattern so you will need to escape
|
||||||
|
-- any special characters, read more https://www.lua.org/pil/20.2.html
|
||||||
|
function _M.extract_cookie(cookie_string, name_matcher)
|
||||||
|
-- nil cookie string safeguard
|
||||||
|
if cookie_string == nil then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local start, stop = string.find(cookie_string, name_matcher .. "=[^;]+")
|
||||||
|
|
||||||
|
if start then
|
||||||
|
return string.sub(cookie_string, start, stop)
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- extract just the cookie value by its name from cookie string
|
||||||
|
-- note: name matcher argument is a pattern so you will need to escape
|
||||||
|
-- any special characters, read more https://www.lua.org/pil/20.2.html
|
||||||
|
function _M.extract_cookie_value(cookie_string, name_matcher)
|
||||||
|
local cookie = _M.extract_cookie(cookie_string, name_matcher)
|
||||||
|
|
||||||
|
if cookie == nil then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local value_start = string.find(cookie, "=") + 1
|
||||||
|
|
||||||
|
return string.sub(cookie, value_start)
|
||||||
|
end
|
||||||
|
|
||||||
|
return _M
|
|
@ -0,0 +1,79 @@
|
||||||
|
local utils = require('utils')
|
||||||
|
|
||||||
|
describe("is_table_empty", function()
|
||||||
|
it("should return true for empty table", function()
|
||||||
|
assert.is_true(utils.is_table_empty({}))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should return false for not empty table", function()
|
||||||
|
assert.is_false(utils.is_table_empty({ ["foo"] = "bar" }))
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("extract_cookie", function()
|
||||||
|
local cookie_string = "aaa=bbb; skynet-jwt=MTY0NzUyr8jD-ytiWtspm0tGabKfooxeIDuWcXhJ3lnY0eEw==; xxx=yyy"
|
||||||
|
|
||||||
|
it("should return nil if cookie string is nil", function()
|
||||||
|
local cookie = utils.extract_cookie_value(nil, "aaa")
|
||||||
|
|
||||||
|
assert.is_nil(cookie)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should return nil if cookie name is not found", function()
|
||||||
|
local cookie = utils.extract_cookie(cookie_string, "foo")
|
||||||
|
|
||||||
|
assert.is_nil(cookie)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should return cookie if cookie_string starts with that cookie name", function()
|
||||||
|
local cookie = utils.extract_cookie(cookie_string, "aaa")
|
||||||
|
|
||||||
|
assert.are.equals(cookie, "aaa=bbb")
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should return cookie if cookie_string ends with that cookie name", function()
|
||||||
|
local cookie = utils.extract_cookie(cookie_string, "xxx")
|
||||||
|
|
||||||
|
assert.are.equals(cookie, "xxx=yyy")
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should return cookie with custom matcher", function()
|
||||||
|
local cookie = utils.extract_cookie(cookie_string, "skynet[-]jwt")
|
||||||
|
|
||||||
|
assert.are.equals(cookie, "skynet-jwt=MTY0NzUyr8jD-ytiWtspm0tGabKfooxeIDuWcXhJ3lnY0eEw==")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("extract_cookie_value", function()
|
||||||
|
local cookie_string = "aaa=bbb; skynet-jwt=MTY0NzUyr8jD-ytiWtspm0tGabKfooxeIDuWcXhJ3lnY0eEw==; xxx=yyy"
|
||||||
|
|
||||||
|
it("should return nil if cookie string is nil", function()
|
||||||
|
local value = utils.extract_cookie_value(nil, "aaa")
|
||||||
|
|
||||||
|
assert.is_nil(value)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should return nil if cookie name is not found", function()
|
||||||
|
local value = utils.extract_cookie_value(cookie_string, "foo")
|
||||||
|
|
||||||
|
assert.is_nil(value)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should return value if cookie_string starts with that cookie name", function()
|
||||||
|
local value = utils.extract_cookie_value(cookie_string, "aaa")
|
||||||
|
|
||||||
|
assert.are.equals(value, "bbb")
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should return cookie if cookie_string ends with that cookie name", function()
|
||||||
|
local value = utils.extract_cookie_value(cookie_string, "xxx")
|
||||||
|
|
||||||
|
assert.are.equals(value, "yyy")
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should return cookie with custom matcher", function()
|
||||||
|
local value = utils.extract_cookie_value(cookie_string, "skynet[-]jwt")
|
||||||
|
|
||||||
|
assert.are.equals(value, "MTY0NzUyr8jD-ytiWtspm0tGabKfooxeIDuWcXhJ3lnY0eEw==")
|
||||||
|
end)
|
||||||
|
end)
|
|
@ -26,8 +26,8 @@ worker_processes auto;
|
||||||
#pid logs/nginx.pid;
|
#pid logs/nginx.pid;
|
||||||
|
|
||||||
# declare env variables to use it in config
|
# declare env variables to use it in config
|
||||||
env SKYNET_PORTAL_API;
|
env PORTAL_DOMAIN;
|
||||||
env SKYNET_SERVER_API;
|
env SERVER_DOMAIN;
|
||||||
env PORTAL_MODULES;
|
env PORTAL_MODULES;
|
||||||
env ACCOUNTS_LIMIT_ACCESS;
|
env ACCOUNTS_LIMIT_ACCESS;
|
||||||
env SIA_API_PASSWORD;
|
env SIA_API_PASSWORD;
|
||||||
|
@ -49,7 +49,7 @@ http {
|
||||||
'"$upstream_http_content_type" "$upstream_cache_status" '
|
'"$upstream_http_content_type" "$upstream_cache_status" '
|
||||||
'"$server_alias" "$sent_http_skynet_skylink" '
|
'"$server_alias" "$sent_http_skynet_skylink" '
|
||||||
'$upstream_connect_time $upstream_header_time '
|
'$upstream_connect_time $upstream_header_time '
|
||||||
'$request_time "$hns_domain" "$skylink"';
|
'$request_time "$hns_domain" "$skylink" $upstream_http_skynet_cache_ratio';
|
||||||
|
|
||||||
access_log logs/access.log main;
|
access_log logs/access.log main;
|
||||||
|
|
||||||
|
@ -74,28 +74,18 @@ http {
|
||||||
# proxy cache definition
|
# proxy cache definition
|
||||||
proxy_cache_path /data/nginx/cache levels=1:2 keys_zone=skynet:10m max_size=50g min_free=100g inactive=48h use_temp_path=off;
|
proxy_cache_path /data/nginx/cache levels=1:2 keys_zone=skynet:10m max_size=50g min_free=100g inactive=48h use_temp_path=off;
|
||||||
|
|
||||||
# create a shared blocklist dictionary with size of 30 megabytes
|
|
||||||
# estimated capacity of 1 megabyte dictionary is 3500 blocklist entries
|
|
||||||
# that gives us capacity of around 100k entries in 30 megabyte dictionary
|
|
||||||
lua_shared_dict blocklist 30m;
|
|
||||||
|
|
||||||
# create a shared dictionary to fill with skylinks that should not
|
|
||||||
# be cached due to the large size or some other reasons
|
|
||||||
lua_shared_dict nocache 10m;
|
|
||||||
|
|
||||||
# this runs before forking out nginx worker processes
|
# this runs before forking out nginx worker processes
|
||||||
init_by_lua_block {
|
init_by_lua_block {
|
||||||
require "cjson"
|
require "cjson"
|
||||||
require "resty.http"
|
require "resty.http"
|
||||||
require "skynet.blocklist"
|
|
||||||
require "skynet.skylink"
|
require "skynet.skylink"
|
||||||
require "skynet.utils"
|
require "skynet.utils"
|
||||||
}
|
}
|
||||||
|
|
||||||
# include skynet-portal-api and skynet-server-api header on every request
|
# include skynet-portal-api and skynet-server-api header on every request
|
||||||
header_filter_by_lua_block {
|
header_filter_by_lua_block {
|
||||||
ngx.header["Skynet-Portal-Api"] = os.getenv("SKYNET_PORTAL_API")
|
ngx.header["Skynet-Portal-Api"] = ngx.var.scheme .. "://" .. ngx.var.skynet_portal_domain
|
||||||
ngx.header["Skynet-Server-Api"] = os.getenv("SKYNET_SERVER_API")
|
ngx.header["Skynet-Server-Api"] = ngx.var.scheme .. "://" .. ngx.var.skynet_server_domain
|
||||||
}
|
}
|
||||||
|
|
||||||
# ratelimit specified IPs
|
# ratelimit specified IPs
|
||||||
|
@ -127,13 +117,6 @@ http {
|
||||||
proxy_set_header X-Forwarded-Host $host;
|
proxy_set_header X-Forwarded-Host $host;
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
|
||||||
# skynet-jwt contains dash so we cannot use $cookie_skynet-jwt
|
|
||||||
# https://richardhart.me/2012/03/18/logging-nginx-cookies-with-dashes/
|
|
||||||
map $http_cookie $skynet_jwt {
|
|
||||||
default '';
|
|
||||||
~skynet-jwt=(?<match>[^\;]+) $match;
|
|
||||||
}
|
|
||||||
|
|
||||||
include /etc/nginx/conf.d/*.conf;
|
include /etc/nginx/conf.d/*.conf;
|
||||||
include /etc/nginx/conf.extra.d/*.conf;
|
include /etc/nginx/conf.extra.d/*.conf;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,12 +5,12 @@ ENV GOARCH amd64
|
||||||
|
|
||||||
ARG branch=portal-latest
|
ARG branch=portal-latest
|
||||||
|
|
||||||
RUN git clone https://gitlab.com/SkynetLabs/skyd.git Sia --single-branch --branch ${branch}
|
RUN git clone https://gitlab.com/SkynetLabs/skyd.git Sia --single-branch --branch ${branch} && \
|
||||||
RUN make release --directory Sia
|
make release --directory Sia
|
||||||
|
|
||||||
FROM nebulouslabs/sia:latest
|
FROM nebulouslabs/sia:1.5.6
|
||||||
|
|
||||||
COPY --from=sia-builder /go/bin/ /usr/bin/
|
COPY --from=sia-builder /go/bin/ /usr/bin/
|
||||||
|
|
||||||
RUN mv /usr/bin/skyd /usr/bin/siad || true && \
|
RUN if [ -f "/usr/bin/skyd" ]; then mv /usr/bin/skyd /usr/bin/siad; fi && \
|
||||||
mv /usr/bin/skyc /usr/bin/siac || true
|
if [ -f "/usr/bin/skyc" ]; then mv /usr/bin/skyc /usr/bin/siac; fi
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
node_modules/
|
||||||
|
.cache/
|
||||||
|
public/
|
||||||
|
storybook-build/
|
|
@ -0,0 +1,6 @@
|
||||||
|
module.exports = {
|
||||||
|
globals: {
|
||||||
|
__PATH_PREFIX__: true,
|
||||||
|
},
|
||||||
|
extends: ["react-app", "plugin:storybook/recommended"],
|
||||||
|
};
|
|
@ -0,0 +1,4 @@
|
||||||
|
node_modules/
|
||||||
|
.cache/
|
||||||
|
public/
|
||||||
|
storybook-build/
|
|
@ -0,0 +1,4 @@
|
||||||
|
node_modules/
|
||||||
|
.cache/
|
||||||
|
public/
|
||||||
|
storybook-build/
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"printWidth": 120
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
module.exports = {
|
||||||
|
stories: ["../src/**/*.stories.@(js|jsx|ts|tsx)"],
|
||||||
|
addons: [
|
||||||
|
"@storybook/addon-links",
|
||||||
|
"@storybook/addon-essentials",
|
||||||
|
"storybook-addon-gatsby",
|
||||||
|
{
|
||||||
|
name: "@storybook/addon-postcss",
|
||||||
|
options: {
|
||||||
|
postcssLoaderOptions: {
|
||||||
|
implementation: require("postcss"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
core: {
|
||||||
|
builder: "webpack5",
|
||||||
|
},
|
||||||
|
};
|
|
@ -0,0 +1,20 @@
|
||||||
|
import "tailwindcss/tailwind.css";
|
||||||
|
import "@fontsource/sora/300.css"; // light
|
||||||
|
import "@fontsource/sora/400.css"; // normal
|
||||||
|
import "@fontsource/sora/500.css"; // medium
|
||||||
|
import "@fontsource/sora/600.css"; // semibold
|
||||||
|
import "@fontsource/source-sans-pro/400.css"; // normal
|
||||||
|
import "@fontsource/source-sans-pro/600.css"; // semibold
|
||||||
|
|
||||||
|
import "../src/styles/global.css";
|
||||||
|
|
||||||
|
export const parameters = {
|
||||||
|
actions: { argTypesRegex: "^on[A-Z].*" },
|
||||||
|
controls: {
|
||||||
|
matchers: {
|
||||||
|
color: /(background|color)$/i,
|
||||||
|
date: /Date$/,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
layout: "fullscreen",
|
||||||
|
};
|
|
@ -0,0 +1,14 @@
|
||||||
|
FROM node:16.14.2-alpine
|
||||||
|
|
||||||
|
WORKDIR /usr/app
|
||||||
|
|
||||||
|
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 ./
|
||||||
|
|
||||||
|
CMD ["sh", "-c", "yarn build && yarn serve --host 0.0.0.0 -p 9000"]
|
|
@ -0,0 +1,25 @@
|
||||||
|
# Skynet Account Dashboard
|
||||||
|
|
||||||
|
Code behind [account.skynetpro.net](https://account.skynetpro.net/)
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
This is a Gatsby application. To run it locally, all you need is:
|
||||||
|
|
||||||
|
- `yarn install`
|
||||||
|
- `yarn start`
|
||||||
|
|
||||||
|
## 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 do the trick, edit your `/etc/hosts` file and add a record like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
127.0.0.1 local.skynetpro.net
|
||||||
|
```
|
||||||
|
|
||||||
|
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`.
|
||||||
|
|
||||||
|
> **NOTE:** This should become easier once we have a docker image for the new dashboard.
|
|
@ -0,0 +1,22 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import "@fontsource/sora/300.css"; // light
|
||||||
|
import "@fontsource/sora/400.css"; // normal
|
||||||
|
import "@fontsource/sora/500.css"; // medium
|
||||||
|
import "@fontsource/sora/600.css"; // semibold
|
||||||
|
import "@fontsource/source-sans-pro/400.css"; // normal
|
||||||
|
import "@fontsource/source-sans-pro/600.css"; // semibold
|
||||||
|
import "./src/styles/global.css";
|
||||||
|
import { MODAL_ROOT_ID } from "./src/components/Modal";
|
||||||
|
import { PortalSettingsProvider } from "./src/contexts/portal-settings";
|
||||||
|
|
||||||
|
export function wrapPageElement({ element, props }) {
|
||||||
|
const Layout = element.type.Layout ?? React.Fragment;
|
||||||
|
return (
|
||||||
|
<PortalSettingsProvider>
|
||||||
|
<Layout {...props}>
|
||||||
|
{element}
|
||||||
|
<div id={MODAL_ROOT_ID} />
|
||||||
|
</Layout>
|
||||||
|
</PortalSettingsProvider>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
const { createProxyMiddleware } = require("http-proxy-middleware");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
siteMetadata: {
|
||||||
|
title: "Skynet Account",
|
||||||
|
siteUrl: `https://account.${process.env.GATSBY_PORTAL_DOMAIN}/`,
|
||||||
|
},
|
||||||
|
trailingSlash: "never",
|
||||||
|
plugins: [
|
||||||
|
"gatsby-plugin-image",
|
||||||
|
"gatsby-plugin-provide-react",
|
||||||
|
"gatsby-plugin-react-helmet",
|
||||||
|
"gatsby-plugin-sharp",
|
||||||
|
"gatsby-transformer-sharp",
|
||||||
|
"gatsby-plugin-styled-components",
|
||||||
|
"gatsby-plugin-postcss",
|
||||||
|
{
|
||||||
|
resolve: "gatsby-source-filesystem",
|
||||||
|
options: {
|
||||||
|
name: "images",
|
||||||
|
path: "./static/images/",
|
||||||
|
},
|
||||||
|
__key: "images",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
developMiddleware: (app) => {
|
||||||
|
app.use(
|
||||||
|
"/api/",
|
||||||
|
createProxyMiddleware({
|
||||||
|
target: "https://account.skynetpro.net",
|
||||||
|
secure: false, // Do not reject self-signed certificates.
|
||||||
|
changeOrigin: true,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
|
@ -0,0 +1,22 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import "@fontsource/sora/300.css"; // light
|
||||||
|
import "@fontsource/sora/400.css"; // normal
|
||||||
|
import "@fontsource/sora/500.css"; // medium
|
||||||
|
import "@fontsource/sora/600.css"; // semibold
|
||||||
|
import "@fontsource/source-sans-pro/400.css"; // normal
|
||||||
|
import "@fontsource/source-sans-pro/600.css"; // semibold
|
||||||
|
import "./src/styles/global.css";
|
||||||
|
import { MODAL_ROOT_ID } from "./src/components/Modal";
|
||||||
|
import { PortalSettingsProvider } from "./src/contexts/portal-settings";
|
||||||
|
|
||||||
|
export function wrapPageElement({ element, props }) {
|
||||||
|
const Layout = element.type.Layout ?? React.Fragment;
|
||||||
|
return (
|
||||||
|
<PortalSettingsProvider>
|
||||||
|
<Layout {...props}>
|
||||||
|
{element}
|
||||||
|
<div id={MODAL_ROOT_ID} />
|
||||||
|
</Layout>
|
||||||
|
</PortalSettingsProvider>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
{
|
||||||
|
"name": "accounts-dashboard",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"description": "Accounts Dashboard",
|
||||||
|
"author": "Skynet Labs",
|
||||||
|
"keywords": [
|
||||||
|
"gatsby"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"develop": "gatsby develop",
|
||||||
|
"develop:secure": "gatsby develop --https --host=local.skynetpro.net -p=443",
|
||||||
|
"start": "gatsby develop",
|
||||||
|
"build": "gatsby build",
|
||||||
|
"serve": "gatsby serve",
|
||||||
|
"clean": "gatsby clean",
|
||||||
|
"lint": "eslint .",
|
||||||
|
"prettier": "prettier .",
|
||||||
|
"storybook": "start-storybook -p 6006",
|
||||||
|
"build-storybook": "build-storybook -o storybook-build"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@fontsource/sora": "^4.5.3",
|
||||||
|
"@fontsource/source-sans-pro": "^4.5.3",
|
||||||
|
"classnames": "^2.3.1",
|
||||||
|
"copy-text-to-clipboard": "^3.0.1",
|
||||||
|
"dayjs": "^1.10.8",
|
||||||
|
"formik": "^2.2.9",
|
||||||
|
"gatsby": "^4.6.2",
|
||||||
|
"gatsby-plugin-postcss": "^5.7.0",
|
||||||
|
"http-status-codes": "^2.2.0",
|
||||||
|
"ky": "^0.30.0",
|
||||||
|
"nanoid": "^3.3.1",
|
||||||
|
"path-browserify": "^1.0.1",
|
||||||
|
"postcss": "^8.4.6",
|
||||||
|
"pretty-bytes": "^6.0.0",
|
||||||
|
"react": "^17.0.1",
|
||||||
|
"react-dom": "^17.0.1",
|
||||||
|
"react-dropzone": "^12.0.4",
|
||||||
|
"react-helmet": "^6.1.0",
|
||||||
|
"react-use": "^17.3.2",
|
||||||
|
"skynet-js": "4.0.27-beta",
|
||||||
|
"swr": "^1.2.2",
|
||||||
|
"tailwindcss": "^3.0.23",
|
||||||
|
"yup": "^0.32.11"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/core": "^7.17.4",
|
||||||
|
"@storybook/addon-actions": "^6.4.19",
|
||||||
|
"@storybook/addon-essentials": "^6.4.19",
|
||||||
|
"@storybook/addon-interactions": "^6.4.19",
|
||||||
|
"@storybook/addon-links": "^6.4.19",
|
||||||
|
"@storybook/addon-postcss": "^2.0.0",
|
||||||
|
"@storybook/builder-webpack5": "^6.4.19",
|
||||||
|
"@storybook/manager-webpack5": "^6.4.19",
|
||||||
|
"@storybook/react": "^6.4.19",
|
||||||
|
"@storybook/testing-library": "^0.0.9",
|
||||||
|
"autoprefixer": "^10.4.2",
|
||||||
|
"babel-eslint": "^10.1.0",
|
||||||
|
"babel-loader": "^8.2.3",
|
||||||
|
"babel-plugin-preval": "^5.1.0",
|
||||||
|
"babel-plugin-styled-components": "^2.0.2",
|
||||||
|
"eslint": "^8.9.0",
|
||||||
|
"eslint-config-react-app": "^7.0.0",
|
||||||
|
"eslint-plugin-storybook": "^0.5.6",
|
||||||
|
"gatsby-plugin-alias-imports": "^1.0.5",
|
||||||
|
"gatsby-plugin-image": "^2.6.0",
|
||||||
|
"gatsby-plugin-preval": "^1.0.0",
|
||||||
|
"gatsby-plugin-provide-react": "^1.0.2",
|
||||||
|
"gatsby-plugin-react-helmet": "^5.6.0",
|
||||||
|
"gatsby-plugin-sharp": "^4.6.0",
|
||||||
|
"gatsby-plugin-styled-components": "^5.8.0",
|
||||||
|
"gatsby-source-filesystem": "^4.6.0",
|
||||||
|
"gatsby-transformer-sharp": "^4.6.0",
|
||||||
|
"http-proxy-middleware": "^1.3.1",
|
||||||
|
"prettier": "2.5.1",
|
||||||
|
"react-is": "^17.0.2",
|
||||||
|
"storybook-addon-gatsby": "^0.0.5",
|
||||||
|
"styled-components": "^5.3.3"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
module.exports = {
|
||||||
|
plugins: [require("tailwindcss/nesting"), require("tailwindcss"), require("autoprefixer")],
|
||||||
|
};
|
|
@ -0,0 +1,155 @@
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import cn from "classnames";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
|
||||||
|
import { Alert } from "../Alert";
|
||||||
|
import { Button } from "../Button";
|
||||||
|
import { AddSkylinkToAPIKeyForm } from "../forms/AddSkylinkToAPIKeyForm";
|
||||||
|
import { CogIcon, TrashIcon } from "../Icons";
|
||||||
|
import { Modal } from "../Modal";
|
||||||
|
|
||||||
|
import { useAPIKeyEdit } from "./useAPIKeyEdit";
|
||||||
|
import { useAPIKeyRemoval } from "./useAPIKeyRemoval";
|
||||||
|
|
||||||
|
export const APIKey = ({ apiKey, onRemoved, onEdited, onRemovalError }) => {
|
||||||
|
const { id, name, createdAt, skylinks } = apiKey;
|
||||||
|
const isPublic = apiKey.public === "true";
|
||||||
|
const [error, setError] = useState(null);
|
||||||
|
|
||||||
|
const onSkylinkListEdited = useCallback(() => {
|
||||||
|
setError(null);
|
||||||
|
onEdited();
|
||||||
|
}, [onEdited]);
|
||||||
|
|
||||||
|
const onSkylinkListEditFailure = (errorMessage) => setError(errorMessage);
|
||||||
|
|
||||||
|
const {
|
||||||
|
removalError,
|
||||||
|
removalInitiated,
|
||||||
|
prompt: promptRemoval,
|
||||||
|
abort: abortRemoval,
|
||||||
|
confirm: confirmRemoval,
|
||||||
|
} = useAPIKeyRemoval({
|
||||||
|
key: apiKey,
|
||||||
|
onSuccess: onRemoved,
|
||||||
|
onFailure: onRemovalError,
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
editInitiated,
|
||||||
|
prompt: promptEdit,
|
||||||
|
abort: abortEdit,
|
||||||
|
addSkylink,
|
||||||
|
removeSkylink,
|
||||||
|
} = useAPIKeyEdit({
|
||||||
|
key: apiKey,
|
||||||
|
onSkylinkListUpdate: onSkylinkListEdited,
|
||||||
|
onSkylinkListUpdateFailure: onSkylinkListEditFailure,
|
||||||
|
});
|
||||||
|
|
||||||
|
const closeEditModal = useCallback(() => {
|
||||||
|
setError(null);
|
||||||
|
abortEdit();
|
||||||
|
}, [abortEdit]);
|
||||||
|
|
||||||
|
const skylinksNumber = skylinks?.length ?? 0;
|
||||||
|
const isNotConfigured = isPublic && skylinksNumber === 0;
|
||||||
|
const skylinksPhrasePrefix = skylinksNumber === 0 ? "No" : skylinksNumber;
|
||||||
|
const skylinksPhrase = `${skylinksPhrasePrefix} ${skylinksNumber === 1 ? "skylink" : "skylinks"} configured`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li
|
||||||
|
className={cn(
|
||||||
|
"grid grid-cols-2 sm:grid-cols-[1fr_repeat(2,_max-content)] py-3 px-4 gap-x-8 items-center bg-white odd:bg-palette-100/50"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<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>
|
||||||
|
<button
|
||||||
|
onClick={promptEdit}
|
||||||
|
className={cn("text-xs hover:underline decoration-dotted", {
|
||||||
|
"text-error": isNotConfigured,
|
||||||
|
"text-palette-400": !isNotConfigured,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{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 && (
|
||||||
|
<button
|
||||||
|
title="Add or remove skylinks"
|
||||||
|
aria-label="Add or remove skylinks"
|
||||||
|
className="p-1 transition-colors hover:text-primary"
|
||||||
|
onClick={promptEdit}
|
||||||
|
>
|
||||||
|
<CogIcon size={22} />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
<button
|
||||||
|
title="Delete this API key"
|
||||||
|
aria-label="Delete this API key"
|
||||||
|
className="p-1 transition-colors hover:text-error"
|
||||||
|
onClick={promptRemoval}
|
||||||
|
>
|
||||||
|
<TrashIcon size={16} />
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
{removalInitiated && (
|
||||||
|
<Modal onClose={abortRemoval} className="flex flex-col gap-4 text-center">
|
||||||
|
<h4>Delete API key</h4>
|
||||||
|
<div>
|
||||||
|
<p>Are you sure you want to delete the following API key?</p>
|
||||||
|
<p className="font-semibold">{name || id}</p>
|
||||||
|
</div>
|
||||||
|
{removalError && <Alert $variant="error">{removalError}</Alert>}
|
||||||
|
|
||||||
|
<div className="flex gap-4 justify-center mt-4">
|
||||||
|
<Button $primary onClick={abortRemoval}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button onClick={confirmRemoval}>Delete</Button>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
)}
|
||||||
|
{editInitiated && (
|
||||||
|
<Modal onClose={closeEditModal} className="flex flex-col gap-4 text-center sm:px-8 sm:py-6">
|
||||||
|
<h4>Covered skylinks</h4>
|
||||||
|
{skylinks?.length > 0 ? (
|
||||||
|
<ul className="text-xs flex flex-col gap-2">
|
||||||
|
{skylinks.map((skylink) => (
|
||||||
|
<li key={skylink} className="grid grid-cols-[1fr_min-content] w-full gap-4 items-center">
|
||||||
|
<code className="whitespace-nowrap select-all truncate bg-palette-100 odd:bg-white p-1">
|
||||||
|
{skylink}
|
||||||
|
</code>
|
||||||
|
<button
|
||||||
|
className="p-1 transition-colors hover:text-error"
|
||||||
|
onClick={() => removeSkylink(skylink)}
|
||||||
|
aria-label="Remove skylink"
|
||||||
|
>
|
||||||
|
<TrashIcon size={16} />
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
) : (
|
||||||
|
<Alert $variant="info">No skylinks here yet. You can add the first one below 🙃</Alert>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="flex flex-col gap-4">
|
||||||
|
{error && <Alert $variant="error">{error}</Alert>}
|
||||||
|
<AddSkylinkToAPIKeyForm addSkylink={addSkylink} />
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-4 justify-center mt-4">
|
||||||
|
<Button onClick={closeEditModal}>Close</Button>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
)}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { APIKey } from "./APIKey";
|
||||||
|
|
||||||
|
export const APIKeyList = ({ keys, reloadKeys, title }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<h6 className="text-palette-300 mb-4">{title}</h6>
|
||||||
|
<ul className="mt-4">
|
||||||
|
{keys.map((key) => (
|
||||||
|
<APIKey key={key.id} apiKey={key} onEdited={reloadKeys} onRemoved={reloadKeys} />
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1 @@
|
||||||
|
export * from "./APIKeyList";
|
|
@ -0,0 +1,43 @@
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
import accountsService from "../../services/accountsService";
|
||||||
|
|
||||||
|
export const useAPIKeyEdit = ({ key, onSkylinkListUpdate, onSkylinkListUpdateFailure }) => {
|
||||||
|
const [editInitiated, setEditInitiated] = useState(false);
|
||||||
|
|
||||||
|
const prompt = () => setEditInitiated(true);
|
||||||
|
const abort = () => setEditInitiated(false);
|
||||||
|
const updateSkylinkList = useCallback(
|
||||||
|
async (action, skylink) => {
|
||||||
|
try {
|
||||||
|
await accountsService.patch(`user/apikeys/${key.id}`, {
|
||||||
|
json: {
|
||||||
|
[action]: [skylink],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
onSkylinkListUpdate();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
if (err.response) {
|
||||||
|
const { message } = await err.response.json();
|
||||||
|
onSkylinkListUpdateFailure(message);
|
||||||
|
} else {
|
||||||
|
onSkylinkListUpdateFailure("Unknown error occured, please try again.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[onSkylinkListUpdate, onSkylinkListUpdateFailure, key]
|
||||||
|
);
|
||||||
|
const addSkylink = (skylink) => updateSkylinkList("add", skylink);
|
||||||
|
const removeSkylink = (skylink) => updateSkylinkList("remove", skylink);
|
||||||
|
|
||||||
|
return {
|
||||||
|
editInitiated,
|
||||||
|
prompt,
|
||||||
|
abort,
|
||||||
|
addSkylink,
|
||||||
|
removeSkylink,
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,41 @@
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
import accountsService from "../../services/accountsService";
|
||||||
|
|
||||||
|
export const useAPIKeyRemoval = ({ key, onSuccess }) => {
|
||||||
|
const [removalInitiated, setRemovalInitiated] = useState(false);
|
||||||
|
const [removalError, setRemovalError] = useState(null);
|
||||||
|
|
||||||
|
const prompt = () => {
|
||||||
|
setRemovalError(null);
|
||||||
|
setRemovalInitiated(true);
|
||||||
|
};
|
||||||
|
const abort = () => setRemovalInitiated(false);
|
||||||
|
|
||||||
|
const confirm = useCallback(async () => {
|
||||||
|
setRemovalError(null);
|
||||||
|
try {
|
||||||
|
await accountsService.delete(`user/apikeys/${key.id}`);
|
||||||
|
setRemovalInitiated(false);
|
||||||
|
onSuccess();
|
||||||
|
} catch (err) {
|
||||||
|
let message = "There was an error processing your request. Please try again later.";
|
||||||
|
|
||||||
|
if (err.response) {
|
||||||
|
const response = await err.response.json();
|
||||||
|
if (response.message) {
|
||||||
|
message = response.message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setRemovalError(message);
|
||||||
|
}
|
||||||
|
}, [onSuccess, key]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
removalInitiated,
|
||||||
|
removalError,
|
||||||
|
prompt,
|
||||||
|
abort,
|
||||||
|
confirm,
|
||||||
|
};
|
||||||
|
};
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue