Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🗃️ Support für LibreSpeed & Cloudflare Speed #677

Merged
merged 39 commits into from
May 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
f6eb4e6
Added the cloudflare speedtest dependency to the package.json
gnmyt May 19, 2024
72dc105
Added binaries for LibreSpeed to the binaries.js config
gnmyt May 19, 2024
f6f879d
Added the serverId to the Speedtests.js model
gnmyt May 19, 2024
6643234
Migrated serverId -> ooklaId,libreId (depending on provider)
gnmyt May 19, 2024
ae7099f
Integrated providers into the speedtests.js route
gnmyt May 19, 2024
6a2b16f
Created the servers.js controller
gnmyt May 19, 2024
668b99d
Implemented the new servers.js controller into the system.js routes
gnmyt May 19, 2024
4280e27
Added new defaults to the config.js
gnmyt May 19, 2024
87c8bb0
Implemented the serverId into the speedtests.js controller
gnmyt May 19, 2024
e724f1d
Implemented the new provider system into the speedtest.js task
gnmyt May 19, 2024
32c8e05
Implemented the libre cli loader
gnmyt May 19, 2024
baabfeb
Moved the ookla cli loader
gnmyt May 19, 2024
7143965
Created a data parser for all 3 providers
gnmyt May 19, 2024
f3fa8d3
Added the data/servers folder to the createFolders.js util
gnmyt May 19, 2024
d86ef95
Implemented both the libre & ookla provider in the loadCli.js util
gnmyt May 19, 2024
3f00efa
The loadServers.js util now loads the nearest ookla & librespeed servers
gnmyt May 19, 2024
e13b3f0
Implemented librespeed into the speedtest.js util
gnmyt May 19, 2024
25cffa6
Updated the authentication message in the server index.js
gnmyt May 19, 2024
3883850
Removed the license check from the ConfigContext.jsx
gnmyt May 19, 2024
11d5776
Removed the acceptDialog from the dialog.jsx
gnmyt May 19, 2024
f68e725
Removed the accept translations from the en.json
gnmyt May 19, 2024
3a7076d
Removed unused parameter in the speedtest.js task
gnmyt May 19, 2024
3e709ac
Fixed a bug in the speedtest.js task
gnmyt May 19, 2024
de1cab9
Added NaN checks to the config.js route
gnmyt May 19, 2024
3992fec
Fixed a bug in the config.js controller
gnmyt May 19, 2024
826a101
Added extended error handling to the speedtest.js util
gnmyt May 19, 2024
ba2a5bd
Fixed a bug in the speedtest.js task
gnmyt May 19, 2024
e9fa2fe
Implemented support for "none" in the config.js route
gnmyt May 19, 2024
6ddf0a3
Updated the LanguageDialog styles.sass
gnmyt May 19, 2024
15676f1
Updated the cloudflare.webp asset
gnmyt May 19, 2024
15553f5
Updated the libre.webp asset
gnmyt May 19, 2024
a6b0d76
Updated the ookla.webp asset
gnmyt May 19, 2024
12af22f
Created the ProviderDialog.jsx
gnmyt May 19, 2024
6884b7a
Created the ProviderDialog index.js
gnmyt May 19, 2024
3ab6306
Created the ProviderDialog styles.sass
gnmyt May 19, 2024
4297103
Implemented the new ProviderDialog into the DropdownComponent.jsx
gnmyt May 19, 2024
2707994
Added new translations
gnmyt May 19, 2024
cb859e2
Updated the translation in the en.json
gnmyt May 19, 2024
fbb891b
Updated the ProviderDialog styles.sass
gnmyt May 19, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions client/public/assets/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,16 @@
"wrong": "The password you entered is incorrect",
"unlock": "Unlock"
},
"accept": {
"title": "We need your permission",
"description": "We use services from Ookla. By clicking <Bold>Accept</Bold>, you acknowledge that you have read and agree to Ookla's <EULA>EULA</EULA>, <Privacy>Privacy Statement</Privacy> and <Terms>Terms of Use</Terms>.",
"button": "Accept"
},
"api": {
"title": "API not reachable",
"description": "MySpeed could not reach the API of this instance. Please try again later."
},
"provider": {
"server": "Server",
"server_id": "Server ID",
"choose_automatically": "Choose automatically",
"ookla_license": "I have read and accept the <Eula>EULA</Eula>, <GDPR>privacy policy</GDPR> and <TOS>terms of service</TOS> of Ookla.",
"cloudflare_note": "Cloudflare does not require any additional settings"
}
},
"dropdown": {
Expand All @@ -35,7 +37,7 @@
"upload": "Optimal up-speed",
"download": "Optimal down-speed",
"recommendations": "Recommendations",
"server": "Change Server",
"change_provider": "Change provider",
"password": "Change password",
"cron": "Set frequency",
"time": "Set period",
Expand Down Expand Up @@ -80,10 +82,8 @@
"download_placeholder": "Down speed (Mbps)",
"recommendations_title": "Optimal recommendations",
"recommendations_set": "Set automatic recommendations?",
"server_title": "Set speedtest server",
"provider_title": "Set speedtest provider",
"manually": "Set manually",
"manual_server_title": "Set speedtest server",
"manual_server_id": "Server ID",
"new_password": "Set a new password",
"password_placeholder": "New password",
"password_removed": "The password lock has been removed and the set password has been removed.",
Expand Down
21 changes: 5 additions & 16 deletions client/src/common/components/Dropdown/DropdownComponent.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,9 @@ import {
faPause,
faPingPongPaddleBall,
faPlay,
faServer,
faWandMagicSparkles,
faCheck,
faExclamationTriangle
faExclamationTriangle, faSliders
} from "@fortawesome/free-solid-svg-icons";
import {ConfigContext} from "@/common/contexts/Config";
import {StatusContext} from "@/common/contexts/Status";
Expand All @@ -36,6 +35,7 @@ import {ToastNotificationContext} from "@/common/contexts/ToastNotification";
import {NodeContext} from "@/common/contexts/Node";
import {IntegrationDialog} from "@/common/components/IntegrationDialog";
import LanguageDialog from "@/common/components/LanguageDialog";
import ProviderDialog from "@/common/components/ProviderDialog";

let icon;

Expand Down Expand Up @@ -65,6 +65,7 @@ function DropdownComponent() {
const [showViewDialog, setShowViewDialog] = useState(false);
const [showIntegrationDialog, setShowIntegrationDialog] = useState(false);
const [showLanguageDialog, setShowLanguageDialog] = useState(false);
const [showProviderDialog, setShowProviderDialog] = useState(false);
const ref = useRef();

useEffect(() => {
Expand Down Expand Up @@ -130,19 +131,6 @@ function DropdownComponent() {
} else setDialog({title: t("update.recommendations_title"), description: t("info.recommendations_error"), buttonText: t("dialog.okay")});
}

const updateServer = () => patchDialog("serverId", async (value) => ({
title: t("update.server_title"),
select: true,
selectOptions: await jsonRequest("/info/server"),
unsetButton: t("update.manually"),
onClear: updateServerManually,
value
}));

const updateServerManually = () => patchDialog("serverId", (value) => ({
title: t("update.manual_server_title"), placeholder: t("update.manual_server_id"), type: "number", value: value,
}));

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

Expand Down Expand Up @@ -239,7 +227,7 @@ function DropdownComponent() {
{run: updateDownload, icon: faArrowDown, text: t("dropdown.download")},
{run: recommendedSettings, icon: faWandMagicSparkles, text: t("dropdown.recommendations")},
{hr: true, key: 1},
{run: updateServer, icon: faServer, text: t("dropdown.server")},
{run: () => setShowProviderDialog(true), icon: faSliders, text: t("dropdown.change_provider")},
{run: updatePassword, icon: faKey, text: t("dropdown.password"), previewHidden: true},
{run: updateCron, icon: faClock, text: t("dropdown.cron")},
{run: exportDialog, icon: faFileExport, text: t("dropdown.export")},
Expand All @@ -258,6 +246,7 @@ function DropdownComponent() {
{showViewDialog && <ViewDialog onClose={() => setShowViewDialog(false)}/>}
{showIntegrationDialog && <IntegrationDialog onClose={() => setShowIntegrationDialog(false)}/>}
{showLanguageDialog && <LanguageDialog onClose={() => setShowLanguageDialog(false)}/>}
{showProviderDialog && <ProviderDialog onClose={() => setShowProviderDialog(false)}/>}
<div className="dropdown dropdown-invisible" id="dropdown" ref={ref}>
<div className="dropdown-content">
<h2>{t("dropdown.settings")}</h2>
Expand Down
5 changes: 4 additions & 1 deletion client/src/common/components/LanguageDialog/styles.sass
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
border-radius: 0.5rem

&:hover
background-color: $light-gray
background-color: $darker-gray

img
width: 2rem
Expand All @@ -38,6 +38,9 @@
background-color: $light-gray
color: $white

&:hover
background-color: $light-gray

@media screen and (max-height: 425px)
.language-chooser-dialog
height: 15rem
Expand Down
138 changes: 138 additions & 0 deletions client/src/common/components/ProviderDialog/ProviderDialog.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import {DialogContext, DialogProvider} from "@/common/contexts/Dialog";
import {t} from "i18next";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {faClose} from "@fortawesome/free-solid-svg-icons";
import "./styles.sass";
import React, {useContext, useEffect, useState} from "react";
import OoklaImage from "./assets/img/ookla.webp";
import LibreImage from "./assets/img/libre.webp";
import CloudflareImage from "./assets/img/cloudflare.webp";
import {jsonRequest, patchRequest} from "@/common/utils/RequestUtil";
import {Trans} from "react-i18next";
import {ConfigContext} from "@/common/contexts/Config";

const providers = [
{id: "ookla", name: "Ookla", image: OoklaImage},
{id: "libre", name: "LibreSpeed", image: LibreImage},
{id: "cloudflare", name: "Cloudflare", image: CloudflareImage}
]


export const Dialog = () => {
const close = useContext(DialogContext);
const [config, reloadConfig] = useContext(ConfigContext);
const [provider, setProvider] = useState(config.provider || "ookla");

const [licenseAccepted, setLicenseAccepted] = useState(false);
const [licenseError, setLicenseError] = useState(false);

const [ooklaServers, setOoklaServers] = useState({});
const [libreServers, setLibreServers] = useState({});

const [serverId, setServerId] = useState("none");

useEffect(() => {
jsonRequest("/info/server/ookla").then((response) => {
setOoklaServers(response);
});
jsonRequest("/info/server/libre").then((response) => {
setLibreServers(response);
});
}, []);

useEffect(() => {
if (config[provider + "Id"]) setServerId(config[provider + "Id"]);
}, [provider]);

useEffect(() => {
if (serverId === "") setServerId("none");
}, [serverId]);

const update = async () => {
if (provider === "ookla" && !licenseAccepted) {
setLicenseError(true);
return;
}

await patchRequest("/config/provider", {value: provider});

if (serverId !== config[provider + "Id"] && provider !== "cloudflare") {
await patchRequest("/config/" + provider + "Id", {value: serverId});
}

reloadConfig();

close();
}

return (
<>
<div className="dialog-header">
<h4 className="dialog-text">{t("update.provider_title")}</h4>
<FontAwesomeIcon icon={faClose} className="dialog-text dialog-icon" onClick={() => close()}/>
</div>
<div className="provider-dialog-content">
<div className="provider-header">
{providers.map((current, index) => (
<div className={`provider-item ${current.id === provider ? "provider-item-active" : ""}`}
key={index} onClick={() => setProvider(current.id)}>
<img src={current.image} alt={current.name}/>
<h3>{current.name}</h3>
</div>
))}
</div>
{provider !== "cloudflare" && <div className="provider-content">
<div className="provider-setting">
<h3>{t("dialog.provider.server")}</h3>
<select className="dialog-input provider-input" value={serverId}
onChange={(e) => setServerId(e.target.value)}>
<option value="none">{t("dialog.provider.choose_automatically")}</option>
{provider === "ookla" && Object.keys(ooklaServers).map((current, index) => (
<option key={index} value={current}>{ooklaServers[current]}</option>
))}
{provider === "libre" && Object.keys(libreServers).map((current, index) => (
<option key={index} value={current}>{libreServers[current]}</option>
))}
</select>
</div>
<div className="provider-setting">
<h3>{t("dialog.provider.server_id")}</h3>
<input type="text" className="dialog-input provider-input" value={serverId === "none" ? "" : serverId}
onChange={(e) => setServerId(e.target.value)}/>
</div>
</div>}
{provider === "cloudflare" && <div className="provider-content">
<p className="cloudflare-provider-info">{t("dialog.provider.cloudflare_note")}</p>
</div>}
</div>
<div className="provider-dialog-footer">
<div className="provider-license-box">
{provider === "ookla" && <>
<input type="checkbox" className={licenseError ? "cb-error" : ""} id="license" name="license"
onChange={(e) => setLicenseAccepted(e.target.checked)}/>
<label htmlFor="license"
><Trans components={{
Eula: <a href="https://www.speedtest.net/about/eula" target="_blank"
rel="noreferrer" />,
GDPR: <a href="https://www.speedtest.net/about/privacy" target="_blank"
rel="noreferrer" />,
TOS: <a href="https://www.speedtest.net/about/terms" target="_blank"
rel="noreferrer" />}}>dialog.provider.ookla_license</Trans></label>
</>}
</div>

<button className="dialog-btn" onClick={update}>{t("dialog.update")}</button>
</div>
</>
)
}

export const ProviderDialog = (props) => {
return (
<>
<DialogProvider close={props.onClose}>
<Dialog/>
</DialogProvider>
</>
)
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
1 change: 1 addition & 0 deletions client/src/common/components/ProviderDialog/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {ProviderDialog as default} from "./ProviderDialog";
95 changes: 95 additions & 0 deletions client/src/common/components/ProviderDialog/styles.sass
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
@import "@/common/styles/colors"

.provider-dialog-content
display: flex
margin: 1rem 0.5rem
user-select: none
flex-direction: column

.provider-header
display: flex
gap: 1rem

.provider-item
display: flex
align-items: center
padding: 0.3rem 0.5rem
gap: 0.5rem
border-radius: 0.8rem
border: 2px solid $light-gray
color: $darker-white
cursor: pointer

img
width: 2.5rem
height: 2.5rem

h3
margin: 0

&:hover
background-color: $darker-gray

.provider-item-active
background-color: $light-gray

&:hover
background-color: $light-gray

.provider-content
display: flex
flex-direction: column
margin-top: 1rem


.provider-setting
display: flex
gap: 1rem
align-items: center
justify-content: space-between

.provider-input
width: 20rem
box-sizing: border-box
margin-top: 0.5rem
margin-bottom: 0.5rem
font-size: 1.3rem

h3
color: $darker-white

.cloudflare-provider-info
color: $subtext
text-align: center

.provider-dialog-footer
display: flex
align-items: center
justify-content: space-between

.provider-license-box
display: flex
align-items: center
gap: 0.5rem

input
border: 2px solid $light-gray

.cb-error
border-color: $red

label
color: $subtext
max-width: 16rem
flex: 1


@media screen and (max-width: 610px)
.provider-dialog-content
.provider-header
flex-direction: column

@media screen and (max-width: 520px)
.provider-dialog-content
.provider-setting .provider-input
width: 60%
10 changes: 1 addition & 9 deletions client/src/common/contexts/Config/ConfigContext.jsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import React, {createContext, useContext, useEffect, useState} from "react";
import {InputDialogContext} from "../InputDialog";
import {request} from "@/common/utils/RequestUtil";
import {acceptDialog, apiErrorDialog, passwordRequiredDialog} from "@/common/contexts/Config/dialog";
import {apiErrorDialog, passwordRequiredDialog} from "@/common/contexts/Config/dialog";

export const ConfigContext = createContext({});

export const ConfigProvider = (props) => {
const [config, setConfig] = useState({});
const [setDialog] = useContext(InputDialogContext);
const [dialogShown, setDialogShown] = useState(false);

const reloadConfig = () => {
request("/config").then(async res => {
Expand All @@ -32,13 +31,6 @@ export const ConfigProvider = (props) => {

const checkConfig = async () => (await request("/config")).json();

useEffect(() => {
if (config.acceptOoklaLicense !== undefined && config.acceptOoklaLicense === "false" && !dialogShown) {
setDialogShown(true);
setDialog(acceptDialog());
}
}, [config]);

useEffect(reloadConfig, []);

return (
Expand Down
Loading