Merge branch 'master' into certbot
This commit is contained in:
commit
66480dd026
|
@ -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,7 +12,7 @@ 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
|
||||||
|
@ -19,8 +20,9 @@ log_by_lua_block {
|
||||||
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
|
||||||
|
|
|
@ -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,7 +15,7 @@ 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
|
||||||
|
@ -22,8 +23,9 @@ log_by_lua_block {
|
||||||
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,7 +11,7 @@ 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
|
||||||
|
@ -19,8 +20,9 @@ log_by_lua_block {
|
||||||
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
|
||||||
|
|
|
@ -174,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?unit=byte", {
|
-- 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?unit=byte: ", 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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -259,19 +260,21 @@ location /skynet/tus {
|
||||||
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)
|
||||||
|
@ -362,19 +365,21 @@ location /skynet/trustless/basesector {
|
||||||
set $limit_rate 0;
|
set $limit_rate 0;
|
||||||
|
|
||||||
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 download speed limit
|
-- apply download speed limit
|
||||||
ngx.var.limit_rate = limits.download
|
ngx.var.limit_rate = limits.download
|
||||||
|
|
|
@ -14,6 +14,34 @@ local anon_limits = {
|
||||||
["registry"] = 250
|
["registry"] = 250
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- get all non empty authentication headers from request, we want to return
|
||||||
|
-- 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 = {}
|
||||||
|
|
||||||
|
-- try to extract skynet-jwt cookie from cookie header
|
||||||
|
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)
|
||||||
ngx.status = ngx.HTTP_UNAUTHORIZED
|
ngx.status = ngx.HTTP_UNAUTHORIZED
|
||||||
|
@ -36,8 +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.skynet_jwt == "" then
|
-- simple case of anonymous request - none of available auth headers exist
|
||||||
|
if utils.is_table_empty(auth_headers) then
|
||||||
return anon_limits
|
return anon_limits
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -46,7 +77,7 @@ function _M.get_account_limits()
|
||||||
|
|
||||||
-- 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?unit=byte", {
|
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
|
||||||
|
@ -78,7 +109,9 @@ function _M.has_subscription()
|
||||||
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()
|
||||||
|
|
|
@ -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)
|
|
@ -117,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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import ky from "ky";
|
import ky from "ky";
|
||||||
|
|
||||||
const prefix = process.env.NEXT_PUBLIC_PORTAL_DOMAIN ? `https://account.${process.env.NEXT_PUBLIC_PORTAL_DOMAIN}` : "";
|
export default ky.create({ prefixUrl: "/api" });
|
||||||
|
|
||||||
export default ky.create({ prefixUrl: `${prefix}/api` });
|
|
||||||
|
|
|
@ -2,8 +2,6 @@ import useSWR from "swr";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { StatusCodes } from "http-status-codes";
|
import { StatusCodes } from "http-status-codes";
|
||||||
|
|
||||||
const prefix = process.env.NEXT_PUBLIC_PORTAL_DOMAIN ? `https://account.${process.env.NEXT_PUBLIC_PORTAL_DOMAIN}` : "";
|
|
||||||
|
|
||||||
const fetcher = (url, router) => {
|
const fetcher = (url, router) => {
|
||||||
return fetch(url).then((res) => {
|
return fetch(url).then((res) => {
|
||||||
if (res.status === StatusCodes.UNAUTHORIZED) {
|
if (res.status === StatusCodes.UNAUTHORIZED) {
|
||||||
|
@ -17,5 +15,5 @@ const fetcher = (url, router) => {
|
||||||
export default function useAccountsApi(key, config) {
|
export default function useAccountsApi(key, config) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
return useSWR(`${prefix}/api/${key}`, (url) => fetcher(url, router), config);
|
return useSWR(`/api/${key}`, (url) => fetcher(url, router), config);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,6 @@ import useSWR from "swr";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { StatusCodes } from "http-status-codes";
|
import { StatusCodes } from "http-status-codes";
|
||||||
|
|
||||||
const prefix = process.env.NEXT_PUBLIC_PORTAL_DOMAIN ? `https://account.${process.env.NEXT_PUBLIC_PORTAL_DOMAIN}` : "";
|
|
||||||
|
|
||||||
const fetcher = (url, router) => {
|
const fetcher = (url, router) => {
|
||||||
return fetch(url).then((res) => {
|
return fetch(url).then((res) => {
|
||||||
if (res.status === StatusCodes.OK) router.push("/");
|
if (res.status === StatusCodes.OK) router.push("/");
|
||||||
|
@ -13,5 +11,5 @@ const fetcher = (url, router) => {
|
||||||
export default function useAnonRoute() {
|
export default function useAnonRoute() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
return useSWR(`${prefix}/api/user`, (url) => fetcher(url, router));
|
return useSWR("/api/user", (url) => fetcher(url, router));
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
"gatsby": "4.10.1",
|
"gatsby": "4.10.1",
|
||||||
"gatsby-background-image": "1.6.0",
|
"gatsby-background-image": "1.6.0",
|
||||||
"gatsby-plugin-image": "2.9.0",
|
"gatsby-plugin-image": "2.9.0",
|
||||||
"gatsby-plugin-manifest": "4.9.1",
|
"gatsby-plugin-manifest": "4.10.1",
|
||||||
"gatsby-plugin-postcss": "5.9.0",
|
"gatsby-plugin-postcss": "5.9.0",
|
||||||
"gatsby-plugin-react-helmet": "5.10.0",
|
"gatsby-plugin-react-helmet": "5.10.0",
|
||||||
"gatsby-plugin-robots-txt": "1.7.0",
|
"gatsby-plugin-robots-txt": "1.7.0",
|
||||||
|
@ -46,7 +46,7 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/typography": "0.5.2",
|
"@tailwindcss/typography": "0.5.2",
|
||||||
"autoprefixer": "10.4.2",
|
"autoprefixer": "10.4.4",
|
||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"cypress": "9.5.1",
|
"cypress": "9.5.1",
|
||||||
"prettier": "2.6.0",
|
"prettier": "2.6.0",
|
||||||
|
|
|
@ -47,15 +47,37 @@ const LogInLink = () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const SubscribeLink = () => {
|
||||||
|
const createAccountsUrl = useAccountsUrl();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
href={createAccountsUrl("payments")}
|
||||||
|
className="uppercase underline-primary hover:text-primary transition-colors duration-200"
|
||||||
|
>
|
||||||
|
available plans
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const Uploader = () => {
|
const Uploader = () => {
|
||||||
const [mode, setMode] = React.useState("file");
|
const [mode, setMode] = React.useState("file");
|
||||||
const [uploads, setUploads] = React.useState([]);
|
const [uploads, setUploads] = React.useState([]);
|
||||||
|
|
||||||
const { data: accounts } = useAccounts();
|
const { data: accounts } = useAccounts();
|
||||||
const showAccountFeatures =
|
|
||||||
accounts && accounts.enabled !== false && !accounts?.auth_required && !accounts?.authenticated;
|
// variables extracted from useAccounts response
|
||||||
const disabledComponent =
|
const isAccountsEnabled = accounts?.enabled;
|
||||||
accounts && accounts.enabled !== false && accounts?.auth_required && !accounts?.authenticated;
|
const isSubscriptionRequired = accounts?.subscription_required;
|
||||||
|
const isAuthRequired = isSubscriptionRequired || accounts?.auth_required;
|
||||||
|
const isAuthenticated = accounts?.authenticated;
|
||||||
|
const hasSubscription = accounts?.subscription;
|
||||||
|
|
||||||
|
// derive current app state and extract it into variables
|
||||||
|
const showAccountFeatures = isAccountsEnabled && !isAuthRequired && !isAuthenticated;
|
||||||
|
const showAuthenticationRequired = isAccountsEnabled && isAuthRequired && !isAuthenticated;
|
||||||
|
const showSubscriptionRequired = isAccountsEnabled && isAuthenticated && isSubscriptionRequired && !hasSubscription;
|
||||||
|
const isComponentDisabled = showAuthenticationRequired || showSubscriptionRequired;
|
||||||
|
|
||||||
const onUploadStateChange = React.useCallback((id, state) => {
|
const onUploadStateChange = React.useCallback((id, state) => {
|
||||||
setUploads((uploads) => {
|
setUploads((uploads) => {
|
||||||
|
@ -104,7 +126,7 @@ const Uploader = () => {
|
||||||
}, [inputElement, mode]);
|
}, [inputElement, mode]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classnames("relative", { "p-8": disabledComponent })}>
|
<div className={classnames("relative", { "p-8": isComponentDisabled })}>
|
||||||
<div className="max-w-content mx-auto rounded-lg shadow bg-white z-0 relative">
|
<div className="max-w-content mx-auto rounded-lg shadow bg-white z-0 relative">
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<button
|
<button
|
||||||
|
@ -156,7 +178,7 @@ const Uploader = () => {
|
||||||
{mode === "directory" && <span>Drop any folder with an index.html file to deploy to Skynet</span>}
|
{mode === "directory" && <span>Drop any folder with an index.html file to deploy to Skynet</span>}
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
{!disabledComponent && (
|
{!isComponentDisabled && (
|
||||||
<div className="absolute left-1/2 -bottom-4 desktop:-bottom-8">
|
<div className="absolute left-1/2 -bottom-4 desktop:-bottom-8">
|
||||||
<div className="relative -left-1/2 transform transition-transform hover:rotate-180" role="button">
|
<div className="relative -left-1/2 transform transition-transform hover:rotate-180" role="button">
|
||||||
<Add />
|
<Add />
|
||||||
|
@ -204,7 +226,7 @@ const Uploader = () => {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{disabledComponent && (
|
{showAuthenticationRequired && (
|
||||||
<div className="absolute inset-0 bg-palette-500 bg-opacity-90 rounded-lg">
|
<div className="absolute inset-0 bg-palette-500 bg-opacity-90 rounded-lg">
|
||||||
<div className="flex h-full">
|
<div className="flex h-full">
|
||||||
<div className="m-auto">
|
<div className="m-auto">
|
||||||
|
@ -215,6 +237,18 @@ const Uploader = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{showSubscriptionRequired && (
|
||||||
|
<div className="absolute inset-0 bg-palette-500 bg-opacity-90 rounded-lg">
|
||||||
|
<div className="flex h-full">
|
||||||
|
<div className="m-auto">
|
||||||
|
<h4 className="font-light text-palette-100 text-lg mt-2 text-center">
|
||||||
|
Active subscription required - <SubscribeLink />
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -3222,14 +3222,14 @@ attr-accept@^2.2.2:
|
||||||
resolved "https://registry.yarnpkg.com/attr-accept/-/attr-accept-2.2.2.tgz#646613809660110749e92f2c10833b70968d929b"
|
resolved "https://registry.yarnpkg.com/attr-accept/-/attr-accept-2.2.2.tgz#646613809660110749e92f2c10833b70968d929b"
|
||||||
integrity sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg==
|
integrity sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg==
|
||||||
|
|
||||||
autoprefixer@10.4.2, autoprefixer@^10.4.0:
|
autoprefixer@10.4.4, autoprefixer@^10.4.0:
|
||||||
version "10.4.2"
|
version "10.4.4"
|
||||||
resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.2.tgz#25e1df09a31a9fba5c40b578936b90d35c9d4d3b"
|
resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.4.tgz#3e85a245b32da876a893d3ac2ea19f01e7ea5a1e"
|
||||||
integrity sha512-9fOPpHKuDW1w/0EKfRmVnxTDt8166MAnLI3mgZ1JCnhNtYWxcJ6Ud5CO/AVOZi/AvFa8DY9RTy3h3+tFBlrrdQ==
|
integrity sha512-Tm8JxsB286VweiZ5F0anmbyGiNI3v3wGv3mz9W+cxEDYB/6jbnj6GM9H9mK3wIL8ftgl+C07Lcwb8PG5PCCPzA==
|
||||||
dependencies:
|
dependencies:
|
||||||
browserslist "^4.19.1"
|
browserslist "^4.20.2"
|
||||||
caniuse-lite "^1.0.30001297"
|
caniuse-lite "^1.0.30001317"
|
||||||
fraction.js "^4.1.2"
|
fraction.js "^4.2.0"
|
||||||
normalize-range "^0.1.2"
|
normalize-range "^0.1.2"
|
||||||
picocolors "^1.0.0"
|
picocolors "^1.0.0"
|
||||||
postcss-value-parser "^4.2.0"
|
postcss-value-parser "^4.2.0"
|
||||||
|
@ -3642,13 +3642,13 @@ browserslist@4.14.2:
|
||||||
escalade "^3.0.2"
|
escalade "^3.0.2"
|
||||||
node-releases "^1.1.61"
|
node-releases "^1.1.61"
|
||||||
|
|
||||||
browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.16.3, browserslist@^4.16.6, browserslist@^4.17.5, browserslist@^4.19.1, browserslist@^4.6.6:
|
browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.16.3, browserslist@^4.16.6, browserslist@^4.17.5, browserslist@^4.19.1, browserslist@^4.20.2, browserslist@^4.6.6:
|
||||||
version "4.19.3"
|
version "4.20.2"
|
||||||
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.19.3.tgz#29b7caad327ecf2859485f696f9604214bedd383"
|
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.20.2.tgz#567b41508757ecd904dab4d1c646c612cd3d4f88"
|
||||||
integrity sha512-XK3X4xtKJ+Txj8G5c30B4gsm71s69lqXlkYui4s6EkKxuv49qjYlY6oVd+IFJ73d4YymtM3+djvvt/R/iJwwDg==
|
integrity sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA==
|
||||||
dependencies:
|
dependencies:
|
||||||
caniuse-lite "^1.0.30001312"
|
caniuse-lite "^1.0.30001317"
|
||||||
electron-to-chromium "^1.4.71"
|
electron-to-chromium "^1.4.84"
|
||||||
escalade "^3.1.1"
|
escalade "^3.1.1"
|
||||||
node-releases "^2.0.2"
|
node-releases "^2.0.2"
|
||||||
picocolors "^1.0.0"
|
picocolors "^1.0.0"
|
||||||
|
@ -3827,10 +3827,10 @@ caniuse-api@^3.0.0:
|
||||||
lodash.memoize "^4.1.2"
|
lodash.memoize "^4.1.2"
|
||||||
lodash.uniq "^4.5.0"
|
lodash.uniq "^4.5.0"
|
||||||
|
|
||||||
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001125, caniuse-lite@^1.0.30001297, caniuse-lite@^1.0.30001312:
|
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001125, caniuse-lite@^1.0.30001317:
|
||||||
version "1.0.30001312"
|
version "1.0.30001319"
|
||||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001312.tgz#e11eba4b87e24d22697dae05455d5aea28550d5f"
|
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001319.tgz#eb4da4eb3ecdd409f7ba1907820061d56096e88f"
|
||||||
integrity sha512-Wiz1Psk2MEK0pX3rUzWaunLTZzqS2JYZFzNKqAiJGiuxIjRPLgV6+VDPOg6lQOUxmDwhTlh198JsTTi8Hzw6aQ==
|
integrity sha512-xjlIAFHucBRSMUo1kb5D4LYgcN1M45qdKP++lhqowDpwJwGkpIRTt5qQqnhxjj1vHcI7nrJxWhCC1ATrCEBTcw==
|
||||||
|
|
||||||
caseless@~0.12.0:
|
caseless@~0.12.0:
|
||||||
version "0.12.0"
|
version "0.12.0"
|
||||||
|
@ -5079,11 +5079,16 @@ ee-first@1.1.1:
|
||||||
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
|
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
|
||||||
integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
|
integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
|
||||||
|
|
||||||
electron-to-chromium@^1.3.564, electron-to-chromium@^1.4.71:
|
electron-to-chromium@^1.3.564:
|
||||||
version "1.4.75"
|
version "1.4.75"
|
||||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.75.tgz#d1ad9bb46f2f1bf432118c2be21d27ffeae82fdd"
|
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.75.tgz#d1ad9bb46f2f1bf432118c2be21d27ffeae82fdd"
|
||||||
integrity sha512-LxgUNeu3BVU7sXaKjUDD9xivocQLxFtq6wgERrutdY/yIOps3ODOZExK1jg8DTEg4U8TUCb5MLGeWFOYuxjF3Q==
|
integrity sha512-LxgUNeu3BVU7sXaKjUDD9xivocQLxFtq6wgERrutdY/yIOps3ODOZExK1jg8DTEg4U8TUCb5MLGeWFOYuxjF3Q==
|
||||||
|
|
||||||
|
electron-to-chromium@^1.4.84:
|
||||||
|
version "1.4.88"
|
||||||
|
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.88.tgz#ebe6a2573b563680c7a7bf3a51b9e465c9c501db"
|
||||||
|
integrity sha512-oA7mzccefkvTNi9u7DXmT0LqvhnOiN2BhSrKerta7HeUC1cLoIwtbf2wL+Ah2ozh5KQd3/1njrGrwDBXx6d14Q==
|
||||||
|
|
||||||
elliptic@^6.5.3:
|
elliptic@^6.5.3:
|
||||||
version "6.5.4"
|
version "6.5.4"
|
||||||
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb"
|
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb"
|
||||||
|
@ -6065,10 +6070,10 @@ forwarded@0.2.0:
|
||||||
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
|
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
|
||||||
integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==
|
integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==
|
||||||
|
|
||||||
fraction.js@^4.1.2:
|
fraction.js@^4.2.0:
|
||||||
version "4.1.3"
|
version "4.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.1.3.tgz#be65b0f20762ef27e1e793860bc2dfb716e99e65"
|
resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.2.0.tgz#448e5109a313a3527f5a3ab2119ec4cf0e0e2950"
|
||||||
integrity sha512-pUHWWt6vHzZZiQJcM6S/0PXfS+g6FM4BF5rj9wZyreivhQPdsh5PpE25VtSNxq80wHS5RfY51Ii+8Z0Zl/pmzg==
|
integrity sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==
|
||||||
|
|
||||||
fragment-cache@^0.2.1:
|
fragment-cache@^0.2.1:
|
||||||
version "0.2.1"
|
version "0.2.1"
|
||||||
|
@ -6320,14 +6325,14 @@ gatsby-plugin-image@2.9.0:
|
||||||
objectFitPolyfill "^2.3.5"
|
objectFitPolyfill "^2.3.5"
|
||||||
prop-types "^15.7.2"
|
prop-types "^15.7.2"
|
||||||
|
|
||||||
gatsby-plugin-manifest@4.9.1:
|
gatsby-plugin-manifest@4.10.1:
|
||||||
version "4.9.1"
|
version "4.10.1"
|
||||||
resolved "https://registry.yarnpkg.com/gatsby-plugin-manifest/-/gatsby-plugin-manifest-4.9.1.tgz#20513c7d942b424795b802506893c0f909c69b7a"
|
resolved "https://registry.yarnpkg.com/gatsby-plugin-manifest/-/gatsby-plugin-manifest-4.10.1.tgz#01949125a70bac22f2d8946d5829b49f7f188ca2"
|
||||||
integrity sha512-Fye2vr7ioc7ETVKdCfpbc5ByU28+EB7ocqSORbazPgAT8OiPazpaBAYm98BONceuK3WaxGoEXMsmwmNBIIPjRA==
|
integrity sha512-D4WYQD1gDdyvWt8RYl4OC/i7thPkgtkm+kZW+d1JVpUTu+BrbdPYCIUMGdSrDyKxx3x0bhMmEf9hZW25acew0Q==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.15.4"
|
"@babel/runtime" "^7.15.4"
|
||||||
gatsby-core-utils "^3.9.1"
|
gatsby-core-utils "^3.10.0"
|
||||||
gatsby-plugin-utils "^3.3.0"
|
gatsby-plugin-utils "^3.4.1"
|
||||||
semver "^7.3.5"
|
semver "^7.3.5"
|
||||||
sharp "^0.30.1"
|
sharp "^0.30.1"
|
||||||
|
|
||||||
|
|
Reference in New Issue