From bccf6b0e5149aaf0692313dbfea68ad3c17ffc71 Mon Sep 17 00:00:00 2001 From: Mathias Wagner Date: Sun, 10 Mar 2024 01:25:26 +0100 Subject: [PATCH 01/29] Added new translations to the en.json --- client/public/assets/locales/en.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/client/public/assets/locales/en.json b/client/public/assets/locales/en.json index d204f48a..8b71e164 100644 --- a/client/public/assets/locales/en.json +++ b/client/public/assets/locales/en.json @@ -245,6 +245,7 @@ "update_password": "Please update the password of this node", "password_outdated": "Password outdated", "password_updated": "The password has been successfully updated", + "preview_active": "Adding and deleting servers is disabled during demo mode.", "placeholder": { "name": "MySpeed instance", "url": "https://your-server.com" @@ -265,8 +266,14 @@ "tests_pending": "Test results pending" } }, + "preview": { + "title": "Demo mode", + "info": "DEMO", + "description": "You are currently in demo mode. Some features are disabled and some settings cannot be changed. If you want to use all features, you can install MySpeed on your own server." + }, "integrations": { "none_active": "This integration is not active.
Create", + "preview_active": "Integrations are disabled during demo mode.", "display_name": "Integration name", "create": "Create", "activity": { From ceddd3576f9b7d52514e53f7065eec6a860ebec5 Mon Sep 17 00:00:00 2001 From: Mathias Wagner Date: Sun, 10 Mar 2024 01:25:45 +0100 Subject: [PATCH 02/29] Randomized test values in demo mode --- server/tasks/speedtest.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/server/tasks/speedtest.js b/server/tasks/speedtest.js index 2a79d41d..68ef69e7 100644 --- a/server/tasks/speedtest.js +++ b/server/tasks/speedtest.js @@ -57,7 +57,18 @@ module.exports.create = async (type = "auto", retried = false) => { if (isRunning && !retried) return 500; try { - let test = await this.run(retried); + let test; + if (process.env.PREVIEW_MODE === "true") { + await new Promise(resolve => setTimeout(resolve, 5000)); + test = { + ping: {latency: Math.floor(Math.random() * 250) + 5}, + download: {bytes: Math.floor(Math.random() * 1000000000) + 1000000, elapsed: 10000}, + upload: {bytes: Math.floor(Math.random() * 1000000000) + 1000000, elapsed: 10000} + } + } else { + test = await this.run(retried); + } + let ping = Math.round(test.ping.latency); let download = roundSpeed(test.download.bytes, test.download.elapsed); let upload = roundSpeed(test.upload.bytes, test.upload.elapsed); From 0fe3decee496fd291be487c6615baefbe27cac20 Mon Sep 17 00:00:00 2001 From: Mathias Wagner Date: Sun, 10 Mar 2024 01:25:56 +0100 Subject: [PATCH 03/29] Updated the server index.js --- server/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/index.js b/server/index.js index 7ca82d5c..c8849b7b 100755 --- a/server/index.js +++ b/server/index.js @@ -43,7 +43,7 @@ const run = async () => { await require('./controller/integrations').initialize(); - await require('./util/loadCli').load(); + if (process.env.PREVIEW_MODE !== "true") await require('./util/loadCli').load(); await config.insertDefaults(); From c1c884a88bb50be2043045022b20b8c8b5a12dc0 Mon Sep 17 00:00:00 2001 From: Mathias Wagner Date: Sun, 10 Mar 2024 01:26:14 +0100 Subject: [PATCH 04/29] Protected the config.js routes --- server/routes/config.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/server/routes/config.js b/server/routes/config.js index cdc2dd45..dc333507 100644 --- a/server/routes/config.js +++ b/server/routes/config.js @@ -9,8 +9,11 @@ app.get("/", password(true), async (req, res) => { (await config.listAll()).forEach(row => { if (row.key !== "password" && !(req.viewMode && ["serverId", "cron", "passwordLevel"].includes(row.key))) configValues[row.key] = row.value; + if (process.env.PREVIEW_MODE === "true" && row.key === "acceptOoklaLicense") + configValues[row.key] = true; }); configValues['viewMode'] = req.viewMode; + configValues['previewMode'] = process.env.PREVIEW_MODE === "true"; if (Object.keys(configValues).length === 0) return res.status(404).json({message: "Hmm. There are no config values. Weird..."}); res.json(configValues); @@ -39,6 +42,12 @@ app.patch("/:key", password(false), async (req, res) => { if (!await config.updateValue(req.params.key, req.body.value.toString())) return res.status(404).json({message: "The provided key does not exist"}); + if (process.env.PREVIEW_MODE === "true" && req.params.key === "acceptOoklaLicense") + return res.status(403).json({message: "You can't change the Ookla license acceptance in preview mode"}); + + if (process.env.PREVIEW_MODE === "true" && (req.params.key === "password" || req.params.key === "passwordLevel")) + return res.status(403).json({message: "You can't change the password in preview mode"}); + if (req.params.key === "cron") { timer.stopTimer(); timer.startTimer(req.body.value.toString()); From 83c094257fda63f8b7469f4cd42b9d6c467e9b4e Mon Sep 17 00:00:00 2001 From: Mathias Wagner Date: Sun, 10 Mar 2024 01:26:25 +0100 Subject: [PATCH 05/29] Protected the integrations.js routes --- server/routes/integrations.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/server/routes/integrations.js b/server/routes/integrations.js index ec9e4a23..0a47d773 100644 --- a/server/routes/integrations.js +++ b/server/routes/integrations.js @@ -11,6 +11,9 @@ app.put("/:integrationName", password(false), async (req, res) => { const integration = integrations.getIntegration(req.params.integrationName); if (!integration) return res.status(404).json({message: "Integration not found"}); + if (process.env.PREVIEW_MODE === "true") + return res.status(403).json({message: "For security reasons, you can't create integrations in preview mode"}); + if (!req.body) return res.status(400).json({message: "Missing data"}); const validatedInput = validateInput(req.params.integrationName, req.body); @@ -23,6 +26,9 @@ app.put("/:integrationName", password(false), async (req, res) => { app.patch("/:id", password(false), async (req, res) => { if (!req.body) return res.status(400).json({message: "Missing data"}); + if (process.env.PREVIEW_MODE === "true") + return res.status(403).json({message: "For security reasons, you can't update integrations in preview mode"}); + const integration = await integrations.getIntegrationById(req.params.id); if (!integration) return res.status(404).json({message: "Integration not found"}); @@ -34,6 +40,9 @@ app.patch("/:id", password(false), async (req, res) => { }); app.delete("/:id", password(false), async (req, res) => { + if (process.env.PREVIEW_MODE === "true") + return res.status(403).json({message: "For security reasons, you can't delete integrations in preview mode"}); + const result = await integrations.delete(req.params.id); if (result === null) return res.status(404).json({message: "Integration not found"}); return res.json({message: "Integration deleted"}); From 0fd8732f0c410ace6d076f8ab8373ff357b4c19f Mon Sep 17 00:00:00 2001 From: Mathias Wagner Date: Sun, 10 Mar 2024 01:26:29 +0100 Subject: [PATCH 06/29] Protected the nodes.js routes --- server/routes/nodes.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/server/routes/nodes.js b/server/routes/nodes.js index 77a6a4a8..757e3835 100644 --- a/server/routes/nodes.js +++ b/server/routes/nodes.js @@ -7,6 +7,9 @@ app.get("/", password(false), async (req, res) => { }); app.put("/", password(false), async (req, res) => { + if (process.env.PREVIEW_MODE === "true") + return res.status(403).json({message: "For security reasons, you can't create nodes in preview mode"}); + if (!req.body.name || !req.body.url) return res.status(400).json({message: "Missing parameters", type: "MISSING_PARAMETERS"}); const url = req.body.url.replace(/\/+$/, ""); @@ -23,6 +26,9 @@ app.put("/", password(false), async (req, res) => { }); app.delete("/:nodeId", password(false), async (req, res) => { + if (process.env.PREVIEW_MODE === "true") + return res.status(403).json({message: "For security reasons, you can't delete nodes in preview mode"}); + const node = await nodes.getOne(req.params.nodeId); if (node === null) return res.status(404).json({message: "Node not found"}); @@ -31,6 +37,9 @@ app.delete("/:nodeId", password(false), async (req, res) => { }); app.patch("/:nodeId/name", password(false), async (req, res) => { + if (process.env.PREVIEW_MODE === "true") + return res.status(403).json({message: "For security reasons, you can't update nodes in preview mode"}); + if (!req.body.name) return res.status(400).json({message: "Missing parameters", type: "MISSING_PARAMETERS"}); const node = await nodes.getOne(req.params.nodeId); @@ -41,6 +50,9 @@ app.patch("/:nodeId/name", password(false), async (req, res) => { }); app.patch("/:nodeId/password", password(false), async (req, res) => { + if (process.env.PREVIEW_MODE === "true") + return res.status(403).json({message: "For security reasons, you can't update nodes in preview mode"}); + if (!req.body.password) return res.status(400).json({message: "Missing parameters", type: "MISSING_PARAMETERS"}); const node = await nodes.getOne(req.params.nodeId); From 149943742241432f1f865c7ae9fd4d2b95afe7a9 Mon Sep 17 00:00:00 2001 From: Mathias Wagner Date: Sun, 10 Mar 2024 01:26:34 +0100 Subject: [PATCH 07/29] Protected the system.js routes --- server/routes/system.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/server/routes/system.js b/server/routes/system.js index fe4bdf8f..8bd50926 100644 --- a/server/routes/system.js +++ b/server/routes/system.js @@ -5,7 +5,11 @@ const axios = require('axios'); const fs = require("fs"); const password = require('../middlewares/password'); +const servers = fs.readFileSync("./data/servers.json", "utf8") || "[]"; + app.get("/version", password(false), async (req, res) => { + if (process.env.PREVIEW_MODE === "true") return res.json({local: version, remote: "0"}); + try { res.json({local: version, remote: ((await axios.get(remote_url)).data.tag_name).replace("v", "")}); } catch (e) { @@ -14,10 +18,7 @@ app.get("/version", password(false), async (req, res) => { }); app.get("/server", password(false), (req, res) => { - fs.readFile("./data/servers.json", "utf8", (err, data) => { - if (err) return res.status(500).json({message: "Could not read servers"}); - res.json(JSON.parse(data.toString())); - }); + res.json(JSON.parse(servers.toString())); }); module.exports = app; \ No newline at end of file From bdc200956a0f67bfaf26c4f1d90e8639e1880741 Mon Sep 17 00:00:00 2001 From: Mathias Wagner Date: Sun, 10 Mar 2024 01:26:52 +0100 Subject: [PATCH 08/29] Removed password security during demo mode --- server/middlewares/password.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/middlewares/password.js b/server/middlewares/password.js index 3284cc7b..3896986e 100644 --- a/server/middlewares/password.js +++ b/server/middlewares/password.js @@ -2,6 +2,8 @@ const config = require('../controller/config'); const bcrypt = require('bcrypt'); module.exports = (allowViewAccess) => async (req, res, next) => { + if (process.env.PREVIEW_MODE === "true") return next(); + let passwordHash = await config.getValue("password"); let passwordLevel = await config.getValue("passwordLevel"); From 84c64876819f5d850657cb7ea2a79642c0a83fac Mon Sep 17 00:00:00 2001 From: Mathias Wagner Date: Sun, 10 Mar 2024 01:27:01 +0100 Subject: [PATCH 09/29] Updated the config.js controller --- server/controller/config.js | 1 + 1 file changed, 1 insertion(+) diff --git a/server/controller/config.js b/server/controller/config.js index 577cc5e5..f5878d67 100644 --- a/server/controller/config.js +++ b/server/controller/config.js @@ -27,6 +27,7 @@ module.exports.listAll = async () => { } module.exports.getValue = async (key) => { + if (process.env.PREVIEW_MODE === "true" && key === "acceptOoklaLicense") return true; return (await config.findByPk(key)).value; } From 7c1c01ec2c0a634ed6f2f7749827af444855ae02 Mon Sep 17 00:00:00 2001 From: Mathias Wagner Date: Sun, 10 Mar 2024 01:27:12 +0100 Subject: [PATCH 10/29] Fixed a bug in the speedtests.js controller --- server/controller/speedtests.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/server/controller/speedtests.js b/server/controller/speedtests.js index 7be0ddd8..10d3661d 100644 --- a/server/controller/speedtests.js +++ b/server/controller/speedtests.js @@ -74,8 +74,11 @@ module.exports.listStatistics = async (days) => { let notFailed = dbEntries.filter((entry) => entry.error === null); - let data = ["ping", "download", "upload", "time"] - .map((item) => days >= 3 ? avgEntries.map(entry => entry[item]) : notFailed.map(entry => entry[item])); + let data = {}; + ["ping", "download", "upload", "time"].forEach(item => { + data[item] = days >= 3 ? avgEntries.map(entry => entry[item]) : notFailed.map(entry => entry[item]); + }); + return { tests: { From 8d2525300ba9e50a58f6c42c1d3c9fc1be40bd35 Mon Sep 17 00:00:00 2001 From: Mathias Wagner Date: Sun, 10 Mar 2024 01:27:26 +0100 Subject: [PATCH 11/29] Added the WEB_URL to the client index.js --- client/src/index.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/client/src/index.jsx b/client/src/index.jsx index 5c62477e..06ef0812 100644 --- a/client/src/index.jsx +++ b/client/src/index.jsx @@ -3,6 +3,7 @@ import ReactDOM from "react-dom/client"; import App from './App'; export const PROJECT_URL = "https://github.com/gnmyt/myspeed"; +export const WEB_URL = "https://myspeed.dev"; export const PROJECT_WIKI = "https://docs.myspeed.dev"; const root = ReactDOM.createRoot(document.getElementById('root')); From 5b23ce8e666889dbff95b82567758b091137efe6 Mon Sep 17 00:00:00 2001 From: Mathias Wagner Date: Sun, 10 Mar 2024 01:27:52 +0100 Subject: [PATCH 12/29] Removed the healthChecksInfo from the infos.jsx util --- client/src/common/components/Dropdown/utils/infos.jsx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/client/src/common/components/Dropdown/utils/infos.jsx b/client/src/common/components/Dropdown/utils/infos.jsx index aebe30e8..775bfa7c 100644 --- a/client/src/common/components/Dropdown/utils/infos.jsx +++ b/client/src/common/components/Dropdown/utils/infos.jsx @@ -1,12 +1,8 @@ -import {PROJECT_URL, PROJECT_WIKI} from "@/index"; +import {WEB_URL} from "@/index"; import {Trans} from "react-i18next"; -const HEALTHCHECKS_URL = "https://healthchecks.io/"; const CLI_URL = "https://www.speedtest.net/apps/cli"; -export const healthChecksInfo = () => , - WIKILink: }}>info.healthchecks - -export const creditsInfo = () => , +export const creditsInfo = () => , CLILink: }}>info.credits export const recommendationsInfo = (ping, down, up) => }} From 64fb533faabba74a2ecb7cd3ca85c13b36106435 Mon Sep 17 00:00:00 2001 From: Mathias Wagner Date: Sun, 10 Mar 2024 01:28:06 +0100 Subject: [PATCH 13/29] Updated the DropdownComponent.jsx --- client/src/common/components/Dropdown/DropdownComponent.jsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/client/src/common/components/Dropdown/DropdownComponent.jsx b/client/src/common/components/Dropdown/DropdownComponent.jsx index 4532f139..3bb9615b 100644 --- a/client/src/common/components/Dropdown/DropdownComponent.jsx +++ b/client/src/common/components/Dropdown/DropdownComponent.jsx @@ -26,7 +26,7 @@ import {StatusContext} from "@/common/contexts/Status"; import {InputDialogContext} from "@/common/contexts/InputDialog"; import {SpeedtestContext} from "@/common/contexts/Speedtests"; import {baseRequest, downloadRequest, jsonRequest, patchRequest, postRequest} from "@/common/utils/RequestUtil"; -import {creditsInfo, healthChecksInfo, recommendationsInfo} from "@/common/components/Dropdown/utils/infos"; +import {creditsInfo, recommendationsInfo} from "@/common/components/Dropdown/utils/infos"; import { exportOptions, languageOptions, levelOptions, selectOptions, timeOptions @@ -249,7 +249,7 @@ function DropdownComponent() { {run: recommendedSettings, icon: faWandMagicSparkles, text: t("dropdown.recommendations")}, {hr: true, key: 1}, {run: updateServer, icon: faServer, text: t("dropdown.server")}, - {run: updatePassword, icon: faKey, text: t("dropdown.password")}, + {run: updatePassword, icon: faKey, text: t("dropdown.password"), previewHidden: true}, {run: updateCron, icon: faClock, text: t("dropdown.cron")}, {run: updateTime, icon: faCalendarDays, text: t("dropdown.time"), allowView: true}, {run: exportDialog, icon: faFileExport, text: t("dropdown.export")}, @@ -270,6 +270,7 @@ function DropdownComponent() {

{t("dropdown.settings")}

{options.map(entry => { + if (entry.previewHidden && config.previewMode) return; if (!config.viewMode || (config.viewMode && entry.allowView)) { if (!entry.hr) { return (
{ From 5f4ae346067a41d315679b6b9969439fbaafeb3e Mon Sep 17 00:00:00 2001 From: Mathias Wagner Date: Sun, 10 Mar 2024 01:28:23 +0100 Subject: [PATCH 14/29] Integrated the demo mode badge into the HeaderComponent.jsx --- .../common/components/Header/HeaderComponent.jsx | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/client/src/common/components/Header/HeaderComponent.jsx b/client/src/common/components/Header/HeaderComponent.jsx index 9b121c58..5d4e4381 100644 --- a/client/src/common/components/Header/HeaderComponent.jsx +++ b/client/src/common/components/Header/HeaderComponent.jsx @@ -18,6 +18,8 @@ import {t} from "i18next"; import {ConfigContext} from "@/common/contexts/Config"; import {LoadingDialog} from "@/common/components/LoadingDialog"; import {NodeContext} from "@/common/contexts/Node"; +import {WEB_URL} from "@/index"; +import {Trans} from "react-i18next"; function HeaderComponent(props) { const findNode = useContext(NodeContext)[4]; @@ -35,6 +37,12 @@ function HeaderComponent(props) { toggleDropdown(setIcon); } + const showDemoDialog = () => setDialog({ + title: t("preview.title"), + description: }}>preview.description, + buttonText: t("dialog.okay") + }); + const showPasswordDialog = () => setDialog({ title: t("header.admin_login"), placeholder: t("dialog.password.placeholder"), @@ -90,7 +98,13 @@ function HeaderComponent(props) {
- {config.viewMode ?

{t("header.title")}

:

props.showNodePage(true)} className="h2-click"> {getNodeName()}

} +
+ {config.viewMode &&

{t("header.title")}

} + {!config.viewMode &&

props.showNodePage(true)} className="h2-click"> {getNodeName()}

} + + {config.previewMode &&

{t("preview.info")}

} +
+
{updateAvailable ? From 86d1a54d89c9647023a9eef2d8dfd6bc91b8e89d Mon Sep 17 00:00:00 2001 From: Mathias Wagner Date: Sun, 10 Mar 2024 01:28:28 +0100 Subject: [PATCH 15/29] Integrated the demo mode badge into the HeaderComponent styles.sass --- .../src/common/components/Header/styles.sass | 37 ++++++++++++++----- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/client/src/common/components/Header/styles.sass b/client/src/common/components/Header/styles.sass index 218e963f..0572253c 100644 --- a/client/src/common/components/Header/styles.sass +++ b/client/src/common/components/Header/styles.sass @@ -16,13 +16,26 @@ .header-main * font-size: 24pt -.header-main h2 +.header-left margin-left: 10% display: flex - gap: 1rem + gap: 0.5rem + align-items: center + +.demo-info + display: flex + cursor: pointer align-items: center + justify-content: center + font-size: 14pt + background-color: $dark-gray + color: $green + margin: 0 + +.header-main h2 padding: 0.5rem 1rem border-radius: 1rem + margin: 0 .header-main .h2-click cursor: pointer @@ -68,24 +81,30 @@ 100% box-shadow: 0 0 0 0 #CCA92C00 +@media screen and (max-width: 600px) + .header-left + flex-direction: column + gap: 0 + @media (max-width: 460px) .header-main justify-content: center - .header-main div + .header-left div margin-right: 0 - .header-main h2 + .header-left .h2-click margin-left: 0 - font-size: 24pt + font-size: 22pt .header-icon width: 25px height: 25px - -@media (max-width: 360px) - .header-main h2 - font-size: 16pt +@media (max-width: 410px) + .header-left .h2-click + font-size: 14pt text-overflow: ellipsis overflow: hidden + .header-left svg + font-size: 14pt .header-icon width: 20px height: 20px \ No newline at end of file From e49b582ec1f5763aaaec8c6a225d8b4ff37c209b Mon Sep 17 00:00:00 2001 From: Mathias Wagner Date: Sun, 10 Mar 2024 01:28:37 +0100 Subject: [PATCH 16/29] Updated the IntegrationItemHeader.jsx --- .../IntegrationItemHeader/IntegrationItemHeader.jsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/client/src/common/components/IntegrationDialog/components/IntegrationItem/components/IntegrationItemHeader/IntegrationItemHeader.jsx b/client/src/common/components/IntegrationDialog/components/IntegrationItem/components/IntegrationItemHeader/IntegrationItemHeader.jsx index a2827e44..abb56e6c 100644 --- a/client/src/common/components/IntegrationDialog/components/IntegrationItem/components/IntegrationItemHeader/IntegrationItemHeader.jsx +++ b/client/src/common/components/IntegrationDialog/components/IntegrationItem/components/IntegrationItemHeader/IntegrationItemHeader.jsx @@ -10,11 +10,13 @@ import { faTrashArrowUp } from "@fortawesome/free-solid-svg-icons"; import "./styles.sass"; -import {useEffect, useState} from "react"; +import {useContext, useEffect, useState} from "react"; +import {ConfigContext} from "@/common/contexts/Config"; export const IntegrationItemHeader = ({integration, displayName, unsavedChanges, changesConfirmed, saveIntegration, deleteConfirmed, deleteIntegration, open, setOpen, data}) => { const [lastActivity, setLastActivity] = useState(generateRelativeTime(data.lastActivity)); + const [config] = useContext(ConfigContext); useEffect(() => { const interval = setInterval(() => { @@ -40,11 +42,11 @@ export const IntegrationItemHeader = ({integration, displayName, unsavedChanges,
- {unsavedChanges && !changesConfirmed && } {changesConfirmed && } - {!deleteConfirmed && + {!config.previewMode && !deleteConfirmed && } {deleteConfirmed && } From 628fd44073e8d97d3a1b59b7ff72b4420d28cbb2 Mon Sep 17 00:00:00 2001 From: Mathias Wagner Date: Sun, 10 Mar 2024 01:28:49 +0100 Subject: [PATCH 17/29] Updated the IntegrationDialog.jsx --- .../components/IntegrationDialog/IntegrationDialog.jsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/client/src/common/components/IntegrationDialog/IntegrationDialog.jsx b/client/src/common/components/IntegrationDialog/IntegrationDialog.jsx index 8571b09d..405ad944 100644 --- a/client/src/common/components/IntegrationDialog/IntegrationDialog.jsx +++ b/client/src/common/components/IntegrationDialog/IntegrationDialog.jsx @@ -2,7 +2,7 @@ import {DialogContext, DialogProvider} from "@/common/contexts/Dialog"; import "./styles.sass"; import React, {useContext, useEffect, useState} from "react"; import {t} from "i18next"; -import {faClose} from "@fortawesome/free-solid-svg-icons"; +import {faClose, faExclamationTriangle} from "@fortawesome/free-solid-svg-icons"; import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; import {jsonRequest} from "@/common/utils/RequestUtil"; import IntegrationItem from "@/common/components/IntegrationDialog/components/IntegrationItem"; @@ -10,9 +10,11 @@ import {v4 as uuid} from 'uuid'; import AvailableIntegrations from "./components/AvailableIntegrations"; import IntegrationAddButton from "@/common/components/IntegrationDialog/components/IntegrationAddButton"; import NoIntegrationsTab from "@/common/components/IntegrationDialog/components/NoIntegrationsTab"; +import {ConfigContext} from "@/common/contexts/Config"; export const Dialog = ({integrations, active, setActive}) => { const close = useContext(DialogContext); + const [config] = useContext(ConfigContext); const [currentTab, setCurrentTab] = useState(integrations[Object.keys(integrations)[0]].name); const addIntegration = () => setActive([...active, {uuid: uuid(), name: currentTab, @@ -31,6 +33,12 @@ export const Dialog = ({integrations, active, setActive}) => { setCurrentTab={setCurrentTab}/>
+ {config.previewMode && active.filter(item => item.name === currentTab).length > 0 + &&
+ +

{t("integrations.preview_active")}

+
} + {active.map((item) => (item.name !== currentTab ? undefined : deleteIntegration(item.uuid)} From 69c72c4c721bfa4ddaace8286b05e8111bfa3834 Mon Sep 17 00:00:00 2001 From: Mathias Wagner Date: Sun, 10 Mar 2024 01:28:58 +0100 Subject: [PATCH 18/29] Updated the IntegrationDialog styles.sass --- .../components/IntegrationDialog/styles.sass | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/client/src/common/components/IntegrationDialog/styles.sass b/client/src/common/components/IntegrationDialog/styles.sass index d34a5cdd..ca6e4f82 100644 --- a/client/src/common/components/IntegrationDialog/styles.sass +++ b/client/src/common/components/IntegrationDialog/styles.sass @@ -17,6 +17,21 @@ overflow-x: clip width: 70% + .pr-integration-container + display: flex + gap: 0.5rem + align-items: center + justify-content: center + padding: 0.5rem + border-radius: 0.5rem + border: 2px solid $red + color: $red + font-size: 1.25rem + + p + margin: 0 + font-weight: 500 + @media (max-width: 781px) .integration-dialog width: 90vw From 3493e5ff586b0b4800c0355a249c28c73169cf9b Mon Sep 17 00:00:00 2001 From: Mathias Wagner Date: Sun, 10 Mar 2024 01:29:36 +0100 Subject: [PATCH 19/29] Updated the nodes.jsx page --- client/src/pages/Nodes/Nodes.jsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/client/src/pages/Nodes/Nodes.jsx b/client/src/pages/Nodes/Nodes.jsx index 86467a43..5535b463 100644 --- a/client/src/pages/Nodes/Nodes.jsx +++ b/client/src/pages/Nodes/Nodes.jsx @@ -5,11 +5,19 @@ import {useContext, useEffect, useState} from "react"; import {NodeContext} from "@/common/contexts/Node"; import {t} from "i18next"; import CreateNodeDialog from "@/pages/Nodes/components/CreateNodeDialog"; +import {ConfigContext} from "@/common/contexts/Config"; +import {InputDialogContext} from "@/common/contexts/InputDialog"; export const Nodes = (props) => { + const [config] = useContext(ConfigContext); const [nodes, updateNodes] = useContext(NodeContext); + const [setDialog] = useContext(InputDialogContext); const [createDialogOpen, setCreateDialogOpen] = useState(false); + const openPreviewInfoDialog = () => { + setDialog({title: t("preview.title"), description: t("nodes.preview_active"), buttonText: t("dialog.close")}); + } + useEffect(() => { updateNodes(); }, []); @@ -24,7 +32,8 @@ export const Nodes = (props) => { {nodes.map(node => )} -
setCreateDialogOpen(true)}> +
config.previewMode + ? openPreviewInfoDialog() : setCreateDialogOpen(true)}>

{t("nodes.add")}

From be5241598164447a27c4ee9f1780074dae185490 Mon Sep 17 00:00:00 2001 From: Mathias Wagner Date: Sun, 10 Mar 2024 01:29:47 +0100 Subject: [PATCH 20/29] Updated the Nodes styles.sass --- client/src/pages/Nodes/styles.sass | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/client/src/pages/Nodes/styles.sass b/client/src/pages/Nodes/styles.sass index 6e74d17e..b6e0a549 100644 --- a/client/src/pages/Nodes/styles.sass +++ b/client/src/pages/Nodes/styles.sass @@ -39,6 +39,19 @@ h1 color: $white +.node-disabled + width: 53rem + border: 2px dashed #696C73 + border-radius: 15px + cursor: not-allowed + user-select: none + +.node-disabled:hover + border: 2px dashed #696C73 + + h1 + color: $darker-white + @media screen and (max-width: 862px) .node-add width: calc(20vw + 11rem) From c77f03513c34d8a498642ea157ef1dce704be257 Mon Sep 17 00:00:00 2001 From: Mathias Wagner Date: Sun, 10 Mar 2024 01:29:57 +0100 Subject: [PATCH 21/29] Fixed a font bug in the Statistics.jsx --- client/src/pages/Statistics/Statistics.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/pages/Statistics/Statistics.jsx b/client/src/pages/Statistics/Statistics.jsx index ca51f638..5c38b8be 100644 --- a/client/src/pages/Statistics/Statistics.jsx +++ b/client/src/pages/Statistics/Statistics.jsx @@ -33,7 +33,7 @@ const generatePath = (level = 1) => { ChartJS.register(ArcElement, Tooltip, CategoryScale, LinearScale, PointElement, LineElement, Title, Legend, RadialLinearScale); ChartJS.defaults.color = "#B0B0B0"; ChartJS.defaults.font.color = "#B0B0B0"; -ChartJS.defaults.font.family = "Roboto"; +ChartJS.defaults.font.family = "Inter, sans-serif"; export const Statistics = () => { From 45fb1f35d618e41e1ca3a6b7815888c007799116 Mon Sep 17 00:00:00 2001 From: Mathias Wagner Date: Sun, 10 Mar 2024 01:33:35 +0100 Subject: [PATCH 22/29] Updated the storage path during preview mode --- server/config/database.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/config/database.js b/server/config/database.js index 464de327..40870711 100644 --- a/server/config/database.js +++ b/server/config/database.js @@ -1,7 +1,9 @@ const {Sequelize} = require('sequelize'); +const STORAGE_PATH = `data/storage${process.env.PREVIEW_MODE === "true" ? "_preview" : ""}.db`; + Sequelize.DATE.prototype._stringify = () => { return new Date().toISOString(); } -module.exports = new Sequelize({dialect: 'sqlite', storage: 'data/storage.db', logging: false, query: {raw: true}}); \ No newline at end of file +module.exports = new Sequelize({dialect: 'sqlite', storage: STORAGE_PATH, logging: false, query: {raw: true}}); \ No newline at end of file From 3e808501890ff16de5e1b431daa0bac3bec82093 Mon Sep 17 00:00:00 2001 From: Mathias Wagner Date: Sun, 10 Mar 2024 11:17:57 +0100 Subject: [PATCH 23/29] Added new translations to the en.json --- client/public/assets/locales/en.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/public/assets/locales/en.json b/client/public/assets/locales/en.json index 8b71e164..9e5ed747 100644 --- a/client/public/assets/locales/en.json +++ b/client/public/assets/locales/en.json @@ -45,6 +45,7 @@ "language": "Change language", "view": "Switch view", "info": "About the project", + "provider": "About the provider", "integrations": "Integrations" }, "options": { @@ -106,6 +107,7 @@ "running_tooltip": "Speedtest running", "start_tooltip": "Start speedtest", "new_update": "Update available", + "download": "Download", "paused": "Speedtests are currently paused. Please continue them if you want to do one.", "running": "A speedtest is already running. Please wait a moment.", "admin_login": "Admin Login", From db3704ba28295939250c714c4902330f7d4fa23d Mon Sep 17 00:00:00 2001 From: Mathias Wagner Date: Sun, 10 Mar 2024 11:18:16 +0100 Subject: [PATCH 24/29] Added custom provider details in the DropdownComponent.jsx --- client/src/common/components/Dropdown/DropdownComponent.jsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/client/src/common/components/Dropdown/DropdownComponent.jsx b/client/src/common/components/Dropdown/DropdownComponent.jsx index 3bb9615b..ef73c01a 100644 --- a/client/src/common/components/Dropdown/DropdownComponent.jsx +++ b/client/src/common/components/Dropdown/DropdownComponent.jsx @@ -242,6 +242,8 @@ function DropdownComponent() { const showCredits = () => setDialog({title: "MySpeed", description: creditsInfo(), buttonText: t("dialog.close")}); + const showProviderDetails = () => setDialog({title: t("dropdown.provider"), description: config.previewMessage, buttonText: t("dialog.close")}); + const options = [ {run: updatePing, icon: faPingPongPaddleBall, text: t("dropdown.ping")}, {run: updateUpload, icon: faArrowUp, text: t("dropdown.upload")}, @@ -258,7 +260,8 @@ function DropdownComponent() { {hr: true, key: 2}, {run: updateLanguage, icon: faGlobeEurope, text: t("dropdown.language"), allowView: true}, {run: () => setShowViewDialog(true), icon: faChartSimple, allowView: true, text: t("dropdown.view")}, - {run: showCredits, icon: faInfo, text: t("dropdown.info"), allowView: true} + {run: showCredits, icon: faInfo, text: t("dropdown.info"), allowView: true, previewHidden: true}, + {run: showProviderDetails, icon: faInfo, text: t("dropdown.provider"), previewShown: true} ]; return ( @@ -271,6 +274,7 @@ function DropdownComponent() {
{options.map(entry => { if (entry.previewHidden && config.previewMode) return; + if (entry.previewShown && !config.previewMode) return; if (!config.viewMode || (config.viewMode && entry.allowView)) { if (!entry.hr) { return (
{ From 65b2187d26271cab9aca99b77f090e9b737394bf Mon Sep 17 00:00:00 2001 From: Mathias Wagner Date: Sun, 10 Mar 2024 11:21:32 +0100 Subject: [PATCH 25/29] Added a download icon to the HeaderComponent.jsx --- client/src/common/components/Header/HeaderComponent.jsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/client/src/common/components/Header/HeaderComponent.jsx b/client/src/common/components/Header/HeaderComponent.jsx index 5d4e4381..a51bf913 100644 --- a/client/src/common/components/Header/HeaderComponent.jsx +++ b/client/src/common/components/Header/HeaderComponent.jsx @@ -1,7 +1,7 @@ import "./styles.sass"; import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; import { - faCircleArrowUp, + faCircleArrowUp, faDownload, faGaugeHigh, faGear, faLock, @@ -78,6 +78,8 @@ function HeaderComponent(props) { postRequest("/speedtests/run").then(updateTests).then(updateStatus).then(() => setStartedManually(false)); } + const openDownloadPage = () => window.open(WEB_URL + "/install", "_blank"); + useEffect(() => { if (Object.keys(config).length === 0) return; async function updateVersion() { @@ -127,6 +129,11 @@ function HeaderComponent(props) { {t("header.admin_login")}
: <>)} + {(config.previewMode ?
+ + {t("header.download")} +
: <>)} +
{t("dropdown.settings")} From 0f46fed8c8374fd18f641dea4fec1ff951b91446 Mon Sep 17 00:00:00 2001 From: Mathias Wagner Date: Sun, 10 Mar 2024 11:21:40 +0100 Subject: [PATCH 26/29] Updated the Header styles.sass --- .../src/common/components/Header/styles.sass | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/client/src/common/components/Header/styles.sass b/client/src/common/components/Header/styles.sass index 0572253c..b53092b7 100644 --- a/client/src/common/components/Header/styles.sass +++ b/client/src/common/components/Header/styles.sass @@ -1,7 +1,7 @@ @import "@/common/styles/colors" .header-main - margin-top: 2rem + margin-top: 3rem display: flex align-items: center justify-content: space-between @@ -41,6 +41,9 @@ cursor: pointer user-select: none + svg + margin-right: 0.3rem + .header-main .h2-click:hover color: $green-hover background-color: $dark-gray @@ -81,12 +84,12 @@ 100% box-shadow: 0 0 0 0 #CCA92C00 -@media screen and (max-width: 600px) +@media screen and (max-width: 650px) .header-left flex-direction: column gap: 0 -@media (max-width: 460px) +@media (max-width: 530px) .header-main justify-content: center .header-left div @@ -98,7 +101,7 @@ width: 25px height: 25px -@media (max-width: 410px) +@media (max-width: 480px) .header-left .h2-click font-size: 14pt text-overflow: ellipsis @@ -107,4 +110,11 @@ font-size: 14pt .header-icon width: 20px - height: 20px \ No newline at end of file + height: 20px + +@media screen and (max-width: 365px) + .header-left svg + display: none + .demo-info + font-size: 12pt + padding: 0 \ No newline at end of file From 66b3ce30a1f4cdf79b443da7ed2bdb465f5dd1c3 Mon Sep 17 00:00:00 2001 From: Mathias Wagner Date: Sun, 10 Mar 2024 11:22:36 +0100 Subject: [PATCH 27/29] Added the previewMessage value to the config.js route --- server/routes/config.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/server/routes/config.js b/server/routes/config.js index dc333507..328e0895 100644 --- a/server/routes/config.js +++ b/server/routes/config.js @@ -15,6 +15,9 @@ app.get("/", password(true), async (req, res) => { configValues['viewMode'] = req.viewMode; configValues['previewMode'] = process.env.PREVIEW_MODE === "true"; + if (process.env.PREVIEW_MODE === "true") + configValues['previewMessage'] = String(process.env.PREVIEW_MESSAGE || "The owner of this instance has not provided a message"); + if (Object.keys(configValues).length === 0) return res.status(404).json({message: "Hmm. There are no config values. Weird..."}); res.json(configValues); }); From f55496d6703be663c91dfa6b71713592a70eb670 Mon Sep 17 00:00:00 2001 From: Mathias Wagner Date: Sun, 10 Mar 2024 11:24:04 +0100 Subject: [PATCH 28/29] Changed environment variable port to SERVER_PORT --- server/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/index.js b/server/index.js index c8849b7b..6e6a79d5 100755 --- a/server/index.js +++ b/server/index.js @@ -7,7 +7,7 @@ const app = express(); app.disable('x-powered-by'); -const port = process.env.port || 5216; +const port = process.env.SERVER_PORT || 5216; require('./util/createFolders'); require('./util/loadServers'); From d07a8eb7eb581c08691639ab7c3ad2814697afb8 Mon Sep 17 00:00:00 2001 From: Mathias Wagner Date: Sun, 10 Mar 2024 19:07:17 +0100 Subject: [PATCH 29/29] Fixed the Header styles.sass --- client/src/common/components/Header/styles.sass | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/client/src/common/components/Header/styles.sass b/client/src/common/components/Header/styles.sass index b53092b7..2842ec60 100644 --- a/client/src/common/components/Header/styles.sass +++ b/client/src/common/components/Header/styles.sass @@ -21,6 +21,9 @@ display: flex gap: 0.5rem align-items: center + justify-content: center + overflow: hidden + text-overflow: ellipsis .demo-info display: flex @@ -38,6 +41,9 @@ margin: 0 .header-main .h2-click + display: flex + gap: 1rem + align-items: center cursor: pointer user-select: none @@ -53,6 +59,8 @@ .header-icon cursor: pointer + display: flex + transition: all 50ms ease-in-out width: 30px height: 30px @@ -87,7 +95,6 @@ @media screen and (max-width: 650px) .header-left flex-direction: column - gap: 0 @media (max-width: 530px) .header-main @@ -113,8 +120,10 @@ height: 20px @media screen and (max-width: 365px) + .header-main .h2-click + gap: 0 .header-left svg - display: none + margin-right: 0 .demo-info font-size: 12pt padding: 0 \ No newline at end of file