Skip to content

Commit

Permalink
Merge pull request #255 from gnmyt/optimizations/1.0.7
Browse files Browse the repository at this point in the history
🆕 Version 1.0.7 - Update
  • Loading branch information
gnmyt authored Apr 6, 2023
2 parents 74774b8 + 8b76f60 commit 95ec9d2
Show file tree
Hide file tree
Showing 18 changed files with 97 additions and 54 deletions.
4 changes: 2 additions & 2 deletions client/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion client/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "client",
"version": "1.0.6",
"version": "1.0.7",
"scripts": {
"dev": "vite",
"build": "vite build",
Expand Down
4 changes: 3 additions & 1 deletion client/public/locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
"password": {
"title": "Passwort erforderlich",
"placeholder": "Dein Passwort",
"wrong": "Das von dir eingegebene Passwort ist falsch"
"wrong": "Das von dir eingegebene Passwort ist falsch",
"unlock": "Sperre aufheben"
},
"accept": {
"title": "Wir brauchen deine Genehmigung",
Expand Down Expand Up @@ -160,6 +161,7 @@
},
"test": {
"not_available": "Es liegen aktuell keine Tests vor",
"no_latest": "Es liegt kein aktueller Test vor. Bitte führe einen Test durch oder warte, bis der nächste Test durchgeführt wurde.",
"unknown_error": "Unbekannter Fehler:",
"failed": "Test fehlgeschlagen",
"recheck": "Bitte überprüfe weitestgehend, ob das öfter passiert.",
Expand Down
6 changes: 4 additions & 2 deletions client/public/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
"password": {
"title": "Password required",
"placeholder": "Your password",
"wrong": "The password you entered is incorrect"
"wrong": "The password you entered is incorrect",
"unlock": "Unlock"
},
"accept": {
"title": "We need your permission",
Expand Down Expand Up @@ -129,7 +130,7 @@
"healthchecks": "MySpeed uses <HCLink>HealthChecks</HCLink> to notify you when your internet is down. To enable this, put your ping URL in the text box. Read more <WIKILink>here</WIKILink>",
"credits": "<Link>MySpeed</Link> is provided by GNMYT and uses the <CLILink>Speedtest CLI</CLILink> from Ookla.",
"recommendations_error": "You have to do at least 10 tests to get an average. It doesn't matter if the tests were done manually or automatically.",
"recommendations_info": "Based on the last 10 tests, it was found that the optimal ping was <Bold>{{ping}} ms</Bold>, the download at <Bold>{{down}} Mbit/s</Bold> and the upload at <Bold>{{up}} Mbit/s</Bold>. It is best to orientate yourself on your internet contract and only adopt it if it matches that.",
"recommendations_info": "Based on the last 10 tests, it was found that the optimal ping was <Bold>{{ping}} ms</Bold>, the download at <Bold>{{down}} Mbps</Bold> and the upload at <Bold>{{up}} Mbps</Bold>. It is best to orientate yourself on your internet contract and only adopt it if it matches that.",
"update": "An update to version {{version}} is available. See <Changes>the changes</Changes> and <DLLink>download the update</DLLink>.",
"down": {
"title": "Download speed",
Expand Down Expand Up @@ -160,6 +161,7 @@
},
"test": {
"not_available": "There are currently no tests available",
"no_latest": "There is no current test available. Please perform a test or wait until the next test is performed.",
"unknown_error": "Unknown error:",
"failed": "Test failed",
"recheck": "Please check as far as possible if this happens often.",
Expand Down
13 changes: 10 additions & 3 deletions client/src/common/components/Dropdown/DropdownComponent.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ export const toggleDropdown = (setIcon) => {
function DropdownComponent() {
const [config, reloadConfig] = useContext(ConfigContext);
const [status, updateStatus] = useContext(StatusContext);
const findNode = useContext(NodeContext)[4];
const updateNodes = useContext(NodeContext)[1];
const currentNode = useContext(NodeContext)[2];
const updateTests = useContext(SpeedtestContext)[1];
const updateToast = useContext(ToastNotificationContext);
Expand Down Expand Up @@ -145,19 +147,24 @@ function DropdownComponent() {

const updatePassword = async () => {
toggleDropdown();
const passwordSet = currentNode !== 0 ? findNode(currentNode).password : localStorage.getItem("password") != null;

setDialog({
title: <>{t("update.new_password")} » <a onClick={updatePasswordLevel}>{t("update.level")}</a></>,
placeholder: t("update.password_placeholder"),
type: "password",
unsetButton: localStorage.getItem("password") != null ? "Sperre aufheben" : undefined,
unsetButton: passwordSet ? t("dialog.password.unlock") : undefined,
onClear: () => patchRequest("/config/password", {value: "none"})
.then(() => showFeedback("update.password_removed", false))
.then(() => localStorage.removeItem("password")),
.then(() => {
currentNode !== 0 ? baseRequest("/nodes/" + currentNode + "/password", "PATCH",
{password: "none"}).then(() => updateNodes()) : localStorage.removeItem("password");
}),
onSuccess: (value) => patchRequest("/config/password", {value})
.then(() => showFeedback(undefined, false))
.then(() => {
currentNode !== 0 ? baseRequest("/nodes/" + currentNode + "/password", "PATCH",
{password: value}) : localStorage.setItem("password", value);
{password: value}).then(() => updateNodes()) : localStorage.setItem("password", value);
})
})
}
Expand Down
5 changes: 2 additions & 3 deletions client/src/common/components/Header/HeaderComponent.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {SpeedtestDialog} from "@/common/components/SpeedtestDialog";
import {NodeContext} from "@/common/contexts/Node";

function HeaderComponent(props) {
const nodes = useContext(NodeContext)[0];
const findNode = useContext(NodeContext)[4];
const currentNode = useContext(NodeContext)[2];

const [setDialog] = useContext(InputDialogContext);
Expand Down Expand Up @@ -82,8 +82,7 @@ function HeaderComponent(props) {
if (!config.viewMode) updateVersion();
}, [config]);

const getNodeName = () =>
currentNode === "0" ? t("header.title") : nodes?.find(node => node.id === currentNode)?.name || t("header.title");
const getNodeName = () => currentNode === "0" ? t("header.title") : findNode(currentNode)?.name || t("header.title");

if (Object.keys(config).length === 0) return <></>;

Expand Down
4 changes: 3 additions & 1 deletion client/src/common/contexts/Node/NodeContext.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@ export const NodeProvider = (props) => {
setCurrentNode(parseInt(node));
}

const findNode = (nodeId) => nodes?.find(node => node.id === nodeId);

return (
<NodeContext.Provider value={[nodes, updateNodes, currentNode, updateCurrentNode]}>
<NodeContext.Provider value={[nodes, updateNodes, currentNode, updateCurrentNode, findNode]}>
{props.children}
</NodeContext.Provider>
)
Expand Down
5 changes: 4 additions & 1 deletion client/src/common/utils/RequestUtil.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@ const getHeaders = () => {

// Run a plain request with all default values using the base path
export const baseRequest = async (path, method = "GET", body = {}, headers = {}) => {
const controller = new AbortController();
setTimeout(() => controller.abort(), 10000);
return await fetch("/api" + path, {
headers: {...getHeaders(), ...headers}, method,
body: method !== "GET" ? JSON.stringify(body) : undefined
body: method !== "GET" ? JSON.stringify(body) : undefined,
signal: controller.signal
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {t} from "i18next";

function LatestTestComponent() {
const status = useContext(StatusContext)[0];
const [latest, setLatest] = useState({});
const [latest, setLatest] = useState(null);
const [latestTestTime, setLatestTestTime] = useState("N/A");
const [setDialog] = useContext(InputDialogContext);
const [speedtests] = useContext(SpeedtestContext);
Expand All @@ -30,6 +30,7 @@ function LatestTestComponent() {
}, [latest]);

if (Object.entries(config).length === 0) return (<></>);
if (latest === null) return (<></>);

return (
<div className={"analyse-area " + (status.paused ? "tests-paused" : "pulse")}>
Expand All @@ -38,7 +39,8 @@ function LatestTestComponent() {
<div className="container-header">
<FontAwesomeIcon onClick={() => setDialog(pingInfo())} icon={faPingPongPaddleBall}
className={"container-icon help-icon icon-" + getIconBySpeed(latest.ping, config.ping, false)}/>
<h2 className="container-text">{t("latest.ping")}<span className="container-subtext">{t("latest.ping_unit")}</span></h2>
<h2 className="container-text">{t("latest.ping")}<span
className="container-subtext">{t("latest.ping_unit")}</span></h2>
</div>
<div className="container-main">
<h2>{latest.ping === -1 ? "N/A" : latest.ping}</h2>
Expand All @@ -50,7 +52,8 @@ function LatestTestComponent() {
<div className="container-header">
<FontAwesomeIcon onClick={() => setDialog(downloadInfo())} icon={faArrowDown}
className={"container-icon help-icon icon-" + getIconBySpeed(latest.download, config.download, true)}/>
<h2 className="container-text">{t("latest.down")}<span className="container-subtext">{t("latest.speed_unit")}</span></h2>
<h2 className="container-text">{t("latest.down")}<span
className="container-subtext">{t("latest.speed_unit")}</span></h2>
</div>
<div className="container-main">
<h2>{latest.download === -1 ? "N/A" : latest.download}</h2>
Expand All @@ -64,7 +67,8 @@ function LatestTestComponent() {
<div className="container-header">
<FontAwesomeIcon onClick={() => setDialog(uploadInfo())} icon={faArrowUp}
className={"container-icon help-icon icon-" + getIconBySpeed(latest.upload, config.upload, true)}/>
<h2 className="container-text">{t("latest.up")}<span className="container-subtext">{t("latest.speed_unit")}</span></h2>
<h2 className="container-text">{t("latest.up")}<span
className="container-subtext">{t("latest.speed_unit")}</span></h2>
</div>
<div className="container-main">
<h2>{latest.upload === -1 ? "N/A" : latest.upload}</h2>
Expand All @@ -76,7 +80,8 @@ function LatestTestComponent() {
<div className="container-header">
<FontAwesomeIcon onClick={() => setDialog(latestTestInfo(latest))} icon={faClockRotateLeft}
className="container-icon icon-blue help-icon"/>
<h2 className="container-text">{t("latest.latest")}<span className="container-subtext">{t("latest.before")}</span></h2>
<h2 className="container-text">{t("latest.latest")}<span
className="container-subtext">{t("latest.before")}</span></h2>
</div>
<div className="container-main">
<h2>{latestTestTime}</h2>
Expand Down
4 changes: 2 additions & 2 deletions client/src/pages/Home/components/LatestTest/utils/dialogs.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ export const uploadInfo = () => ({title: t("info.up.title"), description: t("inf

export const latestTestInfo = (latest) => ({
title: t("info.latest.title"),
description: <Trans components={{Bold: <span className="dialog-value"/>}} values={{date: new Date(latest.created).toLocaleDateString(),
description: latest.created ? <Trans components={{Bold: <span className="dialog-value"/>}} values={{date: new Date(latest.created).toLocaleDateString(),
time: new Date(latest.created).toLocaleTimeString(undefined, {hour: "2-digit", minute: "2-digit"})}}>
info.latest.description</Trans>,
info.latest.description</Trans> : t("test.no_latest"),
buttonText: t("dialog.okay")
});
2 changes: 1 addition & 1 deletion client/src/pages/Nodes/Nodes.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const Nodes = (props) => {
{createDialogOpen && <CreateNodeDialog onClose={() => setCreateDialogOpen(false)}/>}
<NodeHeader/>
<div className="node-area">
<NodeContainer name={t("nodes.this_server")} url={location.href} currentNode={true}
<NodeContainer name={t("nodes.this_server")} url={location.host} currentNode={true}
setShowNodePage={props.setShowNodePage} id={0}/>

{nodes.map(node => <NodeContainer {...node} key={node.id} setShowNodePage={props.setShowNodePage} />)}
Expand Down
1 change: 1 addition & 0 deletions client/src/pages/Nodes/styles.sass
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
border: 2px dashed #696C73
border-radius: 15px
cursor: pointer
user-select: none

.node-add h1
font-size: 24pt
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "myspeed",
"version": "1.0.6",
"version": "1.0.7",
"scripts": {
"client": "cd client && npm run dev",
"server": "nodemon server",
Expand Down
38 changes: 36 additions & 2 deletions server/controller/node.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
const axios = require('axios');
const nodes = require('../models/Node');

// Gets all node entries
module.exports.list = async (excludePassword) => {
return await nodes.findAll({attributes: {exclude: excludePassword ? ['password'] : []}});
module.exports.list = async () => {
return await nodes.findAll().then((result) => result.map((node) => ({...node, password: node.password !== null})));
}

// Create a new node entry
Expand All @@ -28,4 +29,37 @@ module.exports.updateName = async (nodeId, name) => {
// Update the password of the node entry
module.exports.updatePassword = async (nodeId, password) => {
return await nodes.update({password: password}, {where: {id: nodeId}});
}

module.exports.checkNode = async (url, password) => {
if (password === "none") password = undefined;
const api = await axios.get(url + "/api/config", {headers: {password: password}}).catch(() => {
return "INVALID_URL";
});

if (api === "INVALID_URL" || api.status !== 200) return "INVALID_URL";

if (!api.data.ping) return "INVALID_URL";

if (api.data.viewMode) return "PASSWORD_REQUIRED";

return "NODE_VALID";
}

module.exports.proxyRequest = async (url, req, res) => {
const response = await axios(url, {
method: req.method,
headers: req.headers,
data: req.method === "GET" ? undefined : JSON.stringify(req.body),
signal: req.signal,
validateStatus: (status) => status >= 200 && status < 400
}).catch(() => "INVALID_URL");

if (response === "INVALID_URL")
return res.status(500).json({message: "Internal server error"});

if (response.headers["content-disposition"])
res.setHeader("content-disposition", response.headers["content-disposition"]);

res.status(response.status).json(response.data);
}
3 changes: 3 additions & 0 deletions server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ const timerTask = require('./tasks/timer');
const healthCheckTask = require('./tasks/healthchecks');

const app = express();

app.disable('x-powered-by');

const port = process.env.port || 5216;

// Create the data folder and the servers file
Expand Down
2 changes: 2 additions & 0 deletions server/routes/export.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ app.get("/json", password(false), async (req, res) => {
app.get("/csv", password(false), async (req, res) => {
res.set({"Content-Disposition": "attachment; filename=\"speedtests.csv\""});
let list = await tests.list();

if (list.length === 0) return res.send("");
let fields = Object.keys(list[0]);

let replacer = (key, value) => value === null ? '' : value;
Expand Down
Loading

0 comments on commit 95ec9d2

Please sign in to comment.