mirror of
https://github.com/gnmyt/myspeed.git
synced 2026-02-09 23:18:37 -06:00
Merge pull request #1347 from gnmyt/features/custom-librespeed-server
⚙️ Custom Librespeed server
This commit is contained in:
@@ -37,6 +37,8 @@
|
||||
"interface": "Schnittstelle",
|
||||
"server": "Server",
|
||||
"server_id": "Server-ID",
|
||||
"custom_url": "Benutzerdefinierte Server-URL",
|
||||
"custom_url_placeholder": "https://speed.test/backend/",
|
||||
"choose_automatically": "Automatisch wählen",
|
||||
"ookla_license": "Ich habe die <Eula>EULA</Eula>, die <GDPR>Datenschutzrichtlinie</GDPR> und die <TOS>Nutzungsbedingungen</TOS> von Ookla gelesen und akzeptiere sie."
|
||||
}
|
||||
|
||||
@@ -39,6 +39,8 @@
|
||||
"interface": "Interface",
|
||||
"server": "Server",
|
||||
"server_id": "Server ID",
|
||||
"custom_url": "Custom Server URL",
|
||||
"custom_url_placeholder": "https://speed.test/backend/",
|
||||
"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.",
|
||||
"ookla_notice": "By using Ookla, you agree to their <Eula>EULA</Eula>, <GDPR>privacy policy</GDPR> and <TOS>terms of service</TOS>.",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {Dialog, DialogHeader, DialogBody, DialogFooter} from "@/common/contexts/Dialog";
|
||||
import {t} from "i18next";
|
||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||
import {faCheck, faServer, faNetworkWired} from "@fortawesome/free-solid-svg-icons";
|
||||
import {faCheck, faServer, faNetworkWired, faLink} from "@fortawesome/free-solid-svg-icons";
|
||||
import "./styles.sass";
|
||||
import React, {useContext, useEffect, useState} from "react";
|
||||
import OoklaImage from "./assets/img/ookla.webp";
|
||||
@@ -27,6 +27,7 @@ export const ProviderDialog = ({open, onClose}) => {
|
||||
const [ooklaServers, setOoklaServers] = useState({});
|
||||
const [libreServers, setLibreServers] = useState({});
|
||||
const [serverId, setServerId] = useState("none");
|
||||
const [libreUrl, setLibreUrl] = useState(config.libreUrl || "none");
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) return;
|
||||
@@ -37,17 +38,35 @@ export const ProviderDialog = ({open, onClose}) => {
|
||||
|
||||
useEffect(() => {
|
||||
if (config[provider + "Id"]) setServerId(config[provider + "Id"]);
|
||||
if (config.libreUrl) setLibreUrl(config.libreUrl);
|
||||
}, [provider, config]);
|
||||
|
||||
useEffect(() => {
|
||||
if (serverId === "") setServerId("none");
|
||||
}, [serverId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (libreUrl === "") setLibreUrl("none");
|
||||
}, [libreUrl]);
|
||||
|
||||
const handleLibreUrlChange = (value) => {
|
||||
setLibreUrl(value);
|
||||
if (value && value !== "none") setServerId("none");
|
||||
};
|
||||
|
||||
const handleServerIdChange = (value) => {
|
||||
setServerId(value);
|
||||
if (provider === "libre" && value && value !== "none") setLibreUrl("none");
|
||||
};
|
||||
|
||||
const update = async (close) => {
|
||||
await patchRequest("/config/provider", {value: provider});
|
||||
if (serverId !== config[provider + "Id"] && provider !== "cloudflare") {
|
||||
await patchRequest("/config/" + provider + "Id", {value: serverId});
|
||||
}
|
||||
if (provider === "libre" && libreUrl !== config.libreUrl) {
|
||||
await patchRequest("/config/libreUrl", {value: libreUrl});
|
||||
}
|
||||
if (currentInterface !== config.interface) {
|
||||
await patchRequest("/config/interface", {value: currentInterface});
|
||||
}
|
||||
@@ -56,8 +75,10 @@ export const ProviderDialog = ({open, onClose}) => {
|
||||
close();
|
||||
};
|
||||
|
||||
const isUsingCustomUrl = provider === "libre" && libreUrl && libreUrl !== "none";
|
||||
|
||||
return (
|
||||
<Dialog open={open} onClose={onClose} className="provider-dialog">
|
||||
<Dialog open={open} onClose={onClose} className="provider-dialog-wrapper">
|
||||
{({close}) => (
|
||||
<>
|
||||
<DialogHeader onClose={close}>{t("update.provider_title")}</DialogHeader>
|
||||
@@ -87,14 +108,14 @@ export const ProviderDialog = ({open, onClose}) => {
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{provider !== "cloudflare" && (
|
||||
{provider !== "cloudflare" && !isUsingCustomUrl && (
|
||||
<div className="provider-setting">
|
||||
<div className="provider-setting-label">
|
||||
<FontAwesomeIcon icon={faServer}/>
|
||||
<h3>{t("dialog.provider.server")}</h3>
|
||||
</div>
|
||||
<select className="dialog-input provider-input" value={serverId}
|
||||
onChange={(e) => setServerId(e.target.value)}>
|
||||
onChange={(e) => handleServerIdChange(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>
|
||||
@@ -106,14 +127,27 @@ export const ProviderDialog = ({open, onClose}) => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{provider !== "cloudflare" && serverId !== "none" && (
|
||||
{provider !== "cloudflare" && serverId !== "none" && !isUsingCustomUrl && (
|
||||
<div className="provider-setting">
|
||||
<div className="provider-setting-label">
|
||||
<h3>{t("dialog.provider.server_id")}</h3>
|
||||
</div>
|
||||
<input type="text" className="dialog-input provider-input"
|
||||
value={serverId === "none" ? "" : serverId}
|
||||
onChange={(e) => setServerId(e.target.value)}/>
|
||||
onChange={(e) => handleServerIdChange(e.target.value)}/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{provider === "libre" && (
|
||||
<div className="provider-setting">
|
||||
<div className="provider-setting-label">
|
||||
<FontAwesomeIcon icon={faLink}/>
|
||||
<h3>{t("dialog.provider.custom_url")}</h3>
|
||||
</div>
|
||||
<input type="text" className="dialog-input provider-input"
|
||||
placeholder={t("dialog.provider.custom_url_placeholder")}
|
||||
value={libreUrl === "none" ? "" : libreUrl}
|
||||
onChange={(e) => handleLibreUrlChange(e.target.value || "none")}/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@use "@/common/styles/colors" as *
|
||||
|
||||
.provider-dialog
|
||||
.provider-dialog-wrapper
|
||||
width: 28rem
|
||||
max-width: 95vw
|
||||
|
||||
@@ -13,12 +13,14 @@
|
||||
|
||||
.provider-list
|
||||
display: flex
|
||||
flex-wrap: wrap
|
||||
gap: 0.5rem
|
||||
margin-bottom: 1rem
|
||||
|
||||
.provider-item
|
||||
display: flex
|
||||
flex: 1
|
||||
flex: 1 1 auto
|
||||
min-width: 7rem
|
||||
align-items: center
|
||||
justify-content: center
|
||||
gap: 0.5rem
|
||||
|
||||
@@ -20,6 +20,7 @@ const configDefaults = {
|
||||
provider: "none",
|
||||
ooklaId: "none",
|
||||
libreId: "none",
|
||||
libreUrl: "none",
|
||||
password: "none",
|
||||
passwordLevel: "none",
|
||||
interface: "none"
|
||||
@@ -89,6 +90,14 @@ module.exports.validateInput = async (key, value) => {
|
||||
if ((key === "ooklaId" || key === "libreId") && (/[^0-9]/.test(value) && value !== "none"))
|
||||
return "You need to provide a number in order to change this";
|
||||
|
||||
if (key === "libreUrl" && value !== "none") {
|
||||
try {
|
||||
new URL(value);
|
||||
} catch (e) {
|
||||
return "You need to provide a valid URL";
|
||||
}
|
||||
}
|
||||
|
||||
if (key === "passwordLevel" && !["none", "read"].includes(value))
|
||||
return "You need to provide either none or read-access";
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ const password = require('../middlewares/password');
|
||||
app.get("/", password(true), async (req, res) => {
|
||||
let configValues = {};
|
||||
(await config.listAll()).forEach(row => {
|
||||
if (row.key !== "password" && !(req.viewMode && ["ooklaId", "libreId", "cron", "passwordLevel"].includes(row.key)))
|
||||
if (row.key !== "password" && !(req.viewMode && ["ooklaId", "libreId", "libreUrl", "cron", "passwordLevel"].includes(row.key)))
|
||||
configValues[row.key] = row.value;
|
||||
});
|
||||
configValues['viewMode'] = req.viewMode;
|
||||
|
||||
@@ -43,24 +43,31 @@ module.exports.run = async (retryAuto = false) => {
|
||||
}
|
||||
|
||||
let serverId = mode === "cloudflare" ? 0 : await config.getValue(mode + "Id");
|
||||
let serverUrl = mode === "libre" ? await config.getValue("libreUrl") : undefined;
|
||||
|
||||
if (serverId === "none")
|
||||
serverId = undefined;
|
||||
|
||||
if (serverUrl === "none")
|
||||
serverUrl = undefined;
|
||||
|
||||
let speedtest = await (retryAuto ? speedTest(mode) : speedTest(mode, serverId));
|
||||
if (mode === "libre" && serverUrl)
|
||||
serverId = undefined;
|
||||
|
||||
let speedtest = await (retryAuto ? speedTest(mode) : speedTest(mode, serverId, serverUrl));
|
||||
|
||||
if (mode === "ookla" && speedtest.server) {
|
||||
if (serverId === undefined) await config.updateValue("ooklaId", speedtest.server?.id);
|
||||
serverId = speedtest.server?.id;
|
||||
}
|
||||
|
||||
if (mode === "libre" && speedtest.server) {
|
||||
let server = Object.entries(serverController.getLibreServers())
|
||||
.filter(([, value]) => value === speedtest.server.name)[0][0];
|
||||
if (mode === "libre" && speedtest.server && !serverUrl) {
|
||||
let serverEntry = Object.entries(serverController.getLibreServers())
|
||||
.filter(([, value]) => value === speedtest.server.name)[0];
|
||||
|
||||
if (server) {
|
||||
if (serverId === undefined) await config.updateValue("libreId", server);
|
||||
serverId = parseInt(server);
|
||||
if (serverEntry) {
|
||||
if (serverId === undefined) await config.updateValue("libreId", serverEntry[0]);
|
||||
serverId = parseInt(serverEntry[0]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
const {spawn} = require('child_process');
|
||||
const interfaces = require('../util/loadInterfaces');
|
||||
const config = require('../controller/config');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
module.exports = async (mode, serverId) => {
|
||||
module.exports = async (mode, serverId, serverUrl) => {
|
||||
const binaryPath = mode === "ookla" ? './bin/speedtest' + (process.platform === "win32" ? ".exe" : "")
|
||||
: mode === "libre" ? './bin/librespeed-cli' + (process.platform === "win32" ? ".exe" : "")
|
||||
: './bin/cfspeedtest' + (process.platform === "win32" ? ".exe" : "");
|
||||
@@ -27,7 +29,23 @@ module.exports = async (mode, serverId) => {
|
||||
if (serverId) args.push(`--server-id=${serverId}`);
|
||||
} else if (mode === "libre") {
|
||||
args = ['--json', '--duration=5', '--source=' + interfaceIp];
|
||||
if (serverId) args.push(`--server=${serverId}`);
|
||||
if (serverUrl) {
|
||||
const customServerConfig = [{
|
||||
id: 1,
|
||||
name: "Custom Server",
|
||||
server: serverUrl,
|
||||
dlURL: "garbage.php",
|
||||
ulURL: "empty.php",
|
||||
pingURL: "empty.php",
|
||||
getIpURL: "getIP.php"
|
||||
}];
|
||||
const tempJsonPath = path.join('data', 'servers', 'libre_custom.json');
|
||||
fs.writeFileSync(tempJsonPath, JSON.stringify(customServerConfig));
|
||||
args.push(`--local-json=${tempJsonPath}`);
|
||||
args.push('--server=1');
|
||||
} else if (serverId) {
|
||||
args.push(`--server=${serverId}`);
|
||||
}
|
||||
} else if (mode === "cloudflare") {
|
||||
args = ['--output-format=json'];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user