Merge branch 'master' into matt/sky-585-update-webportal-repo
This commit is contained in:
commit
0450b35016
|
@ -1,14 +1,6 @@
|
||||||
version: 2
|
version: 2
|
||||||
updates:
|
updates:
|
||||||
- package-ecosystem: npm
|
|
||||||
directory: "/packages/health-check"
|
|
||||||
schedule:
|
|
||||||
interval: monthly
|
|
||||||
- package-ecosystem: docker
|
- package-ecosystem: docker
|
||||||
directory: "/docker/sia"
|
directory: "/docker/sia"
|
||||||
schedule:
|
schedule:
|
||||||
interval: monthly
|
interval: monthly
|
||||||
- package-ecosystem: docker
|
|
||||||
directory: "/packages/health-check"
|
|
||||||
schedule:
|
|
||||||
interval: monthly
|
|
||||||
|
|
|
@ -14,7 +14,6 @@ jobs:
|
||||||
matrix:
|
matrix:
|
||||||
dockerfile:
|
dockerfile:
|
||||||
- docker/sia/Dockerfile
|
- docker/sia/Dockerfile
|
||||||
- packages/health-check/Dockerfile
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: hadolint/hadolint-action@v2.0.0
|
- uses: hadolint/hadolint-action@v2.0.0
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
name: Lint - packages/health-check
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
paths:
|
|
||||||
- packages/health-check/**
|
|
||||||
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: packages/health-check
|
|
||||||
|
|
||||||
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 .
|
|
|
@ -1,23 +0,0 @@
|
||||||
name: Test - packages/health-check
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
paths:
|
|
||||||
- packages/health-check/**
|
|
||||||
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: packages/health-check
|
|
||||||
|
|
||||||
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 jest
|
|
|
@ -167,9 +167,9 @@ services:
|
||||||
health-check:
|
health-check:
|
||||||
# uncomment "build" and comment out "image" to build from sources
|
# uncomment "build" and comment out "image" to build from sources
|
||||||
# build:
|
# build:
|
||||||
# context: https://github.com/SkynetLabs/skynet-webportal.git#master
|
# context: https://github.com/SkynetLabs/webportal-health-check.git#main
|
||||||
# dockerfile: ./packages/health-check/Dockerfile
|
# dockerfile: Dockerfile
|
||||||
image: skynetlabs/health-check
|
image: skynetlabs/webportal-health-check:0.1.3
|
||||||
container_name: health-check
|
container_name: health-check
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
logging: *default-logging
|
logging: *default-logging
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
state/
|
|
|
@ -1 +0,0 @@
|
||||||
/package.json
|
|
|
@ -1,3 +0,0 @@
|
||||||
{
|
|
||||||
"printWidth": 120
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
FROM node:16.14.2-alpine
|
|
||||||
|
|
||||||
RUN apk add --no-cache dnsmasq~=2
|
|
||||||
|
|
||||||
WORKDIR /usr/app
|
|
||||||
|
|
||||||
ENV PATH="/usr/app/bin:${PATH}"
|
|
||||||
|
|
||||||
# schedule critical checks to run every 5 minutes (any failures will disable server)
|
|
||||||
# schedule extended checks to run on every hour (optional checks, report only)
|
|
||||||
RUN echo '*/5 * * * * source /etc/environment ; /usr/app/bin/cli run critical >> /proc/1/fd/1' >> /etc/crontabs/root && \
|
|
||||||
echo '0 * * * * source /etc/environment ; /usr/app/bin/cli run extended >> /proc/1/fd/1' >> /etc/crontabs/root
|
|
||||||
|
|
||||||
COPY packages/health-check/package.json \
|
|
||||||
packages/health-check/yarn.lock \
|
|
||||||
./
|
|
||||||
|
|
||||||
RUN yarn --frozen-lockfile
|
|
||||||
|
|
||||||
COPY packages/health-check/src src
|
|
||||||
COPY packages/health-check/cli cli
|
|
||||||
COPY packages/health-check/bin bin
|
|
||||||
|
|
||||||
EXPOSE 3100
|
|
||||||
ENV NODE_ENV production
|
|
||||||
|
|
||||||
# 1. get public server ip and save it in /etc/environment (passed to cron tasks as env variable)
|
|
||||||
# 2. start dnsmasq in the background with:
|
|
||||||
# - alias PORTAL_DOMAIN with current server ip so it overrides potential load balancer request
|
|
||||||
# - default docker nameserver 127.0.0.11 for any other request
|
|
||||||
# 3. replace docker nameserver with dnsmasq nameserver in /etc/resolv.conf
|
|
||||||
# 4. start crond in the background to schedule periodic health checks
|
|
||||||
# 5. start the health-check api service
|
|
||||||
CMD [ "sh", "-c", \
|
|
||||||
"export serverip=$(node src/whatismyip.js) && \
|
|
||||||
echo \"export serverip=${serverip}\" >> /etc/environment && \
|
|
||||||
dnsmasq --no-resolv --log-facility=/var/log/dnsmasq.log --address=/$PORTAL_DOMAIN/$serverip --server=127.0.0.11 && \
|
|
||||||
echo \"$(sed 's/127.0.0.11/127.0.0.1/' /etc/resolv.conf)\" > /etc/resolv.conf && \
|
|
||||||
crond && \
|
|
||||||
node src/index.js" \
|
|
||||||
]
|
|
|
@ -1,94 +0,0 @@
|
||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
process.env.NODE_ENV = process.env.NODE_ENV || "production";
|
|
||||||
|
|
||||||
require("yargs/yargs")(process.argv.slice(2))
|
|
||||||
.help()
|
|
||||||
.demandCommand()
|
|
||||||
.strict(true)
|
|
||||||
.command(
|
|
||||||
"__authenticate", // Internal only function - this function will be removed when API keys are implemented
|
|
||||||
false, // hide this function cli help
|
|
||||||
() => {},
|
|
||||||
async () => {
|
|
||||||
const { getAuthCookie } = require("../src/utils");
|
|
||||||
|
|
||||||
console.log(await getAuthCookie(true));
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.command(
|
|
||||||
"enable",
|
|
||||||
"Mark portal as enabled",
|
|
||||||
() => {},
|
|
||||||
() => {
|
|
||||||
const db = require("../src/db");
|
|
||||||
|
|
||||||
db.set("disabled", false).write();
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.command(
|
|
||||||
"disable <reason>",
|
|
||||||
"Mark portal as disabled (provide meaningful reason)",
|
|
||||||
() => {},
|
|
||||||
({ reason }) => {
|
|
||||||
const db = require("../src/db");
|
|
||||||
|
|
||||||
db.set("disabled", reason).write();
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.command(
|
|
||||||
"run <type>",
|
|
||||||
"Skynet portal health checks",
|
|
||||||
(yargs) => {
|
|
||||||
yargs
|
|
||||||
.positional("type", {
|
|
||||||
describe: "Type of checks to run",
|
|
||||||
type: "string",
|
|
||||||
choices: ["critical", "extended"],
|
|
||||||
})
|
|
||||||
.option("portal-url", {
|
|
||||||
describe: "Skynet portal url",
|
|
||||||
default: process.env.PORTAL_DOMAIN ? `https://${process.env.PORTAL_DOMAIN}` : "https://siasky.net",
|
|
||||||
type: "string",
|
|
||||||
})
|
|
||||||
.option("state-dir", {
|
|
||||||
describe: "State directory",
|
|
||||||
default: process.env.STATE_DIR || "state",
|
|
||||||
type: "string",
|
|
||||||
});
|
|
||||||
},
|
|
||||||
async ({ type, portalUrl, stateDir }) => {
|
|
||||||
const { hostname: portalDomain } = new URL(portalUrl); // extract domain from portal url
|
|
||||||
process.env.PORTAL_DOMAIN = portalDomain;
|
|
||||||
process.env.STATE_DIR = stateDir;
|
|
||||||
|
|
||||||
const util = require("util");
|
|
||||||
const { getYesterdayISOString } = require("../src/utils");
|
|
||||||
const createMiddleware = require("../src/checks/middleware");
|
|
||||||
const db = require("../src/db");
|
|
||||||
const checks = require(`../src/checks/${type}`);
|
|
||||||
const middleware = await createMiddleware();
|
|
||||||
|
|
||||||
const entry = {
|
|
||||||
date: new Date().toISOString(),
|
|
||||||
checks: (await Promise.all(checks.map((check) => new Promise(check)))).filter(Boolean).map(middleware),
|
|
||||||
};
|
|
||||||
|
|
||||||
db.read() // read before writing to make sure no external changes are overwritten
|
|
||||||
.get(type) // get the list of records of given type
|
|
||||||
.push(entry) // insert new record
|
|
||||||
.remove(({ date }) => date < getYesterdayISOString()) // drop old records
|
|
||||||
.write();
|
|
||||||
|
|
||||||
// exit with code 1 if any of the checks report failure
|
|
||||||
if (entry.checks.some(({ up }) => !up)) {
|
|
||||||
console.log(
|
|
||||||
util.inspect(
|
|
||||||
entry.checks.filter(({ up }) => !up),
|
|
||||||
{ colors: true, depth: 7 } // increase depth to ensure errors are printed
|
|
||||||
)
|
|
||||||
);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
).argv;
|
|
|
@ -1,5 +0,0 @@
|
||||||
#!/bin/ash
|
|
||||||
|
|
||||||
echo "DEPRECATED: 'cli/disable' command is deprecated, use 'cli disable' instead"
|
|
||||||
|
|
||||||
/usr/app/bin/cli disable $@
|
|
|
@ -1,5 +0,0 @@
|
||||||
#!/bin/ash
|
|
||||||
|
|
||||||
echo "DEPRECATED: 'cli/enable' command is deprecated, use 'cli enable' instead"
|
|
||||||
|
|
||||||
/usr/app/bin/cli enable $@
|
|
|
@ -1,5 +0,0 @@
|
||||||
#!/bin/ash
|
|
||||||
|
|
||||||
echo "DEPRECATED: 'cli/run' command is deprecated, use 'cli run' instead"
|
|
||||||
|
|
||||||
/usr/app/bin/cli run $@
|
|
|
@ -1,24 +0,0 @@
|
||||||
{
|
|
||||||
"name": "health-check",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"main": "index.js",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"deep-object-diff": "^1.1.7",
|
|
||||||
"express": "^4.18.1",
|
|
||||||
"form-data": "^4.0.0",
|
|
||||||
"got": "^11.8.2",
|
|
||||||
"graceful-fs": "^4.2.10",
|
|
||||||
"hasha": "^5.2.2",
|
|
||||||
"http-status-codes": "^2.2.0",
|
|
||||||
"lodash": "^4.17.21",
|
|
||||||
"lowdb": "^1.0.0",
|
|
||||||
"skynet-js": "^4.1.0",
|
|
||||||
"write-file-atomic": "^4.0.1",
|
|
||||||
"yargs": "^17.4.1"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"jest": "^28.0.3",
|
|
||||||
"prettier": "^2.6.2"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
const fs = require("graceful-fs");
|
|
||||||
const Base = require("lowdb/adapters/Base");
|
|
||||||
const { sync: writeFileAtomicSync } = require("write-file-atomic");
|
|
||||||
|
|
||||||
class FileSyncAtomic extends Base {
|
|
||||||
read() {
|
|
||||||
if (fs.existsSync(this.source)) {
|
|
||||||
try {
|
|
||||||
const data = fs.readFileSync(this.source, "utf-8").trim();
|
|
||||||
return data ? this.deserialize(data) : this.defaultValue;
|
|
||||||
} catch (e) {
|
|
||||||
if (e instanceof SyntaxError) {
|
|
||||||
e.message = `Malformed JSON in file: ${this.source}\n${e.message}`;
|
|
||||||
}
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
writeFileAtomicSync(this.source, this.serialize(this.defaultValue));
|
|
||||||
return this.defaultValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
write(data) {
|
|
||||||
return writeFileAtomicSync(this.source, this.serialize(data));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = FileSyncAtomic;
|
|
|
@ -1,14 +0,0 @@
|
||||||
const db = require("../db");
|
|
||||||
const { getYesterdayISOString } = require("../utils");
|
|
||||||
|
|
||||||
// returns all critical health check entries
|
|
||||||
module.exports = (req, res) => {
|
|
||||||
const yesterday = getYesterdayISOString();
|
|
||||||
const entries = db
|
|
||||||
.get("critical")
|
|
||||||
.orderBy("date", "desc")
|
|
||||||
.filter(({ date }) => date > yesterday)
|
|
||||||
.value();
|
|
||||||
|
|
||||||
res.send(entries);
|
|
||||||
};
|
|
|
@ -1,8 +0,0 @@
|
||||||
const db = require("../db");
|
|
||||||
|
|
||||||
// returns a disabled flag status
|
|
||||||
module.exports = (req, res) => {
|
|
||||||
const disabled = db.get("disabled").value();
|
|
||||||
|
|
||||||
res.send({ disabled });
|
|
||||||
};
|
|
|
@ -1,14 +0,0 @@
|
||||||
const db = require("../db");
|
|
||||||
const { getYesterdayISOString } = require("../utils");
|
|
||||||
|
|
||||||
// returns all extended health check entries
|
|
||||||
module.exports = (req, res) => {
|
|
||||||
const yesterday = getYesterdayISOString();
|
|
||||||
const entries = db
|
|
||||||
.get("extended")
|
|
||||||
.orderBy("date", "desc")
|
|
||||||
.filter(({ date }) => date > yesterday)
|
|
||||||
.value();
|
|
||||||
|
|
||||||
res.send(entries);
|
|
||||||
};
|
|
|
@ -1,75 +0,0 @@
|
||||||
const { StatusCodes } = require("http-status-codes");
|
|
||||||
const { sum, sumBy } = require("lodash");
|
|
||||||
const db = require("../db");
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get status code that should be returned in the API response.
|
|
||||||
* - OK (200) in case everything is healthy
|
|
||||||
* - SERVICE_UNAVAILABLE (503) in case of any failures or if disabled
|
|
||||||
*/
|
|
||||||
function getStatusCode() {
|
|
||||||
// check whether the portal has been manually disabled
|
|
||||||
const disabled = getDisabled();
|
|
||||||
|
|
||||||
if (disabled) {
|
|
||||||
return StatusCodes.SERVICE_UNAVAILABLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// grab the most recent critical entry element from DB
|
|
||||||
const entry = getMostRecentCriticalEntry();
|
|
||||||
|
|
||||||
// in case there is no entry yet or at least one check failed in the most recent entry
|
|
||||||
if (!entry || entry.checks.some(({ up }) => !up)) {
|
|
||||||
return StatusCodes.SERVICE_UNAVAILABLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return StatusCodes.OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the sample of most recent critical entries and
|
|
||||||
* calculate the average response time of all of them
|
|
||||||
*/
|
|
||||||
function getAverageResponseTime() {
|
|
||||||
// get most recent 10 successfull checks for the calculation
|
|
||||||
const sample = db
|
|
||||||
.get("critical")
|
|
||||||
.orderBy("date", "desc")
|
|
||||||
.filter(({ checks }) => checks.every(({ up }) => up))
|
|
||||||
.take(10)
|
|
||||||
.value();
|
|
||||||
|
|
||||||
// calculate average time of response
|
|
||||||
return Math.round(sum(sample.map(({ checks }) => sumBy(checks, "time"))) / sample.size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get one, most current critical entry
|
|
||||||
*/
|
|
||||||
function getMostRecentCriticalEntry() {
|
|
||||||
return db.get("critical").orderBy("date", "desc").head().value();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the disabled flag state (manual portal disable)
|
|
||||||
*/
|
|
||||||
function getDisabled() {
|
|
||||||
return db.get("disabled").value();
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = (req, res) => {
|
|
||||||
const statusCode = getStatusCode();
|
|
||||||
const timeout = statusCode === StatusCodes.OK ? getAverageResponseTime() : 0;
|
|
||||||
|
|
||||||
// We want to delay the response for the load balancer to be able to prioritize
|
|
||||||
// servers based on the successful response time of this endpoint. Load balancer
|
|
||||||
// will pull the server if the response is an error so there is no point in delaying
|
|
||||||
// failures, hence 0 timeout on those.
|
|
||||||
setTimeout(() => {
|
|
||||||
// include some health information in the response body
|
|
||||||
const entry = getMostRecentCriticalEntry();
|
|
||||||
const disabled = getDisabled();
|
|
||||||
|
|
||||||
res.status(statusCode).send({ disabled, entry });
|
|
||||||
}, timeout);
|
|
||||||
};
|
|
|
@ -1,239 +0,0 @@
|
||||||
const got = require("got");
|
|
||||||
const FormData = require("form-data");
|
|
||||||
const { isEqual } = require("lodash");
|
|
||||||
const { calculateElapsedTime, getResponseContent, getAuthCookie, isPortalModuleEnabled } = require("../utils");
|
|
||||||
const { SkynetClient, stringToUint8ArrayUtf8, genKeyPairAndSeed } = require("skynet-js");
|
|
||||||
|
|
||||||
const MODULE_BLOCKER = "b";
|
|
||||||
|
|
||||||
const skynetClient = new SkynetClient(`https://${process.env.PORTAL_DOMAIN}`);
|
|
||||||
const exampleSkylink = "AACogzrAimYPG42tDOKhS3lXZD8YvlF8Q8R17afe95iV2Q";
|
|
||||||
|
|
||||||
// check that any relevant configuration is properly set in skyd
|
|
||||||
async function skydConfigCheck(done) {
|
|
||||||
const time = process.hrtime();
|
|
||||||
const data = { up: false };
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await got(`http://10.10.10.10:9980/renter`, { headers: { "User-Agent": "Sia-Agent" } }).json();
|
|
||||||
|
|
||||||
// make sure initial funding is set to 10SC
|
|
||||||
if (response.settings.allowance.paymentcontractinitialfunding !== "10000000000000000000000000") {
|
|
||||||
throw new Error("Skynet Portal Per-Contract Budget is not set correctly!");
|
|
||||||
}
|
|
||||||
|
|
||||||
data.up = true;
|
|
||||||
data.ip = response.ip;
|
|
||||||
} catch (error) {
|
|
||||||
data.statusCode = error.response?.statusCode || error.statusCode || error.status;
|
|
||||||
data.errorMessage = error.message;
|
|
||||||
data.errorResponseContent = getResponseContent(error.response);
|
|
||||||
data.ip = error?.response?.ip ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
done({ name: "skyd_config", time: calculateElapsedTime(time), ...data });
|
|
||||||
}
|
|
||||||
|
|
||||||
// uploadCheck returns the result of uploading a sample file
|
|
||||||
async function uploadCheck(done) {
|
|
||||||
const authCookie = await getAuthCookie();
|
|
||||||
const time = process.hrtime();
|
|
||||||
const form = new FormData();
|
|
||||||
const payload = Buffer.from(new Date()); // current date to ensure data uniqueness
|
|
||||||
const data = { up: false };
|
|
||||||
|
|
||||||
form.append("file", payload, { filename: "time.txt", contentType: "text/plain" });
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await got.post(`https://${process.env.PORTAL_DOMAIN}/skynet/skyfile`, {
|
|
||||||
body: form,
|
|
||||||
headers: { cookie: authCookie },
|
|
||||||
});
|
|
||||||
|
|
||||||
data.statusCode = response.statusCode;
|
|
||||||
data.up = true;
|
|
||||||
data.ip = response.ip;
|
|
||||||
} catch (error) {
|
|
||||||
data.statusCode = error.response?.statusCode || error.statusCode || error.status;
|
|
||||||
data.errorMessage = error.message;
|
|
||||||
data.errorResponseContent = getResponseContent(error.response);
|
|
||||||
data.ip = error?.response?.ip ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
done({ name: "upload_file", time: calculateElapsedTime(time), ...data });
|
|
||||||
}
|
|
||||||
|
|
||||||
// websiteCheck checks whether the main website is working
|
|
||||||
async function websiteCheck(done) {
|
|
||||||
return done(await genericAccessCheck("website", `https://${process.env.PORTAL_DOMAIN}`));
|
|
||||||
}
|
|
||||||
|
|
||||||
// downloadCheck returns the result of downloading the hard coded link
|
|
||||||
async function downloadCheck(done) {
|
|
||||||
const url = await skynetClient.getSkylinkUrl(exampleSkylink);
|
|
||||||
|
|
||||||
return done(await genericAccessCheck("skylink", url));
|
|
||||||
}
|
|
||||||
|
|
||||||
// skylinkSubdomainCheck returns the result of downloading the hard coded link via subdomain
|
|
||||||
async function skylinkSubdomainCheck(done) {
|
|
||||||
const url = await skynetClient.getSkylinkUrl(exampleSkylink, { subdomain: true });
|
|
||||||
|
|
||||||
return done(await genericAccessCheck("skylink_via_subdomain", url));
|
|
||||||
}
|
|
||||||
|
|
||||||
// handshakeSubdomainCheck returns the result of downloading the skylink via handshake domain
|
|
||||||
async function handshakeSubdomainCheck(done) {
|
|
||||||
const url = await skynetClient.getHnsUrl("note-to-self", { subdomain: true });
|
|
||||||
|
|
||||||
return done(await genericAccessCheck("hns_via_subdomain", url));
|
|
||||||
}
|
|
||||||
|
|
||||||
// accountWebsiteCheck returns the result of accessing account dashboard website
|
|
||||||
async function accountWebsiteCheck(done) {
|
|
||||||
const url = `https://account.${process.env.PORTAL_DOMAIN}/auth/login`;
|
|
||||||
|
|
||||||
return done(await genericAccessCheck("account_website", url));
|
|
||||||
}
|
|
||||||
|
|
||||||
// registryWriteAndReadCheck writes to registry and immediately reads and compares the data
|
|
||||||
async function registryWriteAndReadCheck(done) {
|
|
||||||
const authCookie = await getAuthCookie();
|
|
||||||
const time = process.hrtime();
|
|
||||||
const data = { name: "registry_write_and_read", up: false };
|
|
||||||
const { privateKey, publicKey } = genKeyPairAndSeed();
|
|
||||||
const expected = { dataKey: "foo-key", data: stringToUint8ArrayUtf8("foo-data"), revision: BigInt(0) };
|
|
||||||
|
|
||||||
try {
|
|
||||||
await skynetClient.registry.setEntry(privateKey, expected, { customCookie: authCookie });
|
|
||||||
const { entry } = await skynetClient.registry.getEntry(publicKey, expected.dataKey, { customCookie: authCookie });
|
|
||||||
|
|
||||||
if (isEqual(expected, entry)) {
|
|
||||||
data.up = true;
|
|
||||||
} else {
|
|
||||||
data.errors = [{ message: "Data mismatch in registry (read after write)", entry, expected }];
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
data.errors = [{ message: error?.response?.data?.message ?? error.message }];
|
|
||||||
}
|
|
||||||
|
|
||||||
return done({ ...data, time: calculateElapsedTime(time) });
|
|
||||||
}
|
|
||||||
|
|
||||||
// directServerApiAccessCheck returns the basic server api check on direct server address
|
|
||||||
async function directServerApiAccessCheck(done) {
|
|
||||||
// skip if SERVER_DOMAIN is not set or it equals PORTAL_DOMAIN (single server portals)
|
|
||||||
if (!process.env.SERVER_DOMAIN || process.env.SERVER_DOMAIN === process.env.PORTAL_DOMAIN) {
|
|
||||||
return done();
|
|
||||||
}
|
|
||||||
|
|
||||||
const [portalAccessCheck, serverAccessCheck] = await Promise.all([
|
|
||||||
genericAccessCheck("portal_api_access", `https://${process.env.PORTAL_DOMAIN}`),
|
|
||||||
genericAccessCheck("server_api_access", `https://${process.env.SERVER_DOMAIN}`),
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (portalAccessCheck.ip !== serverAccessCheck.ip) {
|
|
||||||
serverAccessCheck.up = false;
|
|
||||||
serverAccessCheck.errors = serverAccessCheck.errors ?? [];
|
|
||||||
serverAccessCheck.errors.push({
|
|
||||||
message: "Access ip mismatch between portal and server access",
|
|
||||||
response: {
|
|
||||||
portal: { name: process.env.PORTAL_DOMAIN, ip: portalAccessCheck.ip },
|
|
||||||
server: { name: process.env.SERVER_DOMAIN, ip: serverAccessCheck.ip },
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return done(serverAccessCheck);
|
|
||||||
}
|
|
||||||
|
|
||||||
// accountHealthCheck returns the result of accounts service health checks
|
|
||||||
async function accountHealthCheck(done) {
|
|
||||||
const time = process.hrtime();
|
|
||||||
const data = { up: false };
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await got(`https://account.${process.env.PORTAL_DOMAIN}/health`, { responseType: "json" });
|
|
||||||
|
|
||||||
data.statusCode = response.statusCode;
|
|
||||||
data.response = response.body;
|
|
||||||
data.up = response.body.dbAlive === true;
|
|
||||||
data.ip = response.ip;
|
|
||||||
} catch (error) {
|
|
||||||
data.statusCode = error?.response?.statusCode || error.statusCode || error.status;
|
|
||||||
data.errorMessage = error.message;
|
|
||||||
data.errorResponseContent = getResponseContent(error.response);
|
|
||||||
data.ip = error?.response?.ip ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
done({ name: "accounts", time: calculateElapsedTime(time), ...data });
|
|
||||||
}
|
|
||||||
|
|
||||||
// blockerHealthCheck returns the result of blocker container health endpoint
|
|
||||||
async function blockerHealthCheck(done) {
|
|
||||||
const time = process.hrtime();
|
|
||||||
const data = { up: false };
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await got(`http://${process.env.BLOCKER_HOST}:${process.env.BLOCKER_PORT}/health`, {
|
|
||||||
responseType: "json",
|
|
||||||
});
|
|
||||||
|
|
||||||
data.statusCode = response.statusCode;
|
|
||||||
data.response = response.body;
|
|
||||||
data.up = response.body.dbAlive === true;
|
|
||||||
} catch (error) {
|
|
||||||
data.statusCode = error?.response?.statusCode || error.statusCode || error.status;
|
|
||||||
data.errorMessage = error.message;
|
|
||||||
data.errorResponseContent = getResponseContent(error.response);
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is a no-op but it's added to explicitly document the ip property
|
|
||||||
// should not be set on the data object to prevent the IP from being compared
|
|
||||||
// to the server's IP - this is not required for this check and will fail
|
|
||||||
delete data.ip;
|
|
||||||
|
|
||||||
done({ name: "blocker", time: calculateElapsedTime(time), ...data });
|
|
||||||
}
|
|
||||||
|
|
||||||
async function genericAccessCheck(name, url) {
|
|
||||||
const authCookie = await getAuthCookie();
|
|
||||||
const time = process.hrtime();
|
|
||||||
const data = { up: false, url };
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await got(url, { headers: { cookie: `nocache=true;${authCookie}` } });
|
|
||||||
|
|
||||||
data.statusCode = response.statusCode;
|
|
||||||
data.up = true;
|
|
||||||
data.ip = response.ip;
|
|
||||||
} catch (error) {
|
|
||||||
data.statusCode = error?.response?.statusCode || error.statusCode || error.status;
|
|
||||||
data.errorMessage = error.message;
|
|
||||||
data.errorResponseContent = getResponseContent(error.response);
|
|
||||||
data.ip = error?.response?.ip ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return { name, time: calculateElapsedTime(time), ...data };
|
|
||||||
}
|
|
||||||
|
|
||||||
const checks = [
|
|
||||||
skydConfigCheck,
|
|
||||||
uploadCheck,
|
|
||||||
websiteCheck,
|
|
||||||
downloadCheck,
|
|
||||||
skylinkSubdomainCheck,
|
|
||||||
handshakeSubdomainCheck,
|
|
||||||
registryWriteAndReadCheck,
|
|
||||||
directServerApiAccessCheck,
|
|
||||||
];
|
|
||||||
|
|
||||||
if (process.env.ACCOUNTS_ENABLED === "true") {
|
|
||||||
checks.push(accountHealthCheck, accountWebsiteCheck);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isPortalModuleEnabled(MODULE_BLOCKER)) {
|
|
||||||
checks.push(blockerHealthCheck);
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = checks;
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,45 +0,0 @@
|
||||||
const got = require("got");
|
|
||||||
const { ipCheckService, ipRegex } = require("../utils");
|
|
||||||
|
|
||||||
const getCurrentAddress = async () => {
|
|
||||||
// use serverip env variable when available (set via Dockerfile)
|
|
||||||
if (process.env.serverip) {
|
|
||||||
if (ipRegex.test(process.env.serverip)) return process.env.serverip;
|
|
||||||
|
|
||||||
// log error to console for future reference but do not break
|
|
||||||
console.log(`Environment variable serverip contains invalid ip: "${process.env.serverip}"`);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { body } = await got(`http://${ipCheckService}`);
|
|
||||||
if (ipRegex.test(body)) {
|
|
||||||
console.info(`Server public ip: ${body} (source: ${ipCheckService})`);
|
|
||||||
|
|
||||||
return body;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error(`${ipCheckService} responded with invalid ip: "${body}"`);
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error.message); // log error to console for future reference
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = async function middleware() {
|
|
||||||
const ip = await getCurrentAddress();
|
|
||||||
|
|
||||||
return (check) => {
|
|
||||||
// check only if current ip and check ip are provided
|
|
||||||
if (ip && check.ip && check.ip !== ip) {
|
|
||||||
check.up = false;
|
|
||||||
check.errors = check.errors ?? [];
|
|
||||||
check.errors.push({
|
|
||||||
message: "Response ip was different than current server ip - possibly there was an error with routing request",
|
|
||||||
data: { response: check.ip, server: ip },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return check;
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -1,12 +0,0 @@
|
||||||
const fs = require("graceful-fs");
|
|
||||||
const low = require("lowdb");
|
|
||||||
const FileSyncAtomic = require("./adapters/FileSyncAtomic");
|
|
||||||
|
|
||||||
if (!fs.existsSync(process.env.STATE_DIR)) fs.mkdirSync(process.env.STATE_DIR);
|
|
||||||
|
|
||||||
const adapter = new FileSyncAtomic(`${process.env.STATE_DIR}/state.json`);
|
|
||||||
const db = low(adapter);
|
|
||||||
|
|
||||||
db.defaults({ disabled: false, critical: [], extended: [] }).write();
|
|
||||||
|
|
||||||
module.exports = db;
|
|
|
@ -1,322 +0,0 @@
|
||||||
{
|
|
||||||
"filename": "output",
|
|
||||||
"subfiles": {
|
|
||||||
".well-known/brave-rewards-verification.txt": {
|
|
||||||
"filename": ".well-known/brave-rewards-verification.txt",
|
|
||||||
"contenttype": "text/plain",
|
|
||||||
"len": 154
|
|
||||||
},
|
|
||||||
"404.html": { "filename": "404.html", "contenttype": "text/html", "offset": 154, "len": 5482 },
|
|
||||||
"assets/bootstrap/bootstrap-grid.css": {
|
|
||||||
"filename": "assets/bootstrap/bootstrap-grid.css",
|
|
||||||
"contenttype": "text/css",
|
|
||||||
"offset": 5636,
|
|
||||||
"len": 49901
|
|
||||||
},
|
|
||||||
"assets/bootstrap/bootstrap-reboot.css": {
|
|
||||||
"filename": "assets/bootstrap/bootstrap-reboot.css",
|
|
||||||
"contenttype": "text/css",
|
|
||||||
"offset": 55537,
|
|
||||||
"len": 4187
|
|
||||||
},
|
|
||||||
"assets/bootstrap/bootstrap.css": {
|
|
||||||
"filename": "assets/bootstrap/bootstrap.css",
|
|
||||||
"contenttype": "text/css",
|
|
||||||
"offset": 59724,
|
|
||||||
"len": 172594
|
|
||||||
},
|
|
||||||
"assets/css/styles.css": {
|
|
||||||
"filename": "assets/css/styles.css",
|
|
||||||
"contenttype": "text/css",
|
|
||||||
"offset": 232318,
|
|
||||||
"len": 4887
|
|
||||||
},
|
|
||||||
"assets/fonts/dm-serif-display-v4-latin-regular.woff": {
|
|
||||||
"filename": "assets/fonts/dm-serif-display-v4-latin-regular.woff",
|
|
||||||
"contenttype": "application/font-woff",
|
|
||||||
"offset": 237205,
|
|
||||||
"len": 29916
|
|
||||||
},
|
|
||||||
"assets/fonts/dm-serif-display-v4-latin-regular.woff2": {
|
|
||||||
"filename": "assets/fonts/dm-serif-display-v4-latin-regular.woff2",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 267121,
|
|
||||||
"len": 24980
|
|
||||||
},
|
|
||||||
"assets/fonts/open-sans-v16-latin-regular.woff": {
|
|
||||||
"filename": "assets/fonts/open-sans-v16-latin-regular.woff",
|
|
||||||
"contenttype": "application/font-woff",
|
|
||||||
"offset": 292101,
|
|
||||||
"len": 18100
|
|
||||||
},
|
|
||||||
"assets/fonts/open-sans-v16-latin-regular.woff2": {
|
|
||||||
"filename": "assets/fonts/open-sans-v16-latin-regular.woff2",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 310201,
|
|
||||||
"len": 14380
|
|
||||||
},
|
|
||||||
"assets/fonts/questrial-v9-latin-regular.woff": {
|
|
||||||
"filename": "assets/fonts/questrial-v9-latin-regular.woff",
|
|
||||||
"contenttype": "application/font-woff",
|
|
||||||
"offset": 324581,
|
|
||||||
"len": 23048
|
|
||||||
},
|
|
||||||
"assets/fonts/questrial-v9-latin-regular.woff2": {
|
|
||||||
"filename": "assets/fonts/questrial-v9-latin-regular.woff2",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 347629,
|
|
||||||
"len": 13776
|
|
||||||
},
|
|
||||||
"assets/images/blog/2a40df99-1847-4726-9c5b-af4779eeb667-w1920-h1440.jpg": {
|
|
||||||
"filename": "assets/images/blog/2a40df99-1847-4726-9c5b-af4779eeb667-w1920-h1440.jpg",
|
|
||||||
"contenttype": "image/jpeg",
|
|
||||||
"offset": 361405,
|
|
||||||
"len": 79551
|
|
||||||
},
|
|
||||||
"assets/images/blog/2a40df99-1847-4726-9c5b-af4779eeb667-w960-h720.jpg": {
|
|
||||||
"filename": "assets/images/blog/2a40df99-1847-4726-9c5b-af4779eeb667-w960-h720.jpg",
|
|
||||||
"contenttype": "image/jpeg",
|
|
||||||
"offset": 440956,
|
|
||||||
"len": 31700
|
|
||||||
},
|
|
||||||
"assets/images/blog/2a40df99-1847-4726-9c5b-af4779eeb667.jpg": {
|
|
||||||
"filename": "assets/images/blog/2a40df99-1847-4726-9c5b-af4779eeb667.jpg",
|
|
||||||
"contenttype": "image/jpeg",
|
|
||||||
"offset": 472656,
|
|
||||||
"len": 69094
|
|
||||||
},
|
|
||||||
"assets/images/blog/512e4dd1-6b3d-41aa-80a1-b96c3370b3c3-w1920-h1440.jpg": {
|
|
||||||
"filename": "assets/images/blog/512e4dd1-6b3d-41aa-80a1-b96c3370b3c3-w1920-h1440.jpg",
|
|
||||||
"contenttype": "image/jpeg",
|
|
||||||
"offset": 541750,
|
|
||||||
"len": 219602
|
|
||||||
},
|
|
||||||
"assets/images/blog/512e4dd1-6b3d-41aa-80a1-b96c3370b3c3-w960-h720.jpg": {
|
|
||||||
"filename": "assets/images/blog/512e4dd1-6b3d-41aa-80a1-b96c3370b3c3-w960-h720.jpg",
|
|
||||||
"contenttype": "image/jpeg",
|
|
||||||
"offset": 761352,
|
|
||||||
"len": 67741
|
|
||||||
},
|
|
||||||
"assets/images/blog/512e4dd1-6b3d-41aa-80a1-b96c3370b3c3.jpg": {
|
|
||||||
"filename": "assets/images/blog/512e4dd1-6b3d-41aa-80a1-b96c3370b3c3.jpg",
|
|
||||||
"contenttype": "image/jpeg",
|
|
||||||
"offset": 829093,
|
|
||||||
"len": 226910
|
|
||||||
},
|
|
||||||
"assets/images/blog/823a7764-af7c-4687-a42e-bd70768068ab-w1920-h1440.jpg": {
|
|
||||||
"filename": "assets/images/blog/823a7764-af7c-4687-a42e-bd70768068ab-w1920-h1440.jpg",
|
|
||||||
"contenttype": "image/jpeg",
|
|
||||||
"offset": 1056003,
|
|
||||||
"len": 258292
|
|
||||||
},
|
|
||||||
"assets/images/blog/823a7764-af7c-4687-a42e-bd70768068ab-w960-h720.jpg": {
|
|
||||||
"filename": "assets/images/blog/823a7764-af7c-4687-a42e-bd70768068ab-w960-h720.jpg",
|
|
||||||
"contenttype": "image/jpeg",
|
|
||||||
"offset": 1314295,
|
|
||||||
"len": 93250
|
|
||||||
},
|
|
||||||
"assets/images/blog/823a7764-af7c-4687-a42e-bd70768068ab.jpg": {
|
|
||||||
"filename": "assets/images/blog/823a7764-af7c-4687-a42e-bd70768068ab.jpg",
|
|
||||||
"contenttype": "image/jpeg",
|
|
||||||
"offset": 1407545,
|
|
||||||
"len": 236722
|
|
||||||
},
|
|
||||||
"assets/images/blog/9aeea0d6-737c-4be8-8b63-5ec38cbf394b-w1920-h1440.jpg": {
|
|
||||||
"filename": "assets/images/blog/9aeea0d6-737c-4be8-8b63-5ec38cbf394b-w1920-h1440.jpg",
|
|
||||||
"contenttype": "image/jpeg",
|
|
||||||
"offset": 1644267,
|
|
||||||
"len": 285727
|
|
||||||
},
|
|
||||||
"assets/images/blog/9aeea0d6-737c-4be8-8b63-5ec38cbf394b-w960-h720.jpg": {
|
|
||||||
"filename": "assets/images/blog/9aeea0d6-737c-4be8-8b63-5ec38cbf394b-w960-h720.jpg",
|
|
||||||
"contenttype": "image/jpeg",
|
|
||||||
"offset": 1929994,
|
|
||||||
"len": 115524
|
|
||||||
},
|
|
||||||
"assets/images/blog/9aeea0d6-737c-4be8-8b63-5ec38cbf394b.jpg": {
|
|
||||||
"filename": "assets/images/blog/9aeea0d6-737c-4be8-8b63-5ec38cbf394b.jpg",
|
|
||||||
"contenttype": "image/jpeg",
|
|
||||||
"offset": 2045518,
|
|
||||||
"len": 338905
|
|
||||||
},
|
|
||||||
"assets/images/blog/a1ee6dcf-55ef-43cd-ae05-682d2e28e932-w1920-h1440.jpg": {
|
|
||||||
"filename": "assets/images/blog/a1ee6dcf-55ef-43cd-ae05-682d2e28e932-w1920-h1440.jpg",
|
|
||||||
"contenttype": "image/jpeg",
|
|
||||||
"offset": 2384423,
|
|
||||||
"len": 66608
|
|
||||||
},
|
|
||||||
"assets/images/blog/a1ee6dcf-55ef-43cd-ae05-682d2e28e932-w960-h720.jpg": {
|
|
||||||
"filename": "assets/images/blog/a1ee6dcf-55ef-43cd-ae05-682d2e28e932-w960-h720.jpg",
|
|
||||||
"contenttype": "image/jpeg",
|
|
||||||
"offset": 2451031,
|
|
||||||
"len": 23239
|
|
||||||
},
|
|
||||||
"assets/images/blog/a1ee6dcf-55ef-43cd-ae05-682d2e28e932.jpg": {
|
|
||||||
"filename": "assets/images/blog/a1ee6dcf-55ef-43cd-ae05-682d2e28e932.jpg",
|
|
||||||
"contenttype": "image/jpeg",
|
|
||||||
"offset": 2474270,
|
|
||||||
"len": 82334
|
|
||||||
},
|
|
||||||
"assets/images/blog/content/17343f27-a62f-4193-a0e5-4190d948eb2e.png": {
|
|
||||||
"filename": "assets/images/blog/content/17343f27-a62f-4193-a0e5-4190d948eb2e.png",
|
|
||||||
"contenttype": "image/png",
|
|
||||||
"offset": 2556604,
|
|
||||||
"len": 8571
|
|
||||||
},
|
|
||||||
"assets/images/blog/content/1748cc9c-9ea0-47b8-a110-ad3a114408d1.png": {
|
|
||||||
"filename": "assets/images/blog/content/1748cc9c-9ea0-47b8-a110-ad3a114408d1.png",
|
|
||||||
"contenttype": "image/png",
|
|
||||||
"offset": 2565175,
|
|
||||||
"len": 19776
|
|
||||||
},
|
|
||||||
"assets/images/blog/content/27b98c5e-ba57-47e6-9fe7-9b82fb89868b.jpg": {
|
|
||||||
"filename": "assets/images/blog/content/27b98c5e-ba57-47e6-9fe7-9b82fb89868b.jpg",
|
|
||||||
"contenttype": "image/jpeg",
|
|
||||||
"offset": 2584951,
|
|
||||||
"len": 68054
|
|
||||||
},
|
|
||||||
"assets/images/blog/content/39374de9-f24a-46f6-9955-982687607c6d.png": {
|
|
||||||
"filename": "assets/images/blog/content/39374de9-f24a-46f6-9955-982687607c6d.png",
|
|
||||||
"contenttype": "image/png",
|
|
||||||
"offset": 2653005,
|
|
||||||
"len": 30305
|
|
||||||
},
|
|
||||||
"assets/images/blog/content/5c660f5c-04fb-46cd-9846-edccb9a7b778.jpg": {
|
|
||||||
"filename": "assets/images/blog/content/5c660f5c-04fb-46cd-9846-edccb9a7b778.jpg",
|
|
||||||
"contenttype": "image/jpeg",
|
|
||||||
"offset": 2683310,
|
|
||||||
"len": 10409
|
|
||||||
},
|
|
||||||
"assets/images/blog/content/5cb6fb87-75d0-4aa4-99c7-b7815ca7ea70.png": {
|
|
||||||
"filename": "assets/images/blog/content/5cb6fb87-75d0-4aa4-99c7-b7815ca7ea70.png",
|
|
||||||
"contenttype": "image/png",
|
|
||||||
"offset": 2693719,
|
|
||||||
"len": 123977
|
|
||||||
},
|
|
||||||
"assets/images/blog/content/765827f6-192b-48c9-b3e1-cb7b33e3b881.png": {
|
|
||||||
"filename": "assets/images/blog/content/765827f6-192b-48c9-b3e1-cb7b33e3b881.png",
|
|
||||||
"contenttype": "image/png",
|
|
||||||
"offset": 2817696,
|
|
||||||
"len": 110297
|
|
||||||
},
|
|
||||||
"assets/images/blog/content/7b39a2f8-8060-43e7-a439-43f799d3e069.jpg": {
|
|
||||||
"filename": "assets/images/blog/content/7b39a2f8-8060-43e7-a439-43f799d3e069.jpg",
|
|
||||||
"contenttype": "image/jpeg",
|
|
||||||
"offset": 2927993,
|
|
||||||
"len": 24372
|
|
||||||
},
|
|
||||||
"assets/images/blog/content/8af4faff-e011-4e31-ba28-5023f65d1003.png": {
|
|
||||||
"filename": "assets/images/blog/content/8af4faff-e011-4e31-ba28-5023f65d1003.png",
|
|
||||||
"contenttype": "image/png",
|
|
||||||
"offset": 2952365,
|
|
||||||
"len": 106400
|
|
||||||
},
|
|
||||||
"assets/images/blog/content/ae29cd58-f28f-4a0e-bffb-a7e4e1235797.png": {
|
|
||||||
"filename": "assets/images/blog/content/ae29cd58-f28f-4a0e-bffb-a7e4e1235797.png",
|
|
||||||
"contenttype": "image/png",
|
|
||||||
"offset": 3058765,
|
|
||||||
"len": 33357
|
|
||||||
},
|
|
||||||
"assets/images/blog/content/b3be6c1c-725a-4af2-a85f-e47e09bbceef.png": {
|
|
||||||
"filename": "assets/images/blog/content/b3be6c1c-725a-4af2-a85f-e47e09bbceef.png",
|
|
||||||
"contenttype": "image/png",
|
|
||||||
"offset": 3092122,
|
|
||||||
"len": 37074
|
|
||||||
},
|
|
||||||
"assets/images/blog/content/b4e772a3-effb-4a5d-82d9-db9596ccfe51.png": {
|
|
||||||
"filename": "assets/images/blog/content/b4e772a3-effb-4a5d-82d9-db9596ccfe51.png",
|
|
||||||
"contenttype": "image/png",
|
|
||||||
"offset": 3129196,
|
|
||||||
"len": 79662
|
|
||||||
},
|
|
||||||
"assets/images/blog/content/d2731109-b50f-4c1f-b4f9-7ab8cac196da.png": {
|
|
||||||
"filename": "assets/images/blog/content/d2731109-b50f-4c1f-b4f9-7ab8cac196da.png",
|
|
||||||
"contenttype": "image/png",
|
|
||||||
"offset": 3208858,
|
|
||||||
"len": 104535
|
|
||||||
},
|
|
||||||
"assets/images/blog/content/fed0e592-d063-497b-9a3b-2bfc29b04d1a.jpg": {
|
|
||||||
"filename": "assets/images/blog/content/fed0e592-d063-497b-9a3b-2bfc29b04d1a.jpg",
|
|
||||||
"contenttype": "image/jpeg",
|
|
||||||
"offset": 3313393,
|
|
||||||
"len": 9535
|
|
||||||
},
|
|
||||||
"assets/images/blog/e4956336-3662-46ae-bea2-7fd3059919c3-w1920-h1440.jpg": {
|
|
||||||
"filename": "assets/images/blog/e4956336-3662-46ae-bea2-7fd3059919c3-w1920-h1440.jpg",
|
|
||||||
"contenttype": "image/jpeg",
|
|
||||||
"offset": 3322928,
|
|
||||||
"len": 402770
|
|
||||||
},
|
|
||||||
"assets/images/blog/e4956336-3662-46ae-bea2-7fd3059919c3-w960-h720.jpg": {
|
|
||||||
"filename": "assets/images/blog/e4956336-3662-46ae-bea2-7fd3059919c3-w960-h720.jpg",
|
|
||||||
"contenttype": "image/jpeg",
|
|
||||||
"offset": 3725698,
|
|
||||||
"len": 143539
|
|
||||||
},
|
|
||||||
"assets/images/blog/e4956336-3662-46ae-bea2-7fd3059919c3.jpg": {
|
|
||||||
"filename": "assets/images/blog/e4956336-3662-46ae-bea2-7fd3059919c3.jpg",
|
|
||||||
"contenttype": "image/jpeg",
|
|
||||||
"offset": 3869237,
|
|
||||||
"len": 375170
|
|
||||||
},
|
|
||||||
"assets/images/logo.svg": {
|
|
||||||
"filename": "assets/images/logo.svg",
|
|
||||||
"contenttype": "image/svg+xml",
|
|
||||||
"offset": 4244407,
|
|
||||||
"len": 2183
|
|
||||||
},
|
|
||||||
"assets/js/themes.js": {
|
|
||||||
"filename": "assets/js/themes.js",
|
|
||||||
"contenttype": "text/javascript",
|
|
||||||
"offset": 4246590,
|
|
||||||
"len": 779
|
|
||||||
},
|
|
||||||
"blog/building_a_web_farm_with_docker_and_raspberry_pi.html": {
|
|
||||||
"filename": "blog/building_a_web_farm_with_docker_and_raspberry_pi.html",
|
|
||||||
"contenttype": "text/html",
|
|
||||||
"offset": 4247369,
|
|
||||||
"len": 23111
|
|
||||||
},
|
|
||||||
"blog/continuously_deploy_a_static_website_with_azure_pipelines.html": {
|
|
||||||
"filename": "blog/continuously_deploy_a_static_website_with_azure_pipelines.html",
|
|
||||||
"contenttype": "text/html",
|
|
||||||
"offset": 4270480,
|
|
||||||
"len": 24738
|
|
||||||
},
|
|
||||||
"blog/decentralise_your_website_as_much_as_possible.html": {
|
|
||||||
"filename": "blog/decentralise_your_website_as_much_as_possible.html",
|
|
||||||
"contenttype": "text/html",
|
|
||||||
"offset": 4295218,
|
|
||||||
"len": 14825
|
|
||||||
},
|
|
||||||
"blog/developing_smart_contracts_for_business.html": {
|
|
||||||
"filename": "blog/developing_smart_contracts_for_business.html",
|
|
||||||
"contenttype": "text/html",
|
|
||||||
"offset": 4310043,
|
|
||||||
"len": 25783
|
|
||||||
},
|
|
||||||
"blog/getting_to_grips_with_jwt_in_asp_net_core.html": {
|
|
||||||
"filename": "blog/getting_to_grips_with_jwt_in_asp_net_core.html",
|
|
||||||
"contenttype": "text/html",
|
|
||||||
"offset": 4335826,
|
|
||||||
"len": 20915
|
|
||||||
},
|
|
||||||
"blog/index.html": { "filename": "blog/index.html", "contenttype": "text/html", "offset": 4356741, "len": 7345 },
|
|
||||||
"blog/setting_up_an_asp_net_core_web_farm.html": {
|
|
||||||
"filename": "blog/setting_up_an_asp_net_core_web_farm.html",
|
|
||||||
"contenttype": "text/html",
|
|
||||||
"offset": 4364086,
|
|
||||||
"len": 11464
|
|
||||||
},
|
|
||||||
"favicon-16x16.png": { "filename": "favicon-16x16.png", "contenttype": "image/png", "offset": 4375550, "len": 430 },
|
|
||||||
"favicon-32x32.png": { "filename": "favicon-32x32.png", "contenttype": "image/png", "offset": 4375980, "len": 540 },
|
|
||||||
"favicon.ico": { "filename": "favicon.ico", "contenttype": "image/x-icon", "offset": 4376520, "len": 15086 },
|
|
||||||
"feed.atom": {
|
|
||||||
"filename": "feed.atom",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 4391606,
|
|
||||||
"len": 95092
|
|
||||||
},
|
|
||||||
"index.html": { "filename": "index.html", "contenttype": "text/html", "offset": 4486698, "len": 4981 }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,122 +0,0 @@
|
||||||
{
|
|
||||||
"filename": "skygallery-v0.1.1-76c4c115fcb526716b2564568850f433",
|
|
||||||
"subfiles": {
|
|
||||||
"css/app.84a130ed.css": { "filename": "css/app.84a130ed.css", "contenttype": "text/css", "len": 698 },
|
|
||||||
"css/chunk-5ce44031.d4e78528.css": {
|
|
||||||
"filename": "css/chunk-5ce44031.d4e78528.css",
|
|
||||||
"contenttype": "text/css",
|
|
||||||
"offset": 698,
|
|
||||||
"len": 45
|
|
||||||
},
|
|
||||||
"css/chunk-6bef839b.593aa2be.css": {
|
|
||||||
"filename": "css/chunk-6bef839b.593aa2be.css",
|
|
||||||
"contenttype": "text/css",
|
|
||||||
"offset": 743,
|
|
||||||
"len": 5013
|
|
||||||
},
|
|
||||||
"css/chunk-8ed50a48.8ba8c09d.css": {
|
|
||||||
"filename": "css/chunk-8ed50a48.8ba8c09d.css",
|
|
||||||
"contenttype": "text/css",
|
|
||||||
"offset": 5756,
|
|
||||||
"len": 7204
|
|
||||||
},
|
|
||||||
"css/chunk-eb4c1efc.2a7e25ed.css": {
|
|
||||||
"filename": "css/chunk-eb4c1efc.2a7e25ed.css",
|
|
||||||
"contenttype": "text/css",
|
|
||||||
"offset": 12960,
|
|
||||||
"len": 45
|
|
||||||
},
|
|
||||||
"css/chunk-vendors.b4f58487.css": {
|
|
||||||
"filename": "css/chunk-vendors.b4f58487.css",
|
|
||||||
"contenttype": "text/css",
|
|
||||||
"offset": 13005,
|
|
||||||
"len": 382063
|
|
||||||
},
|
|
||||||
"img/skygallery_logo.2336197e.svg": {
|
|
||||||
"filename": "img/skygallery_logo.2336197e.svg",
|
|
||||||
"contenttype": "image/svg+xml",
|
|
||||||
"offset": 395068,
|
|
||||||
"len": 923
|
|
||||||
},
|
|
||||||
"img/skynet-logo-animated.4d24345c.svg": {
|
|
||||||
"filename": "img/skynet-logo-animated.4d24345c.svg",
|
|
||||||
"contenttype": "image/svg+xml",
|
|
||||||
"offset": 395991,
|
|
||||||
"len": 2600
|
|
||||||
},
|
|
||||||
"index.html": { "filename": "index.html", "contenttype": "text/html", "offset": 398591, "len": 2534 },
|
|
||||||
"js/app.cff1e0a4.js": {
|
|
||||||
"filename": "js/app.cff1e0a4.js",
|
|
||||||
"contenttype": "application/javascript",
|
|
||||||
"offset": 401125,
|
|
||||||
"len": 15604
|
|
||||||
},
|
|
||||||
"js/app.cff1e0a4.js.map": {
|
|
||||||
"filename": "js/app.cff1e0a4.js.map",
|
|
||||||
"contenttype": "application/json",
|
|
||||||
"offset": 416729,
|
|
||||||
"len": 54424
|
|
||||||
},
|
|
||||||
"js/chunk-5ce44031.7fb55da9.js": {
|
|
||||||
"filename": "js/chunk-5ce44031.7fb55da9.js",
|
|
||||||
"contenttype": "application/javascript",
|
|
||||||
"offset": 471153,
|
|
||||||
"len": 3644
|
|
||||||
},
|
|
||||||
"js/chunk-5ce44031.7fb55da9.js.map": {
|
|
||||||
"filename": "js/chunk-5ce44031.7fb55da9.js.map",
|
|
||||||
"contenttype": "application/json",
|
|
||||||
"offset": 474797,
|
|
||||||
"len": 13494
|
|
||||||
},
|
|
||||||
"js/chunk-6bef839b.b543fe7d.js": {
|
|
||||||
"filename": "js/chunk-6bef839b.b543fe7d.js",
|
|
||||||
"contenttype": "application/javascript",
|
|
||||||
"offset": 488291,
|
|
||||||
"len": 13349
|
|
||||||
},
|
|
||||||
"js/chunk-6bef839b.b543fe7d.js.map": {
|
|
||||||
"filename": "js/chunk-6bef839b.b543fe7d.js.map",
|
|
||||||
"contenttype": "application/json",
|
|
||||||
"offset": 501640,
|
|
||||||
"len": 46690
|
|
||||||
},
|
|
||||||
"js/chunk-8ed50a48.35f8ef35.js": {
|
|
||||||
"filename": "js/chunk-8ed50a48.35f8ef35.js",
|
|
||||||
"contenttype": "application/javascript",
|
|
||||||
"offset": 548330,
|
|
||||||
"len": 130329
|
|
||||||
},
|
|
||||||
"js/chunk-8ed50a48.35f8ef35.js.map": {
|
|
||||||
"filename": "js/chunk-8ed50a48.35f8ef35.js.map",
|
|
||||||
"contenttype": "application/json",
|
|
||||||
"offset": 678659,
|
|
||||||
"len": 507145
|
|
||||||
},
|
|
||||||
"js/chunk-eb4c1efc.57b6e01c.js": {
|
|
||||||
"filename": "js/chunk-eb4c1efc.57b6e01c.js",
|
|
||||||
"contenttype": "application/javascript",
|
|
||||||
"offset": 1185804,
|
|
||||||
"len": 4407
|
|
||||||
},
|
|
||||||
"js/chunk-eb4c1efc.57b6e01c.js.map": {
|
|
||||||
"filename": "js/chunk-eb4c1efc.57b6e01c.js.map",
|
|
||||||
"contenttype": "application/json",
|
|
||||||
"offset": 1190211,
|
|
||||||
"len": 15355
|
|
||||||
},
|
|
||||||
"js/chunk-vendors.1fd55121.js": {
|
|
||||||
"filename": "js/chunk-vendors.1fd55121.js",
|
|
||||||
"contenttype": "application/javascript",
|
|
||||||
"offset": 1205566,
|
|
||||||
"len": 749829
|
|
||||||
},
|
|
||||||
"js/chunk-vendors.1fd55121.js.map": {
|
|
||||||
"filename": "js/chunk-vendors.1fd55121.js.map",
|
|
||||||
"contenttype": "application/json",
|
|
||||||
"offset": 1955395,
|
|
||||||
"len": 2793251
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"defaultpath": "/index.html"
|
|
||||||
}
|
|
|
@ -1,658 +0,0 @@
|
||||||
{
|
|
||||||
"filename": "build",
|
|
||||||
"subfiles": {
|
|
||||||
"451.html": { "filename": "451.html", "contenttype": "text/html", "offset": 20181232, "len": 200 },
|
|
||||||
"asset-manifest.json": {
|
|
||||||
"filename": "asset-manifest.json",
|
|
||||||
"contenttype": "application/json",
|
|
||||||
"offset": 485031,
|
|
||||||
"len": 4561
|
|
||||||
},
|
|
||||||
"favicon.png": { "filename": "favicon.png", "contenttype": "image/png", "offset": 489592, "len": 7072 },
|
|
||||||
"images/192x192_App_Icon.png": {
|
|
||||||
"filename": "images/192x192_App_Icon.png",
|
|
||||||
"contenttype": "image/png",
|
|
||||||
"offset": 434153,
|
|
||||||
"len": 50878
|
|
||||||
},
|
|
||||||
"images/512x512_App_Icon.png": {
|
|
||||||
"filename": "images/512x512_App_Icon.png",
|
|
||||||
"contenttype": "image/png",
|
|
||||||
"offset": 47542,
|
|
||||||
"len": 386611
|
|
||||||
},
|
|
||||||
"index.html": { "filename": "index.html", "contenttype": "text/html", "len": 3268 },
|
|
||||||
"locales/de.json": {
|
|
||||||
"filename": "locales/de.json",
|
|
||||||
"contenttype": "application/json",
|
|
||||||
"offset": 7491,
|
|
||||||
"len": 4376
|
|
||||||
},
|
|
||||||
"locales/en.json": {
|
|
||||||
"filename": "locales/en.json",
|
|
||||||
"contenttype": "application/json",
|
|
||||||
"offset": 23709,
|
|
||||||
"len": 4321
|
|
||||||
},
|
|
||||||
"locales/es-AR.json": {
|
|
||||||
"filename": "locales/es-AR.json",
|
|
||||||
"contenttype": "application/json",
|
|
||||||
"offset": 16866,
|
|
||||||
"len": 3624
|
|
||||||
},
|
|
||||||
"locales/es-US.json": {
|
|
||||||
"filename": "locales/es-US.json",
|
|
||||||
"contenttype": "application/json",
|
|
||||||
"offset": 43912,
|
|
||||||
"len": 3630
|
|
||||||
},
|
|
||||||
"locales/it-IT.json": {
|
|
||||||
"filename": "locales/it-IT.json",
|
|
||||||
"contenttype": "application/json",
|
|
||||||
"offset": 3268,
|
|
||||||
"len": 4223
|
|
||||||
},
|
|
||||||
"locales/iw.json": {
|
|
||||||
"filename": "locales/iw.json",
|
|
||||||
"contenttype": "application/json",
|
|
||||||
"offset": 28030,
|
|
||||||
"len": 3929
|
|
||||||
},
|
|
||||||
"locales/ro.json": {
|
|
||||||
"filename": "locales/ro.json",
|
|
||||||
"contenttype": "application/json",
|
|
||||||
"offset": 31959,
|
|
||||||
"len": 3794
|
|
||||||
},
|
|
||||||
"locales/ru.json": {
|
|
||||||
"filename": "locales/ru.json",
|
|
||||||
"contenttype": "application/json",
|
|
||||||
"offset": 11867,
|
|
||||||
"len": 4999
|
|
||||||
},
|
|
||||||
"locales/vi.json": {
|
|
||||||
"filename": "locales/vi.json",
|
|
||||||
"contenttype": "application/json",
|
|
||||||
"offset": 39011,
|
|
||||||
"len": 4901
|
|
||||||
},
|
|
||||||
"locales/zh-CN.json": {
|
|
||||||
"filename": "locales/zh-CN.json",
|
|
||||||
"contenttype": "application/json",
|
|
||||||
"offset": 20490,
|
|
||||||
"len": 3219
|
|
||||||
},
|
|
||||||
"locales/zh-TW.json": {
|
|
||||||
"filename": "locales/zh-TW.json",
|
|
||||||
"contenttype": "application/json",
|
|
||||||
"offset": 35753,
|
|
||||||
"len": 3258
|
|
||||||
},
|
|
||||||
"manifest.json": { "filename": "manifest.json", "contenttype": "application/json", "offset": 20190818, "len": 470 },
|
|
||||||
"precache-manifest.5ce41899d70d2e0450f591b3e917c2a4.js": {
|
|
||||||
"filename": "precache-manifest.5ce41899d70d2e0450f591b3e917c2a4.js",
|
|
||||||
"contenttype": "application/x-javascript",
|
|
||||||
"offset": 20181432,
|
|
||||||
"len": 9386
|
|
||||||
},
|
|
||||||
"service-worker.js": {
|
|
||||||
"filename": "service-worker.js",
|
|
||||||
"contenttype": "application/x-javascript",
|
|
||||||
"offset": 20191288,
|
|
||||||
"len": 1183
|
|
||||||
},
|
|
||||||
"static/css/4.f04942fe.chunk.css": {
|
|
||||||
"filename": "static/css/4.f04942fe.chunk.css",
|
|
||||||
"contenttype": "text/css",
|
|
||||||
"offset": 496664,
|
|
||||||
"len": 5331
|
|
||||||
},
|
|
||||||
"static/css/4.f04942fe.chunk.css.map": {
|
|
||||||
"filename": "static/css/4.f04942fe.chunk.css.map",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 501995,
|
|
||||||
"len": 8394
|
|
||||||
},
|
|
||||||
"static/js/0.1043efff.chunk.js": {
|
|
||||||
"filename": "static/js/0.1043efff.chunk.js",
|
|
||||||
"contenttype": "application/x-javascript",
|
|
||||||
"offset": 3451819,
|
|
||||||
"len": 226756
|
|
||||||
},
|
|
||||||
"static/js/0.1043efff.chunk.js.map": {
|
|
||||||
"filename": "static/js/0.1043efff.chunk.js.map",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 8495570,
|
|
||||||
"len": 811341
|
|
||||||
},
|
|
||||||
"static/js/1.722d768c.chunk.js": {
|
|
||||||
"filename": "static/js/1.722d768c.chunk.js",
|
|
||||||
"contenttype": "application/x-javascript",
|
|
||||||
"offset": 2503781,
|
|
||||||
"len": 20289
|
|
||||||
},
|
|
||||||
"static/js/1.722d768c.chunk.js.map": {
|
|
||||||
"filename": "static/js/1.722d768c.chunk.js.map",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 11896220,
|
|
||||||
"len": 44729
|
|
||||||
},
|
|
||||||
"static/js/4.cebcd4f8.chunk.js": {
|
|
||||||
"filename": "static/js/4.cebcd4f8.chunk.js",
|
|
||||||
"contenttype": "application/x-javascript",
|
|
||||||
"offset": 11941212,
|
|
||||||
"len": 1486762
|
|
||||||
},
|
|
||||||
"static/js/4.cebcd4f8.chunk.js.LICENSE.txt": {
|
|
||||||
"filename": "static/js/4.cebcd4f8.chunk.js.LICENSE.txt",
|
|
||||||
"contenttype": "text/plain",
|
|
||||||
"offset": 14378677,
|
|
||||||
"len": 3519
|
|
||||||
},
|
|
||||||
"static/js/4.cebcd4f8.chunk.js.map": {
|
|
||||||
"filename": "static/js/4.cebcd4f8.chunk.js.map",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 3678575,
|
|
||||||
"len": 4816995
|
|
||||||
},
|
|
||||||
"static/js/5.428f04e8.chunk.js": {
|
|
||||||
"filename": "static/js/5.428f04e8.chunk.js",
|
|
||||||
"contenttype": "application/x-javascript",
|
|
||||||
"offset": 1887438,
|
|
||||||
"len": 616343
|
|
||||||
},
|
|
||||||
"static/js/5.428f04e8.chunk.js.LICENSE.txt": {
|
|
||||||
"filename": "static/js/5.428f04e8.chunk.js.LICENSE.txt",
|
|
||||||
"contenttype": "text/plain",
|
|
||||||
"offset": 3450983,
|
|
||||||
"len": 426
|
|
||||||
},
|
|
||||||
"static/js/5.428f04e8.chunk.js.map": {
|
|
||||||
"filename": "static/js/5.428f04e8.chunk.js.map",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 10046674,
|
|
||||||
"len": 1553345
|
|
||||||
},
|
|
||||||
"static/js/6.29fcca22.chunk.js": {
|
|
||||||
"filename": "static/js/6.29fcca22.chunk.js",
|
|
||||||
"contenttype": "application/x-javascript",
|
|
||||||
"offset": 11600019,
|
|
||||||
"len": 296095
|
|
||||||
},
|
|
||||||
"static/js/6.29fcca22.chunk.js.map": {
|
|
||||||
"filename": "static/js/6.29fcca22.chunk.js.map",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 13440646,
|
|
||||||
"len": 938031
|
|
||||||
},
|
|
||||||
"static/js/7.8d2bc3b4.chunk.js": {
|
|
||||||
"filename": "static/js/7.8d2bc3b4.chunk.js",
|
|
||||||
"contenttype": "application/x-javascript",
|
|
||||||
"offset": 9306911,
|
|
||||||
"len": 263
|
|
||||||
},
|
|
||||||
"static/js/7.8d2bc3b4.chunk.js.map": {
|
|
||||||
"filename": "static/js/7.8d2bc3b4.chunk.js.map",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 11896114,
|
|
||||||
"len": 106
|
|
||||||
},
|
|
||||||
"static/js/8.3d784f08.chunk.js": {
|
|
||||||
"filename": "static/js/8.3d784f08.chunk.js",
|
|
||||||
"contenttype": "application/x-javascript",
|
|
||||||
"offset": 11940949,
|
|
||||||
"len": 263
|
|
||||||
},
|
|
||||||
"static/js/8.3d784f08.chunk.js.map": {
|
|
||||||
"filename": "static/js/8.3d784f08.chunk.js.map",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 3450877,
|
|
||||||
"len": 106
|
|
||||||
},
|
|
||||||
"static/js/9.08920d68.chunk.js": {
|
|
||||||
"filename": "static/js/9.08920d68.chunk.js",
|
|
||||||
"contenttype": "application/x-javascript",
|
|
||||||
"offset": 2524070,
|
|
||||||
"len": 626875
|
|
||||||
},
|
|
||||||
"static/js/9.08920d68.chunk.js.LICENSE.txt": {
|
|
||||||
"filename": "static/js/9.08920d68.chunk.js.LICENSE.txt",
|
|
||||||
"contenttype": "text/plain",
|
|
||||||
"offset": 3451409,
|
|
||||||
"len": 410
|
|
||||||
},
|
|
||||||
"static/js/9.08920d68.chunk.js.map": {
|
|
||||||
"filename": "static/js/9.08920d68.chunk.js.map",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 512852,
|
|
||||||
"len": 1374586
|
|
||||||
},
|
|
||||||
"static/js/main.d2a5ca05.chunk.js": {
|
|
||||||
"filename": "static/js/main.d2a5ca05.chunk.js",
|
|
||||||
"contenttype": "application/x-javascript",
|
|
||||||
"offset": 3150945,
|
|
||||||
"len": 299932
|
|
||||||
},
|
|
||||||
"static/js/main.d2a5ca05.chunk.js.map": {
|
|
||||||
"filename": "static/js/main.d2a5ca05.chunk.js.map",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 9307174,
|
|
||||||
"len": 739500
|
|
||||||
},
|
|
||||||
"static/js/runtime-main.712341b8.js": {
|
|
||||||
"filename": "static/js/runtime-main.712341b8.js",
|
|
||||||
"contenttype": "application/x-javascript",
|
|
||||||
"offset": 510389,
|
|
||||||
"len": 2463
|
|
||||||
},
|
|
||||||
"static/js/runtime-main.712341b8.js.map": {
|
|
||||||
"filename": "static/js/runtime-main.712341b8.js.map",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 13427974,
|
|
||||||
"len": 12672
|
|
||||||
},
|
|
||||||
"static/media/Inter-Black.09f4068b.woff2": {
|
|
||||||
"filename": "static/media/Inter-Black.09f4068b.woff2",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 16311114,
|
|
||||||
"len": 104656
|
|
||||||
},
|
|
||||||
"static/media/Inter-Black.e3735483.woff": {
|
|
||||||
"filename": "static/media/Inter-Black.e3735483.woff",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 16415770,
|
|
||||||
"len": 139648
|
|
||||||
},
|
|
||||||
"static/media/Inter-BlackItalic.07e69b53.woff": {
|
|
||||||
"filename": "static/media/Inter-BlackItalic.07e69b53.woff",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 15020483,
|
|
||||||
"len": 145816
|
|
||||||
},
|
|
||||||
"static/media/Inter-BlackItalic.daa1ca3c.woff2": {
|
|
||||||
"filename": "static/media/Inter-BlackItalic.daa1ca3c.woff2",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 19675808,
|
|
||||||
"len": 109900
|
|
||||||
},
|
|
||||||
"static/media/Inter-Bold.79260e5b.woff": {
|
|
||||||
"filename": "static/media/Inter-Bold.79260e5b.woff",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 15781749,
|
|
||||||
"len": 143464
|
|
||||||
},
|
|
||||||
"static/media/Inter-Bold.aed27700.woff2": {
|
|
||||||
"filename": "static/media/Inter-Bold.aed27700.woff2",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 16555739,
|
|
||||||
"len": 107144
|
|
||||||
},
|
|
||||||
"static/media/Inter-BoldItalic.8ef77a03.woff2": {
|
|
||||||
"filename": "static/media/Inter-BoldItalic.8ef77a03.woff2",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 17104768,
|
|
||||||
"len": 112276
|
|
||||||
},
|
|
||||||
"static/media/Inter-BoldItalic.e0879d64.woff": {
|
|
||||||
"filename": "static/media/Inter-BoldItalic.e0879d64.woff",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 15483981,
|
|
||||||
"len": 149360
|
|
||||||
},
|
|
||||||
"static/media/Inter-ExtraBold.38bc51bc.woff": {
|
|
||||||
"filename": "static/media/Inter-ExtraBold.38bc51bc.woff",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 19419594,
|
|
||||||
"len": 143256
|
|
||||||
},
|
|
||||||
"static/media/Inter-ExtraBold.92d16aee.woff2": {
|
|
||||||
"filename": "static/media/Inter-ExtraBold.92d16aee.woff2",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 19312290,
|
|
||||||
"len": 107304
|
|
||||||
},
|
|
||||||
"static/media/Inter-ExtraBoldItalic.0e4b21eb.woff": {
|
|
||||||
"filename": "static/media/Inter-ExtraBoldItalic.0e4b21eb.woff",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 16671312,
|
|
||||||
"len": 149116
|
|
||||||
},
|
|
||||||
"static/media/Inter-ExtraBoldItalic.57ea76d0.woff2": {
|
|
||||||
"filename": "static/media/Inter-ExtraBoldItalic.57ea76d0.woff2",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 18732262,
|
|
||||||
"len": 112656
|
|
||||||
},
|
|
||||||
"static/media/Inter-ExtraLight.4bd040df.woff": {
|
|
||||||
"filename": "static/media/Inter-ExtraLight.4bd040df.woff",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 14746958,
|
|
||||||
"len": 141344
|
|
||||||
},
|
|
||||||
"static/media/Inter-ExtraLight.4d9f96f8.woff2": {
|
|
||||||
"filename": "static/media/Inter-ExtraLight.4d9f96f8.woff2",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 18945450,
|
|
||||||
"len": 105444
|
|
||||||
},
|
|
||||||
"static/media/Inter-ExtraLightItalic.54d3d9a5.woff2": {
|
|
||||||
"filename": "static/media/Inter-ExtraLightItalic.54d3d9a5.woff2",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 14888302,
|
|
||||||
"len": 111804
|
|
||||||
},
|
|
||||||
"static/media/Inter-ExtraLightItalic.84c26656.woff": {
|
|
||||||
"filename": "static/media/Inter-ExtraLightItalic.84c26656.woff",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 18569981,
|
|
||||||
"len": 148416
|
|
||||||
},
|
|
||||||
"static/media/Inter-Italic.9528384c.woff2": {
|
|
||||||
"filename": "static/media/Inter-Italic.9528384c.woff2",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 14383371,
|
|
||||||
"len": 108172
|
|
||||||
},
|
|
||||||
"static/media/Inter-Italic.e4ad3666.woff": {
|
|
||||||
"filename": "static/media/Inter-Italic.e4ad3666.woff",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 20037756,
|
|
||||||
"len": 143476
|
|
||||||
},
|
|
||||||
"static/media/Inter-Light.5baca21a.woff2": {
|
|
||||||
"filename": "static/media/Inter-Light.5baca21a.woff2",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 18016306,
|
|
||||||
"len": 105556
|
|
||||||
},
|
|
||||||
"static/media/Inter-Light.b9920de0.woff": {
|
|
||||||
"filename": "static/media/Inter-Light.b9920de0.woff",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 18428717,
|
|
||||||
"len": 141264
|
|
||||||
},
|
|
||||||
"static/media/Inter-LightItalic.0555a46c.woff": {
|
|
||||||
"filename": "static/media/Inter-LightItalic.0555a46c.woff",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 15633341,
|
|
||||||
"len": 148408
|
|
||||||
},
|
|
||||||
"static/media/Inter-LightItalic.adc70179.woff2": {
|
|
||||||
"filename": "static/media/Inter-LightItalic.adc70179.woff2",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 19200250,
|
|
||||||
"len": 112040
|
|
||||||
},
|
|
||||||
"static/media/Inter-Medium.7a8cc724.woff": {
|
|
||||||
"filename": "static/media/Inter-Medium.7a8cc724.woff",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 17763598,
|
|
||||||
"len": 142780
|
|
||||||
},
|
|
||||||
"static/media/Inter-Medium.f6cf0a0b.woff2": {
|
|
||||||
"filename": "static/media/Inter-Medium.f6cf0a0b.woff2",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 17431014,
|
|
||||||
"len": 106484
|
|
||||||
},
|
|
||||||
"static/media/Inter-MediumItalic.417907d2.woff": {
|
|
||||||
"filename": "static/media/Inter-MediumItalic.417907d2.woff",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 16955424,
|
|
||||||
"len": 149344
|
|
||||||
},
|
|
||||||
"static/media/Inter-MediumItalic.565a7104.woff2": {
|
|
||||||
"filename": "static/media/Inter-MediumItalic.565a7104.woff2",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 17318374,
|
|
||||||
"len": 112640
|
|
||||||
},
|
|
||||||
"static/media/Inter-Regular.4dd66a11.woff2": {
|
|
||||||
"filename": "static/media/Inter-Regular.4dd66a11.woff2",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 18844918,
|
|
||||||
"len": 100368
|
|
||||||
},
|
|
||||||
"static/media/Inter-Regular.7c539936.woff": {
|
|
||||||
"filename": "static/media/Inter-Regular.7c539936.woff",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 16820428,
|
|
||||||
"len": 134996
|
|
||||||
},
|
|
||||||
"static/media/Inter-SemiBold.1db6c55c.woff": {
|
|
||||||
"filename": "static/media/Inter-SemiBold.1db6c55c.woff",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 19785708,
|
|
||||||
"len": 143148
|
|
||||||
},
|
|
||||||
"static/media/Inter-SemiBold.dd8a55ef.woff2": {
|
|
||||||
"filename": "static/media/Inter-SemiBold.dd8a55ef.woff2",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 14491543,
|
|
||||||
"len": 106916
|
|
||||||
},
|
|
||||||
"static/media/Inter-SemiBoldItalic.81678d1a.woff": {
|
|
||||||
"filename": "static/media/Inter-SemiBoldItalic.81678d1a.woff",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 19050894,
|
|
||||||
"len": 149356
|
|
||||||
},
|
|
||||||
"static/media/Inter-SemiBoldItalic.ac201e30.woff2": {
|
|
||||||
"filename": "static/media/Inter-SemiBoldItalic.ac201e30.woff2",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 19562850,
|
|
||||||
"len": 112768
|
|
||||||
},
|
|
||||||
"static/media/Inter-Thin.850febbe.woff2": {
|
|
||||||
"filename": "static/media/Inter-Thin.850febbe.woff2",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 17217044,
|
|
||||||
"len": 101004
|
|
||||||
},
|
|
||||||
"static/media/Inter-Thin.ead42837.woff": {
|
|
||||||
"filename": "static/media/Inter-Thin.ead42837.woff",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 18230961,
|
|
||||||
"len": 137068
|
|
||||||
},
|
|
||||||
"static/media/Inter-ThinItalic.a76db065.woff": {
|
|
||||||
"filename": "static/media/Inter-ThinItalic.a76db065.woff",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 16166265,
|
|
||||||
"len": 144528
|
|
||||||
},
|
|
||||||
"static/media/Inter-ThinItalic.e08d9b2a.woff2": {
|
|
||||||
"filename": "static/media/Inter-ThinItalic.e08d9b2a.woff2",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 19930172,
|
|
||||||
"len": 107584
|
|
||||||
},
|
|
||||||
"static/media/Inter-italic.var.2690e3c2.woff2": {
|
|
||||||
"filename": "static/media/Inter-italic.var.2690e3c2.woff2",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 15925213,
|
|
||||||
"len": 241052
|
|
||||||
},
|
|
||||||
"static/media/Inter-roman.var.90e8f61d.woff2": {
|
|
||||||
"filename": "static/media/Inter-roman.var.90e8f61d.woff2",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 17537498,
|
|
||||||
"len": 226100
|
|
||||||
},
|
|
||||||
"static/media/Inter.var.4b976905.woff2": {
|
|
||||||
"filename": "static/media/Inter.var.4b976905.woff2",
|
|
||||||
"contenttype": "application/octet-stream",
|
|
||||||
"offset": 15166461,
|
|
||||||
"len": 317520
|
|
||||||
},
|
|
||||||
"static/media/arrow-down-blue.cd061363.svg": {
|
|
||||||
"filename": "static/media/arrow-down-blue.cd061363.svg",
|
|
||||||
"contenttype": "image/svg+xml",
|
|
||||||
"offset": 17318048,
|
|
||||||
"len": 326
|
|
||||||
},
|
|
||||||
"static/media/arrow-down-grey.c0dedd2f.svg": {
|
|
||||||
"filename": "static/media/arrow-down-grey.c0dedd2f.svg",
|
|
||||||
"contenttype": "image/svg+xml",
|
|
||||||
"offset": 15000106,
|
|
||||||
"len": 326
|
|
||||||
},
|
|
||||||
"static/media/arrow-right-white.337ad716.png": {
|
|
||||||
"filename": "static/media/arrow-right-white.337ad716.png",
|
|
||||||
"contenttype": "image/png",
|
|
||||||
"offset": 15000432,
|
|
||||||
"len": 12999
|
|
||||||
},
|
|
||||||
"static/media/arrow-right.d285b6cf.svg": {
|
|
||||||
"filename": "static/media/arrow-right.d285b6cf.svg",
|
|
||||||
"contenttype": "image/svg+xml",
|
|
||||||
"offset": 19929626,
|
|
||||||
"len": 263
|
|
||||||
},
|
|
||||||
"static/media/blue-loader.904b44c2.svg": {
|
|
||||||
"filename": "static/media/blue-loader.904b44c2.svg",
|
|
||||||
"contenttype": "image/svg+xml",
|
|
||||||
"offset": 19929889,
|
|
||||||
"len": 283
|
|
||||||
},
|
|
||||||
"static/media/circle-grey.ed2a1dad.svg": {
|
|
||||||
"filename": "static/media/circle-grey.ed2a1dad.svg",
|
|
||||||
"contenttype": "image/svg+xml",
|
|
||||||
"offset": 16310793,
|
|
||||||
"len": 321
|
|
||||||
},
|
|
||||||
"static/media/circle.2d975615.svg": {
|
|
||||||
"filename": "static/media/circle.2d975615.svg",
|
|
||||||
"contenttype": "image/svg+xml",
|
|
||||||
"offset": 16555418,
|
|
||||||
"len": 321
|
|
||||||
},
|
|
||||||
"static/media/coinbaseWalletIcon.62578f59.svg": {
|
|
||||||
"filename": "static/media/coinbaseWalletIcon.62578f59.svg",
|
|
||||||
"contenttype": "image/svg+xml",
|
|
||||||
"offset": 18375091,
|
|
||||||
"len": 53626
|
|
||||||
},
|
|
||||||
"static/media/dropdown-blue.b20914ec.svg": {
|
|
||||||
"filename": "static/media/dropdown-blue.b20914ec.svg",
|
|
||||||
"contenttype": "image/svg+xml",
|
|
||||||
"offset": 14382513,
|
|
||||||
"len": 164
|
|
||||||
},
|
|
||||||
"static/media/dropdown.7d32d2fa.svg": {
|
|
||||||
"filename": "static/media/dropdown.7d32d2fa.svg",
|
|
||||||
"contenttype": "image/svg+xml",
|
|
||||||
"offset": 18945286,
|
|
||||||
"len": 164
|
|
||||||
},
|
|
||||||
"static/media/dropup-blue.b96d70e1.svg": {
|
|
||||||
"filename": "static/media/dropup-blue.b96d70e1.svg",
|
|
||||||
"contenttype": "image/svg+xml",
|
|
||||||
"offset": 15166299,
|
|
||||||
"len": 162
|
|
||||||
},
|
|
||||||
"static/media/link.50c67f3c.svg": {
|
|
||||||
"filename": "static/media/link.50c67f3c.svg",
|
|
||||||
"contenttype": "image/svg+xml",
|
|
||||||
"offset": 14382196,
|
|
||||||
"len": 317
|
|
||||||
},
|
|
||||||
"static/media/logo.5827780d.svg": {
|
|
||||||
"filename": "static/media/logo.5827780d.svg",
|
|
||||||
"contenttype": "image/svg+xml",
|
|
||||||
"offset": 15013431,
|
|
||||||
"len": 7052
|
|
||||||
},
|
|
||||||
"static/media/logo_white.edb44e56.svg": {
|
|
||||||
"filename": "static/media/logo_white.edb44e56.svg",
|
|
||||||
"contenttype": "image/svg+xml",
|
|
||||||
"offset": 18368029,
|
|
||||||
"len": 7062
|
|
||||||
},
|
|
||||||
"static/media/magnifying-glass.67440097.svg": {
|
|
||||||
"filename": "static/media/magnifying-glass.67440097.svg",
|
|
||||||
"contenttype": "image/svg+xml",
|
|
||||||
"offset": 16662883,
|
|
||||||
"len": 8429
|
|
||||||
},
|
|
||||||
"static/media/menu.4f2c4440.svg": {
|
|
||||||
"filename": "static/media/menu.4f2c4440.svg",
|
|
||||||
"contenttype": "image/svg+xml",
|
|
||||||
"offset": 18015579,
|
|
||||||
"len": 727
|
|
||||||
},
|
|
||||||
"static/media/metamask.023762b6.png": {
|
|
||||||
"filename": "static/media/metamask.023762b6.png",
|
|
||||||
"contenttype": "image/png",
|
|
||||||
"offset": 14611832,
|
|
||||||
"len": 114217
|
|
||||||
},
|
|
||||||
"static/media/plus-blue.e8021e51.svg": {
|
|
||||||
"filename": "static/media/plus-blue.e8021e51.svg",
|
|
||||||
"contenttype": "image/svg+xml",
|
|
||||||
"offset": 14746469,
|
|
||||||
"len": 190
|
|
||||||
},
|
|
||||||
"static/media/plus-grey.d8e0be7d.svg": {
|
|
||||||
"filename": "static/media/plus-grey.d8e0be7d.svg",
|
|
||||||
"contenttype": "image/svg+xml",
|
|
||||||
"offset": 19675618,
|
|
||||||
"len": 190
|
|
||||||
},
|
|
||||||
"static/media/portisIcon.b234b2bf.png": {
|
|
||||||
"filename": "static/media/portisIcon.b234b2bf.png",
|
|
||||||
"contenttype": "image/png",
|
|
||||||
"offset": 18718397,
|
|
||||||
"len": 13865
|
|
||||||
},
|
|
||||||
"static/media/question-mark.1ae4d9f4.svg": {
|
|
||||||
"filename": "static/media/question-mark.1ae4d9f4.svg",
|
|
||||||
"contenttype": "image/svg+xml",
|
|
||||||
"offset": 14726049,
|
|
||||||
"len": 818
|
|
||||||
},
|
|
||||||
"static/media/question.a46e8bc1.svg": {
|
|
||||||
"filename": "static/media/question.a46e8bc1.svg",
|
|
||||||
"contenttype": "image/svg+xml",
|
|
||||||
"offset": 19928856,
|
|
||||||
"len": 770
|
|
||||||
},
|
|
||||||
"static/media/spinner.be00fc4a.svg": {
|
|
||||||
"filename": "static/media/spinner.be00fc4a.svg",
|
|
||||||
"contenttype": "image/svg+xml",
|
|
||||||
"offset": 14382677,
|
|
||||||
"len": 694
|
|
||||||
},
|
|
||||||
"static/media/trustWallet.edcc1ab5.png": {
|
|
||||||
"filename": "static/media/trustWallet.edcc1ab5.png",
|
|
||||||
"contenttype": "image/png",
|
|
||||||
"offset": 14726867,
|
|
||||||
"len": 19602
|
|
||||||
},
|
|
||||||
"static/media/walletConnectIcon.8215855c.svg": {
|
|
||||||
"filename": "static/media/walletConnectIcon.8215855c.svg",
|
|
||||||
"contenttype": "image/svg+xml",
|
|
||||||
"offset": 14598459,
|
|
||||||
"len": 13373
|
|
||||||
},
|
|
||||||
"static/media/wordmark.b75565ae.svg": {
|
|
||||||
"filename": "static/media/wordmark.b75565ae.svg",
|
|
||||||
"contenttype": "image/svg+xml",
|
|
||||||
"offset": 18121862,
|
|
||||||
"len": 109099
|
|
||||||
},
|
|
||||||
"static/media/wordmark_white.9914390f.svg": {
|
|
||||||
"filename": "static/media/wordmark_white.9914390f.svg",
|
|
||||||
"contenttype": "image/svg+xml",
|
|
||||||
"offset": 17906378,
|
|
||||||
"len": 109201
|
|
||||||
},
|
|
||||||
"static/media/x.5b8e2186.svg": {
|
|
||||||
"filename": "static/media/x.5b8e2186.svg",
|
|
||||||
"contenttype": "image/svg+xml",
|
|
||||||
"offset": 14746659,
|
|
||||||
"len": 299
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"defaultpath": "/index.html"
|
|
||||||
}
|
|
|
@ -1,48 +0,0 @@
|
||||||
process.env.NODE_ENV = process.env.NODE_ENV || "development";
|
|
||||||
|
|
||||||
if (!process.env.PORTAL_DOMAIN) {
|
|
||||||
throw new Error("You need to provide PORTAL_DOMAIN environment variable");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (process.env.ACCOUNTS_ENABLED === "true") {
|
|
||||||
if (["authenticated", "subscription"].includes(process.env.ACCOUNTS_LIMIT_ACCESS)) {
|
|
||||||
if (!process.env.ACCOUNTS_TEST_USER_EMAIL) {
|
|
||||||
throw new Error("ACCOUNTS_TEST_USER_EMAIL cannot be empty");
|
|
||||||
}
|
|
||||||
if (!process.env.ACCOUNTS_TEST_USER_PASSWORD) {
|
|
||||||
throw new Error("ACCOUNTS_TEST_USER_PASSWORD cannot be empty");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const express = require("express");
|
|
||||||
const db = require("./db");
|
|
||||||
|
|
||||||
const host = process.env.HOSTNAME || "0.0.0.0";
|
|
||||||
const port = Number(process.env.PORT) || 3100;
|
|
||||||
|
|
||||||
const server = express();
|
|
||||||
|
|
||||||
server.use(express.urlencoded({ extended: false }));
|
|
||||||
server.use(express.json());
|
|
||||||
server.use((req, res, next) => {
|
|
||||||
db.read();
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
|
|
||||||
server.get("/health-check", require("./api/index"));
|
|
||||||
server.get("/health-check/critical", require("./api/critical"));
|
|
||||||
server.get("/health-check/extended", require("./api/extended"));
|
|
||||||
server.get("/health-check/disabled", require("./api/disabled"));
|
|
||||||
|
|
||||||
server.listen(port, host, (error) => {
|
|
||||||
if (error) throw error;
|
|
||||||
|
|
||||||
console.info(`Server listening at http://${host}:${port} (NODE_ENV: ${process.env.NODE_ENV})`);
|
|
||||||
|
|
||||||
const { ipRegex } = require("./utils");
|
|
||||||
|
|
||||||
if (ipRegex.test(process.env.serverip)) {
|
|
||||||
console.info(`Server public ip: ${process.env.serverip}`);
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,144 +0,0 @@
|
||||||
const ipCheckService = "whatismyip.akamai.com";
|
|
||||||
const ipRegex = new RegExp(
|
|
||||||
`^(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}$`
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the time between start and now in milliseconds
|
|
||||||
*/
|
|
||||||
function calculateElapsedTime(start) {
|
|
||||||
const diff = process.hrtime(start);
|
|
||||||
|
|
||||||
return Math.round((diff[0] * 1e9 + diff[1]) / 1e6); // msec
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the ISO string with yesterday's date set (- 24 hours)
|
|
||||||
*/
|
|
||||||
function getYesterdayISOString() {
|
|
||||||
const date = new Date();
|
|
||||||
|
|
||||||
date.setDate(date.getDate() - 1);
|
|
||||||
|
|
||||||
return date.toISOString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get response from response object if available
|
|
||||||
*/
|
|
||||||
function getResponseContent(response) {
|
|
||||||
try {
|
|
||||||
return JSON.parse(response?.body || response?.text);
|
|
||||||
} catch {
|
|
||||||
return response?.body || response?.text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ensures that the object serializes to JSON properly
|
|
||||||
*/
|
|
||||||
function ensureValidJSON(object) {
|
|
||||||
const replacer = (key, value) => (value === undefined ? "--undefined--" : value);
|
|
||||||
const stringified = JSON.stringify(object, replacer);
|
|
||||||
|
|
||||||
return JSON.parse(stringified);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get variable value from environment (process.env)
|
|
||||||
* Exit with code 1 if variable is not set or empty
|
|
||||||
* @param {string} name variable name
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
function getRequiredEnvironmentVariable(name) {
|
|
||||||
const value = process.env[name];
|
|
||||||
|
|
||||||
if (!value) {
|
|
||||||
console.log(`${name} cannot be empty`);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Authenticate with given credentials and return auth cookie
|
|
||||||
* Creates new account if username does not exist
|
|
||||||
* Only authenticates when portal is set to authenticated users only mode
|
|
||||||
* @param {boolean} forceAuth forcibly ensure authentication with test credentials
|
|
||||||
*/
|
|
||||||
function getAuthCookie(forceAuth = false) {
|
|
||||||
// cache auth promise so only one actual request will be made
|
|
||||||
if (getAuthCookie.cache) return getAuthCookie.cache;
|
|
||||||
|
|
||||||
// accounts disabled, do not try to authenticate
|
|
||||||
if (!isPortalModuleEnabled("a")) return "";
|
|
||||||
|
|
||||||
// do not authenticate if it is not required by portal limit access rule
|
|
||||||
if (!forceAuth && !["authenticated", "subscription"].includes(process.env.ACCOUNTS_LIMIT_ACCESS)) return "";
|
|
||||||
|
|
||||||
// assign all required environment variables
|
|
||||||
const portalDomain = getRequiredEnvironmentVariable("PORTAL_DOMAIN");
|
|
||||||
const email = getRequiredEnvironmentVariable("ACCOUNTS_TEST_USER_EMAIL");
|
|
||||||
const password = getRequiredEnvironmentVariable("ACCOUNTS_TEST_USER_PASSWORD");
|
|
||||||
|
|
||||||
async function authenticate() {
|
|
||||||
const got = require("got");
|
|
||||||
|
|
||||||
try {
|
|
||||||
// authenticate with given test user credentials
|
|
||||||
const response = await got.post(`https://account.${portalDomain}/api/login`, {
|
|
||||||
json: { email, password },
|
|
||||||
});
|
|
||||||
|
|
||||||
// extract set-cookie from successful authentication request
|
|
||||||
const cookies = response.headers["set-cookie"];
|
|
||||||
|
|
||||||
// throw meaningful error when set-cookie header is missing
|
|
||||||
if (!cookies) throw new Error(`Auth successful (code ${response.statusCode}) but 'set-cookie' header is missing`);
|
|
||||||
|
|
||||||
// find the skynet-jwt cookie
|
|
||||||
const jwtcookie = cookies.find((cookie) => cookie.startsWith("skynet-jwt"));
|
|
||||||
|
|
||||||
// throw meaningful error when skynet-jwt cookie is missing
|
|
||||||
if (!jwtcookie) throw new Error(`Header 'set-cookie' found but 'skynet-jwt' cookie is missing`);
|
|
||||||
|
|
||||||
// extract just the cookie value (no set-cookie props) from set-cookie
|
|
||||||
return jwtcookie.match(/skynet-jwt=[^;]+;/)[0];
|
|
||||||
} catch (error) {
|
|
||||||
// 401 means that service worked but user could not have been authenticated
|
|
||||||
if (error.response && error.response.statusCode === 401) {
|
|
||||||
// sign up with the given credentials
|
|
||||||
await got.post(`https://account.${portalDomain}/api/user`, {
|
|
||||||
json: { email, password },
|
|
||||||
});
|
|
||||||
|
|
||||||
// retry authentication
|
|
||||||
return authenticate();
|
|
||||||
}
|
|
||||||
|
|
||||||
// rethrow unhandled exception
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (getAuthCookie.cache = authenticate());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* isPortalModuleEnabled returns true if the given module is enabled
|
|
||||||
*/
|
|
||||||
function isPortalModuleEnabled(module) {
|
|
||||||
return process.env.PORTAL_MODULES && process.env.PORTAL_MODULES.indexOf(module) !== -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
calculateElapsedTime,
|
|
||||||
getYesterdayISOString,
|
|
||||||
getResponseContent,
|
|
||||||
ensureValidJSON,
|
|
||||||
getAuthCookie,
|
|
||||||
isPortalModuleEnabled,
|
|
||||||
ipCheckService,
|
|
||||||
ipRegex,
|
|
||||||
};
|
|
|
@ -1,19 +0,0 @@
|
||||||
describe("ipRegex", () => {
|
|
||||||
const { ipRegex } = require("./utils");
|
|
||||||
|
|
||||||
test("should test true for valid ip", () => {
|
|
||||||
expect(ipRegex.test("8.8.8.8")).toEqual(true);
|
|
||||||
expect(ipRegex.test("127.0.0.1")).toEqual(true);
|
|
||||||
expect(ipRegex.test("192.168.0.1")).toEqual(true);
|
|
||||||
expect(ipRegex.test("10.10.10.10")).toEqual(true);
|
|
||||||
expect(ipRegex.test("135.124.12.47")).toEqual(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("should test false for invalid ip", () => {
|
|
||||||
expect(ipRegex.test("888.8.8.8")).toEqual(false);
|
|
||||||
expect(ipRegex.test("....")).toEqual(false);
|
|
||||||
expect(ipRegex.test(null)).toEqual(false);
|
|
||||||
expect(ipRegex.test("foo")).toEqual(false);
|
|
||||||
expect(ipRegex.test("")).toEqual(false);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,18 +0,0 @@
|
||||||
const http = require("http");
|
|
||||||
const { ipCheckService, ipRegex } = require("./utils");
|
|
||||||
|
|
||||||
const request = http.request({ host: ipCheckService }, (response) => {
|
|
||||||
response.on("data", (data) => {
|
|
||||||
if (ipRegex.test(data)) {
|
|
||||||
process.stdout.write(data);
|
|
||||||
} else {
|
|
||||||
throw new Error(`${ipCheckService} responded with invalid ip: "${data}"`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
request.on("error", (error) => {
|
|
||||||
throw error; // throw error to exit with code 1
|
|
||||||
});
|
|
||||||
|
|
||||||
request.end();
|
|
File diff suppressed because it is too large
Load Diff
Reference in New Issue