Merge branch 'master' of https://github.com/SkynetLabs/skynet-webportal into pj/add-blocker-health-check

This commit is contained in:
PJ 2022-01-17 14:37:53 +01:00
commit aa2618a8a6
No known key found for this signature in database
GPG Key ID: F345964979FA8971
19 changed files with 140 additions and 67 deletions

View File

@ -1,4 +1,4 @@
FROM node:16.13.1-alpine
FROM node:16.13.2-alpine
WORKDIR /opt/hsd

View File

@ -1,4 +1,4 @@
FROM node:16.13.1-alpine
FROM node:16.13.2-alpine
WORKDIR /usr/app

View File

@ -27,7 +27,7 @@
"react-dom": "17.0.2",
"react-toastify": "8.1.0",
"skynet-js": "3.0.2",
"stripe": "8.195.0",
"stripe": "8.197.0",
"swr": "1.1.2",
"yup": "0.32.11"
},
@ -35,7 +35,7 @@
"@tailwindcss/forms": "0.4.0",
"@tailwindcss/typography": "0.5.0",
"autoprefixer": "10.4.2",
"eslint": "8.6.0",
"eslint": "8.7.0",
"eslint-config-next": "12.0.7",
"postcss": "8.4.5",
"prettier": "2.5.1",

View File

@ -398,11 +398,6 @@ anser@1.4.9:
resolved "https://registry.yarnpkg.com/anser/-/anser-1.4.9.tgz#1f85423a5dcf8da4631a341665ff675b96845760"
integrity sha512-AI+BjTeGt2+WFk4eWcqbQ7snZpDBt8SaLlj0RT2h5xfdWaiy51OjYvqwMrNzJLGy8iOAL6nKDITWO+rd4MkYEA==
ansi-colors@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348"
integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==
ansi-regex@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
@ -1153,13 +1148,6 @@ encoding@0.1.13:
dependencies:
iconv-lite "^0.6.2"
enquirer@^2.3.5:
version "2.3.6"
resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d"
integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==
dependencies:
ansi-colors "^4.1.1"
error-ex@^1.3.1:
version "1.3.2"
resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
@ -1371,15 +1359,15 @@ eslint-visitor-keys@^2.0.0:
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303"
integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==
eslint-visitor-keys@^3.0.0, eslint-visitor-keys@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.1.0.tgz#eee4acea891814cda67a7d8812d9647dd0179af2"
integrity sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA==
eslint-visitor-keys@^3.0.0, eslint-visitor-keys@^3.1.0, eslint-visitor-keys@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.2.0.tgz#6fbb166a6798ee5991358bc2daa1ba76cc1254a1"
integrity sha512-IOzT0X126zn7ALX0dwFiUQEdsfzrm4+ISsQS8nukaJXwEyYKRSnEIIDULYg1mCtGp7UUXgfGl7BIolXREQK+XQ==
eslint@8.6.0:
version "8.6.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.6.0.tgz#4318c6a31c5584838c1a2e940c478190f58d558e"
integrity sha512-UvxdOJ7mXFlw7iuHZA4jmzPaUqIw54mZrv+XPYKNbKdLR0et4rf60lIZUU9kiNtnzzMzGWxMV+tQ7uG7JG8DPw==
eslint@8.7.0:
version "8.7.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.7.0.tgz#22e036842ee5b7cf87b03fe237731675b4d3633c"
integrity sha512-ifHYzkBGrzS2iDU7KjhCAVMGCvF6M3Xfs8X8b37cgrUlDt6bWRTpRh6T/gtSXv1HJ/BUGgmjvNvOEGu85Iif7w==
dependencies:
"@eslint/eslintrc" "^1.0.5"
"@humanwhocodes/config-array" "^0.9.2"
@ -1388,11 +1376,10 @@ eslint@8.6.0:
cross-spawn "^7.0.2"
debug "^4.3.2"
doctrine "^3.0.0"
enquirer "^2.3.5"
escape-string-regexp "^4.0.0"
eslint-scope "^7.1.0"
eslint-utils "^3.0.0"
eslint-visitor-keys "^3.1.0"
eslint-visitor-keys "^3.2.0"
espree "^9.3.0"
esquery "^1.4.0"
esutils "^2.0.2"
@ -1401,7 +1388,7 @@ eslint@8.6.0:
functional-red-black-tree "^1.0.1"
glob-parent "^6.0.1"
globals "^13.6.0"
ignore "^4.0.6"
ignore "^5.2.0"
import-fresh "^3.0.0"
imurmurhash "^0.1.4"
is-glob "^4.0.0"
@ -1412,9 +1399,7 @@ eslint@8.6.0:
minimatch "^3.0.4"
natural-compare "^1.4.0"
optionator "^0.9.1"
progress "^2.0.0"
regexpp "^3.2.0"
semver "^7.2.1"
strip-ansi "^6.0.1"
strip-json-comments "^3.1.0"
text-table "^0.2.0"
@ -1838,10 +1823,10 @@ ignore@^4.0.6:
resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==
ignore@^5.1.4:
version "5.1.9"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.9.tgz#9ec1a5cbe8e1446ec60d4420060d43aa6e7382fb"
integrity sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==
ignore@^5.1.4, ignore@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a"
integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==
image-size@1.0.0:
version "1.0.0"
@ -2813,11 +2798,6 @@ process@0.11.10:
resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI=
progress@^2.0.0:
version "2.0.3"
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
prop-types@^15.7.2:
version "15.7.2"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
@ -3089,7 +3069,7 @@ semver@^6.0.0, semver@^6.3.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
semver@^7.2.1, semver@^7.3.5:
semver@^7.3.5:
version "7.3.5"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7"
integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==
@ -3287,10 +3267,10 @@ strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
stripe@8.195.0:
version "8.195.0"
resolved "https://registry.yarnpkg.com/stripe/-/stripe-8.195.0.tgz#4d253e247aadb64d972488da9481fff743b58a11"
integrity sha512-pXEZFNJb4p9uZ69+B4A+zJEmBiFw3BzNG51ctPxUZij7ghFTnk2/RuUHmSGto2XVCcC46uG75czXVAvCUkOGtQ==
stripe@8.197.0:
version "8.197.0"
resolved "https://registry.yarnpkg.com/stripe/-/stripe-8.197.0.tgz#7875403ce4303788355b40f8a897f835b05b7784"
integrity sha512-EQLgqFiX1wNQEeve6QnUcGIby6XtXXzbzEWPJiZ68qFj02YFjNjLLxl9atueKgUQ+YRbrMAlrc6ECwvm+cf/Rw==
dependencies:
"@types/node" ">=8.1.0"
qs "^6.6.0"

