diff --git a/docker/handshake/Dockerfile b/docker/handshake/Dockerfile index 0745b338..7a1fd0eb 100644 --- a/docker/handshake/Dockerfile +++ b/docker/handshake/Dockerfile @@ -1,4 +1,4 @@ -FROM node:16.13.1-alpine +FROM node:16.13.2-alpine WORKDIR /opt/hsd diff --git a/packages/dashboard/Dockerfile b/packages/dashboard/Dockerfile index 48a7c233..fd163537 100644 --- a/packages/dashboard/Dockerfile +++ b/packages/dashboard/Dockerfile @@ -1,4 +1,4 @@ -FROM node:16.13.1-alpine +FROM node:16.13.2-alpine WORKDIR /usr/app diff --git a/packages/dashboard/package.json b/packages/dashboard/package.json index 4e94af80..6b5febcb 100644 --- a/packages/dashboard/package.json +++ b/packages/dashboard/package.json @@ -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", diff --git a/packages/dashboard/yarn.lock b/packages/dashboard/yarn.lock index e53af24a..74fe548c 100644 --- a/packages/dashboard/yarn.lock +++ b/packages/dashboard/yarn.lock @@ -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" diff --git a/packages/dnslink-api/Dockerfile b/packages/dnslink-api/Dockerfile index 29eaae92..ecae48c7 100644 --- a/packages/dnslink-api/Dockerfile +++ b/packages/dnslink-api/Dockerfile @@ -1,4 +1,4 @@ -FROM node:16.13.1-alpine +FROM node:16.13.2-alpine WORKDIR /usr/app diff --git a/packages/handshake-api/Dockerfile b/packages/handshake-api/Dockerfile index f4b6b8d8..ba544777 100644 --- a/packages/handshake-api/Dockerfile +++ b/packages/handshake-api/Dockerfile @@ -1,4 +1,4 @@ -FROM node:16.13.1-alpine +FROM node:16.13.2-alpine WORKDIR /usr/app diff --git a/packages/health-check/Dockerfile b/packages/health-check/Dockerfile index 232365ff..de548462 100644 --- a/packages/health-check/Dockerfile +++ b/packages/health-check/Dockerfile @@ -1,4 +1,4 @@ -FROM node:16.13.1-alpine +FROM node:16.13.2-alpine RUN apk update && apk add dnsmasq diff --git a/packages/health-check/src/checks/critical.js b/packages/health-check/src/checks/critical.js index 0cd16a95..22051bda 100644 --- a/packages/health-check/src/checks/critical.js +++ b/packages/health-check/src/checks/critical.js @@ -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; diff --git a/packages/health-check/src/checks/extended.js b/packages/health-check/src/checks/extended.js index 01953d75..28a96f0e 100644 --- a/packages/health-check/src/checks/extended.js +++ b/packages/health-check/src/checks/extended.js @@ -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 = {}; diff --git a/packages/health-check/src/index.js b/packages/health-check/src/index.js index 9d928156..5bf10868 100644 --- a/packages/health-check/src/index.js +++ b/packages/health-check/src/index.js @@ -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) { - throw new Error("You need to provide SKYNET_DASHBOARD_URL environment variable when accounts are enabled"); +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"); diff --git a/packages/health-check/src/utils.js b/packages/health-check/src/utils.js index 414771f8..6e35f75e 100644 --- a/packages/health-check/src/utils.js +++ b/packages/health-check/src/utils.js @@ -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, }; diff --git a/packages/website/Dockerfile b/packages/website/Dockerfile index d8ae4529..a75d7392 100644 --- a/packages/website/Dockerfile +++ b/packages/website/Dockerfile @@ -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 diff --git a/packages/website/data/team.yaml b/packages/website/data/team.yaml index 82f61cd3..9c6c3e04 100644 --- a/packages/website/data/team.yaml +++ b/packages/website/data/team.yaml @@ -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 diff --git a/packages/website/data/team/alice-hlidkova.jpeg b/packages/website/data/team/alice-hlidkova.jpeg new file mode 100644 index 00000000..6207d846 Binary files /dev/null and b/packages/website/data/team/alice-hlidkova.jpeg differ diff --git a/packages/website/data/team/ayoung-jeon.jpeg b/packages/website/data/team/ayoung-jeon.jpeg new file mode 100644 index 00000000..63b18ff1 Binary files /dev/null and b/packages/website/data/team/ayoung-jeon.jpeg differ diff --git a/packages/website/data/team/marissa-hudson.jpeg b/packages/website/data/team/marissa-hudson.jpeg new file mode 100644 index 00000000..16befed9 Binary files /dev/null and b/packages/website/data/team/marissa-hudson.jpeg differ diff --git a/packages/website/src/components/Icons/Gitlab.svg b/packages/website/src/components/Icons/Gitlab.svg new file mode 100644 index 00000000..d84a8e9d --- /dev/null +++ b/packages/website/src/components/Icons/Gitlab.svg @@ -0,0 +1 @@ +GitLab \ No newline at end of file diff --git a/packages/website/src/components/Icons/index.js b/packages/website/src/components/Icons/index.js index c2181d6a..7efa5223 100644 --- a/packages/website/src/components/Icons/index.js +++ b/packages/website/src/components/Icons/index.js @@ -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"; diff --git a/packages/website/src/pages/about.js b/packages/website/src/pages/about.js index 35907f58..b0500363 100644 --- a/packages/website/src/pages/about.js +++ b/packages/website/src/pages/about.js @@ -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 ; case "gitlab": - return ; + return ; default: throw new Error(`Cannot find an icon for "${name}"`); } @@ -83,7 +83,7 @@ const SocialIcon = ({ name }) => { const TeamCard = ({ image, name, position, social }) => (
- +
{name} @@ -94,7 +94,7 @@ const TeamCard = ({ image, name, position, social }) => ( {Object.entries(social) .filter(([platform, href]) => href) .map(([platform, href]) => ( - + ))} @@ -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]) } } }