Merge pull request #388 from NebulousLabs/improve-health-checks
improve health checks
This commit is contained in:
commit
da1cf14e5f
|
@ -0,0 +1 @@
|
||||||
|
state/
|
|
@ -5,7 +5,8 @@ WORKDIR /usr/app
|
||||||
COPY package.json .
|
COPY package.json .
|
||||||
RUN yarn --no-lockfile
|
RUN yarn --no-lockfile
|
||||||
COPY src/* src/
|
COPY src/* src/
|
||||||
|
COPY cli/* cli/
|
||||||
|
|
||||||
EXPOSE 3100
|
EXPOSE 3100
|
||||||
ENV NODE_ENV production
|
ENV NODE_ENV production
|
||||||
CMD [ "node", "src/index.js" ]
|
CMD [ "node", "--max-http-header-size=64000", "src/index.js" ]
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
process.env.NODE_ENV = process.env.NODE_ENV || "production";
|
||||||
|
|
||||||
|
const db = require("../src/db");
|
||||||
|
|
||||||
|
db.set("disabled", true).write();
|
|
@ -0,0 +1,7 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
process.env.NODE_ENV = process.env.NODE_ENV || "production";
|
||||||
|
|
||||||
|
const db = require("../src/db");
|
||||||
|
|
||||||
|
db.set("disabled", false).write();
|
|
@ -0,0 +1,8 @@
|
||||||
|
const db = require("../db");
|
||||||
|
|
||||||
|
// returns all health check entries
|
||||||
|
module.exports = (req, res) => {
|
||||||
|
const entries = db.get("entries").orderBy("date", "desc").value();
|
||||||
|
|
||||||
|
res.send(entries);
|
||||||
|
};
|
|
@ -0,0 +1,8 @@
|
||||||
|
const db = require("../db");
|
||||||
|
|
||||||
|
// returns a disabled flag status
|
||||||
|
module.exports = (req, res) => {
|
||||||
|
const disabled = db.get("disabled").value();
|
||||||
|
|
||||||
|
res.send({ disabled });
|
||||||
|
};
|
|
@ -1,9 +1,15 @@
|
||||||
const { StatusCodes } = require("http-status-codes");
|
const { StatusCodes } = require("http-status-codes");
|
||||||
const { sum, sumBy } = require("lodash");
|
const { sum, sumBy } = require("lodash");
|
||||||
const db = require("./db");
|
const db = require("../db");
|
||||||
|
|
||||||
// getStatus returns the server's current health check status
|
// getStatus returns the server's current health check status
|
||||||
function getStatus() {
|
function getStatus() {
|
||||||
|
const disabled = db.get("disabled").value();
|
||||||
|
|
||||||
|
if (disabled) {
|
||||||
|
return StatusCodes.SERVICE_UNAVAILABLE;
|
||||||
|
}
|
||||||
|
|
||||||
// Grab entry element from DB
|
// Grab entry element from DB
|
||||||
const entry = db.get("entries").orderBy("date", "desc").head().value();
|
const entry = db.get("entries").orderBy("date", "desc").head().value();
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
const db = require("../db");
|
||||||
|
|
||||||
|
// returns all health check entries that are not older than one day
|
||||||
|
module.exports = (req, res) => {
|
||||||
|
const yesterday = new Date();
|
||||||
|
|
||||||
|
yesterday.setDate(yesterday.getDate() - 1);
|
||||||
|
|
||||||
|
const entries = db
|
||||||
|
.get("entries")
|
||||||
|
.orderBy("date", "desc")
|
||||||
|
.filter(({ date }) => date >= yesterday.toISOString())
|
||||||
|
.value();
|
||||||
|
|
||||||
|
res.send(entries);
|
||||||
|
};
|
|
@ -1,51 +0,0 @@
|
||||||
const superagent = require("superagent");
|
|
||||||
const { StatusCodes } = require("http-status-codes");
|
|
||||||
|
|
||||||
// uploadCheck returns the result of uploading a sample file
|
|
||||||
async function uploadCheck(done) {
|
|
||||||
const time = process.hrtime();
|
|
||||||
|
|
||||||
superagent
|
|
||||||
.post(`http://${process.env.PORTAL_URL}/skynet/skyfile`)
|
|
||||||
.attach("file", "package.json", "package.json")
|
|
||||||
.end((err, res) => {
|
|
||||||
const statusCode = (res && res.statusCode) || (err && err.statusCode) || null;
|
|
||||||
|
|
||||||
done({
|
|
||||||
name: "upload_file",
|
|
||||||
up: statusCode === StatusCodes.OK,
|
|
||||||
statusCode,
|
|
||||||
time: catchRequestTime(time),
|
|
||||||
critical: true,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// downloadCheck returns the result of downloading the hard coded link
|
|
||||||
function downloadCheck(done) {
|
|
||||||
const time = process.hrtime();
|
|
||||||
const skylink = "AACogzrAimYPG42tDOKhS3lXZD8YvlF8Q8R17afe95iV2Q";
|
|
||||||
|
|
||||||
superagent.get(`http://${process.env.PORTAL_URL}/${skylink}?nocache=true`).end((err, res) => {
|
|
||||||
const statusCode = (res && res.statusCode) || (err && err.statusCode) || null;
|
|
||||||
|
|
||||||
done({
|
|
||||||
name: "download_file",
|
|
||||||
up: statusCode === StatusCodes.OK,
|
|
||||||
statusCode,
|
|
||||||
time: catchRequestTime(time),
|
|
||||||
critical: true,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// catchRequestTime records the time it took to resolve the request in
|
|
||||||
// milliseconds
|
|
||||||
function catchRequestTime(start) {
|
|
||||||
const diff = process.hrtime(start);
|
|
||||||
|
|
||||||
return Math.round((diff[0] * 1e9 + diff[1]) / 1e6); // msec
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports.basicChecks = [uploadCheck, downloadCheck];
|
|
||||||
module.exports.catchRequestTime = catchRequestTime;
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
const superagent = require("superagent");
|
||||||
|
const { StatusCodes } = require("http-status-codes");
|
||||||
|
const { calculateElapsedTime } = require("../utils");
|
||||||
|
|
||||||
|
// uploadCheck returns the result of uploading a sample file
|
||||||
|
async function uploadCheck(done) {
|
||||||
|
const time = process.hrtime();
|
||||||
|
|
||||||
|
superagent
|
||||||
|
.post(`http://${process.env.PORTAL_URL}/skynet/skyfile`)
|
||||||
|
.attach("file", "package.json", "package.json")
|
||||||
|
.end((error, response) => {
|
||||||
|
const statusCode = (response && response.statusCode) || (error && error.statusCode) || null;
|
||||||
|
|
||||||
|
done({
|
||||||
|
name: "upload_file",
|
||||||
|
up: statusCode === StatusCodes.OK,
|
||||||
|
statusCode,
|
||||||
|
time: calculateElapsedTime(time),
|
||||||
|
critical: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// downloadCheck returns the result of downloading the hard coded link
|
||||||
|
async function downloadCheck(done) {
|
||||||
|
const time = process.hrtime();
|
||||||
|
const skylink = "AACogzrAimYPG42tDOKhS3lXZD8YvlF8Q8R17afe95iV2Q";
|
||||||
|
let statusCode;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await superagent.get(`http://${process.env.PORTAL_URL}/${skylink}?nocache=true`);
|
||||||
|
|
||||||
|
statusCode = response.statusCode;
|
||||||
|
} catch (error) {
|
||||||
|
statusCode = error.statusCode || error.status;
|
||||||
|
}
|
||||||
|
|
||||||
|
done({
|
||||||
|
name: "download_file",
|
||||||
|
up: statusCode === StatusCodes.OK,
|
||||||
|
statusCode,
|
||||||
|
time: calculateElapsedTime(time),
|
||||||
|
critical: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.criticalChecks = [uploadCheck, downloadCheck];
|
|
@ -2,13 +2,13 @@ const superagent = require("superagent");
|
||||||
const hash = require("object-hash");
|
const hash = require("object-hash");
|
||||||
const { detailedDiff } = require("deep-object-diff");
|
const { detailedDiff } = require("deep-object-diff");
|
||||||
const { isEqual } = require("lodash");
|
const { isEqual } = require("lodash");
|
||||||
const checks = require("./basicChecks");
|
const { calculateElapsedTime } = require("../utils");
|
||||||
|
|
||||||
// audioExampleCheck returns the result of trying to download the skylink
|
// audioExampleCheck returns the result of trying to download the skylink
|
||||||
// for the Example audio file on siasky.net
|
// for the Example audio file on siasky.net
|
||||||
function audioExampleCheck(done) {
|
function audioExampleCheck(done) {
|
||||||
const linkInfo = {
|
const linkInfo = {
|
||||||
description: "Audio Example",
|
name: "Audio Example",
|
||||||
skylink: "_A2zt5SKoqwnnZU4cBF8uBycSKULXMyeg1c5ZISBr2Q3dA",
|
skylink: "_A2zt5SKoqwnnZU4cBF8uBycSKULXMyeg1c5ZISBr2Q3dA",
|
||||||
bodyHash: "be335f5ad9bc357248f3d35c7e49df491afb6b12",
|
bodyHash: "be335f5ad9bc357248f3d35c7e49df491afb6b12",
|
||||||
metadata: { filename: "feel-good.mp3" },
|
metadata: { filename: "feel-good.mp3" },
|
||||||
|
@ -21,7 +21,7 @@ function audioExampleCheck(done) {
|
||||||
// for a known Covid19 paper
|
// for a known Covid19 paper
|
||||||
function covid19PaperCheck(done) {
|
function covid19PaperCheck(done) {
|
||||||
const linkInfo = {
|
const linkInfo = {
|
||||||
description: "Covid-19 Paper",
|
name: "Covid-19 Paper",
|
||||||
skylink: "PAMZVmfutxWoG6Wnl5BRKuWLkDNZR42k_okRRvksJekA3A",
|
skylink: "PAMZVmfutxWoG6Wnl5BRKuWLkDNZR42k_okRRvksJekA3A",
|
||||||
bodyHash: "81b9fb74829a96ceafa429840d1ef0ce44376ddd",
|
bodyHash: "81b9fb74829a96ceafa429840d1ef0ce44376ddd",
|
||||||
metadata: {
|
metadata: {
|
||||||
|
@ -43,7 +43,7 @@ function covid19PaperCheck(done) {
|
||||||
// for another known Covid19 paper
|
// for another known Covid19 paper
|
||||||
function covid19CoroNopePaperCheck(done) {
|
function covid19CoroNopePaperCheck(done) {
|
||||||
const linkInfo = {
|
const linkInfo = {
|
||||||
description: "Covid-19 CoroNope Paper",
|
name: "Covid-19 CoroNope Paper",
|
||||||
skylink: "bACLKGmcmX4NCp47WwOOJf0lU666VLeT5HRWpWVtqZPjEA",
|
skylink: "bACLKGmcmX4NCp47WwOOJf0lU666VLeT5HRWpWVtqZPjEA",
|
||||||
bodyHash: "901f6fd65ef595f70b6bfebbb2d05942351ef2b3",
|
bodyHash: "901f6fd65ef595f70b6bfebbb2d05942351ef2b3",
|
||||||
metadata: { filename: "coronope.pdf" },
|
metadata: { filename: "coronope.pdf" },
|
||||||
|
@ -56,8 +56,8 @@ function covid19CoroNopePaperCheck(done) {
|
||||||
// for the Example Dapp on siasky.net
|
// for the Example Dapp on siasky.net
|
||||||
function dappExampleCheck(done) {
|
function dappExampleCheck(done) {
|
||||||
const linkInfo = {
|
const linkInfo = {
|
||||||
description: "Dapp Example (UniSwap)",
|
name: "Dapp Example (UniSwap)",
|
||||||
skylink: "EAC5HJr5Pu086EAZG4fP_r6Pnd7Ft366vt6t2AnjkoFb9Q/index.html",
|
skylink: "EADWpKD0myqH2tZa6xtKebg6kNnwYnI94fl4R8UKgNrmOA",
|
||||||
bodyHash: "d6ad2506590bb45b5acc6a8a964a3da4d657354f",
|
bodyHash: "d6ad2506590bb45b5acc6a8a964a3da4d657354f",
|
||||||
metadata: {
|
metadata: {
|
||||||
filename: "/index.html",
|
filename: "/index.html",
|
||||||
|
@ -73,7 +73,7 @@ function dappExampleCheck(done) {
|
||||||
// for the Develop Momentum Application with a trailing /index.html
|
// for the Develop Momentum Application with a trailing /index.html
|
||||||
function developMomentumIndexFileCheck(done) {
|
function developMomentumIndexFileCheck(done) {
|
||||||
const linkInfo = {
|
const linkInfo = {
|
||||||
description: "Develop Momentum Index File",
|
name: "Develop Momentum Index File",
|
||||||
skylink: "EAA1fG_ip4C1Vi1Ijvsr1oyr8jpH0Bo9HXya0T3kw-elGw/index.html",
|
skylink: "EAA1fG_ip4C1Vi1Ijvsr1oyr8jpH0Bo9HXya0T3kw-elGw/index.html",
|
||||||
bodyHash: "53b44a9d3cfa9b3d66ce5c29976f4383725d3652",
|
bodyHash: "53b44a9d3cfa9b3d66ce5c29976f4383725d3652",
|
||||||
metadata: {
|
metadata: {
|
||||||
|
@ -90,7 +90,7 @@ function developMomentumIndexFileCheck(done) {
|
||||||
// for the Example HTML file on siasky.net
|
// for the Example HTML file on siasky.net
|
||||||
function htmlExampleCheck(done) {
|
function htmlExampleCheck(done) {
|
||||||
const linkInfo = {
|
const linkInfo = {
|
||||||
description: "HTML Example",
|
name: "HTML Example",
|
||||||
skylink: "PAL0w4SdA5rFCDGEutgpeQ50Om-YkBabtXVOJAkmedslKw",
|
skylink: "PAL0w4SdA5rFCDGEutgpeQ50Om-YkBabtXVOJAkmedslKw",
|
||||||
bodyHash: "c932fd56f98b6db589e56be8018817f13bb29f72",
|
bodyHash: "c932fd56f98b6db589e56be8018817f13bb29f72",
|
||||||
metadata: { filename: "introduction â Sia API Documentation.html" },
|
metadata: { filename: "introduction â Sia API Documentation.html" },
|
||||||
|
@ -103,7 +103,7 @@ function htmlExampleCheck(done) {
|
||||||
// for the Example image on siasky.net
|
// for the Example image on siasky.net
|
||||||
function imageExampleCheck(done) {
|
function imageExampleCheck(done) {
|
||||||
const linkInfo = {
|
const linkInfo = {
|
||||||
description: "Image Example",
|
name: "Image Example",
|
||||||
skylink: "IADUs8d9CQjUO34LmdaaNPK_STuZo24rpKVfYW3wPPM2uQ",
|
skylink: "IADUs8d9CQjUO34LmdaaNPK_STuZo24rpKVfYW3wPPM2uQ",
|
||||||
bodyHash: "313207978d0a88bf2b961f098804e9ab0f82837f",
|
bodyHash: "313207978d0a88bf2b961f098804e9ab0f82837f",
|
||||||
metadata: { filename: "sia-lm.png" },
|
metadata: { filename: "sia-lm.png" },
|
||||||
|
@ -116,7 +116,7 @@ function imageExampleCheck(done) {
|
||||||
// for the Example JSON file on siasky.net
|
// for the Example JSON file on siasky.net
|
||||||
function jsonExampleCheck(done) {
|
function jsonExampleCheck(done) {
|
||||||
const linkInfo = {
|
const linkInfo = {
|
||||||
description: "JSON Example",
|
name: "JSON Example",
|
||||||
skylink: "AAC0uO43g64ULpyrW0zO3bjEknSFbAhm8c-RFP21EQlmSQ",
|
skylink: "AAC0uO43g64ULpyrW0zO3bjEknSFbAhm8c-RFP21EQlmSQ",
|
||||||
bodyHash: "198771c3d07d5c7302aadcc0697a7298e5e8ccc3",
|
bodyHash: "198771c3d07d5c7302aadcc0697a7298e5e8ccc3",
|
||||||
metadata: { filename: "consensus.json" },
|
metadata: { filename: "consensus.json" },
|
||||||
|
@ -129,7 +129,7 @@ function jsonExampleCheck(done) {
|
||||||
// for the Example PDF file on siasky.net
|
// for the Example PDF file on siasky.net
|
||||||
function pdfExampleCheck(done) {
|
function pdfExampleCheck(done) {
|
||||||
const linkInfo = {
|
const linkInfo = {
|
||||||
description: "PDF Example",
|
name: "PDF Example",
|
||||||
skylink: "XABvi7JtJbQSMAcDwnUnmp2FKDPjg8_tTTFP4BwMSxVdEg",
|
skylink: "XABvi7JtJbQSMAcDwnUnmp2FKDPjg8_tTTFP4BwMSxVdEg",
|
||||||
bodyHash: "9bd8162e1575569a9041972f7f62d65887063dc3",
|
bodyHash: "9bd8162e1575569a9041972f7f62d65887063dc3",
|
||||||
metadata: { filename: "sia.pdf" },
|
metadata: { filename: "sia.pdf" },
|
||||||
|
@ -142,7 +142,7 @@ function pdfExampleCheck(done) {
|
||||||
// a Random Image.
|
// a Random Image.
|
||||||
function randomImageCheck(done) {
|
function randomImageCheck(done) {
|
||||||
const linkInfo = {
|
const linkInfo = {
|
||||||
description: "Random Image",
|
name: "Random Image",
|
||||||
skylink: "PAHx7JmsU9EFGbqm5q0LNKT2wKfoJ_mhPI8zWlNEXZ8uOQ/",
|
skylink: "PAHx7JmsU9EFGbqm5q0LNKT2wKfoJ_mhPI8zWlNEXZ8uOQ/",
|
||||||
bodyHash: "4c73c5a0eddd5823be677d7f93bf80cc9338ee9f",
|
bodyHash: "4c73c5a0eddd5823be677d7f93bf80cc9338ee9f",
|
||||||
metadata: {
|
metadata: {
|
||||||
|
@ -159,7 +159,7 @@ function randomImageCheck(done) {
|
||||||
// a Random Image with no trailing slash.
|
// a Random Image with no trailing slash.
|
||||||
function randomImageRedirectCheck(done) {
|
function randomImageRedirectCheck(done) {
|
||||||
const linkInfo = {
|
const linkInfo = {
|
||||||
description: "Random Image Redirect",
|
name: "Random Image Redirect",
|
||||||
skylink: "PAHx7JmsU9EFGbqm5q0LNKT2wKfoJ_mhPI8zWlNEXZ8uOQ",
|
skylink: "PAHx7JmsU9EFGbqm5q0LNKT2wKfoJ_mhPI8zWlNEXZ8uOQ",
|
||||||
bodyHash: "4c73c5a0eddd5823be677d7f93bf80cc9338ee9f",
|
bodyHash: "4c73c5a0eddd5823be677d7f93bf80cc9338ee9f",
|
||||||
metadata: {
|
metadata: {
|
||||||
|
@ -175,7 +175,7 @@ function randomImageRedirectCheck(done) {
|
||||||
// skyBayCheck returns the result of trying to download the skylink for the SkyBay Application.
|
// skyBayCheck returns the result of trying to download the skylink for the SkyBay Application.
|
||||||
function skyBayCheck(done) {
|
function skyBayCheck(done) {
|
||||||
const linkInfo = {
|
const linkInfo = {
|
||||||
description: "SkyBay",
|
name: "SkyBay",
|
||||||
skylink: "EABkMjXzxJRpPz0eO0Or5fy2eo-rz3prdigGwRlyNd9mwA/",
|
skylink: "EABkMjXzxJRpPz0eO0Or5fy2eo-rz3prdigGwRlyNd9mwA/",
|
||||||
bodyHash: "25d63937c9734fb08d2749c6517d1b3de8ecb856",
|
bodyHash: "25d63937c9734fb08d2749c6517d1b3de8ecb856",
|
||||||
metadata: {
|
metadata: {
|
||||||
|
@ -191,7 +191,7 @@ function skyBayCheck(done) {
|
||||||
// for the SkyBay Application with no trailing slash.
|
// for the SkyBay Application with no trailing slash.
|
||||||
function skyBayRedirectCheck(done) {
|
function skyBayRedirectCheck(done) {
|
||||||
const linkInfo = {
|
const linkInfo = {
|
||||||
description: "SkyBay Redirect",
|
name: "SkyBay Redirect",
|
||||||
skylink: "EABkMjXzxJRpPz0eO0Or5fy2eo-rz3prdigGwRlyNd9mwA",
|
skylink: "EABkMjXzxJRpPz0eO0Or5fy2eo-rz3prdigGwRlyNd9mwA",
|
||||||
bodyHash: "25d63937c9734fb08d2749c6517d1b3de8ecb856",
|
bodyHash: "25d63937c9734fb08d2749c6517d1b3de8ecb856",
|
||||||
metadata: {
|
metadata: {
|
||||||
|
@ -206,7 +206,7 @@ function skyBayRedirectCheck(done) {
|
||||||
// skyBinCheck returns the result of trying to download the skylink for the SkyBin Application.
|
// skyBinCheck returns the result of trying to download the skylink for the SkyBin Application.
|
||||||
function skyBinCheck(done) {
|
function skyBinCheck(done) {
|
||||||
const linkInfo = {
|
const linkInfo = {
|
||||||
description: "SkyBin",
|
name: "SkyBin",
|
||||||
skylink: "CAAVU14pB9GRIqCrejD7rlS27HltGGiiCLICzmrBV0wVtA/",
|
skylink: "CAAVU14pB9GRIqCrejD7rlS27HltGGiiCLICzmrBV0wVtA/",
|
||||||
bodyHash: "767ec67c417e11b97c5db7dad9ea3b6b27cb0d39",
|
bodyHash: "767ec67c417e11b97c5db7dad9ea3b6b27cb0d39",
|
||||||
metadata: { filename: "skybin.html" },
|
metadata: { filename: "skybin.html" },
|
||||||
|
@ -219,7 +219,7 @@ function skyBinCheck(done) {
|
||||||
// for the SkyBin Application with no trailing slash.
|
// for the SkyBin Application with no trailing slash.
|
||||||
function skyBinRedirectCheck(done) {
|
function skyBinRedirectCheck(done) {
|
||||||
const linkInfo = {
|
const linkInfo = {
|
||||||
description: "SkyBin Redirect",
|
name: "SkyBin Redirect",
|
||||||
skylink: "CAAVU14pB9GRIqCrejD7rlS27HltGGiiCLICzmrBV0wVtA",
|
skylink: "CAAVU14pB9GRIqCrejD7rlS27HltGGiiCLICzmrBV0wVtA",
|
||||||
bodyHash: "767ec67c417e11b97c5db7dad9ea3b6b27cb0d39",
|
bodyHash: "767ec67c417e11b97c5db7dad9ea3b6b27cb0d39",
|
||||||
metadata: { filename: "skybin.html" },
|
metadata: { filename: "skybin.html" },
|
||||||
|
@ -231,7 +231,7 @@ function skyBinRedirectCheck(done) {
|
||||||
// skyGalleryCheck returns the result of trying to download the skylink for the SkyGallery Application.
|
// skyGalleryCheck returns the result of trying to download the skylink for the SkyGallery Application.
|
||||||
function skyGalleryCheck(done) {
|
function skyGalleryCheck(done) {
|
||||||
const linkInfo = {
|
const linkInfo = {
|
||||||
description: "SkyGallery",
|
name: "SkyGallery",
|
||||||
skylink: "AADW6GsQcetwDBaDYnGCSTbYjSKY743NtY1A5VRx5sj3Dg/",
|
skylink: "AADW6GsQcetwDBaDYnGCSTbYjSKY743NtY1A5VRx5sj3Dg/",
|
||||||
bodyHash: "077e54054748d278114f1870f8045a162eb73641",
|
bodyHash: "077e54054748d278114f1870f8045a162eb73641",
|
||||||
metadata: {
|
metadata: {
|
||||||
|
@ -365,7 +365,7 @@ function skyGalleryCheck(done) {
|
||||||
// for the SkyGallery Application with a trailing /index.html
|
// for the SkyGallery Application with a trailing /index.html
|
||||||
function skyGalleryIndexFileCheck(done) {
|
function skyGalleryIndexFileCheck(done) {
|
||||||
const linkInfo = {
|
const linkInfo = {
|
||||||
description: "SkyGallery Index File",
|
name: "SkyGallery Index File",
|
||||||
skylink: "AADW6GsQcetwDBaDYnGCSTbYjSKY743NtY1A5VRx5sj3Dg/index.html",
|
skylink: "AADW6GsQcetwDBaDYnGCSTbYjSKY743NtY1A5VRx5sj3Dg/index.html",
|
||||||
bodyHash: "077e54054748d278114f1870f8045a162eb73641",
|
bodyHash: "077e54054748d278114f1870f8045a162eb73641",
|
||||||
metadata: {
|
metadata: {
|
||||||
|
@ -382,7 +382,7 @@ function skyGalleryIndexFileCheck(done) {
|
||||||
// for the SkyGallery Application with no trailing slash.
|
// for the SkyGallery Application with no trailing slash.
|
||||||
function skyGalleryRedirectCheck(done) {
|
function skyGalleryRedirectCheck(done) {
|
||||||
const linkInfo = {
|
const linkInfo = {
|
||||||
description: "SkyGallery Redirect",
|
name: "SkyGallery Redirect",
|
||||||
skylink: "AADW6GsQcetwDBaDYnGCSTbYjSKY743NtY1A5VRx5sj3Dg",
|
skylink: "AADW6GsQcetwDBaDYnGCSTbYjSKY743NtY1A5VRx5sj3Dg",
|
||||||
bodyHash: "077e54054748d278114f1870f8045a162eb73641",
|
bodyHash: "077e54054748d278114f1870f8045a162eb73641",
|
||||||
metadata: {
|
metadata: {
|
||||||
|
@ -516,7 +516,7 @@ function skyGalleryRedirectCheck(done) {
|
||||||
// for the uncensored library skylink
|
// for the uncensored library skylink
|
||||||
function uncensoredLibraryCheck(done) {
|
function uncensoredLibraryCheck(done) {
|
||||||
const linkInfo = {
|
const linkInfo = {
|
||||||
description: "Uncensored Library",
|
name: "Uncensored Library",
|
||||||
skylink: "AAC5glnZyNJ4Ieb4MhnYJGtID6qdMqEjl0or5EvEMt7bWQ",
|
skylink: "AAC5glnZyNJ4Ieb4MhnYJGtID6qdMqEjl0or5EvEMt7bWQ",
|
||||||
bodyHash: "60da6cb958699c5acd7f2a2911656ff32fca89a7",
|
bodyHash: "60da6cb958699c5acd7f2a2911656ff32fca89a7",
|
||||||
metadata: {
|
metadata: {
|
||||||
|
@ -538,7 +538,7 @@ function uncensoredLibraryCheck(done) {
|
||||||
// for the Uniswap Application with a trailing /index.html
|
// for the Uniswap Application with a trailing /index.html
|
||||||
function uniswapIndexFileCheck(done) {
|
function uniswapIndexFileCheck(done) {
|
||||||
const linkInfo = {
|
const linkInfo = {
|
||||||
description: "Uniswap Skylink Index File",
|
name: "Uniswap Skylink Index File",
|
||||||
skylink: "IAC6CkhNYuWZqMVr1gob1B6tPg4MrBGRzTaDvAIAeu9A9w/index.html",
|
skylink: "IAC6CkhNYuWZqMVr1gob1B6tPg4MrBGRzTaDvAIAeu9A9w/index.html",
|
||||||
bodyHash: "3965f9a7def085b3a764ddc76a528eda38d72359",
|
bodyHash: "3965f9a7def085b3a764ddc76a528eda38d72359",
|
||||||
metadata: {
|
metadata: {
|
||||||
|
@ -551,53 +551,48 @@ function uniswapIndexFileCheck(done) {
|
||||||
skylinkVerification(done, linkInfo);
|
skylinkVerification(done, linkInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
// skylinkVerification verifies a skylink against known information provided in
|
// verifies a skylink against provided information
|
||||||
// the linkInfo.
|
function skylinkVerification(done, { name, skylink, bodyHash, metadata }) {
|
||||||
function skylinkVerification(done, linkInfo) {
|
|
||||||
const time = process.hrtime();
|
const time = process.hrtime();
|
||||||
|
|
||||||
// Create the query for the skylink
|
// Create the query for the skylink
|
||||||
const query = `http://${process.env.PORTAL_URL}/${linkInfo.skylink}?nocache=true`;
|
const query = `http://${process.env.PORTAL_URL}/${skylink}?nocache=true`;
|
||||||
|
|
||||||
// Get the Skylink
|
// Get the Skylink
|
||||||
superagent
|
superagent
|
||||||
.get(query)
|
.get(query)
|
||||||
.responseType("blob")
|
.responseType("blob")
|
||||||
.end((err, res) => {
|
.then(
|
||||||
// Record the statusCode
|
(response) => {
|
||||||
const statusCode = (res && res.statusCode) || (err && err.statusCode) || null;
|
const entry = { name, up: true, statusCode: response.statusCode, time: calculateElapsedTime(time) };
|
||||||
let info = null;
|
const info = {};
|
||||||
|
|
||||||
// Determine if the skylink is up. Start with checking if there was an
|
// Check if the response body is valid by checking against the known hash
|
||||||
// error in the request.
|
const currentBodyHash = hash(response.body);
|
||||||
let up = err === null;
|
if (currentBodyHash !== bodyHash) {
|
||||||
if (up) {
|
entry.up = false;
|
||||||
// Check if the response body is valid by checking against the known
|
info.bodyHash = { expected: bodyHash, current: currentBodyHash };
|
||||||
// hash
|
|
||||||
const validBody = hash(res.body) === linkInfo.bodyHash;
|
|
||||||
// Check if the metadata is valid
|
|
||||||
const metadata = res.header["skynet-file-metadata"] ? JSON.parse(res.header["skynet-file-metadata"]) : null;
|
|
||||||
const validMetadata = isEqual(metadata, linkInfo.metadata);
|
|
||||||
// Redetermine if the Skylink is up based on the results from the body
|
|
||||||
// and metadata hash checks
|
|
||||||
up = up && validBody && validMetadata;
|
|
||||||
|
|
||||||
info = {
|
|
||||||
body: { valid: validBody },
|
|
||||||
metadata: { valid: validMetadata, diff: detailedDiff(metadata, linkInfo.metadata) },
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the entry information
|
// Check if the metadata is valid by deep comparing expected value with response
|
||||||
done({
|
const currentMetadata =
|
||||||
name: linkInfo.description,
|
response.header["skynet-file-metadata"] && JSON.parse(response.header["skynet-file-metadata"]);
|
||||||
up,
|
if (!isEqual(currentMetadata, metadata)) {
|
||||||
info,
|
entry.up = false;
|
||||||
statusCode,
|
info.metadata = detailedDiff(currentMetadata, metadata);
|
||||||
time: checks.catchRequestTime(time),
|
}
|
||||||
critical: true,
|
|
||||||
});
|
if (Object.keys(info).length) entry.info = info; // add info only if it exists
|
||||||
});
|
|
||||||
|
done(entry); // Return the entry information
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
const statusCode = error.statusCode || error.status;
|
||||||
|
const entry = { name, up: false, statusCode, time: calculateElapsedTime(time) };
|
||||||
|
|
||||||
|
done(entry); // Return the entry information
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.verboseChecks = [
|
module.exports.verboseChecks = [
|
|
@ -1,10 +1,13 @@
|
||||||
|
const fs = require("fs");
|
||||||
const low = require("lowdb");
|
const low = require("lowdb");
|
||||||
const FileSync = require("lowdb/adapters/FileSync");
|
const FileSync = require("lowdb/adapters/FileSync");
|
||||||
const Memory = require("lowdb/adapters/Memory");
|
const Memory = require("lowdb/adapters/Memory");
|
||||||
|
|
||||||
|
if (!fs.existsSync("state")) fs.mkdirSync("state");
|
||||||
|
|
||||||
const adapter = process.env.NODE_ENV === "production" ? new FileSync("state/state.json") : new Memory();
|
const adapter = process.env.NODE_ENV === "production" ? new FileSync("state/state.json") : new Memory();
|
||||||
const db = low(adapter);
|
const db = low(adapter);
|
||||||
|
|
||||||
db.defaults({ entries: [] }).write();
|
db.defaults({ disabled: false, entries: [] }).write();
|
||||||
|
|
||||||
module.exports = db;
|
module.exports = db;
|
||||||
|
|
|
@ -17,7 +17,10 @@ const server = express();
|
||||||
server.use(bodyparser.urlencoded({ extended: false }));
|
server.use(bodyparser.urlencoded({ extended: false }));
|
||||||
server.use(bodyparser.json());
|
server.use(bodyparser.json());
|
||||||
|
|
||||||
server.get("/health-check", require("./endpointHealthCheck"));
|
server.get("/health-check", require("./api/index"));
|
||||||
|
server.get("/health-check/recent", require("./api/recent"));
|
||||||
|
server.get("/health-check/all", require("./api/all"));
|
||||||
|
server.get("/health-check/disabled", require("./api/disabled"));
|
||||||
|
|
||||||
server.listen(port, host, (error) => {
|
server.listen(port, host, (error) => {
|
||||||
if (error) throw error;
|
if (error) throw error;
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
const schedule = require("node-schedule");
|
const schedule = require("node-schedule");
|
||||||
const db = require("./db");
|
const db = require("./db");
|
||||||
const { basicChecks } = require("./basicChecks");
|
const { criticalChecks } = require("./checks/critical");
|
||||||
const { verboseChecks } = require("./verboseChecks");
|
const { verboseChecks } = require("./checks/verbose");
|
||||||
|
|
||||||
// execute the basic health-check script every 5 minutes
|
// execute the critical health-check script every 5 minutes
|
||||||
const basicJob = schedule.scheduleJob("*/5 * * * *", async () => {
|
const basicJob = schedule.scheduleJob("*/5 * * * *", async () => {
|
||||||
const entry = { date: new Date().toISOString(), checks: [] };
|
const entry = { date: new Date().toISOString(), checks: [] };
|
||||||
|
|
||||||
entry.checks = await Promise.all(basicChecks.map((check) => new Promise(check)));
|
entry.checks = await Promise.all(criticalChecks.map((check) => new Promise(check)));
|
||||||
|
|
||||||
db.get("entries").push(entry).write();
|
db.get("entries").push(entry).write();
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
// return 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
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { calculateElapsedTime };
|
Reference in New Issue