View File

@ -1,4 +1,4 @@
FROM node:16.13.1-alpine
FROM node:16.13.2-alpine
WORKDIR /usr/app

View File

@ -1,4 +1,4 @@
FROM node:16.13.1-alpine
FROM node:16.13.2-alpine
WORKDIR /usr/app

View File

@ -1,4 +1,4 @@
FROM node:16.13.1-alpine
FROM node:16.13.2-alpine
RUN apk update && apk add dnsmasq

View File

@ -1,7 +1,7 @@
const got = require("got");
const FormData = require("form-data");
const { isEqual } = require("lodash");
const { calculateElapsedTime, getResponseContent, isPortalModuleEnabled } = require("../utils");
const { calculateElapsedTime, getResponseContent, getAuthCookie, isPortalModuleEnabled } = require("../utils");
const { SkynetClient, stringToUint8ArrayUtf8, genKeyPairAndSeed } = require("skynet-js");
const MODULE_BLOCKER = "b";
@ -36,6 +36,7 @@ async function skydConfigCheck(done) {
// 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
@ -44,7 +45,10 @@ async function uploadCheck(done) {
form.append("file", payload, { filename: "time.txt", contentType: "text/plain" });
try {
const response = await got.post(`${process.env.SKYNET_PORTAL_API}/skynet/skyfile`, { body: form });
const response = await got.post(`${process.env.SKYNET_PORTAL_API}/skynet/skyfile`, {
body: form,
headers: { cookie: authCookie },
});
data.statusCode = response.statusCode;
data.up = true;
@ -199,11 +203,12 @@ async function blockerHealthCheck(done) {
}
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" } });
const response = await got(url, { headers: { cookie: `nocache=true;${authCookie}` } });
data.statusCode = response.statusCode;
data.up = true;

View File

@ -2,7 +2,7 @@ const got = require("got");
const hasha = require("hasha");
const { detailedDiff } = require("deep-object-diff");
const { isEqual } = require("lodash");
const { calculateElapsedTime, ensureValidJSON, getResponseContent } = require("../utils");
const { calculateElapsedTime, ensureValidJSON, getResponseContent, getAuthCookie } = require("../utils");
const { parseSkylink } = require("skynet-js");
// audioExampleCheck returns the result of trying to download the skylink
@ -1130,12 +1130,13 @@ function parseHeaderString(header) {
// skylinkVerification verifies a skylink against provided information.
async function skylinkVerification(done, expected, { followRedirect = true, method = "get" } = {}) {
const authCookie = await getAuthCookie();
const time = process.hrtime();
const details = { name: expected.name, skylink: expected.skylink };
try {
const query = `${process.env.SKYNET_PORTAL_API}/${expected.skylink}`;
const response = await got[method](query, { followRedirect, headers: { cookie: "nocache=true" } });
const response = await got[method](query, { followRedirect, headers: { cookie: `nocache=true;${authCookie}` } });
const entry = { ...details, up: true, statusCode: response.statusCode, time: calculateElapsedTime(time) };
const info = {};

View File

@ -4,8 +4,18 @@ if (!process.env.SKYNET_PORTAL_API) {
throw new Error("You need to provide SKYNET_PORTAL_API environment variable");
}
if (process.env.ACCOUNTS_ENABLED === "true" && !process.env.SKYNET_DASHBOARD_URL) {
if (process.env.ACCOUNTS_ENABLED === "true") {
if (!process.env.SKYNET_DASHBOARD_URL) {
throw new Error("You need to provide SKYNET_DASHBOARD_URL environment variable when accounts are enabled");
}
if (process.env.ACCOUNTS_LIMIT_ACCESS === "authenticated") {
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");

View File

@ -1,3 +1,5 @@
const got = require("got");
/**
* Get the time between start and now in milliseconds
*/
@ -39,6 +41,65 @@ function ensureValidJSON(object) {
return JSON.parse(stringified);
}
/**
* 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
*/
function getAuthCookie() {
// cache auth promise so only one actual request will be made
if (getAuthCookie.cache) return getAuthCookie.cache;
// do not authenticate if it is not necessary
if (process.env.ACCOUNTS_LIMIT_ACCESS !== "authenticated") return {};
const email = process.env.ACCOUNTS_TEST_USER_EMAIL;
const password = process.env.ACCOUNTS_TEST_USER_PASSWORD;
if (!email) throw new Error("ACCOUNTS_TEST_USER_EMAIL cannot be empty");
if (!password) throw new Error("ACCOUNTS_TEST_USER_PASSWORD cannot be empty");
async function authenticate() {
try {
// authenticate with given test user credentials
const response = await got.post(`${process.env.SKYNET_DASHBOARD_URL}/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(`${process.env.SKYNET_DASHBOARD_URL}/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
*/
@ -51,5 +112,6 @@ module.exports = {
getYesterdayISOString,
getResponseContent,
ensureValidJSON,
getAuthCookie,
isPortalModuleEnabled,
};

View File

@ -1,4 +1,4 @@
FROM node:16.13.1-alpine
FROM node:16.13.2-alpine
RUN apk update && apk add autoconf automake build-base libtool nasm pkgconfig

View File

@ -76,13 +76,6 @@
social:
linkedin: https://www.linkedin.com/in/filiprysavy/
- name: Nicole Tay
position: Head of Marketing
image: ./team/nicole-tay.png
social:
linkedin: https://www.linkedin.com/in/nicolehtay/
twitter: https://twitter.com/NicoleHTay
- name: Daniel Helm
position: Developer Evangelist
image: ./team/daniel-helm.png
@ -90,3 +83,24 @@
github: https://github.com/dghelm
linkedin: https://www.linkedin.com/in/dghelm/
twitter: https://twitter.com/danielgileshelm
- name: Marissa Hudson
position: Engineering Intern
image: ./team/marissa-hudson.jpeg
social:
github: https://github.com/fluffy9
- name: Alice Hlidkova
position: Executive Assistant
image: ./team/alice-hlidkova.jpeg
social:
linkedin: https://www.linkedin.com/in/alice-hlidkova/
- name: Ayoung Jeon
position: Developer Advocate
image: ./team/ayoung-jeon.jpeg
social:
github: https://github.com/ayoungjeon
gitlab: https://gitlab.com/ayoung_j
linkedin: https://www.linkedin.com/in/ajeon/
twitter: https://twitter.com/ayoung_jeon

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 788 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 705 KiB

View File

@ -0,0 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>GitLab</title><path d="M4.845.904c-.435 0-.82.28-.955.692C2.639 5.449 1.246 9.728.07 13.335a1.437 1.437 0 00.522 1.607l11.071 8.045c.2.145.472.144.67-.004l11.073-8.04a1.436 1.436 0 00.522-1.61c-1.285-3.942-2.683-8.256-3.817-11.746a1.004 1.004 0 00-.957-.684.987.987 0 00-.949.69l-2.405 7.408H8.203l-2.41-7.408a.987.987 0 00-.942-.69h-.006zm-.006 1.42l2.173 6.678H2.675zm14.326 0l2.168 6.678h-4.341zm-10.593 7.81h6.862c-1.142 3.52-2.288 7.04-3.434 10.559L8.572 10.135zm-5.514.005h4.321l3.086 9.5zm13.567 0h4.325c-2.467 3.17-4.95 6.328-7.411 9.502 1.028-3.167 2.059-6.334 3.086-9.502zM2.1 10.762l6.977 8.947-7.817-5.682a.305.305 0 01-.112-.341zm19.798 0l.952 2.922a.305.305 0 01-.11.341v.002l-7.82 5.68.026-.035z"/></svg>

After

Width:  |  Height:  |  Size: 797 B

View File

@ -16,7 +16,7 @@ export { ReactComponent as ExternalLink } from "./ExternalLink.svg";
export { ReactComponent as FacebookSmall } from "./FacebookSmall.svg";
export { ReactComponent as Fingerprint } from "./Fingerprint.svg";
export { ReactComponent as GithubSmall } from "./GithubSmall.svg";
export { ReactComponent as GitlabSmall } from "./GitlabSmall.svg";
export { ReactComponent as Gitlab } from "./Gitlab.svg";
export { ReactComponent as Info } from "./Info.svg";
export { ReactComponent as Join } from "./Join.svg";
export { ReactComponent as Layers } from "./Layers.svg";

View File

@ -14,7 +14,7 @@ import {
Cogs,
TwitterSmall,
GithubSmall,
GitlabSmall,
Gitlab,
LinkedinSmall,
} from "../components/Icons";
import Link from "../components/Link";
@ -75,7 +75,7 @@ const SocialIcon = ({ name }) => {
case "github":
return <GithubSmall />;
case "gitlab":
return <GitlabSmall />;
return <Gitlab width={16} height={16} />;
default:
throw new Error(`Cannot find an icon for "${name}"`);
}
@ -83,7 +83,7 @@ const SocialIcon = ({ name }) => {
const TeamCard = ({ image, name, position, social }) => (
<div className="flex">
<GatsbyImage image={getImage(image)} alt={name} />
<GatsbyImage image={getImage(image)} alt={name} className="rounded" />
<div className="flex flex-col justify-between ml-3">
<div className="flex flex-col">
<span className="font-light text-lg">{name}</span>
@ -94,7 +94,7 @@ const TeamCard = ({ image, name, position, social }) => (
{Object.entries(social)
.filter(([platform, href]) => href)
.map(([platform, href]) => (
<Link key={platform} href={href} title={platform}>
<Link key={platform} href={href} title={platform} className="w-6 h-6 flex items-center justify-center">
<SocialIcon name={platform} />
</Link>
))}
@ -293,7 +293,7 @@ export const query = graphql`
}
image {
childImageSharp {
gatsbyImageData(width: 80, placeholder: BLURRED, formats: [AUTO, WEBP, AVIF])
gatsbyImageData(width: 80, height: 80, placeholder: BLURRED, formats: [AUTO, WEBP, AVIF])
}
}
}