From 974527c4c3491b341b5b5534acf46eeeecec2aa2 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Sat, 20 Jan 2024 08:48:19 -0500 Subject: [PATCH 001/137] update uninstall and save tz in db --- index.php | 2 +- scripts/pi-hole/js/speedtest.js | 18 ++-- scripts/pi-hole/speedtest/speedtest.sh | 8 +- scripts/pi-hole/speedtest/uninstall.sh | 111 +++++++++++++------------ 4 files changed, 71 insertions(+), 68 deletions(-) diff --git a/index.php b/index.php index 39ef7dcda..4773eef11 100644 --- a/index.php +++ b/index.php @@ -119,7 +119,7 @@

Speedtest results over last

-
+
>
diff --git a/scripts/pi-hole/js/speedtest.js b/scripts/pi-hole/js/speedtest.js index d798b770b..641dea93c 100644 --- a/scripts/pi-hole/js/speedtest.js +++ b/scripts/pi-hole/js/speedtest.js @@ -1,4 +1,4 @@ -/* global Chart:false, moment:false, utils:false */ +/* global Chart:false, moment:false */ $(function () { var speedlabels = []; @@ -17,7 +17,8 @@ $(function () { if (last.diff(first, "hours") >= 24) format = "Do " + format; } - return moment(itemdate).format(format); + // always return the time in the browser's timezone + return moment(itemdate).utcOffset(moment().utcOffset()).format(format); } $.ajax({ @@ -97,18 +98,13 @@ $(function () { }, }, tooltip: { - mode: "index", + mode: 'index', intersect: utils.getGraphType(1) === "bar", - yAlign: "bottom", + yAlign: 'bottom', callbacks: { label: function (context) { - const varParsed = - context.parsed !== "undefined" && context.parsed !== "undefined" - ? context.parsed.y - : null; - const varLabel = context.dataset !== "undefined" ? context.dataset.label : null; - return Math.round(varParsed) + " " + varLabel; - }, + return Math.round(context?.parsed?.y) + " " + context?.dataset?.label || null; + } }, }, }, diff --git a/scripts/pi-hole/speedtest/speedtest.sh b/scripts/pi-hole/speedtest/speedtest.sh index 65601b7e0..9e7ecbc2c 100755 --- a/scripts/pi-hole/speedtest/speedtest.sh +++ b/scripts/pi-hole/speedtest/speedtest.sh @@ -2,7 +2,7 @@ FILE=/tmp/speedtest.log readonly setupVars="/etc/pihole/setupVars.conf" serverid=$(grep 'SPEEDTEST_SERVER' ${setupVars} | cut -d '=' -f2) -start=$(date +"%Y-%m-%d %H:%M:%S") +start=$(date +"%Y-%m-%d %H:%M:%S %Z") speedtest() { if grep -q official <<< "$(/usr/bin/speedtest --version)"; then @@ -54,15 +54,15 @@ internet() { } nointernet(){ - stop=$(date +"%Y-%m-%d %H:%M:%S") + stop=$(date +"%Y-%m-%d %H:%M:%S %Z") echo "No Internet" sqlite3 /etc/pihole/speedtest.db "insert into speedtest values (NULL, '${start}', '${stop}', 'No Internet', '-', '-', 0, 0, 0, 0, '#');" exit 1 } tryagain(){ - start=$(date +"%Y-%m-%d %H:%M:%S") - speedtest > $FILE && internet || nointernet + start=$(date +"%Y-%m-%d %H:%M:%S %Z") + speedtest > "$FILE" && internet || nointernet } main() { diff --git a/scripts/pi-hole/speedtest/uninstall.sh b/scripts/pi-hole/speedtest/uninstall.sh index 5e2c62dfd..bb1a08d40 100644 --- a/scripts/pi-hole/speedtest/uninstall.sh +++ b/scripts/pi-hole/speedtest/uninstall.sh @@ -1,55 +1,62 @@ #!/bin/bash -echo "$(date) - Restoring Pi-hole..." - -cd /opt/ -if [ ! -f /opt/pihole/webpage.sh.org ]; then - rm -rf org_pihole - git clone https://github.com/pi-hole/pi-hole org_pihole - cd org_pihole - git fetch --tags -q - localVer=$(pihole -v | grep "Pi-hole" | cut -d ' ' -f 6) - remoteVer=$(curl -s https://api.github.com/repos/pi-hole/pi-hole/releases/latest | grep "tag_name" | cut -d '"' -f 4) - if [[ "$localVer" < "$remoteVer" && "$localVer" == *.* ]]; then - remoteVer=$localVer - fi - git checkout -q $remoteVer - cp advanced/Scripts/webpage.sh ../pihole/webpage.sh.org - cd - > /dev/null - rm -rf org_pihole -fi - -cd /var/www/html -if [ ! -d /var/www/html/org_admin ]; then - rm -rf org_admin - git clone https://github.com/pi-hole/AdminLTE org_admin - cd org_admin - git fetch --tags -q - localVer=$(pihole -v | grep "AdminLTE" | cut -d ' ' -f 6) - remoteVer=$(curl -s https://api.github.com/repos/pi-hole/AdminLTE/releases/latest | grep "tag_name" | cut -d '"' -f 4) - if [[ "$localVer" < "$remoteVer" && "$localVer" == *.* ]]; then - remoteVer=$localVer - fi - git checkout -q $remoteVer - cd - > /dev/null -fi - -if [ "${1-}" == "db" ] && [ -f /etc/pihole/speedtest.db ]; then - mv /etc/pihole/speedtest.db /etc/pihole/speedtest.db.old - echo "$(date) - Configured Database..." -fi - -echo "$(date) - Uninstalling Current Speedtest Mod..." - -if [ -d /var/www/html/admin ]; then - rm -rf mod_admin - mv admin mod_admin -fi -mv org_admin admin -cd /opt/pihole/ -cp webpage.sh webpage.sh.mod -mv webpage.sh.org webpage.sh -chmod +x webpage.sh - -echo "$(date) - Uninstall Complete" +clone() { + local path=$1 + local dest=$2 + local src=$3 + local name=${4-} # if set, will keep local tag if older than latest + + cd "$path" + rm -rf "$dest" + git clone --depth=1 "$src" "$dest" + cd "$dest" + git fetch --tags -q + local latestTag=$(git describe --tags $(git rev-list --tags --max-count=1)) + if [ ! -z "$name" ]; then + local localTag=$(pihole -v | grep "$name" | cut -d ' ' -f 6) + [ "$localTag" == "HEAD" ] && localTag=$(pihole -v | grep "$name" | cut -d ' ' -f 7) + if [[ "$localTag" == *.* ]] && [[ "$localTag" < "$latestTag" ]]; then + latestTag=$localTag + git fetch --unshallow + fi + fi + git -c advice.detachedHead=false checkout $latestTag +} + +uninstall() { + if cat /opt/pihole/webpage.sh | grep -q SpeedTest; then + echo "$(date) - Uninstalling Current Speedtest Mod..." + + if [ ! -f /opt/pihole/webpage.sh.org ]; then + clone /opt org_pihole https://github.com/pi-hole/pi-hole Pi-hole + cp advanced/Scripts/webpage.sh ../pihole/webpage.sh.org + cd .. + rm -rf org_pihole + fi + + if [ ! -d /var/www/html/org_admin ]; then + clone /var/www/html org_admin https://github.com/pi-hole/AdminLTE web + fi + + cd /var/www/html + if [ -d /var/www/html/admin ]; then + rm -rf mod_admin + mv admin mod_admin + fi + mv org_admin admin + cd /opt/pihole/ + cp webpage.sh webpage.sh.mod + mv webpage.sh.org webpage.sh + chmod +x webpage.sh + fi + + if [ "${1-}" == "db" ] && [ -f /etc/pihole/speedtest.db ]; then + echo "$(date) - Flushing Database..." + mv /etc/pihole/speedtest.db /etc/pihole/speedtest.db.old + fi + + echo "$(date) - Speedtest Mod is Uninstalled!" +} + +uninstall ${1-} exit 0 From fba8bccf133a8f9097b9367542e39f7cdd12b04d Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Sun, 21 Jan 2024 00:44:24 -0500 Subject: [PATCH 002/137] dynamic db button and colorful confirm --- scripts/pi-hole/js/settings.js | 81 +++++++++++++++++++++----- scripts/pi-hole/php/savesettings.php | 8 +-- scripts/pi-hole/speedtest/uninstall.sh | 11 +++- settings.php | 2 +- 4 files changed, 81 insertions(+), 21 deletions(-) diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index eb2f204bb..fbf8e76ba 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -474,15 +474,16 @@ $(function () { $(function () { const speedtestChartType = $("#speedtestcharttype"); const speedtestChartTypeSave = $("#speedtestcharttypesave"); - let type = localStorage?.getItem("speedtest_chart_type") || speedtestChartType.attr("value"); - - speedtestChartType.prop("checked", type === "bar"); - localStorage.setItem("speedtest_chart_type", type); - const speedtestUpdate = $("#speedtestupdate"); const speedtestUninstall = $("#speedtestuninstall"); const speedtestDelete = $("#speedtestdelete"); const speedtestTest = $("#speedtesttest"); + const speedtestServer = $("#speedtestserver"); + const speedtestSubmit = $("#st-submit"); + + let type = localStorage?.getItem("speedtest_chart_type") || speedtestChartType.attr("value"); + speedtestChartType.prop("checked", type === "bar"); + localStorage.setItem("speedtest_chart_type", type); document.addEventListener("DOMContentLoaded", function () { speedtestChartTypeSave.attr("value", null); @@ -512,21 +513,75 @@ $(function () { speedtestUninstall.on("click", function () { speedtestUninstall.attr("value", speedtestUninstall.attr("value") ? null : "un"); - }); - - speedtestDelete.on("click", function () { - speedtestDelete.attr("value", speedtestDelete.attr("value") ? null : "db"); - $("#st-submit").toggleClass("btn-primary"); - $("#st-submit").toggleClass("btn-danger"); + if (speedtestUninstall.attr("value")) { + if (!speedtestSubmit.hasClass("btn-warning")) { + speedtestSubmit.addClass("btn-warning"); + } + } else { + speedtestSubmit.removeClass("btn-warning"); + } }); speedtestTest.on("click", function () { speedtestTest.attr("value", speedtestTest.attr("value") ? null : "yes"); }); - const speedtestServer = $("#speedtestserver"); - speedtestServer.on("change", function () { speedtestServer.attr("value", speedtestServer.val()); }); + + canRestore = () => { + return new Promise((resolve, reject) => { + $.ajax({ + url: "api.php?getAllSpeedTestData&PHP", + dataType: "json", + }).done(function (results) { + resolve(localStorage.getItem("speedtest_flushed") === "true" && results?.data?.length === 0); + }).fail(function (error) { + resolve(false); + }); + }); + }; + + speedtestDelete.on("click", function () { + speedtestDelete.attr("value", speedtestDelete.attr("value") ? null : "db"); + if (speedtestDelete.attr("value")) { + canRestore().then(didFlush => { + if (didFlush) { + if (!speedtestSubmit.hasClass("btn-success")) { + speedtestSubmit.addClass("btn-success"); + } + } else { + if (!speedtestSubmit.hasClass("btn-danger")) { + speedtestSubmit.addClass("btn-danger"); + } + } + }).catch(error => { + console.error("Error in canRestore:", error); + }); + } else { + speedtestSubmit.removeClass("btn-danger"); + speedtestSubmit.removeClass("btn-success"); + } + }); + + $("#speedtestform").on("submit", function (e) { + e.preventDefault(); // prevent the form from submitting immediately + if (speedtestDelete.attr("value")) { + canRestore().then(didFlush => { + localStorage.setItem("speedtest_flushed", !didFlush); + this.submit(); // submit the form after the localStorage is set + }).catch(error => { + console.error("Error in canRestore:", error); + }); + } + }); + + setInterval(() => { + canRestore().then(didFlush => { + speedtestDelete.parent().children("label").text(didFlush ? "Restore History (Available until the next speedtest)" : "Clear History"); + }).catch(error => { + console.error("Error in canRestore:", error); + }); + }, 1000); }); diff --git a/scripts/pi-hole/php/savesettings.php b/scripts/pi-hole/php/savesettings.php index f223c0953..adec0ac17 100644 --- a/scripts/pi-hole/php/savesettings.php +++ b/scripts/pi-hole/php/savesettings.php @@ -630,14 +630,14 @@ function addStaticDHCPLease($mac, $ip, $hostname) if (isset($_POST['speedtestuninstall'])) { $success .= ', but the Mod will be uninstalled'; if (isset($_POST['speedtestdelete'])) { - $success .= ' and the database will be deleted'; + $success .= ' and its history will be deleted'; pihole_execute('-a -up un db', true); } else { pihole_execute('-a -up un', true); } } else { if (isset($_POST['speedtestdelete'])) { - $success .= ' and the database will be flushed'; + $success .= ' and its history will be modified'; pihole_execute('-a -up db', true); } else { pihole_execute('-a -up', true); @@ -646,13 +646,13 @@ function addStaticDHCPLease($mac, $ip, $hostname) } elseif (isset($_POST['speedtestuninstall'])) { $success .= ' and the Mod will be uninstalled'; if (isset($_POST['speedtestdelete'])) { - $success .= ' and the database will be deleted'; + $success .= ' and its history will be deleted'; pihole_execute('-a -un db', true); } else { pihole_execute('-a -un', true); } } elseif (isset($_POST['speedtestdelete'])) { - $success .= ' and the database will be flushed'; + $success .= ' and its history will be modified'; pihole_execute('-a -db', true); } break; diff --git a/scripts/pi-hole/speedtest/uninstall.sh b/scripts/pi-hole/speedtest/uninstall.sh index bb1a08d40..25721aadc 100644 --- a/scripts/pi-hole/speedtest/uninstall.sh +++ b/scripts/pi-hole/speedtest/uninstall.sh @@ -50,9 +50,14 @@ uninstall() { chmod +x webpage.sh fi - if [ "${1-}" == "db" ] && [ -f /etc/pihole/speedtest.db ]; then - echo "$(date) - Flushing Database..." - mv /etc/pihole/speedtest.db /etc/pihole/speedtest.db.old + if [ "${1-}" == "db" ]; then + if [ -f /etc/pihole/speedtest.db ] && [ "$(hashFile /etc/pihole/speedtest.db)" != "$(hashFile /var/www/html/admin/scripts/pi-hole/speedtest/speedtest.db)" ]; then + echo "$(date) - Flushing Database..." + mv -f /etc/pihole/speedtest.db /etc/pihole/speedtest.db.old + elif [ -f /etc/pihole/speedtest.db.old ]; then + echo "$(date) - Restoring Database..." + mv -f /etc/pihole/speedtest.db.old /etc/pihole/speedtest.db + fi fi echo "$(date) - Speedtest Mod is Uninstalled!" diff --git a/settings.php b/settings.php index 919814034..fc386c1a6 100644 --- a/settings.php +++ b/settings.php @@ -1533,7 +1533,7 @@
-
+
From 8de1f9df5b531239be999f631099df219a27d267 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Mon, 22 Jan 2024 04:02:41 -0500 Subject: [PATCH 003/137] fix chart and color --- api_speedtest.php | 6 ++ scripts/pi-hole/js/settings.js | 110 +++++++++++---------- scripts/pi-hole/js/speedtest.js | 19 ++-- scripts/pi-hole/speedtest/speedtest.sh | 2 +- scripts/pi-hole/speedtest/uninstall.sh | 129 +++++++++++++++---------- settings.php | 2 +- 6 files changed, 154 insertions(+), 114 deletions(-) diff --git a/api_speedtest.php b/api_speedtest.php index abfba81f6..6f612226c 100644 --- a/api_speedtest.php +++ b/api_speedtest.php @@ -13,6 +13,7 @@ // $data = array(); $dbSpeedtest = '/etc/pihole/speedtest.db'; +$dbSpeedtestOld = '/etc/pihole/speedtest.db.old'; $setupVars = parse_ini_file('/etc/pihole/setupVars.conf'); @@ -28,6 +29,11 @@ $data = array_merge($data, getAllSpeedTestData($dbSpeedtest)); } +function hasSpeedTestBackup($dbSpeedtestOld) +{ + return file_exists($dbSpeedtestOld); +} + function getAllSpeedTestData($dbSpeedtest) { $data = getSpeedTestData($dbSpeedtest, -1); diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index fbf8e76ba..3296b3ec8 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -472,14 +472,20 @@ $(function () { // Speedtest toggles $(function () { + const speedtestTest = $("#speedtesttest"); + const speedtestServer = $("#speedtestserver"); const speedtestChartType = $("#speedtestcharttype"); const speedtestChartTypeSave = $("#speedtestcharttypesave"); + const speedtestUpdate = $("#speedtestupdate"); const speedtestUninstall = $("#speedtestuninstall"); const speedtestDelete = $("#speedtestdelete"); - const speedtestTest = $("#speedtesttest"); - const speedtestServer = $("#speedtestserver"); + const speedtestDeleteLabel = speedtestDelete.parent().children("label"); + const speedtestSubmit = $("#st-submit"); + const defaultClass = "btn-primary"; + const colorClasses = ["btn-success", "btn-warning", "btn-danger"]; + let activeClass = defaultClass; let type = localStorage?.getItem("speedtest_chart_type") || speedtestChartType.attr("value"); speedtestChartType.prop("checked", type === "bar"); @@ -511,17 +517,6 @@ $(function () { speedtestUpdate.attr("value", speedtestUpdate.attr("value") ? null : "up"); }); - speedtestUninstall.on("click", function () { - speedtestUninstall.attr("value", speedtestUninstall.attr("value") ? null : "un"); - if (speedtestUninstall.attr("value")) { - if (!speedtestSubmit.hasClass("btn-warning")) { - speedtestSubmit.addClass("btn-warning"); - } - } else { - speedtestSubmit.removeClass("btn-warning"); - } - }); - speedtestTest.on("click", function () { speedtestTest.attr("value", speedtestTest.attr("value") ? null : "yes"); }); @@ -530,58 +525,75 @@ $(function () { speedtestServer.attr("value", speedtestServer.val()); }); - canRestore = () => { + hasBackup = () => { return new Promise((resolve, reject) => { $.ajax({ - url: "api.php?getAllSpeedTestData&PHP", + url: "api.php?hasSpeedTestBackup&PHP", dataType: "json", - }).done(function (results) { - resolve(localStorage.getItem("speedtest_flushed") === "true" && results?.data?.length === 0); + }).done(function (backupExists) { + resolve(backupExists); }).fail(function (error) { resolve(false); }); }); - }; + } - speedtestDelete.on("click", function () { - speedtestDelete.attr("value", speedtestDelete.attr("value") ? null : "db"); - if (speedtestDelete.attr("value")) { - canRestore().then(didFlush => { - if (didFlush) { - if (!speedtestSubmit.hasClass("btn-success")) { - speedtestSubmit.addClass("btn-success"); - } + canRestore = () => { + return new Promise((resolve, reject) => { + hasBackup().then(backupExists => { + if (backupExists) { + $.ajax({ + url: "api.php?getAllSpeedTestData&PHP", + dataType: "json", + }).done(function (results) { + resolve(results?.data?.length === 0); + }).fail(function (error) { + resolve(false); + }); } else { - if (!speedtestSubmit.hasClass("btn-danger")) { - speedtestSubmit.addClass("btn-danger"); - } + resolve(false); } }).catch(error => { - console.error("Error in canRestore:", error); + resolve(false); }); - } else { - speedtestSubmit.removeClass("btn-danger"); - speedtestSubmit.removeClass("btn-success"); - } + }); + } + + checkCanRestore = () => { + canRestore().then(didFlush => { + let newClass = defaultClass; + let setClass = false; + speedtestDeleteLabel.text(didFlush ? "Restore History (available until the next speedtest)" : "Clear History"); + didFlush = speedtestDelete.attr("value") ? didFlush : undefined; + + if (speedtestUninstall.attr("value")) { + newClass = (didFlush !== undefined && !didFlush) ? colorClasses[2] : colorClasses[1]; + } else if (didFlush !== undefined) { + newClass = didFlush ? colorClasses[0] : colorClasses[1]; + setClass = true; + } else if (speedtestDelete.attr("value")) { + newClass = activeClass; + } + + speedtestSubmit.removeClass([...colorClasses, defaultClass].join(" ")).addClass(newClass); + + if (setClass || !speedtestDelete.attr("value")) { + activeClass = newClass; + } + }); + } + + speedtestUninstall.on("click", function () { + speedtestUninstall.attr("value", speedtestUninstall.attr("value") ? null : "un"); + checkCanRestore(); }); - $("#speedtestform").on("submit", function (e) { - e.preventDefault(); // prevent the form from submitting immediately - if (speedtestDelete.attr("value")) { - canRestore().then(didFlush => { - localStorage.setItem("speedtest_flushed", !didFlush); - this.submit(); // submit the form after the localStorage is set - }).catch(error => { - console.error("Error in canRestore:", error); - }); - } + speedtestDelete.on("click", function () { + speedtestDelete.attr("value", speedtestDelete.attr("value") ? null : "db"); + checkCanRestore(); }); setInterval(() => { - canRestore().then(didFlush => { - speedtestDelete.parent().children("label").text(didFlush ? "Restore History (Available until the next speedtest)" : "Clear History"); - }).catch(error => { - console.error("Error in canRestore:", error); - }); + checkCanRestore(); }, 1000); }); diff --git a/scripts/pi-hole/js/speedtest.js b/scripts/pi-hole/js/speedtest.js index 641dea93c..148dae599 100644 --- a/scripts/pi-hole/js/speedtest.js +++ b/scripts/pi-hole/js/speedtest.js @@ -8,17 +8,17 @@ $(function () { function updateSpeedTestData() { function formatDate(itemdate, results) { - // if the the first and last time are 24 hours apart or more - // then return the date and time, otherwise return the time - let format = "HH:mm"; + if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) { // Test for Safari + return moment(itemdate, "YYYY-MM-DD HH:mm:ss Z").utcOffset(moment().utcOffset()).format("HH:mm"); + } + + let output = "HH:mm"; if (results.length > 1) { const first = moment(results[0].start_time); const last = moment(results.at(-1).start_time); - if (last.diff(first, "hours") >= 24) format = "Do " + format; + if (last.diff(first, "hours") >= 24) output = "Do " + output; } - - // always return the time in the browser's timezone - return moment(itemdate).utcOffset(moment().utcOffset()).format(format); + return moment(itemdate).utcOffset(moment().utcOffset()).format(output); } $.ajax({ @@ -43,9 +43,8 @@ $(function () { updateSpeedTestData(); }, 6000); - var gridColor = $(".graphs-grid").css("background-color"); - var ticksColor = $(".graphs-ticks").css("color"); - + var gridColor = utils.getCSSval("graphs-grid", "background-color"); + var ticksColor = utils.getCSSval("graphs-ticks", "color"); var speedChartctx = document.getElementById("speedOverTimeChart").getContext("2d"); var speedChart = new Chart(speedChartctx, { type: utils.getGraphType(1), diff --git a/scripts/pi-hole/speedtest/speedtest.sh b/scripts/pi-hole/speedtest/speedtest.sh index 9e7ecbc2c..522d3525e 100755 --- a/scripts/pi-hole/speedtest/speedtest.sh +++ b/scripts/pi-hole/speedtest/speedtest.sh @@ -21,7 +21,7 @@ speedtest() { } internet() { - stop=$(date +"%Y-%m-%d %H:%M:%S") + stop=$(date +"%Y-%m-%d %H:%M:%S %Z") res="$(<$FILE)" server_name=$(jq -r '.server.name' <<< "$res") server_dist=0 diff --git a/scripts/pi-hole/speedtest/uninstall.sh b/scripts/pi-hole/speedtest/uninstall.sh index 25721aadc..e5b06ed26 100644 --- a/scripts/pi-hole/speedtest/uninstall.sh +++ b/scripts/pi-hole/speedtest/uninstall.sh @@ -1,67 +1,90 @@ #!/bin/bash -clone() { - local path=$1 - local dest=$2 - local src=$3 - local name=${4-} # if set, will keep local tag if older than latest +setTags() { + local path=${1-} + local name=${2-} - cd "$path" - rm -rf "$dest" - git clone --depth=1 "$src" "$dest" - cd "$dest" - git fetch --tags -q - local latestTag=$(git describe --tags $(git rev-list --tags --max-count=1)) - if [ ! -z "$name" ]; then - local localTag=$(pihole -v | grep "$name" | cut -d ' ' -f 6) - [ "$localTag" == "HEAD" ] && localTag=$(pihole -v | grep "$name" | cut -d ' ' -f 7) - if [[ "$localTag" == *.* ]] && [[ "$localTag" < "$latestTag" ]]; then - latestTag=$localTag - git fetch --unshallow - fi - fi - git -c advice.detachedHead=false checkout $latestTag + if [ ! -z "$path" ]; then + cd "$path" + git fetch --tags -q + latestTag=$(git describe --tags $(git rev-list --tags --max-count=1)) + fi + if [ ! -z "$name" ]; then + localTag=$(pihole -v | grep "$name" | cut -d ' ' -f 6) + [ "$localTag" == "HEAD" ] && localTag=$(pihole -v | grep "$name" | cut -d ' ' -f 7) + fi } -uninstall() { - if cat /opt/pihole/webpage.sh | grep -q SpeedTest; then - echo "$(date) - Uninstalling Current Speedtest Mod..." +refresh() { + local path=$1 + local name=$2 + local url=$3 + local src=${4-} + local dest=$path/$name + + if [ ! -d $dest ]; then # replicate + cd "$path" + rm -rf "$name" + git clone --depth=1 "$url" "$name" + setTags "$name" "$src" + if [ ! -z "$src" ]; then + if [[ "$localTag" == *.* ]] && [[ "$localTag" < "$latestTag" ]]; then + latestTag=$localTag + git fetch --unshallow + fi + fi + elif [ ! -z "$src" ]; then # revert + setTags $dest + git remote | grep -q upstream && git remote remove upstream + git remote add upstream $url + git fetch upstream -q + git reset --hard upstream/master + else # reset + setTags $dest + git reset --hard origin/master + fi + + git -c advice.detachedHead=false checkout $latestTag +} - if [ ! -f /opt/pihole/webpage.sh.org ]; then - clone /opt org_pihole https://github.com/pi-hole/pi-hole Pi-hole - cp advanced/Scripts/webpage.sh ../pihole/webpage.sh.org - cd .. - rm -rf org_pihole - fi +manageHistory() { + local init_db=/var/www/html/admin/scripts/pi-hole/speedtest/speedtest.db + local curr_db=/etc/pihole/speedtest.db + local last_db=/etc/pihole/speedtest.db.old + if [ "${1-}" == "db" ]; then + if [ -f $curr_db ] && [ -f $init_db ] && [ "$(hashFile $curr_db)" != "$(hashFile $init_db)" ]; then + echo "$(date) - Flushing Database..." + mv -f $curr_db $last_db + elif [ -f $last_db ]; then + echo "$(date) - Restoring Database..." + mv -f $last_db $curr_db + fi + fi +} - if [ ! -d /var/www/html/org_admin ]; then - clone /var/www/html org_admin https://github.com/pi-hole/AdminLTE web - fi +uninstall() { + if cat /opt/pihole/webpage.sh | grep -q SpeedTest; then + echo "$(date) - Uninstalling Current Speedtest Mod..." - cd /var/www/html - if [ -d /var/www/html/admin ]; then - rm -rf mod_admin - mv admin mod_admin - fi - mv org_admin admin - cd /opt/pihole/ - cp webpage.sh webpage.sh.mod - mv webpage.sh.org webpage.sh - chmod +x webpage.sh - fi + if [ ! -f /opt/pihole/webpage.sh.org ]; then + if [ ! -d /opt/org_pihole ]; then + refresh /opt org_pihole https://github.com/pi-hole/pi-hole Pi-hole + fi + cd /opt/org_pihole + cp advanced/Scripts/webpage.sh ../pihole/webpage.sh.org + cd .. + rm -rf org_pihole + fi - if [ "${1-}" == "db" ]; then - if [ -f /etc/pihole/speedtest.db ] && [ "$(hashFile /etc/pihole/speedtest.db)" != "$(hashFile /var/www/html/admin/scripts/pi-hole/speedtest/speedtest.db)" ]; then - echo "$(date) - Flushing Database..." - mv -f /etc/pihole/speedtest.db /etc/pihole/speedtest.db.old - elif [ -f /etc/pihole/speedtest.db.old ]; then - echo "$(date) - Restoring Database..." - mv -f /etc/pihole/speedtest.db.old /etc/pihole/speedtest.db - fi - fi + refresh /var/www/html admin https://github.com/pi-hole/AdminLTE web + cd /opt/pihole/ + mv webpage.sh.org webpage.sh + chmod +x webpage.sh + fi - echo "$(date) - Speedtest Mod is Uninstalled!" + manageHistory ${1-} } uninstall ${1-} +echo "$(date) - Done!" exit 0 diff --git a/settings.php b/settings.php index fc386c1a6..919814034 100644 --- a/settings.php +++ b/settings.php @@ -1533,7 +1533,7 @@
- +
From 3be4148a9c3dde49ef2ca73fd5eff1e9e3430d19 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Mon, 22 Jan 2024 09:06:34 -0500 Subject: [PATCH 004/137] fix color, replace "deprecated" method --- README.md | 27 +++---------- scripts/pi-hole/js/settings.js | 61 +++++++++++------------------- scripts/pi-hole/js/speedresults.js | 2 +- 3 files changed, 29 insertions(+), 61 deletions(-) diff --git a/README.md b/README.md index 88e392610..e7ede4391 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,13 @@ -## This project is part of +# Pihole Speedtest Modded Web Interface -https://github.com/arevindh/pihole-speedtest +[![Join the chat at https://gitter.im/pihole-speedtest/community](https://badges.gitter.im/pihole-speedtest/community.svg)](https://gitter.im/pihole-speedtest/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.gg/TW9TfyM) -## About the project +Test your connection speed directly in the Pi-hole web interface! -This project is just another fun project integrating speedtest to PiHole Web UI. +Please go to the [main repository](https://github.com/arevindh/pihole-speedtest) for more information, including installation instructions, pull requests, and issues. -It will be using speedtest.net on background for testing. More frequent the speed tests more data will used. - -What does this mod have in extra ? - -1. Speedtest results of 1/2/4/7/30 days as graph. -2. Custom speed test server selection. -3. Detailed speedtest results page. -4. Ability to schedule speedtest interval. - -## Wiki - -Wiki is available here https://github.com/arevindh/pihole-speedtest/wiki +[Uninstall Instructions](https://github.com/arevindh/pihole-speedtest/wiki/Uninstalling-Speedtest-Mod) ## Disclaimer -We are not affiliated or endorced by [Pi-hole](https://github.com/pi-hole/AdminLTE) - -## Use Official CLI Mode for best results. - -[Uninstall Instructions](https://github.com/arevindh/pihole-speedtest/wiki/Uninstalling-Speedtest-Mod) +We are not affiliated or endorsed by [Pi-hole](https://github.com/pi-hole/AdminLTE) diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index 3296b3ec8..5ca99fedb 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -485,7 +485,6 @@ $(function () { const speedtestSubmit = $("#st-submit"); const defaultClass = "btn-primary"; const colorClasses = ["btn-success", "btn-warning", "btn-danger"]; - let activeClass = defaultClass; let type = localStorage?.getItem("speedtest_chart_type") || speedtestChartType.attr("value"); speedtestChartType.prop("checked", type === "bar"); @@ -538,62 +537,46 @@ $(function () { }); } - canRestore = () => { + hasHistory = () => { return new Promise((resolve, reject) => { - hasBackup().then(backupExists => { - if (backupExists) { - $.ajax({ - url: "api.php?getAllSpeedTestData&PHP", - dataType: "json", - }).done(function (results) { - resolve(results?.data?.length === 0); - }).fail(function (error) { - resolve(false); - }); - } else { - resolve(false); - } - }).catch(error => { + $.ajax({ + url: "api.php?getAllSpeedTestData&PHP", + dataType: "json", + }).done(function (results) { + resolve(results?.data?.length !== 0); + }).fail(function (error) { resolve(false); }); }); } - checkCanRestore = () => { - canRestore().then(didFlush => { - let newClass = defaultClass; - let setClass = false; - speedtestDeleteLabel.text(didFlush ? "Restore History (available until the next speedtest)" : "Clear History"); - didFlush = speedtestDelete.attr("value") ? didFlush : undefined; - - if (speedtestUninstall.attr("value")) { - newClass = (didFlush !== undefined && !didFlush) ? colorClasses[2] : colorClasses[1]; - } else if (didFlush !== undefined) { + canRestore = () => { + hasBackup().then(backupExists => { + hasHistory().then(historyExists => { + const didFlush = backupExists && !historyExists; + let newClass = defaultClass; + speedtestDeleteLabel.text(didFlush ? "Restore History (available until the next speedtest)" : "Clear History"); + if (speedtestUninstall.attr("value")) { + newClass = (didFlush && speedtestDelete.attr("value")) || (historyExists && !speedtestDelete.attr("value")) ? colorClasses[1] : colorClasses[2]; + } else if (speedtestDelete.attr("value")) { newClass = didFlush ? colorClasses[0] : colorClasses[1]; - setClass = true; - } else if (speedtestDelete.attr("value")) { - newClass = activeClass; - } - - speedtestSubmit.removeClass([...colorClasses, defaultClass].join(" ")).addClass(newClass); - - if (setClass || !speedtestDelete.attr("value")) { - activeClass = newClass; - } + } + speedtestSubmit.removeClass([...colorClasses, defaultClass].join(" ")).addClass(newClass); + }); }); } speedtestUninstall.on("click", function () { speedtestUninstall.attr("value", speedtestUninstall.attr("value") ? null : "un"); - checkCanRestore(); + canRestore(); }); speedtestDelete.on("click", function () { speedtestDelete.attr("value", speedtestDelete.attr("value") ? null : "db"); - checkCanRestore(); + canRestore(); }); setInterval(() => { - checkCanRestore(); + canRestore(); }, 1000); }); diff --git a/scripts/pi-hole/js/speedresults.js b/scripts/pi-hole/js/speedresults.js index 01b54c44f..814a2cd78 100644 --- a/scripts/pi-hole/js/speedresults.js +++ b/scripts/pi-hole/js/speedresults.js @@ -26,7 +26,7 @@ $(document).ready(function () { // Do we want to filter queries? var GETDict = {}; location.search - .substr(1) + .slice(1) .split("&") .forEach(function (item) { GETDict[item.split("=")[0]] = item.split("=")[1]; From 3fe24ffbb2e40dce46bb1ad6a3c1ed71cd3e55c0 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Mon, 22 Jan 2024 11:35:42 -0500 Subject: [PATCH 005/137] pass tests --- package-lock.json | 2 +- package.json | 5 +-- scripts/pi-hole/js/settings.js | 68 +++++++++++++++++++-------------- scripts/pi-hole/js/speedtest.js | 41 +++++++++++++++----- scripts/pi-hole/php/func.php | 16 ++++---- scripts/pi-hole/php/groups.php | 38 +++++++++--------- scripts/pi-hole/php/message.php | 2 +- scripts/pi-hole/php/network.php | 2 +- settings.php | 10 ++--- 9 files changed, 108 insertions(+), 76 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8b145c3ab..1160757ff 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "eslint-plugin-compat": "^4.2.0", "postcss": "^8.4.31", "postcss-cli": "^10.1.0", - "prettier": "3.1.0", + "prettier": "^3.1.0", "xo": "^0.56.0" } }, diff --git a/package.json b/package.json index 102cd2eae..cf5af2df8 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "eslint-plugin-compat": "^4.2.0", "postcss": "^8.4.31", "postcss-cli": "^10.1.0", - "prettier": "3.1.0", + "prettier": "^3.1.0", "xo": "^0.56.0" }, "browserslist": [ @@ -104,6 +104,5 @@ "unicorn/switch-case-braces": "off", "unicorn/no-negated-condition": "off" } - }, - "dependencies": {} + } } diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index 5ca99fedb..5d8578dc5 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -524,47 +524,59 @@ $(function () { speedtestServer.attr("value", speedtestServer.val()); }); - hasBackup = () => { - return new Promise((resolve, reject) => { - $.ajax({ - url: "api.php?hasSpeedTestBackup&PHP", - dataType: "json", - }).done(function (backupExists) { - resolve(backupExists); - }).fail(function (error) { - resolve(false); + const hasBackup = callback => { + $.ajax({ + url: "api.php?hasSpeedTestBackup&PHP", + dataType: "json", + }) + .done(function (backupExists) { + callback(true, backupExists); + }) + .fail(function () { + callback(true, false); }); - }); - } + }; - hasHistory = () => { - return new Promise((resolve, reject) => { - $.ajax({ - url: "api.php?getAllSpeedTestData&PHP", - dataType: "json", - }).done(function (results) { - resolve(results?.data?.length !== 0); - }).fail(function (error) { - resolve(false); + const hasHistory = callback => { + $.ajax({ + url: "api.php?getAllSpeedTestData&PHP", + dataType: "json", + }) + .done(function (results) { + callback(null, results?.data?.length !== 0); + }) + .fail(function () { + callback(true, false); }); - }); - } + }; + + const canRestore = () => { + hasBackup((errorBackup, backupExists) => { + hasHistory((errorHistory, historyExists) => { + if (errorBackup && errorHistory && !errorHistory) { + return; + } - canRestore = () => { - hasBackup().then(backupExists => { - hasHistory().then(historyExists => { const didFlush = backupExists && !historyExists; let newClass = defaultClass; - speedtestDeleteLabel.text(didFlush ? "Restore History (available until the next speedtest)" : "Clear History"); + speedtestDeleteLabel.text( + didFlush ? "Restore History (available until the next speedtest)" : "Clear History" + ); + if (speedtestUninstall.attr("value")) { - newClass = (didFlush && speedtestDelete.attr("value")) || (historyExists && !speedtestDelete.attr("value")) ? colorClasses[1] : colorClasses[2]; + newClass = + (didFlush && speedtestDelete.attr("value")) || + (historyExists && !speedtestDelete.attr("value")) + ? colorClasses[1] + : colorClasses[2]; } else if (speedtestDelete.attr("value")) { newClass = didFlush ? colorClasses[0] : colorClasses[1]; } + speedtestSubmit.removeClass([...colorClasses, defaultClass].join(" ")).addClass(newClass); }); }); - } + }; speedtestUninstall.on("click", function () { speedtestUninstall.attr("value", speedtestUninstall.attr("value") ? null : "un"); diff --git a/scripts/pi-hole/js/speedtest.js b/scripts/pi-hole/js/speedtest.js index 148dae599..40539e23d 100644 --- a/scripts/pi-hole/js/speedtest.js +++ b/scripts/pi-hole/js/speedtest.js @@ -1,5 +1,21 @@ /* global Chart:false, moment:false */ +function getGraphType(speedtest = 0) { + // Only return line if `barchart_chkbox` is explicitly set to false. Else return bar + if (!speedtest) { + return localStorage?.getItem("barchart_chkbox") === "false" ? "line" : "bar"; + } + + return localStorage?.getItem("speedtest_chart_type") || "line"; +} + +function getCSSval(cssclass, cssproperty) { + var elem = $("
"), + val = elem.appendTo("body").css(cssproperty); + elem.remove(); + return val; +} + $(function () { var speedlabels = []; var downloadspeed = []; @@ -8,8 +24,10 @@ $(function () { function updateSpeedTestData() { function formatDate(itemdate, results) { - if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) { // Test for Safari - return moment(itemdate, "YYYY-MM-DD HH:mm:ss Z").utcOffset(moment().utcOffset()).format("HH:mm"); + if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) { + return moment(itemdate, "YYYY-MM-DD HH:mm:ss Z") + .utcOffset(moment().utcOffset()) + .format("HH:mm"); } let output = "HH:mm"; @@ -18,6 +36,7 @@ $(function () { const last = moment(results.at(-1).start_time); if (last.diff(first, "hours") >= 24) output = "Do " + output; } + return moment(itemdate).utcOffset(moment().utcOffset()).format(output); } @@ -43,11 +62,11 @@ $(function () { updateSpeedTestData(); }, 6000); - var gridColor = utils.getCSSval("graphs-grid", "background-color"); - var ticksColor = utils.getCSSval("graphs-ticks", "color"); + var gridColor = getCSSval("graphs-grid", "background-color"); + var ticksColor = getCSSval("graphs-ticks", "color"); var speedChartctx = document.getElementById("speedOverTimeChart").getContext("2d"); var speedChart = new Chart(speedChartctx, { - type: utils.getGraphType(1), + type: getGraphType(1), data: { labels: speedlabels, datasets: [ @@ -97,13 +116,15 @@ $(function () { }, }, tooltip: { - mode: 'index', - intersect: utils.getGraphType(1) === "bar", - yAlign: 'bottom', + mode: "index", + intersect: getGraphType(1) === "bar", + yAlign: "bottom", callbacks: { label: function (context) { - return Math.round(context?.parsed?.y) + " " + context?.dataset?.label || null; - } + return ( + Math.round(context?.parsed?.y ?? 0) + " " + (context?.dataset?.label ?? "") || null + ); + }, }, }, }, diff --git a/scripts/pi-hole/php/func.php b/scripts/pi-hole/php/func.php index 74e1f55b3..a23938a22 100644 --- a/scripts/pi-hole/php/func.php +++ b/scripts/pi-hole/php/func.php @@ -211,7 +211,7 @@ function getCustomDNSEntries() continue; } - $data = new \stdClass(); + $data = new stdClass(); $data->ip = $explodedLine[0]; $data->domain = $explodedLine[1]; $data->domains = array_slice($explodedLine, 0, -1); @@ -289,7 +289,7 @@ function addCustomDNSEntry($ip = '', $domain = '', $reload = '', $json = true, $ } return returnSuccess('', $json); - } catch (\Exception $ex) { + } catch (Exception $ex) { return returnError($ex->getMessage(), $json); } } @@ -328,7 +328,7 @@ function deleteCustomDNSEntry() pihole_execute('-a removecustomdns '.$ip.' '.$domain); return returnSuccess(); - } catch (\Exception $ex) { + } catch (Exception $ex) { return returnError($ex->getMessage()); } } @@ -345,7 +345,7 @@ function deleteAllCustomDNSEntries($reload = '') foreach ($existingEntries as $entry) { pihole_execute('-a removecustomdns '.$entry->ip.' '.$entry->domain.' '.$reload); } - } catch (\Exception $ex) { + } catch (Exception $ex) { return returnError($ex->getMessage()); } @@ -389,7 +389,7 @@ function getCustomCNAMEEntries() continue; } - $data = new \stdClass(); + $data = new stdClass(); $data->domains = array_slice($explodedLine, 0, -1); $data->domain = implode(',', $data->domains); $data->target = $explodedLine[count($explodedLine) - 1]; @@ -471,7 +471,7 @@ function addCustomCNAMEEntry($domain = '', $target = '', $reload = '', $json = t } return returnSuccess('', $json); - } catch (\Exception $ex) { + } catch (Exception $ex) { return returnError($ex->getMessage(), $json); } } @@ -510,7 +510,7 @@ function deleteCustomCNAMEEntry() pihole_execute('-a removecustomcname '.$domain.' '.$target); return returnSuccess(); - } catch (\Exception $ex) { + } catch (Exception $ex) { return returnError($ex->getMessage()); } } @@ -527,7 +527,7 @@ function deleteAllCustomCNAMEEntries($reload = '') foreach ($existingEntries as $entry) { pihole_execute('-a removecustomcname '.$entry->domain.' '.$entry->target.' '.$reload); } - } catch (\Exception $ex) { + } catch (Exception $ex) { return returnError($ex->getMessage()); } diff --git a/scripts/pi-hole/php/groups.php b/scripts/pi-hole/php/groups.php index 7cd418609..67b125091 100644 --- a/scripts/pi-hole/php/groups.php +++ b/scripts/pi-hole/php/groups.php @@ -51,7 +51,7 @@ function verify_ID_array($arr) header('Content-type: application/json'); echo json_encode(array('data' => $data)); - } catch (\Exception $ex) { + } catch (Exception $ex) { JSON_error($ex->getMessage()); } } elseif ($_POST['action'] == 'add_group') { @@ -93,7 +93,7 @@ function verify_ID_array($arr) $reload = true; JSON_success(); - } catch (\Exception $ex) { + } catch (Exception $ex) { JSON_error($ex->getMessage()); } } elseif ($_POST['action'] == 'edit_group') { @@ -134,7 +134,7 @@ function verify_ID_array($arr) $reload = true; JSON_success(); - } catch (\Exception $ex) { + } catch (Exception $ex) { JSON_error($ex->getMessage()); } } elseif ($_POST['action'] == 'delete_group') { @@ -167,7 +167,7 @@ function verify_ID_array($arr) } $reload = true; JSON_success(); - } catch (\Exception $ex) { + } catch (Exception $ex) { JSON_error($ex->getMessage()); } } elseif ($_POST['action'] == 'get_clients') { @@ -247,7 +247,7 @@ function verify_ID_array($arr) header('Content-type: application/json'); echo json_encode(array('data' => $data)); - } catch (\Exception $ex) { + } catch (Exception $ex) { JSON_error($ex->getMessage()); } } elseif ($_POST['action'] == 'get_unconfigured_clients') { @@ -329,7 +329,7 @@ function verify_ID_array($arr) header('Content-type: application/json'); echo json_encode($ips); - } catch (\Exception $ex) { + } catch (Exception $ex) { JSON_error($ex->getMessage()); } } elseif ($_POST['action'] == 'add_client') { @@ -372,7 +372,7 @@ function verify_ID_array($arr) $reload = true; JSON_success(); - } catch (\Exception $ex) { + } catch (Exception $ex) { JSON_error($ex->getMessage()); } } elseif ($_POST['action'] == 'edit_client') { @@ -443,7 +443,7 @@ function verify_ID_array($arr) $reload = true; JSON_success(); - } catch (\Exception $ex) { + } catch (Exception $ex) { JSON_error($ex->getMessage()); } } elseif ($_POST['action'] == 'delete_client') { @@ -481,7 +481,7 @@ function verify_ID_array($arr) $reload = true; JSON_success(); - } catch (\Exception $ex) { + } catch (Exception $ex) { JSON_error($ex->getMessage()); } } elseif ($_POST['action'] == 'get_domains') { @@ -525,7 +525,7 @@ function verify_ID_array($arr) header('Content-type: application/json'); echo json_encode(array('data' => $data)); - } catch (\Exception $ex) { + } catch (Exception $ex) { JSON_error($ex->getMessage()); } } elseif ($_POST['action'] == 'add_domain' || $_POST['action'] == 'replace_domain') { @@ -693,7 +693,7 @@ function verify_ID_array($arr) } $reload = true; JSON_success($msg); - } catch (\Exception $ex) { + } catch (Exception $ex) { JSON_error($ex->getMessage()); } } elseif ($_POST['action'] == 'edit_domain') { @@ -778,7 +778,7 @@ function verify_ID_array($arr) $reload = true; JSON_success(); - } catch (\Exception $ex) { + } catch (Exception $ex) { JSON_error($ex->getMessage()); } } elseif ($_POST['action'] == 'delete_domain') { @@ -817,7 +817,7 @@ function verify_ID_array($arr) $reload = true; JSON_success(); - } catch (\Exception $ex) { + } catch (Exception $ex) { JSON_error($ex->getMessage()); } } elseif ($_POST['action'] == 'delete_domain_string') { @@ -869,7 +869,7 @@ function verify_ID_array($arr) $reload = true; JSON_success(); - } catch (\Exception $ex) { + } catch (Exception $ex) { JSON_error($ex->getMessage()); } } elseif ($_POST['action'] == 'get_adlists') { @@ -897,7 +897,7 @@ function verify_ID_array($arr) header('Content-type: application/json'); echo json_encode(array('data' => $data)); - } catch (\Exception $ex) { + } catch (Exception $ex) { JSON_error($ex->getMessage()); } } elseif ($_POST['action'] == 'add_adlist') { @@ -978,7 +978,7 @@ function verify_ID_array($arr) $msg = $added_list.'
Total: '.$total.' adlist(s) processed.'; JSON_success($msg); } - } catch (\Exception $ex) { + } catch (Exception $ex) { JSON_error($ex->getMessage()); } } elseif ($_POST['action'] == 'edit_adlist') { @@ -1059,7 +1059,7 @@ function verify_ID_array($arr) $reload = true; JSON_success(); - } catch (\Exception $ex) { + } catch (Exception $ex) { JSON_error($ex->getMessage()); } } elseif ($_POST['action'] == 'delete_adlist') { @@ -1100,7 +1100,7 @@ function verify_ID_array($arr) $reload = true; JSON_success(); - } catch (\Exception $ex) { + } catch (Exception $ex) { JSON_error($ex->getMessage()); } } elseif ($_POST['action'] == 'add_audit') { @@ -1157,7 +1157,7 @@ function verify_ID_array($arr) // Reloading isn't necessary for audit domains (no effect on blocking) $reload = false; JSON_success($msg); - } catch (\Exception $ex) { + } catch (Exception $ex) { JSON_error($ex->getMessage()); } } else { diff --git a/scripts/pi-hole/php/message.php b/scripts/pi-hole/php/message.php index 7ecfccea6..3f54bba37 100644 --- a/scripts/pi-hole/php/message.php +++ b/scripts/pi-hole/php/message.php @@ -50,7 +50,7 @@ $reload = true; JSON_success(); - } catch (\Exception $ex) { + } catch (Exception $ex) { JSON_error($ex->getMessage()); } } else { diff --git a/scripts/pi-hole/php/network.php b/scripts/pi-hole/php/network.php index 4b570cea4..be6b68811 100644 --- a/scripts/pi-hole/php/network.php +++ b/scripts/pi-hole/php/network.php @@ -57,7 +57,7 @@ $reload = true; JSON_success(); - } catch (\Exception $ex) { + } catch (Exception $ex) { JSON_error($ex->getMessage()); } } else { diff --git a/settings.php b/settings.php index 919814034..9692a0d9a 100644 --- a/settings.php +++ b/settings.php @@ -460,11 +460,11 @@ $DHCPIPv6 = false; $DHCP_rapid_commit = false; } - if (isset($setupVars['PIHOLE_DOMAIN'])) { - $piHoleDomain = $setupVars['PIHOLE_DOMAIN']; - } else { - $piHoleDomain = 'lan'; - } +if (isset($setupVars['PIHOLE_DOMAIN'])) { + $piHoleDomain = $setupVars['PIHOLE_DOMAIN']; +} else { + $piHoleDomain = 'lan'; +} ?>
From b373620a3ed0ad04079b3eb5836ce5375c3788c4 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Mon, 22 Jan 2024 13:56:33 -0500 Subject: [PATCH 006/137] better retry --- README.md | 2 +- scripts/pi-hole/speedtest/speedtest.sh | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e7ede4391..e76b5f12e 100644 --- a/README.md +++ b/README.md @@ -10,4 +10,4 @@ Please go to the [main repository](https://github.com/arevindh/pihole-speedtest) ## Disclaimer -We are not affiliated or endorsed by [Pi-hole](https://github.com/pi-hole/AdminLTE) +We are not affiliated with or endorsed by [Pi-hole](https://github.com/pi-hole/AdminLTE) diff --git a/scripts/pi-hole/speedtest/speedtest.sh b/scripts/pi-hole/speedtest/speedtest.sh index 522d3525e..12eed9e51 100755 --- a/scripts/pi-hole/speedtest/speedtest.sh +++ b/scripts/pi-hole/speedtest/speedtest.sh @@ -61,7 +61,11 @@ nointernet(){ } tryagain(){ - start=$(date +"%Y-%m-%d %H:%M:%S %Z") + if dpkg -s speedtest &> /dev/null; then + apt-get install -y speedtest-cli speedtest- || nointernet + else + apt-get install -y speedtest-cli- speedtest || nointernet + fi speedtest > "$FILE" && internet || nointernet } From c876d4cd547f1cef4f2cd5a0a55f6eed698329c4 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Mon, 22 Jan 2024 14:40:08 -0500 Subject: [PATCH 007/137] rm extras --- scripts/pi-hole/speedtest/speedtest.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/pi-hole/speedtest/speedtest.sh b/scripts/pi-hole/speedtest/speedtest.sh index 12eed9e51..aa0772c13 100755 --- a/scripts/pi-hole/speedtest/speedtest.sh +++ b/scripts/pi-hole/speedtest/speedtest.sh @@ -62,9 +62,9 @@ nointernet(){ tryagain(){ if dpkg -s speedtest &> /dev/null; then - apt-get install -y speedtest-cli speedtest- || nointernet + apt-get install -y speedtest-cli speedtest- else - apt-get install -y speedtest-cli- speedtest || nointernet + apt-get install -y speedtest-cli- speedtest fi speedtest > "$FILE" && internet || nointernet } From 2192c19df3e732f483e302c21659cfd518109bdd Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Mon, 22 Jan 2024 15:35:13 -0500 Subject: [PATCH 008/137] change if --- scripts/pi-hole/speedtest/speedtest.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/pi-hole/speedtest/speedtest.sh b/scripts/pi-hole/speedtest/speedtest.sh index aa0772c13..fddea98b6 100755 --- a/scripts/pi-hole/speedtest/speedtest.sh +++ b/scripts/pi-hole/speedtest/speedtest.sh @@ -61,10 +61,10 @@ nointernet(){ } tryagain(){ - if dpkg -s speedtest &> /dev/null; then + if apt-cache policy speedtest-cli | grep -q 'Installed: (none)'; then apt-get install -y speedtest-cli speedtest- else - apt-get install -y speedtest-cli- speedtest + apt-get install -y speedtest speedtest-cli- fi speedtest > "$FILE" && internet || nointernet } From 2dae9dcfdf81f76b3e00e022971debe7d326eadb Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Mon, 22 Jan 2024 17:41:20 -0500 Subject: [PATCH 009/137] change date in log --- scripts/pi-hole/js/speedresults.js | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/scripts/pi-hole/js/speedresults.js b/scripts/pi-hole/js/speedresults.js index 814a2cd78..a9a159fbf 100644 --- a/scripts/pi-hole/js/speedresults.js +++ b/scripts/pi-hole/js/speedresults.js @@ -48,12 +48,34 @@ $(document).ready(function () { null, { render: function (data, type, _full, _meta) { - return type === "display" ? moment(data).format("Y-MM-DD HH:mm:ss z") : data; + if (type === "display") { + if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) { + data = moment(data, "YYYY-MM-DD HH:mm:ss Z") + .utcOffset(moment().utcOffset()) + .format("YYYY-MM-DD HH:mm:ss Z"); + } else { + data = moment(data) + .utcOffset(moment().utcOffset()) + .format("YYYY-MM-DD HH:mm:ss Z"); + } + } + return data; }, }, { render: function (data, type, _full, _meta) { - return type === "display" ? moment(data).format("Y-MM-DD HH:mm:ss z") : data; + if (type === "display") { + if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) { + data = moment(data, "YYYY-MM-DD HH:mm:ss Z") + .utcOffset(moment().utcOffset()) + .format("YYYY-MM-DD HH:mm:ss Z"); + } else { + data = moment(data) + .utcOffset(moment().utcOffset()) + .format("YYYY-MM-DD HH:mm:ss Z"); + } + } + return data; }, }, null, From 434f69e89c0083f7071107b545e827ea17d7a4ca Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Mon, 22 Jan 2024 17:53:12 -0500 Subject: [PATCH 010/137] prettier --- scripts/pi-hole/js/speedresults.js | 8 ++------ scripts/pi-hole/php/header_authenticated.php | 4 ++-- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/scripts/pi-hole/js/speedresults.js b/scripts/pi-hole/js/speedresults.js index a9a159fbf..5540bc004 100644 --- a/scripts/pi-hole/js/speedresults.js +++ b/scripts/pi-hole/js/speedresults.js @@ -54,9 +54,7 @@ $(document).ready(function () { .utcOffset(moment().utcOffset()) .format("YYYY-MM-DD HH:mm:ss Z"); } else { - data = moment(data) - .utcOffset(moment().utcOffset()) - .format("YYYY-MM-DD HH:mm:ss Z"); + data = moment(data).utcOffset(moment().utcOffset()).format("YYYY-MM-DD HH:mm:ss Z"); } } return data; @@ -70,9 +68,7 @@ $(document).ready(function () { .utcOffset(moment().utcOffset()) .format("YYYY-MM-DD HH:mm:ss Z"); } else { - data = moment(data) - .utcOffset(moment().utcOffset()) - .format("YYYY-MM-DD HH:mm:ss Z"); + data = moment(data).utcOffset(moment().utcOffset()).format("YYYY-MM-DD HH:mm:ss Z"); } } return data; diff --git a/scripts/pi-hole/php/header_authenticated.php b/scripts/pi-hole/php/header_authenticated.php index c35a9398d..4e3d564ff 100644 --- a/scripts/pi-hole/php/header_authenticated.php +++ b/scripts/pi-hole/php/header_authenticated.php @@ -155,9 +155,9 @@ function getTemperature() $speedtestshedule = false; } if (isset($setupVars['SPEEDTEST_CHART_DAYS']) && $setupVars['SPEEDTEST_CHART_DAYS'] > 1) { - $speedtestdays = $setupVars['SPEEDTEST_CHART_DAYS'].' Days'; + $speedtestdays = $setupVars['SPEEDTEST_CHART_DAYS'].' days'; } else { - $speedtestdays = '24 Hours'; + $speedtestdays = '24 hours'; } $piholeFTLConf = piholeFTLConfig(); From 47541656be300b689a8d9c214a06ffb324cbb66f Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Mon, 22 Jan 2024 18:03:13 -0500 Subject: [PATCH 011/137] space --- scripts/pi-hole/js/speedresults.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/pi-hole/js/speedresults.js b/scripts/pi-hole/js/speedresults.js index 5540bc004..d84b8ecc4 100644 --- a/scripts/pi-hole/js/speedresults.js +++ b/scripts/pi-hole/js/speedresults.js @@ -57,6 +57,7 @@ $(document).ready(function () { data = moment(data).utcOffset(moment().utcOffset()).format("YYYY-MM-DD HH:mm:ss Z"); } } + return data; }, }, @@ -71,6 +72,7 @@ $(document).ready(function () { data = moment(data).utcOffset(moment().utcOffset()).format("YYYY-MM-DD HH:mm:ss Z"); } } + return data; }, }, From 941c0ed7141d139bf2cd6d853c661dc9d68af0a3 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Mon, 22 Jan 2024 18:33:38 -0500 Subject: [PATCH 012/137] punctuation --- speedtest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/speedtest.php b/speedtest.php index 6901f4ddf..1efe66702 100644 --- a/speedtest.php +++ b/speedtest.php @@ -16,7 +16,7 @@
-

Recent Speedtests , Export as CSV

+

Recent Speedtests | Export as CSV

From 92ae6ab50bfc1cb71ea459fca0a61bb6d49104bc Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Mon, 22 Jan 2024 18:43:11 -0500 Subject: [PATCH 013/137] update uninstall --- scripts/pi-hole/speedtest/uninstall.sh | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/scripts/pi-hole/speedtest/uninstall.sh b/scripts/pi-hole/speedtest/uninstall.sh index e5b06ed26..01cdff2f5 100644 --- a/scripts/pi-hole/speedtest/uninstall.sh +++ b/scripts/pi-hole/speedtest/uninstall.sh @@ -15,7 +15,7 @@ setTags() { fi } -refresh() { +download() { local path=$1 local name=$2 local url=$3 @@ -39,7 +39,7 @@ refresh() { git remote add upstream $url git fetch upstream -q git reset --hard upstream/master - else # reset + else # refresh setTags $dest git reset --hard origin/master fi @@ -68,17 +68,16 @@ uninstall() { if [ ! -f /opt/pihole/webpage.sh.org ]; then if [ ! -d /opt/org_pihole ]; then - refresh /opt org_pihole https://github.com/pi-hole/pi-hole Pi-hole + download /opt org_pihole https://github.com/pi-hole/pi-hole Pi-hole fi - cd /opt/org_pihole - cp advanced/Scripts/webpage.sh ../pihole/webpage.sh.org - cd .. + cd /opt + cp org_pihole/advanced/Scripts/webpage.sh ../pihole/webpage.sh.org rm -rf org_pihole fi - refresh /var/www/html admin https://github.com/pi-hole/AdminLTE web + download /var/www/html admin https://github.com/pi-hole/AdminLTE web cd /opt/pihole/ - mv webpage.sh.org webpage.sh + cp webpage.sh.org webpage.sh chmod +x webpage.sh fi From 6ab6aa2c818e9788a256ca0598c19deac1023085 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Wed, 24 Jan 2024 22:09:14 -0500 Subject: [PATCH 014/137] allow arbitrary number of hours --- settings.php | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/settings.php b/settings.php index 9692a0d9a..86e807fa3 100644 --- a/settings.php +++ b/settings.php @@ -1553,16 +1553,22 @@
- - + +
+
+ Every +
+ +
+ Hours +
+
From c8de073eef73904c204b28683584ac73b24b15f7 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Wed, 24 Jan 2024 23:31:26 -0500 Subject: [PATCH 015/137] cleaner --- settings.php | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/settings.php b/settings.php index 86e807fa3..bb53cc86b 100644 --- a/settings.php +++ b/settings.php @@ -1546,18 +1546,15 @@
- +
- +
- +
-
- Every -
-
- Hours -
From 591c7c6e7122c6201bdcac8f496a7da17b9e64d9 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Wed, 24 Jan 2024 23:43:33 -0500 Subject: [PATCH 016/137] when x > 0 --- settings.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.php b/settings.php index bb53cc86b..47091be71 100644 --- a/settings.php +++ b/settings.php @@ -1565,7 +1565,7 @@ class="form-control"
- + @@ -1554,7 +1554,7 @@
-
+
-
- -

You can find the closest servers with speedtest or here. Experts only!

-
-
-
Speedtest.net Server
- -
-
-
@@ -1599,7 +1587,7 @@ class="form-control"
-
+
@@ -1613,6 +1601,22 @@ class="form-control"
+
+ +
+ +

You can find the closest servers with speedtest or here. Experts only!

+
+
+
Speedtest.net Server
+ +
+
+
+ +

Attach to a running process with

sudo tmux attach-session -t pimod

Or access the latest log whenever with

From c365f12fc3fd516c5ba8d9ae66aef4063c026392 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Wed, 24 Jan 2024 23:53:05 -0500 Subject: [PATCH 018/137] again --- settings.php | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/settings.php b/settings.php index 1603a9dd0..e1e37f7ba 100644 --- a/settings.php +++ b/settings.php @@ -1566,7 +1566,7 @@ class="form-control"
- @@ -1587,6 +1587,19 @@ class="form-control"
+
+ +

You can find the closest servers with speedtest or here. Experts only!

+
+
+
Speedtest.net Server
+ +
+
+
+
@@ -1603,19 +1616,6 @@ class="form-control"
-
- -

You can find the closest servers with speedtest or here. Experts only!

-
-
-
Speedtest.net Server
- -
-
-
-

Attach to a running process with

sudo tmux attach-session -t pimod
From 4841f2d71acd6832953fbacdb3205cfe880ea696 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Wed, 24 Jan 2024 23:55:39 -0500 Subject: [PATCH 019/137] 3 9 --- settings.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/settings.php b/settings.php index e1e37f7ba..5ee342555 100644 --- a/settings.php +++ b/settings.php @@ -1545,14 +1545,14 @@
-
+
-
+
-
+
-
+
/> @@ -1587,7 +1587,7 @@ class="form-control"
-
+

You can find the closest servers with speedtest or here. Experts only!

@@ -1600,7 +1600,7 @@ class="form-control"
-
+
From c9b1614f76ecd6f21d34f0a75e077cafefd940ac Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Wed, 24 Jan 2024 23:58:59 -0500 Subject: [PATCH 020/137] mb3 --- settings.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/settings.php b/settings.php index 5ee342555..520db5c21 100644 --- a/settings.php +++ b/settings.php @@ -1545,14 +1545,14 @@
-
+
-
+
-
+
- @@ -1575,7 +1575,7 @@ class="form-control"
-
+
/> @@ -1587,7 +1587,7 @@ class="form-control"
-
+

You can find the closest servers with speedtest or here. Experts only!

@@ -1600,7 +1600,7 @@ class="form-control"
-
+
From a3cb27eda270c09389341bb6265a5ac4e9236800 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Fri, 26 Jan 2024 00:08:39 -0500 Subject: [PATCH 021/137] create db --- scripts/pi-hole/speedtest/speedtest.db | Bin 3072 -> 0 bytes scripts/pi-hole/speedtest/uninstall.sh | 190 ++++++++++++++++--------- 2 files changed, 123 insertions(+), 67 deletions(-) delete mode 100644 scripts/pi-hole/speedtest/speedtest.db diff --git a/scripts/pi-hole/speedtest/speedtest.db b/scripts/pi-hole/speedtest/speedtest.db deleted file mode 100644 index b15cec5d6a9bf465f53a12d399ce018c19d5e9cd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3072 zcmeHGJ5B>J5VbcERMaUmN|h*~;sPwlLJ>&_%TjE$YuE{_w7=L6(9n=8a2QTR&kdN( zPb5u58+jyu-t&0udG_4h4ibYfDO4s6oUuCNoSg$O#yGVawY62az6LZ_c%N~0`tx-{ zUXJt0oNwx^Sq1heFm{d_-7XJ|d`MAGQ~IEx#_0_6$6Z*v^n~jR@ZF1nfF0jXa+UWJf*^1NJ@GWRVl*AGAN4~fc9*n*I(Deei76G{T{!M>G zJBMQA_aM?nDifL{!;SxfEy@*X@XZi2QAI`xsTWic{N<8zgLjsfFpLvzBA}E9Rp3R0 zuuSr&2o%bcxVU)D(?ZgPSH!H`g8Er13}-4`9t54LHn*yJ&)e8#tAJJDa1^lhe>iVx KGqDQ%SAkz|o`+2U diff --git a/scripts/pi-hole/speedtest/uninstall.sh b/scripts/pi-hole/speedtest/uninstall.sh index 01cdff2f5..1cf29c766 100644 --- a/scripts/pi-hole/speedtest/uninstall.sh +++ b/scripts/pi-hole/speedtest/uninstall.sh @@ -1,89 +1,145 @@ #!/bin/bash -setTags() { - local path=${1-} - local name=${2-} +admin_dir=/var/www/html +curr_wp=/opt/pihole/webpage.sh +last_wp=$curr_wp.old +org_wp=$curr_wp.org + +curr_db=/etc/pihole/speedtest.db +last_db=$curr_db.old +db_table="speedtest" +create_table="create table if not exists $db_table ( + id integer primary key autoincrement, + start_time integer, + stop_time text, + from_server text, + from_ip text, + server text, + server_dist real, + server_ping real, + download real, + upload real, + share_url text +);" - if [ ! -z "$path" ]; then - cd "$path" - git fetch --tags -q - latestTag=$(git describe --tags $(git rev-list --tags --max-count=1)) - fi - if [ ! -z "$name" ]; then - localTag=$(pihole -v | grep "$name" | cut -d ' ' -f 6) - [ "$localTag" == "HEAD" ] && localTag=$(pihole -v | grep "$name" | cut -d ' ' -f 7) - fi +setTags() { + local path=${1-} + local name=${2-} + if [ ! -z "$path" ]; then + cd "$path" + git fetch origin -q + git fetch --tags -f -q + latestTag=$(git describe --tags $(git rev-list --tags --max-count=1)) + fi + if [ ! -z "$name" ]; then + localTag=$(pihole -v | grep "$name" | cut -d ' ' -f 6) + [ "$localTag" == "HEAD" ] && localTag=$(pihole -v | grep "$name" | cut -d ' ' -f 7) + fi } download() { - local path=$1 - local name=$2 - local url=$3 - local src=${4-} - local dest=$path/$name + local path=$1 + local name=$2 + local url=$3 + local src=${4-} + local dest=$path/$name + if [ ! -d $dest ]; then # replicate + cd "$path" + rm -rf "$name" + git clone --depth=1 "$url" "$name" + setTags "$name" "$src" + if [ ! -z "$src" ]; then + if [[ "$localTag" == *.* ]] && [[ "$localTag" < "$latestTag" ]]; then + latestTag=$localTag + git fetch --unshallow + fi + fi + else # replace + setTags $dest + if [ ! -z "$src" ]; then + if [ "$url" != "old" ]; then + git config --global --add safe.directory "$dest" + git remote -v | grep -q "old" || git remote rename origin old + git remote -v | grep -q "origin" && git remote remove origin + git remote add origin $url + else + git remote remove origin + git remote rename old origin + fi + git fetch origin -q + fi + git reset --hard origin/master + fi - if [ ! -d $dest ]; then # replicate - cd "$path" - rm -rf "$name" - git clone --depth=1 "$url" "$name" - setTags "$name" "$src" - if [ ! -z "$src" ]; then - if [[ "$localTag" == *.* ]] && [[ "$localTag" < "$latestTag" ]]; then - latestTag=$localTag - git fetch --unshallow - fi - fi - elif [ ! -z "$src" ]; then # revert - setTags $dest - git remote | grep -q upstream && git remote remove upstream - git remote add upstream $url - git fetch upstream -q - git reset --hard upstream/master - else # refresh - setTags $dest - git reset --hard origin/master - fi + git -c advice.detachedHead=false checkout $latestTag + cd .. +} - git -c advice.detachedHead=false checkout $latestTag +isEmpty() { + db=$1 + if [ -f $db ]; then + if ! sqlite3 "$db" "select * from $db_table limit 1;" >/dev/null 2>&1 || [ -z "$(sqlite3 "$db" "select * from $db_table limit 1;")" ]; then + return 0 + fi + fi + return 1 } manageHistory() { - local init_db=/var/www/html/admin/scripts/pi-hole/speedtest/speedtest.db - local curr_db=/etc/pihole/speedtest.db - local last_db=/etc/pihole/speedtest.db.old - if [ "${1-}" == "db" ]; then - if [ -f $curr_db ] && [ -f $init_db ] && [ "$(hashFile $curr_db)" != "$(hashFile $init_db)" ]; then - echo "$(date) - Flushing Database..." - mv -f $curr_db $last_db - elif [ -f $last_db ]; then - echo "$(date) - Restoring Database..." - mv -f $last_db $curr_db - fi - fi + if [ "${1-}" == "db" ]; then + if [ -f $curr_db ] && ! isEmpty $curr_db; then + if [ -z "${2-}" ]; then + echo "$(date) - Flushing Database..." + mv -f $curr_db $last_db + fi + elif [ -f $last_db ]; then + echo "$(date) - Restoring Database..." + mv -f $last_db $curr_db + fi + echo "$(date) - Configuring Database..." + sqlite3 "$curr_db" "$create_table" + fi } uninstall() { - if cat /opt/pihole/webpage.sh | grep -q SpeedTest; then - echo "$(date) - Uninstalling Current Speedtest Mod..." + if [ -f $curr_wp ] && cat $curr_wp | grep -q SpeedTest; then + echo "$(date) - Uninstalling Current Speedtest Mod..." - if [ ! -f /opt/pihole/webpage.sh.org ]; then - if [ ! -d /opt/org_pihole ]; then - download /opt org_pihole https://github.com/pi-hole/pi-hole Pi-hole - fi - cd /opt - cp org_pihole/advanced/Scripts/webpage.sh ../pihole/webpage.sh.org - rm -rf org_pihole - fi + if [ ! -f $org_wp ]; then + if [ ! -d /opt/org_pihole ]; then + download /opt org_pihole https://github.com/pi-hole/pi-hole Pi-hole + fi + cd /opt + cp org_pihole/advanced/Scripts/webpage.sh $org_wp + rm -rf org_pihole + fi + + pihole -a -su + download /var/www/html admin https://github.com/pi-hole/AdminLTE web + if [ ! -f $last_wp ]; then + cp $curr_wp $last_wp + fi + cp $org_wp $curr_wp + chmod +x $curr_wp + fi + + manageHistory ${1-} +} - download /var/www/html admin https://github.com/pi-hole/AdminLTE web - cd /opt/pihole/ - cp webpage.sh.org webpage.sh - chmod +x webpage.sh - fi +purge() { + echo "$(date) - Cleaning up..." + rm -rf "$curr_wp".* + rm -rf "$admin_dir"*_admin + rm -rf "$curr_db".* + rm -rf "$curr_db"_* + rm -rf /opt/mod_pihole - manageHistory ${1-} + if isEmpty $curr_db; then + rm -f $curr_db + fi } uninstall ${1-} +purge echo "$(date) - Done!" exit 0 From 4ee4cddad0393ee6b65135581bb39abdd53f5672 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Fri, 26 Jan 2024 00:39:31 -0500 Subject: [PATCH 022/137] no header and title case and rename th --- settings.php | 2 +- speedtest.php | 10 +++------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/settings.php b/settings.php index 520db5c21..1908037aa 100644 --- a/settings.php +++ b/settings.php @@ -1625,7 +1625,7 @@ class="form-control"
diff --git a/speedtest.php b/speedtest.php index 1efe66702..ceba0c406 100644 --- a/speedtest.php +++ b/speedtest.php @@ -8,15 +8,11 @@ require 'scripts/pi-hole/php/header_authenticated.php'; ?> - -
-

Recent Speedtests | Export as CSV

+

Speedtest Results Export as CSV

@@ -25,7 +21,7 @@ ID - Test Time + Timestamp End Time Provider Your IP @@ -40,7 +36,7 @@ ID - Test Time + Timestamp End Time Provider Your IP From d8a0d530e002215e31c7700613f4a1bff5d27cc8 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Fri, 26 Jan 2024 00:58:09 -0500 Subject: [PATCH 023/137] comma --- speedtest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/speedtest.php b/speedtest.php index ceba0c406..a0b7891d0 100644 --- a/speedtest.php +++ b/speedtest.php @@ -12,7 +12,7 @@
-

Speedtest Results Export as CSV

+

Speedtest Results, Export as CSV

From 005e3760c208d2957e01e59ea4f5ae76c59a7764 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Fri, 26 Jan 2024 02:18:02 -0500 Subject: [PATCH 024/137] Allow fractional hours --- settings.php | 1 + 1 file changed, 1 insertion(+) diff --git a/settings.php b/settings.php index 1908037aa..f266085bb 100644 --- a/settings.php +++ b/settings.php @@ -1561,6 +1561,7 @@ class="form-control" value="" min="0" + step="0.00027778" >
From a03c18ef5d0181104cfcc4d8391a993df6500beb Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Sat, 27 Jan 2024 00:49:41 -0500 Subject: [PATCH 025/137] revamp settings, move chart --- index.php | 47 ++--- scripts/pi-hole/php/header_authenticated.php | 19 +- scripts/pi-hole/speedtest/uninstall.sh | 210 +++++++++---------- settings.php | 133 +++++++----- speedtest.php | 2 +- 5 files changed, 219 insertions(+), 192 deletions(-) diff --git a/index.php b/index.php index 4773eef11..b695af3c8 100644 --- a/index.php +++ b/index.php @@ -106,32 +106,6 @@ ?> - -
-
-
-
-

Speedtest results over last

-
-
-
- > -
-
- -
- -
-
-
-
- -
@@ -151,6 +125,27 @@
+ +
+
+
+
+

Speedtest results over last

+
+
+
+ > +
+
+ +
+ +
+
+
+
+ +
diff --git a/scripts/pi-hole/php/header_authenticated.php b/scripts/pi-hole/php/header_authenticated.php index 4e3d564ff..8f9cf9893 100644 --- a/scripts/pi-hole/php/header_authenticated.php +++ b/scripts/pi-hole/php/header_authenticated.php @@ -149,15 +149,22 @@ function getTemperature() // Get memory usage $memory_usage = getMemUsage(); +$speedtestshedule = false; +$speedtestdays = ''; +$speedtestcharttype = 'line'; if (isset($setupVars['SPEEDTESTSCHEDULE'])) { $speedtestshedule = $setupVars['SPEEDTESTSCHEDULE']; -} else { - $speedtestshedule = false; } -if (isset($setupVars['SPEEDTEST_CHART_DAYS']) && $setupVars['SPEEDTEST_CHART_DAYS'] > 1) { - $speedtestdays = $setupVars['SPEEDTEST_CHART_DAYS'].' days'; -} else { - $speedtestdays = '24 hours'; +if (isset($setupVars['SPEEDTEST_CHART_DAYS'])) { + if ($setupVars['SPEEDTEST_CHART_DAYS'] > 1) { + $speedtestdays = $setupVars['SPEEDTEST_CHART_DAYS'].' days'; + } + if ($setupVars['SPEEDTEST_CHART_DAYS'] == 1) { + $speedtestdays = '24 hours'; + } +} +if (isset($setupVars['SPEEDTEST_CHART_TYPE'])) { + $speedtestcharttype = $setupVars['SPEEDTEST_CHART_TYPE']; } $piholeFTLConf = piholeFTLConfig(); diff --git a/scripts/pi-hole/speedtest/uninstall.sh b/scripts/pi-hole/speedtest/uninstall.sh index 1cf29c766..1b49117b4 100644 --- a/scripts/pi-hole/speedtest/uninstall.sh +++ b/scripts/pi-hole/speedtest/uninstall.sh @@ -9,134 +9,134 @@ curr_db=/etc/pihole/speedtest.db last_db=$curr_db.old db_table="speedtest" create_table="create table if not exists $db_table ( - id integer primary key autoincrement, - start_time integer, - stop_time text, - from_server text, - from_ip text, - server text, - server_dist real, - server_ping real, - download real, - upload real, - share_url text +id integer primary key autoincrement, +start_time integer, +stop_time text, +from_server text, +from_ip text, +server text, +server_dist real, +server_ping real, +download real, +upload real, +share_url text );" setTags() { - local path=${1-} - local name=${2-} - if [ ! -z "$path" ]; then - cd "$path" - git fetch origin -q - git fetch --tags -f -q - latestTag=$(git describe --tags $(git rev-list --tags --max-count=1)) - fi - if [ ! -z "$name" ]; then - localTag=$(pihole -v | grep "$name" | cut -d ' ' -f 6) - [ "$localTag" == "HEAD" ] && localTag=$(pihole -v | grep "$name" | cut -d ' ' -f 7) - fi + local path=${1-} + local name=${2-} + if [ ! -z "$path" ]; then + cd "$path" + git fetch origin -q + git fetch --tags -f -q + latestTag=$(git describe --tags $(git rev-list --tags --max-count=1)) + fi + if [ ! -z "$name" ]; then + localTag=$(pihole -v | grep "$name" | cut -d ' ' -f 6) + [ "$localTag" == "HEAD" ] && localTag=$(pihole -v | grep "$name" | cut -d ' ' -f 7) + fi } download() { - local path=$1 - local name=$2 - local url=$3 - local src=${4-} - local dest=$path/$name - if [ ! -d $dest ]; then # replicate - cd "$path" - rm -rf "$name" - git clone --depth=1 "$url" "$name" - setTags "$name" "$src" - if [ ! -z "$src" ]; then - if [[ "$localTag" == *.* ]] && [[ "$localTag" < "$latestTag" ]]; then - latestTag=$localTag - git fetch --unshallow - fi - fi - else # replace - setTags $dest - if [ ! -z "$src" ]; then - if [ "$url" != "old" ]; then - git config --global --add safe.directory "$dest" - git remote -v | grep -q "old" || git remote rename origin old - git remote -v | grep -q "origin" && git remote remove origin - git remote add origin $url - else - git remote remove origin - git remote rename old origin - fi - git fetch origin -q - fi - git reset --hard origin/master - fi + local path=$1 + local name=$2 + local url=$3 + local src=${4-} + local dest=$path/$name + if [ ! -d $dest ]; then # replicate + cd "$path" + rm -rf "$name" + git clone --depth=1 "$url" "$name" + setTags "$name" "$src" + if [ ! -z "$src" ]; then + if [[ "$localTag" == *.* ]] && [[ "$localTag" < "$latestTag" ]]; then + latestTag=$localTag + git fetch --unshallow + fi + fi + else # replace + setTags $dest + if [ ! -z "$src" ]; then + if [ "$url" != "old" ]; then + git config --global --add safe.directory "$dest" + git remote -v | grep -q "old" || git remote rename origin old + git remote -v | grep -q "origin" && git remote remove origin + git remote add origin $url + else + git remote remove origin + git remote rename old origin + fi + git fetch origin -q + fi + git reset --hard origin/master + fi - git -c advice.detachedHead=false checkout $latestTag - cd .. + git -c advice.detachedHead=false checkout $latestTag + cd .. } isEmpty() { - db=$1 - if [ -f $db ]; then - if ! sqlite3 "$db" "select * from $db_table limit 1;" >/dev/null 2>&1 || [ -z "$(sqlite3 "$db" "select * from $db_table limit 1;")" ]; then - return 0 - fi - fi - return 1 + db=$1 + if [ -f $db ]; then + if ! sqlite3 "$db" "select * from $db_table limit 1;" >/dev/null 2>&1 || [ -z "$(sqlite3 "$db" "select * from $db_table limit 1;")" ]; then + return 0 + fi + fi + return 1 } manageHistory() { - if [ "${1-}" == "db" ]; then - if [ -f $curr_db ] && ! isEmpty $curr_db; then - if [ -z "${2-}" ]; then - echo "$(date) - Flushing Database..." - mv -f $curr_db $last_db - fi - elif [ -f $last_db ]; then - echo "$(date) - Restoring Database..." - mv -f $last_db $curr_db - fi - echo "$(date) - Configuring Database..." - sqlite3 "$curr_db" "$create_table" - fi + if [ "${1-}" == "db" ]; then + if [ -f $curr_db ] && ! isEmpty $curr_db; then + if [ -z "${2-}" ]; then + echo "$(date) - Flushing Database..." + mv -f $curr_db $last_db + fi + elif [ -f $last_db ]; then + echo "$(date) - Restoring Database..." + mv -f $last_db $curr_db + fi + echo "$(date) - Configuring Database..." + sqlite3 "$curr_db" "$create_table" + fi } uninstall() { - if [ -f $curr_wp ] && cat $curr_wp | grep -q SpeedTest; then - echo "$(date) - Uninstalling Current Speedtest Mod..." + if [ -f $curr_wp ] && cat $curr_wp | grep -q SpeedTest; then + echo "$(date) - Uninstalling Current Speedtest Mod..." - if [ ! -f $org_wp ]; then - if [ ! -d /opt/org_pihole ]; then - download /opt org_pihole https://github.com/pi-hole/pi-hole Pi-hole - fi - cd /opt - cp org_pihole/advanced/Scripts/webpage.sh $org_wp - rm -rf org_pihole - fi + if [ ! -f $org_wp ]; then + if [ ! -d /opt/org_pihole ]; then + download /opt org_pihole https://github.com/pi-hole/pi-hole Pi-hole + fi + cd /opt + cp org_pihole/advanced/Scripts/webpage.sh $org_wp + rm -rf org_pihole + fi - pihole -a -su - download /var/www/html admin https://github.com/pi-hole/AdminLTE web - if [ ! -f $last_wp ]; then - cp $curr_wp $last_wp - fi - cp $org_wp $curr_wp - chmod +x $curr_wp - fi + pihole -a -su + download /var/www/html admin https://github.com/pi-hole/AdminLTE web + if [ ! -f $last_wp ]; then + cp $curr_wp $last_wp + fi + cp $org_wp $curr_wp + chmod +x $curr_wp + fi - manageHistory ${1-} + manageHistory ${1-} } purge() { - echo "$(date) - Cleaning up..." - rm -rf "$curr_wp".* - rm -rf "$admin_dir"*_admin - rm -rf "$curr_db".* - rm -rf "$curr_db"_* - rm -rf /opt/mod_pihole + echo "$(date) - Cleaning up..." + rm -rf "$curr_wp".* + rm -rf "$admin_dir"*_admin + rm -rf "$curr_db".* + rm -rf "$curr_db"_* + rm -rf /opt/mod_pihole - if isEmpty $curr_db; then - rm -f $curr_db - fi + if isEmpty $curr_db; then + rm -f $curr_db + fi } uninstall ${1-} diff --git a/settings.php b/settings.php index f266085bb..3884f82f7 100644 --- a/settings.php +++ b/settings.php @@ -1530,108 +1530,133 @@
- -
-
-
- - + + + +
+ +
- -

Speedtest settings

- +

Basic

+
- -
- - + +

Any real number, down to a minute

+
+
Hours
+
+
- -
+ +

Integers only, max number to show

+
+
Days
+
- - +
+ + +
- +
/> - +
+
+
+
+
+
+ +
+
+
+

Advanced

+
+
+
+
-
- -

You can find the closest servers with speedtest or here. Experts only!

-
-
-
Speedtest.net Server
- -
-
-
- -
+
- +
- +
- +
+

Follow the process (if fast enough) and check the log with

+
sudo tmux attach-session -t pimod
+cat /var/log/pimod.log
-

Attach to a running process with

-
sudo tmux attach-session -t pimod
-

Or access the latest log whenever with

-
cat /var/log/pimod.log
+ +

The closest servers can be found using speedtest or here

+
+
id
+ +
-
- +
+
-
+ +
diff --git a/speedtest.php b/speedtest.php index a0b7891d0..88d2a9f2b 100644 --- a/speedtest.php +++ b/speedtest.php @@ -12,7 +12,7 @@
-

Speedtest Results, Export as CSV

+

Speedtest Results, Export as CSV

From 611d42a150d286b5389a87adb026171e9ace7265 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Sat, 27 Jan 2024 23:37:21 -0500 Subject: [PATCH 026/137] servers btn --- api_speedtest.php | 52 +++++++++++++++++++++++++++------- scripts/pi-hole/js/settings.js | 41 +++++++++++++++++++++++++++ settings.php | 7 +++-- 3 files changed, 86 insertions(+), 14 deletions(-) diff --git a/api_speedtest.php b/api_speedtest.php index 6f612226c..89136ebc5 100644 --- a/api_speedtest.php +++ b/api_speedtest.php @@ -14,24 +14,29 @@ $dbSpeedtest = '/etc/pihole/speedtest.db'; $dbSpeedtestOld = '/etc/pihole/speedtest.db.old'; +$url = 'https://c.speedtest.net/speedtest-servers-static.php'; $setupVars = parse_ini_file('/etc/pihole/setupVars.conf'); -if (isset($_GET['getSpeedData24hrs']) && $auth) { - $data = array_merge($data, getSpeedData24hrs($dbSpeedtest)); -} - -if (isset($_GET['getLastSpeedtestResult']) && $auth) { - $data = array_merge($data, getLastSpeedtestResult($dbSpeedtest)); -} - -if (isset($_GET['getAllSpeedTestData']) && $auth) { - $data = array_merge($data, getAllSpeedTestData($dbSpeedtest)); +if ($auth) { + if (isset($_GET['hasSpeedTestBackup'])) { + $data = array_merge($data, hasSpeedTestBackup($dbSpeedtestOld)); + } else if (isset($_GET['getSpeedData24hrs'])) { + $data = array_merge($data, getSpeedData24hrs($dbSpeedtest)); + } else if (isset($_GET['getLastSpeedtestResult'])) { + $data = array_merge($data, getLastSpeedtestResult($dbSpeedtest)); + } else if (isset($_GET['getAllSpeedTestData'])) { + $data = array_merge($data, getAllSpeedTestData($dbSpeedtest)); + } else if (isset($_GET['getClosestServers'])) { + $data = array_merge($data, getClosestServers($url)); + } } function hasSpeedTestBackup($dbSpeedtestOld) { - return file_exists($dbSpeedtestOld); + $data = file_exists($dbSpeedtestOld); + + return array('data' => $data); } function getAllSpeedTestData($dbSpeedtest) @@ -189,3 +194,28 @@ function print_titles($row) { echo implode(',', array_keys($row))."\n"; } + +function getClosestServers($url) +{ + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + + $xmlContent = curl_exec($ch); + curl_close($ch); + + if ($xmlContent === FALSE) { + return ["error" => "Error fetching XML"]; + } else { + $xml = simplexml_load_string($xmlContent); + if ($xml === FALSE) { + return ["error" => "Error parsing XML"]; + } + + // Convert XML to JSON and then to an associative array + $json = json_encode($xml); + $data = json_decode($json, true); + + return $data; + } +} diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index 5d8578dc5..731354e86 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -496,6 +496,47 @@ $(function () { speedtestUninstall.attr("value", null); speedtestDelete.attr("value", null); speedtestTest.attr("value", null); + + const btn = document.getElementById('closestServersBtn'); + const container = document.getElementById('closestServers'); + + btn.addEventListener('click', function() { + if (container.innerHTML) { + while (container.firstChild) { + container.removeChild(container.firstChild); + } + } else { + const xhr = new XMLHttpRequest(); + const dafaultStr = `

You can find them here

`; + + xhr.open("GET", "api.php?getClosestServers", true); + + xhr.onload = function () { + if (xhr.status === 200) { + const jsonData = JSON.parse(xhr.responseText); + if (!jsonData.error) { + jsonData.servers.server.forEach(server => { + const serverInfo = document.createElement('div'); + serverInfo.className = 'server'; + serverInfo.textContent = `${server.id}: ${server.name}, ${server.cc} (${server.sponsor})`; + container.appendChild(serverInfo); + }); + } + } + container.innerHTML = dafaultStr; + }; + + xhr.onerror = function () { + container.innerHTML = dafaultStr; + }; + + xhr.ontimeout = function () { + container.innerHTML = dafaultStr; + }; + + xhr.send(); + } + }); }); speedtestChartType.on("click", function () { diff --git a/settings.php b/settings.php index 3884f82f7..97fe554a8 100644 --- a/settings.php +++ b/settings.php @@ -1593,7 +1593,7 @@ class="form-control"
- +
@@ -1631,8 +1631,9 @@ class="form-control"
- -

The closest servers can be found using speedtest or here

+ + +
id
Date: Sun, 28 Jan 2024 10:47:37 -0500 Subject: [PATCH 027/137] buttons --- api_speedtest.php | 64 +++++--- scripts/pi-hole/js/settings.js | 215 +++++++++++++++++---------- scripts/pi-hole/php/savesettings.php | 2 +- settings.php | 21 +-- 4 files changed, 191 insertions(+), 111 deletions(-) diff --git a/api_speedtest.php b/api_speedtest.php index 89136ebc5..21a7665a2 100644 --- a/api_speedtest.php +++ b/api_speedtest.php @@ -14,21 +14,27 @@ $dbSpeedtest = '/etc/pihole/speedtest.db'; $dbSpeedtestOld = '/etc/pihole/speedtest.db.old'; -$url = 'https://c.speedtest.net/speedtest-servers-static.php'; $setupVars = parse_ini_file('/etc/pihole/setupVars.conf'); if ($auth) { if (isset($_GET['hasSpeedTestBackup'])) { $data = array_merge($data, hasSpeedTestBackup($dbSpeedtestOld)); - } else if (isset($_GET['getSpeedData24hrs'])) { + } + if (isset($_GET['getSpeedData24hrs'])) { $data = array_merge($data, getSpeedData24hrs($dbSpeedtest)); - } else if (isset($_GET['getLastSpeedtestResult'])) { + } + if (isset($_GET['getLastSpeedtestResult'])) { $data = array_merge($data, getLastSpeedtestResult($dbSpeedtest)); - } else if (isset($_GET['getAllSpeedTestData'])) { + } + if (isset($_GET['getAllSpeedTestData'])) { $data = array_merge($data, getAllSpeedTestData($dbSpeedtest)); - } else if (isset($_GET['getClosestServers'])) { - $data = array_merge($data, getClosestServers($url)); + } + if (isset($_GET['getLatestLog'])) { + $data = array_merge($data, getLatestLog()); + } + if (isset($_GET['getClosestServers'])) { + $data = array_merge($data, getClosestServers()); } } @@ -195,27 +201,39 @@ function print_titles($row) echo implode(',', array_keys($row))."\n"; } -function getClosestServers($url) +function speedtestExecute($command) { - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + $output = array(); + $return_status = -1; + exec('/bin/bash -c \''.$command.'\' 2>&1', $output, $return_status); - $xmlContent = curl_exec($ch); - curl_close($ch); + if ($return_status !== 0) { + trigger_error("Executing {$command} failed.", E_USER_WARNING); + } - if ($xmlContent === FALSE) { - return ["error" => "Error fetching XML"]; - } else { - $xml = simplexml_load_string($xmlContent); - if ($xml === FALSE) { - return ["error" => "Error parsing XML"]; - } + return $output; +} + +function getLatestLog() +{ + $log = speedtestExecute('cat /var/log/pimod.log'); + + $log = array_reverse($log); + $log = implode("\n", $log); - // Convert XML to JSON and then to an associative array - $json = json_encode($xml); - $data = json_decode($json, true); + return array('data' => $log); +} + +function getClosestServers() +{ + $closestServers = speedtestExecute('speedtest -h | grep -q official && speedtest -L || speedtest --list'); + // $closestServers = speedtestExecute('echo "set -x" > /tmp/speedtest.sh && echo "speedtest -h | grep -q speedtest-cli && speedtest --list || speedtest -L" >> /tmp/speedtest.sh && chmod +x /tmp/speedtest.sh && /tmp/speedtest.sh'); - return $data; + $closestServers = array_filter($closestServers); + if (count($closestServers) > 1) { + $closestServers = array_slice($closestServers, 1); } + $closestServers = implode("\n", $closestServers); + + return array('data' => $closestServers); } diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index 731354e86..ec7949f7e 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -474,6 +474,8 @@ $(function () { $(function () { const speedtestTest = $("#speedtesttest"); const speedtestServer = $("#speedtestserver"); + const speedtestServerBtn = $("#closestServersBtn"); + const speedtestServerCtr = $("#closestServers"); const speedtestChartType = $("#speedtestcharttype"); const speedtestChartTypeSave = $("#speedtestcharttypesave"); @@ -482,92 +484,88 @@ $(function () { const speedtestDelete = $("#speedtestdelete"); const speedtestDeleteLabel = speedtestDelete.parent().children("label"); + const speedtestLog = $("#latestLog"); + const speedtestLogBtn = $("#latestLogBtn"); + const speedtestSubmit = $("#st-submit"); const defaultClass = "btn-primary"; const colorClasses = ["btn-success", "btn-warning", "btn-danger"]; - let type = localStorage?.getItem("speedtest_chart_type") || speedtestChartType.attr("value"); - speedtestChartType.prop("checked", type === "bar"); - localStorage.setItem("speedtest_chart_type", type); - - document.addEventListener("DOMContentLoaded", function () { - speedtestChartTypeSave.attr("value", null); - speedtestUpdate.attr("value", null); - speedtestUninstall.attr("value", null); - speedtestDelete.attr("value", null); - speedtestTest.attr("value", null); - - const btn = document.getElementById('closestServersBtn'); - const container = document.getElementById('closestServers'); - - btn.addEventListener('click', function() { - if (container.innerHTML) { - while (container.firstChild) { - container.removeChild(container.firstChild); + const latestLog = () => { + $.ajax({ + url: "api.php?getLatestLog", + dataType: "json", + }) + .done(function (data) { + const log = data?.data; + speedtestLogBtn.text("Hide output"); + if (speedtestLog.find("pre").length > 0) { + speedtestLog.find("pre code").text(log); + } else if (log) { + const pre = document.createElement("pre"); + const code = document.createElement("code"); + code.textContent = log; + code.style.whiteSpace = "pre"; + pre.appendChild(code); + pre.style.width = "100%"; + pre.style.maxWidth = "100%"; + pre.style.overflowX = "auto"; + pre.style.whiteSpace = "pre"; + pre.style.overflowWrap = "normal"; + pre.style.marginTop = "1vw"; + speedtestLog.find("p").remove(); + speedtestLog.append(pre); } - } else { - const xhr = new XMLHttpRequest(); - const dafaultStr = `

You can find them here

`; - - xhr.open("GET", "api.php?getClosestServers", true); - - xhr.onload = function () { - if (xhr.status === 200) { - const jsonData = JSON.parse(xhr.responseText); - if (!jsonData.error) { - jsonData.servers.server.forEach(server => { - const serverInfo = document.createElement('div'); - serverInfo.className = 'server'; - serverInfo.textContent = `${server.id}: ${server.name}, ${server.cc} (${server.sponsor})`; - container.appendChild(serverInfo); - }); - } - } - container.innerHTML = dafaultStr; - }; - - xhr.onerror = function () { - container.innerHTML = dafaultStr; - }; - - xhr.ontimeout = function () { - container.innerHTML = dafaultStr; - }; - - xhr.send(); - } - }); - }); - - speedtestChartType.on("click", function () { - // if type null, set to "bar", else toggle - type = type ? (type === "bar" ? "line" : "bar") : "bar"; - speedtestChartType.attr("value", type); - localStorage.setItem("speedtest_chart_type", type); - - // Call check messages to make new setting effective - checkMessages(); - }); - - speedtestChartTypeSave.on("click", function () { - speedtestChartTypeSave.attr("value", speedtestChartTypeSave.attr("value") ? null : type); - }); - - speedtestUpdate.on("click", function () { - speedtestUpdate.attr("value", speedtestUpdate.attr("value") ? null : "up"); - }); - - speedtestTest.on("click", function () { - speedtestTest.attr("value", speedtestTest.attr("value") ? null : "yes"); - }); + }) + .fail(function () { + speedtestLogBtn.text("No log found"); + }); + }; - speedtestServer.on("change", function () { - speedtestServer.attr("value", speedtestServer.val()); - }); + const closestServers = () => { + $.ajax({ + url: "api.php?getClosestServers", + dataType: "json", + }) + .done(function (data) { + const serversInfo = data?.data; + if (serversInfo) { + const pre = document.createElement("pre"); + const code = document.createElement("code"); + code.textContent = serversInfo; + code.style.whiteSpace = "pre"; + pre.appendChild(code); + pre.style.width = "100%"; + pre.style.maxWidth = "100%"; + pre.style.overflowX = "auto"; + pre.style.whiteSpace = "pre"; + pre.style.overflowWrap = "normal"; + pre.style.marginTop = "1vw"; + speedtestServerCtr.find("p").remove(); + speedtestServerCtr.append(pre); + speedtestServerBtn.text("Hide servers"); + } else { + speedtestServerBtn.text("Failed to get servers"); + if (speedtestServerCtr.find("p").length === 0) { + speedtestServerCtr.append( + `

You can also open this XML file to see them

` + ); + } + } + }) + .fail(function () { + speedtestServerBtn.text("Failed to get servers"); + if (speedtestServerCtr.find("p").length === 0) { + speedtestServerCtr.append( + `

You can also open this XML file to see them

` + ); + } + }); + }; const hasBackup = callback => { $.ajax({ - url: "api.php?hasSpeedTestBackup&PHP", + url: "api.php?hasSpeedTestBackup", dataType: "json", }) .done(function (backupExists) { @@ -580,7 +578,7 @@ $(function () { const hasHistory = callback => { $.ajax({ - url: "api.php?getAllSpeedTestData&PHP", + url: "api.php?getAllSpeedTestData", dataType: "json", }) .done(function (results) { @@ -619,6 +617,64 @@ $(function () { }); }; + let type = localStorage?.getItem("speedtest_chart_type") || speedtestChartType.attr("value"); + speedtestChartType.prop("checked", type === "bar"); + localStorage.setItem("speedtest_chart_type", type); + + document.addEventListener("DOMContentLoaded", function () { + speedtestChartTypeSave.attr("value", null); + speedtestUpdate.attr("value", null); + speedtestUninstall.attr("value", null); + speedtestDelete.attr("value", null); + speedtestTest.attr("value", null); + }); + + speedtestChartType.on("click", function () { + // if type null, set to "bar", else toggle + type = type ? (type === "bar" ? "line" : "bar") : "bar"; + speedtestChartType.attr("value", type); + localStorage.setItem("speedtest_chart_type", type); + + // Call check messages to make new setting effective + checkMessages(); + }); + + speedtestChartTypeSave.on("click", function () { + speedtestChartTypeSave.attr("value", speedtestChartTypeSave.attr("value") ? null : type); + }); + + speedtestUpdate.on("click", function () { + speedtestUpdate.attr("value", speedtestUpdate.attr("value") ? null : "up"); + }); + + speedtestTest.on("click", function () { + speedtestTest.attr("value", speedtestTest.attr("value") ? null : "yes"); + }); + + speedtestServer.on("change", function () { + speedtestServer.attr("value", speedtestServer.val()); + }); + + speedtestLogBtn.on("click", function () { + const log = speedtestLog.find("pre"); + if (log.length > 0) { + log.remove(); + speedtestLogBtn.text("Show latest log"); + } else { + latestLog(); + } + }); + + speedtestServerBtn.on("click", function () { + const closestServersList = speedtestServerCtr.find("pre"); + if (closestServersList.length > 0) { + closestServersList.remove(); + speedtestServerBtn.text("Show closest servers"); + } else { + closestServers(); + } + }); + speedtestUninstall.on("click", function () { speedtestUninstall.attr("value", speedtestUninstall.attr("value") ? null : "un"); canRestore(); @@ -630,6 +686,9 @@ $(function () { }); setInterval(() => { + if (speedtestLog.find("pre").length > 0) { + latestLog(); + } canRestore(); }, 1000); }); diff --git a/scripts/pi-hole/php/savesettings.php b/scripts/pi-hole/php/savesettings.php index adec0ac17..6e2809a27 100644 --- a/scripts/pi-hole/php/savesettings.php +++ b/scripts/pi-hole/php/savesettings.php @@ -622,7 +622,7 @@ function addStaticDHCPLease($mac, $ip, $hostname) if (isset($_POST['speedtesttest'])) { $success .= ' and a speedtest has been started'; - pihole_execute('-a -sn'); + pihole_execute('-a -sn', !isset($_POST['speedtestuninstall'])); } if (isset($_POST['speedtestupdate'])) { diff --git a/settings.php b/settings.php index 97fe554a8..cf272e97e 100644 --- a/settings.php +++ b/settings.php @@ -1611,8 +1611,9 @@ class="form-control"
-
+
+

For the adventurous

@@ -1625,15 +1626,14 @@ class="form-control"
-

Follow the process (if fast enough) and check the log with

-
sudo tmux attach-session -t pimod
-cat /var/log/pimod.log
+

+ +

-
- - -
+
+ +

If you know better

id
+

+ +

From 307e0ef9ea29e63fb3ff95b0b9b5e7e61898f0d3 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Sun, 28 Jan 2024 11:09:23 -0500 Subject: [PATCH 028/137] sudo? --- api_speedtest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api_speedtest.php b/api_speedtest.php index 21a7665a2..357e141c3 100644 --- a/api_speedtest.php +++ b/api_speedtest.php @@ -205,7 +205,7 @@ function speedtestExecute($command) { $output = array(); $return_status = -1; - exec('/bin/bash -c \''.$command.'\' 2>&1', $output, $return_status); + exec('sudo /bin/bash -c \''.$command.'\' 2>&1', $output, $return_status); if ($return_status !== 0) { trigger_error("Executing {$command} failed.", E_USER_WARNING); @@ -226,7 +226,7 @@ function getLatestLog() function getClosestServers() { - $closestServers = speedtestExecute('speedtest -h | grep -q official && speedtest -L || speedtest --list'); + $closestServers = speedtestExecute('sudo speedtest -h | grep -q official && sudo speedtest -L || speedtest --list'); // $closestServers = speedtestExecute('echo "set -x" > /tmp/speedtest.sh && echo "speedtest -h | grep -q speedtest-cli && speedtest --list || speedtest -L" >> /tmp/speedtest.sh && chmod +x /tmp/speedtest.sh && /tmp/speedtest.sh'); $closestServers = array_filter($closestServers); From a335821e3059403bdf84adbc1bf8907fe42a671d Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Sun, 28 Jan 2024 11:27:36 -0500 Subject: [PATCH 029/137] sudo in bash --- api_speedtest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api_speedtest.php b/api_speedtest.php index 357e141c3..5a9d2f13b 100644 --- a/api_speedtest.php +++ b/api_speedtest.php @@ -205,7 +205,7 @@ function speedtestExecute($command) { $output = array(); $return_status = -1; - exec('sudo /bin/bash -c \''.$command.'\' 2>&1', $output, $return_status); + exec('/bin/bash -c \''.$command.'\' 2>&1', $output, $return_status); if ($return_status !== 0) { trigger_error("Executing {$command} failed.", E_USER_WARNING); From 5b9065f6d7e09fa2f9ae03dd7e817166e5951ba9 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Sun, 28 Jan 2024 12:30:53 -0500 Subject: [PATCH 030/137] noerr --- api_speedtest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api_speedtest.php b/api_speedtest.php index 5a9d2f13b..84115ba5d 100644 --- a/api_speedtest.php +++ b/api_speedtest.php @@ -205,7 +205,7 @@ function speedtestExecute($command) { $output = array(); $return_status = -1; - exec('/bin/bash -c \''.$command.'\' 2>&1', $output, $return_status); + exec('/bin/bash -c \''.$command.'\'', $output, $return_status); if ($return_status !== 0) { trigger_error("Executing {$command} failed.", E_USER_WARNING); @@ -226,7 +226,7 @@ function getLatestLog() function getClosestServers() { - $closestServers = speedtestExecute('sudo speedtest -h | grep -q official && sudo speedtest -L || speedtest --list'); + $closestServers = speedtestExecute('speedtest -h | grep -q official && sudo speedtest -L || speedtest --list'); // $closestServers = speedtestExecute('echo "set -x" > /tmp/speedtest.sh && echo "speedtest -h | grep -q speedtest-cli && speedtest --list || speedtest -L" >> /tmp/speedtest.sh && chmod +x /tmp/speedtest.sh && /tmp/speedtest.sh'); $closestServers = array_filter($closestServers); From aeab80c86d041c5597a63f95eedc3616e4c48a1d Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Sun, 28 Jan 2024 12:33:17 -0500 Subject: [PATCH 031/137] pretty --- scripts/pi-hole/js/settings.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index ec7949f7e..a0d797c1c 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -506,7 +506,7 @@ $(function () { const code = document.createElement("code"); code.textContent = log; code.style.whiteSpace = "pre"; - pre.appendChild(code); + pre.append(code); pre.style.width = "100%"; pre.style.maxWidth = "100%"; pre.style.overflowX = "auto"; @@ -534,7 +534,7 @@ $(function () { const code = document.createElement("code"); code.textContent = serversInfo; code.style.whiteSpace = "pre"; - pre.appendChild(code); + pre.append(code); pre.style.width = "100%"; pre.style.maxWidth = "100%"; pre.style.overflowX = "auto"; @@ -689,6 +689,7 @@ $(function () { if (speedtestLog.find("pre").length > 0) { latestLog(); } + canRestore(); }, 1000); }); From e801aa2d56a161aba96bb3fc9c86142afb64ecf8 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Tue, 30 Jan 2024 02:57:05 -0500 Subject: [PATCH 032/137] new buttons!!!! --- api_speedtest.php | 76 +++++-- scripts/pi-hole/js/index.js | 2 + scripts/pi-hole/js/settings.js | 214 ++++++++++++++++--- scripts/pi-hole/js/speedresults.js | 2 +- scripts/pi-hole/js/speedtest.js | 104 ++++----- scripts/pi-hole/php/header_authenticated.php | 3 + settings.php | 19 +- speedtest.php | 4 +- 8 files changed, 314 insertions(+), 110 deletions(-) diff --git a/api_speedtest.php b/api_speedtest.php index 84115ba5d..b6bed2979 100644 --- a/api_speedtest.php +++ b/api_speedtest.php @@ -17,12 +17,18 @@ $setupVars = parse_ini_file('/etc/pihole/setupVars.conf'); +$cmdLog = 'cat /var/log/pimod.log'; +$cmdServers = 'speedtest -h | grep -q official && sudo speedtest -L || speedtest --list'; +$cmdStatus = 'systemctl status pihole-speedtest.timer ; systemctl status pihole-speedtest --no-pager -l'; +$cmdRun = 'cat /tmp/speedtest.log'; +$cmdServersCurl = "curl 'https://c.speedtest.net/speedtest-servers-static.php' --compressed -H 'Upgrade-Insecure-Requests: 1' -H 'DNT: 1' -H 'Sec-GPC: 1'"; + if ($auth) { if (isset($_GET['hasSpeedTestBackup'])) { $data = array_merge($data, hasSpeedTestBackup($dbSpeedtestOld)); } - if (isset($_GET['getSpeedData24hrs'])) { - $data = array_merge($data, getSpeedData24hrs($dbSpeedtest)); + if (isset($_GET['getSpeedData'])) { + $data = array_merge($data, getSpeedData($dbSpeedtest, $_GET['getSpeedData'])); } if (isset($_GET['getLastSpeedtestResult'])) { $data = array_merge($data, getLastSpeedtestResult($dbSpeedtest)); @@ -31,10 +37,19 @@ $data = array_merge($data, getAllSpeedTestData($dbSpeedtest)); } if (isset($_GET['getLatestLog'])) { - $data = array_merge($data, getLatestLog()); + $data = array_merge($data, speedtestExecute($cmdLog)); } if (isset($_GET['getClosestServers'])) { - $data = array_merge($data, getClosestServers()); + $data = array_merge($data, getServers($cmdServers)); + } + if (isset($_GET['getSpeedTestStatus'])) { + $data = array_merge($data, speedtestExecute($cmdStatus)); + } + if (isset($_GET['getLatestRun'])) { + $data = array_merge($data, speedtestExecute($cmdRun)); + } + if (isset($_GET['curlClosestServers'])) { + $data = array_merge($data, curlServers($cmdServersCurl)); } } @@ -140,13 +155,14 @@ function getSpeedTestData($dbSpeedtest, $durationdays = '1') $db->close(); } -function getSpeedData24hrs($dbSpeedtest) +function getSpeedData($dbSpeedtest, $durationdays = '-2') { global $log, $setupVars; - if (isset($setupVars['SPEEDTEST_CHART_DAYS'])) { + if (isset($setupVars['SPEEDTEST_CHART_DAYS']) && $durationdays == '-2') { $dataFromSpeedDB = getSpeedTestData($dbSpeedtest, $setupVars['SPEEDTEST_CHART_DAYS']); } else { - $dataFromSpeedDB = getSpeedTestData($dbSpeedtest); + $durationdays = (int) $durationdays < -1 ? '1' : $durationdays; + $dataFromSpeedDB = getSpeedTestData($dbSpeedtest, $durationdays); } return $dataFromSpeedDB; @@ -211,29 +227,45 @@ function speedtestExecute($command) trigger_error("Executing {$command} failed.", E_USER_WARNING); } - return $output; + return array('data' => implode("\n", $output)); } -function getLatestLog() +function getServers($cmdServers) { - $log = speedtestExecute('cat /var/log/pimod.log'); + $array = speedtestExecute($cmdServers); + $servers = $array['data']; - $log = array_reverse($log); - $log = implode("\n", $log); + $output = explode("\n", $servers); + $output = array_filter($output); + if (count($output) > 1) { + array_shift($output); + } + $servers = implode("\n", $output); - return array('data' => $log); + if ($servers === false) { + return array('error' => 'Error fetching servers'); + } else { + return array('data' => $servers); + } } -function getClosestServers() +function curlServers($cmdServersCurl) { - $closestServers = speedtestExecute('speedtest -h | grep -q official && sudo speedtest -L || speedtest --list'); - // $closestServers = speedtestExecute('echo "set -x" > /tmp/speedtest.sh && echo "speedtest -h | grep -q speedtest-cli && speedtest --list || speedtest -L" >> /tmp/speedtest.sh && chmod +x /tmp/speedtest.sh && /tmp/speedtest.sh'); + $array = speedtestExecute($cmdServersCurl); + $xmlContent = $array['data']; - $closestServers = array_filter($closestServers); - if (count($closestServers) > 1) { - $closestServers = array_slice($closestServers, 1); - } - $closestServers = implode("\n", $closestServers); + if ($xmlContent === false) { + return array('error' => 'Error fetching XML'); + } else { + $xml = simplexml_load_string($xmlContent); + if ($xml === false) { + return array('error' => 'Error parsing XML'); + } + $serverList = array(); + foreach ($xml->servers->server as $server) { + $serverList[] = str_pad($server['id'], 5, ' ', STR_PAD_LEFT).') '.$server['sponsor'].' ('.$server['name'].', '.$server['cc'].') ('.$server['lat'].', '.$server['lon'].')'; + } - return array('data' => $closestServers); + return array('data' => implode("\n", $serverList)); + } } diff --git a/scripts/pi-hole/js/index.js b/scripts/pi-hole/js/index.js index 329bf04d2..3411e8af5 100644 --- a/scripts/pi-hole/js/index.js +++ b/scripts/pi-hole/js/index.js @@ -1230,3 +1230,5 @@ $(function () { window.addEventListener("resize", function () { $(".chartjs-tooltip").remove(); }); + +localStorage.setItem("speedtest_days", -1); diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index a0d797c1c..28d5e36f4 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -472,12 +472,19 @@ $(function () { // Speedtest toggles $(function () { + const speedtestDays = $("#speedtestdays"); const speedtestTest = $("#speedtesttest"); + const speedtestStatus = $("#speedteststatus"); + const speedtestStatusBtn = $("#speedteststatusBtn"); + const speedtestServer = $("#speedtestserver"); const speedtestServerBtn = $("#closestServersBtn"); const speedtestServerCtr = $("#closestServers"); + const speedtestChartType = $("#speedtestcharttype"); const speedtestChartTypeSave = $("#speedtestcharttypesave"); + const speedtestChartPreview = $("#speedtestchartpreview"); + const speedtestChartPreviewBtn = $("#speedtestchartpreviewBtn"); const speedtestUpdate = $("#speedtestupdate"); const speedtestUninstall = $("#speedtestuninstall"); @@ -491,6 +498,114 @@ $(function () { const defaultClass = "btn-primary"; const colorClasses = ["btn-success", "btn-warning", "btn-danger"]; + let type = localStorage?.getItem("speedtest_chart_type") || speedtestChartType.attr("value"); + speedtestChartType.prop("checked", type === "bar"); + localStorage.setItem("speedtest_chart_type", type); + + const codeBlock = text => { + const pre = document.createElement("pre"); + const code = document.createElement("code"); + code.textContent = text; + code.style.whiteSpace = "pre"; + pre.append(code); + pre.style.width = "100%"; + pre.style.maxWidth = "100%"; + pre.style.overflowX = "auto"; + pre.style.whiteSpace = "pre"; + pre.style.overflowWrap = "normal"; + pre.style.marginTop = "1vw"; + return pre; + }; + + const serviceStatus = () => { + $.ajax({ + url: "api.php?getSpeedTestStatus", + dataType: "json", + }) + .done(function (data) { + const status = data?.data; + let triggerText = speedtestTest.attr("value") ? " awaiting confirmation" : " disabled"; + let pre = codeBlock("Schedule is inactive\nNext run is" + triggerText); + if (status) { + const scheduleStatusPattern = /pihole-speedtest\.timer.*?Active:\s+(\w+)/s; + const triggerPattern = /Trigger:.*?;\s*([\d\s\w]+)\s+left/s; + //const lastRunPattern = /pihole-speedtest\.service.*?Active: inactive \(dead\) since.*?;\s+([\w\s]+ago).*?pihole-speedtest\.service: (\w+)/s; + + const scheduleStatusMatch = status.match(scheduleStatusPattern); + const triggerMatch = status.match(triggerPattern); + //const lastRunMatch = status.match(lastRunPattern); + + const scheduleStatusText = scheduleStatusMatch ? scheduleStatusMatch[1] : "off"; + triggerText = speedtestTest.attr("value") + ? " awaiting confirmation" + : triggerMatch + ? ` in ${triggerMatch[1]}` + : " disabled"; + //const lastRunText = lastRunMatch ? ` ${lastRunMatch[1]} ago, it ${lastRunMatch[2].toLowerCase()}` : ' never'; + pre = codeBlock(`Schedule is ${scheduleStatusText}\nNext run is${triggerText}`); //\nLast run was${lastRunText}`); + } + + speedtestStatus.find("pre").remove(); + speedtestStatus.append(pre); + }) + .fail(function () { + speedtestStatusBtn.text("Failed to get status"); + }); + }; + + const previewChart = preview => { + if (!preview) { + speedtestChartPreview.find("div").remove(); + } else { + let speedtestdays = speedtestDays.val(); + const speedtestcharttype = speedtestChartType.val(); + + if (speedtestdays === "1") { + speedtestdays = "24 hours"; + } else if (speedtestdays === "-1") { + speedtestdays = "however many days"; + } else { + speedtestdays += " days"; + } + + const colDiv = document.createElement("div"); + const boxDiv = document.createElement("div"); + const boxHeaderDiv = document.createElement("div"); + const h3 = document.createElement("h3"); + const boxBodyDiv = document.createElement("div"); + const chartDiv = document.createElement("div"); + const canvas = document.createElement("canvas"); + + colDiv.className = "col-md-12"; + boxDiv.className = "box"; + boxDiv.id = "queries-over-time"; + boxHeaderDiv.className = "box-header with-border"; + h3.className = "box-title"; + h3.textContent = `Speedtest results over last ${speedtestdays}`; + boxBodyDiv.className = "box-body"; + chartDiv.className = "chart"; + chartDiv.style.position = "relative"; + chartDiv.style.width = "100%"; + chartDiv.style.height = "180px"; + canvas.id = "speedOverTimeChart"; + canvas.setAttribute("value", speedtestcharttype); + + colDiv.style.marginTop = "1vw"; + + colDiv.append(boxDiv); + boxDiv.append(boxHeaderDiv); + boxHeaderDiv.append(h3); + boxDiv.append(boxBodyDiv); + boxBodyDiv.append(chartDiv); + chartDiv.append(canvas); + + speedtestChartPreview.find("div").remove(); + speedtestChartPreview.append(colDiv); + } + + speedtestChartPreviewBtn.text(preview ? "Hide preview" : "Show chart preview"); + }; + const latestLog = () => { $.ajax({ url: "api.php?getLatestLog", @@ -502,17 +617,7 @@ $(function () { if (speedtestLog.find("pre").length > 0) { speedtestLog.find("pre code").text(log); } else if (log) { - const pre = document.createElement("pre"); - const code = document.createElement("code"); - code.textContent = log; - code.style.whiteSpace = "pre"; - pre.append(code); - pre.style.width = "100%"; - pre.style.maxWidth = "100%"; - pre.style.overflowX = "auto"; - pre.style.whiteSpace = "pre"; - pre.style.overflowWrap = "normal"; - pre.style.marginTop = "1vw"; + const pre = codeBlock(log); speedtestLog.find("p").remove(); speedtestLog.append(pre); } @@ -530,34 +635,46 @@ $(function () { .done(function (data) { const serversInfo = data?.data; if (serversInfo) { - const pre = document.createElement("pre"); - const code = document.createElement("code"); - code.textContent = serversInfo; - code.style.whiteSpace = "pre"; - pre.append(code); - pre.style.width = "100%"; - pre.style.maxWidth = "100%"; - pre.style.overflowX = "auto"; - pre.style.whiteSpace = "pre"; - pre.style.overflowWrap = "normal"; - pre.style.marginTop = "1vw"; + const pre = codeBlock(serversInfo); speedtestServerCtr.find("p").remove(); speedtestServerCtr.append(pre); speedtestServerBtn.text("Hide servers"); } else { - speedtestServerBtn.text("Failed to get servers"); - if (speedtestServerCtr.find("p").length === 0) { - speedtestServerCtr.append( - `

You can also open this XML file to see them

` - ); - } + $.ajax({ + url: "api.php?curlClosestServers", + dataType: "json", + }) + .done(function (data) { + const serversInfo = data?.data; + if (serversInfo) { + const pre = codeBlock(serversInfo); + speedtestServerCtr.find("p").remove(); + speedtestServerCtr.append(pre); + speedtestServerBtn.text("Hide servers"); + } else { + speedtestServerBtn.text("Failed to get servers"); + if (speedtestServerCtr.find("p").length === 0) { + speedtestServerCtr.append( + `

You can also open this XML file to see them

` + ); + } + } + }) + .fail(function () { + speedtestServerBtn.text("Failed to get servers"); + if (speedtestServerCtr.find("p").length === 0) { + speedtestServerCtr.append( + `

You can also open this XML file to see them

` + ); + } + }); } }) .fail(function () { speedtestServerBtn.text("Failed to get servers"); if (speedtestServerCtr.find("p").length === 0) { speedtestServerCtr.append( - `

You can also open this XML file to see them

` + `

You can also open this XML file to see them

` ); } }); @@ -617,11 +734,8 @@ $(function () { }); }; - let type = localStorage?.getItem("speedtest_chart_type") || speedtestChartType.attr("value"); - speedtestChartType.prop("checked", type === "bar"); - localStorage.setItem("speedtest_chart_type", type); - document.addEventListener("DOMContentLoaded", function () { + speedtestDays.attr("value", speedtestDays.val()); speedtestChartTypeSave.attr("value", null); speedtestUpdate.attr("value", null); speedtestUninstall.attr("value", null); @@ -629,6 +743,15 @@ $(function () { speedtestTest.attr("value", null); }); + localStorage.setItem("speedtest_days", speedtestDays.val()); + speedtestDays.on("change", function () { + speedtestDays.attr("value", speedtestDays.val()); + if (speedtestDays.val()) { + localStorage.setItem("speedtest_days", speedtestDays.val()); + previewChart(speedtestChartPreview.find("div").length > 0); + } + }); + speedtestChartType.on("click", function () { // if type null, set to "bar", else toggle type = type ? (type === "bar" ? "line" : "bar") : "bar"; @@ -637,18 +760,38 @@ $(function () { // Call check messages to make new setting effective checkMessages(); + previewChart(speedtestChartPreview.find("div").length > 0); }); speedtestChartTypeSave.on("click", function () { speedtestChartTypeSave.attr("value", speedtestChartTypeSave.attr("value") ? null : type); }); + speedtestChartPreviewBtn.on("click", function () { + previewChart(speedtestChartPreview.find("div").length === 0); + }); + speedtestUpdate.on("click", function () { speedtestUpdate.attr("value", speedtestUpdate.attr("value") ? null : "up"); }); speedtestTest.on("click", function () { speedtestTest.attr("value", speedtestTest.attr("value") ? null : "yes"); + const status = speedtestStatus.find("pre"); + if (status.length > 0) { + serviceStatus(); + } + }); + + speedtestStatusBtn.on("click", function () { + const status = speedtestStatus.find("pre"); + if (status.length > 0) { + speedtestStatusBtn.text("Show service status"); + status.remove(); + } else { + speedtestStatusBtn.text("Hide status"); + serviceStatus(); + } }); speedtestServer.on("change", function () { @@ -671,6 +814,7 @@ $(function () { closestServersList.remove(); speedtestServerBtn.text("Show closest servers"); } else { + speedtestServerBtn.text("Retrieving servers..."); closestServers(); } }); @@ -686,6 +830,10 @@ $(function () { }); setInterval(() => { + if (speedtestStatus.find("pre").length > 0) { + serviceStatus(); + } + if (speedtestLog.find("pre").length > 0) { latestLog(); } diff --git a/scripts/pi-hole/js/speedresults.js b/scripts/pi-hole/js/speedresults.js index d84b8ecc4..1a1b087e2 100644 --- a/scripts/pi-hole/js/speedresults.js +++ b/scripts/pi-hole/js/speedresults.js @@ -32,7 +32,7 @@ $(document).ready(function () { GETDict[item.split("=")[0]] = item.split("=")[1]; }); - var APIstring = "api.php?getAllSpeedTestData&PHP"; + var APIstring = "api.php?getAllSpeedTestData"; tableApi = $("#all-queries").DataTable({ dom: diff --git a/scripts/pi-hole/js/speedtest.js b/scripts/pi-hole/js/speedtest.js index 40539e23d..f744e0ed7 100644 --- a/scripts/pi-hole/js/speedtest.js +++ b/scripts/pi-hole/js/speedtest.js @@ -16,56 +16,17 @@ function getCSSval(cssclass, cssproperty) { return val; } -$(function () { - var speedlabels = []; - var downloadspeed = []; - var uploadspeed = []; - var serverPing = []; - - function updateSpeedTestData() { - function formatDate(itemdate, results) { - if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) { - return moment(itemdate, "YYYY-MM-DD HH:mm:ss Z") - .utcOffset(moment().utcOffset()) - .format("HH:mm"); - } - - let output = "HH:mm"; - if (results.length > 1) { - const first = moment(results[0].start_time); - const last = moment(results.at(-1).start_time); - if (last.diff(first, "hours") >= 24) output = "Do " + output; - } - - return moment(itemdate).utcOffset(moment().utcOffset()).format(output); - } - - $.ajax({ - url: "api.php?getSpeedData24hrs&PHP", - dataType: "json", - }).done(function (results) { - results.forEach(function (packet) { - // console.log(speedlabels.indexOf(formatDate(packet.start_time))); - if (speedlabels.indexOf(formatDate(packet.start_time, results)) === -1) { - speedlabels.push(formatDate(packet.start_time, results)); - uploadspeed.push(parseFloat(packet.upload)); - downloadspeed.push(parseFloat(packet.download)); - serverPing.push(parseFloat(packet.server_ping)); - } - }); - speedChart.update(); - }); - } - - setInterval(function () { - // console.log('updateSpeedTestData'); - updateSpeedTestData(); - }, 6000); - +var speedChart = null; +var speedlabels = []; +var downloadspeed = []; +var uploadspeed = []; +var serverPing = []; +function createChart() { var gridColor = getCSSval("graphs-grid", "background-color"); var ticksColor = getCSSval("graphs-ticks", "color"); - var speedChartctx = document.getElementById("speedOverTimeChart").getContext("2d"); - var speedChart = new Chart(speedChartctx, { + var speedChartctx = document.getElementById("speedOverTimeChart")?.getContext("2d"); + if (speedChartctx === null || speedChartctx === undefined) return; + speedChart = new Chart(speedChartctx, { type: getGraphType(1), data: { labels: speedlabels, @@ -154,6 +115,53 @@ $(function () { }, }, }); +} +function formatDate(itemdate, results) { + if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) { + return moment(itemdate, "YYYY-MM-DD HH:mm:ss Z") + .utcOffset(moment().utcOffset()) + .format("HH:mm"); + } + + let output = "HH:mm"; + if (results.length > 1) { + const first = moment(results[0].start_time, "YYYY-MM-DD HH:mm:ss Z"); + const last = moment(results.at(-1).start_time, "YYYY-MM-DD HH:mm:ss Z"); + if (last.diff(first, "hours") >= 24) output = "Do " + output; + } + + return moment(itemdate).utcOffset(moment().utcOffset()).format(output); +} + +function updateSpeedTestData() { + const days = localStorage?.getItem("speedtest_days") || -1; + speedlabels = []; + downloadspeed = []; + uploadspeed = []; + serverPing = []; + + $.ajax({ + url: "api.php?getSpeedData=" + days, + dataType: "json", + }).done(function (results) { + results.forEach(function (packet) { + // console.log(speedlabels.indexOf(formatDate(packet.start_time))); + if (speedlabels.indexOf(formatDate(packet.start_time, results)) === -1) { + speedlabels.push(formatDate(packet.start_time, results)); + uploadspeed.push(parseFloat(packet.upload)); + downloadspeed.push(parseFloat(packet.download)); + serverPing.push(parseFloat(packet.server_ping)); + } + }); + if (speedChart && !days) speedChart.update(); + else createChart(speedlabels); + }); +} + +$(function () { updateSpeedTestData(); + setInterval(function () { + updateSpeedTestData(); + }, 6000); }); diff --git a/scripts/pi-hole/php/header_authenticated.php b/scripts/pi-hole/php/header_authenticated.php index 8f9cf9893..4cf335dd4 100644 --- a/scripts/pi-hole/php/header_authenticated.php +++ b/scripts/pi-hole/php/header_authenticated.php @@ -162,6 +162,9 @@ function getTemperature() if ($setupVars['SPEEDTEST_CHART_DAYS'] == 1) { $speedtestdays = '24 hours'; } + if ($setupVars['SPEEDTEST_CHART_DAYS'] == -1) { + $speedtestdays = 'however many days'; + } } if (isset($setupVars['SPEEDTEST_CHART_TYPE'])) { $speedtestcharttype = $setupVars['SPEEDTEST_CHART_TYPE']; diff --git a/settings.php b/settings.php index cf272e97e..0aba09f2f 100644 --- a/settings.php +++ b/settings.php @@ -1567,26 +1567,31 @@ class="form-control"
Days
+
- +
+

+ +

- +
/> @@ -1595,6 +1600,9 @@ class="form-control"
+

+ +

@@ -1673,3 +1681,6 @@ class="form-control" + + + diff --git a/speedtest.php b/speedtest.php index 88d2a9f2b..53e4681d5 100644 --- a/speedtest.php +++ b/speedtest.php @@ -21,7 +21,7 @@ ID - Timestamp + Start Time End Time Provider Your IP @@ -36,7 +36,7 @@ ID - Timestamp + Start Time End Time Provider Your IP From 8ef4a691f9b90d17756580fd69183e7e5793f1db Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Tue, 30 Jan 2024 03:46:41 -0500 Subject: [PATCH 033/137] update uninstall --- scripts/pi-hole/speedtest/uninstall.sh | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/scripts/pi-hole/speedtest/uninstall.sh b/scripts/pi-hole/speedtest/uninstall.sh index 1b49117b4..4d9ac8ab2 100644 --- a/scripts/pi-hole/speedtest/uninstall.sh +++ b/scripts/pi-hole/speedtest/uninstall.sh @@ -89,21 +89,21 @@ manageHistory() { if [ "${1-}" == "db" ]; then if [ -f $curr_db ] && ! isEmpty $curr_db; then if [ -z "${2-}" ]; then - echo "$(date) - Flushing Database..." + echo "Flushing Database..." mv -f $curr_db $last_db fi elif [ -f $last_db ]; then - echo "$(date) - Restoring Database..." + echo "Restoring Database..." mv -f $last_db $curr_db fi - echo "$(date) - Configuring Database..." + echo "Configuring Database..." sqlite3 "$curr_db" "$create_table" fi } uninstall() { if [ -f $curr_wp ] && cat $curr_wp | grep -q SpeedTest; then - echo "$(date) - Uninstalling Current Speedtest Mod..." + echo "Restoring Pi-hole..." if [ ! -f $org_wp ]; then if [ ! -d /opt/org_pihole ]; then @@ -127,7 +127,6 @@ uninstall() { } purge() { - echo "$(date) - Cleaning up..." rm -rf "$curr_wp".* rm -rf "$admin_dir"*_admin rm -rf "$curr_db".* @@ -139,7 +138,9 @@ purge() { fi } +echo "$(date)" uninstall ${1-} purge -echo "$(date) - Done!" +echo "Done!" +echo "$(date)" exit 0 From fbe9f5d35bedc7921d450b91d4727689be564879 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Tue, 30 Jan 2024 03:58:30 -0500 Subject: [PATCH 034/137] typo --- settings.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.php b/settings.php index 0aba09f2f..ed2fc8e57 100644 --- a/settings.php +++ b/settings.php @@ -1586,7 +1586,7 @@ class="form-control"

- +

From f5d85179e6359319fbb78f98a46e8f650a016f25 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Tue, 30 Jan 2024 04:03:02 -0500 Subject: [PATCH 035/137] typo --- scripts/pi-hole/js/settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index 28d5e36f4..6c3a148bf 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -540,7 +540,7 @@ $(function () { ? " awaiting confirmation" : triggerMatch ? ` in ${triggerMatch[1]}` - : " disabled"; + : " active"; //const lastRunText = lastRunMatch ? ` ${lastRunMatch[1]} ago, it ${lastRunMatch[2].toLowerCase()}` : ' never'; pre = codeBlock(`Schedule is ${scheduleStatusText}\nNext run is${triggerText}`); //\nLast run was${lastRunText}`); } From 4de5d189599bd8b7561c2666a41c83a482b8c229 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Tue, 30 Jan 2024 04:30:49 -0500 Subject: [PATCH 036/137] numo --- scripts/pi-hole/js/speedtest.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/pi-hole/js/speedtest.js b/scripts/pi-hole/js/speedtest.js index f744e0ed7..daeb28fab 100644 --- a/scripts/pi-hole/js/speedtest.js +++ b/scripts/pi-hole/js/speedtest.js @@ -135,7 +135,7 @@ function formatDate(itemdate, results) { } function updateSpeedTestData() { - const days = localStorage?.getItem("speedtest_days") || -1; + const days = localStorage?.getItem("speedtest_days") || -2; speedlabels = []; downloadspeed = []; uploadspeed = []; From 2e64f49823b69cd35aae8cb5785b979a1fddb852 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Tue, 30 Jan 2024 04:33:48 -0500 Subject: [PATCH 037/137] typo --- scripts/pi-hole/js/speedtest.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/pi-hole/js/speedtest.js b/scripts/pi-hole/js/speedtest.js index daeb28fab..7df7f1598 100644 --- a/scripts/pi-hole/js/speedtest.js +++ b/scripts/pi-hole/js/speedtest.js @@ -135,7 +135,7 @@ function formatDate(itemdate, results) { } function updateSpeedTestData() { - const days = localStorage?.getItem("speedtest_days") || -2; + const days = localStorage?.getItem("speedtest_days") || "-2"; speedlabels = []; downloadspeed = []; uploadspeed = []; From e02407be80f81947128aeb1707b657abac8ab9d8 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Tue, 30 Jan 2024 04:37:36 -0500 Subject: [PATCH 038/137] no, numo --- scripts/pi-hole/js/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/pi-hole/js/index.js b/scripts/pi-hole/js/index.js index 3411e8af5..9ea2769d7 100644 --- a/scripts/pi-hole/js/index.js +++ b/scripts/pi-hole/js/index.js @@ -1231,4 +1231,4 @@ window.addEventListener("resize", function () { $(".chartjs-tooltip").remove(); }); -localStorage.setItem("speedtest_days", -1); +localStorage.setItem("speedtest_days", '-2'); From 3356feadc50987178aa146bf3f00de286521c20d Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Tue, 30 Jan 2024 04:49:28 -0500 Subject: [PATCH 039/137] "" --- scripts/pi-hole/js/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/pi-hole/js/index.js b/scripts/pi-hole/js/index.js index 9ea2769d7..89dc38fe2 100644 --- a/scripts/pi-hole/js/index.js +++ b/scripts/pi-hole/js/index.js @@ -1231,4 +1231,4 @@ window.addEventListener("resize", function () { $(".chartjs-tooltip").remove(); }); -localStorage.setItem("speedtest_days", '-2'); +localStorage.setItem("speedtest_days", "-2"); From 84430f671d1a9309df9eebb2727d78a60fb50622 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Tue, 30 Jan 2024 05:08:52 -0500 Subject: [PATCH 040/137] if --- scripts/pi-hole/js/speedtest.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/pi-hole/js/speedtest.js b/scripts/pi-hole/js/speedtest.js index 7df7f1598..6a07d94c2 100644 --- a/scripts/pi-hole/js/speedtest.js +++ b/scripts/pi-hole/js/speedtest.js @@ -154,7 +154,7 @@ function updateSpeedTestData() { serverPing.push(parseFloat(packet.server_ping)); } }); - if (speedChart && !days) speedChart.update(); + if (speedChart && days === "-2") speedChart.update(); else createChart(speedlabels); }); } From 23d6f05ef994abd8b19778b94d907299d8650eda Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Tue, 30 Jan 2024 05:15:55 -0500 Subject: [PATCH 041/137] destroy --- scripts/pi-hole/js/speedtest.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/pi-hole/js/speedtest.js b/scripts/pi-hole/js/speedtest.js index 6a07d94c2..e718f17f8 100644 --- a/scripts/pi-hole/js/speedtest.js +++ b/scripts/pi-hole/js/speedtest.js @@ -155,7 +155,10 @@ function updateSpeedTestData() { } }); if (speedChart && days === "-2") speedChart.update(); - else createChart(speedlabels); + else { + if (speedChart) speedChart.destroy(); + createChart(speedlabels); + } }); } From 46078d5b2c3b2e883ce9051351cd6131a2a4786d Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Tue, 30 Jan 2024 05:31:50 -0500 Subject: [PATCH 042/137] typo --- api_speedtest.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/api_speedtest.php b/api_speedtest.php index b6bed2979..5d77501c4 100644 --- a/api_speedtest.php +++ b/api_speedtest.php @@ -33,9 +33,6 @@ if (isset($_GET['getLastSpeedtestResult'])) { $data = array_merge($data, getLastSpeedtestResult($dbSpeedtest)); } - if (isset($_GET['getAllSpeedTestData'])) { - $data = array_merge($data, getAllSpeedTestData($dbSpeedtest)); - } if (isset($_GET['getLatestLog'])) { $data = array_merge($data, speedtestExecute($cmdLog)); } @@ -63,7 +60,7 @@ function hasSpeedTestBackup($dbSpeedtestOld) function getAllSpeedTestData($dbSpeedtest) { $data = getSpeedTestData($dbSpeedtest, -1); - if (isset($data['errr'])) { + if (isset($data['error'])) { return array(); } $newarr = array(); From 365d5e9492a88faf0cf90d6d7e6d4ced951b9b21 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Tue, 30 Jan 2024 05:45:11 -0500 Subject: [PATCH 043/137] revert --- api_speedtest.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/api_speedtest.php b/api_speedtest.php index 5d77501c4..56cf9790f 100644 --- a/api_speedtest.php +++ b/api_speedtest.php @@ -30,6 +30,9 @@ if (isset($_GET['getSpeedData'])) { $data = array_merge($data, getSpeedData($dbSpeedtest, $_GET['getSpeedData'])); } + if (isset($_GET['getAllSpeedTestData'])) { + $data = array_merge($data, getAllSpeedTestData($dbSpeedtest)); + } if (isset($_GET['getLastSpeedtestResult'])) { $data = array_merge($data, getLastSpeedtestResult($dbSpeedtest)); } From b6d9e6f85f87c507d959baf755a6fbdc00b65e7e Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Tue, 30 Jan 2024 06:03:39 -0500 Subject: [PATCH 044/137] workaround --- scripts/pi-hole/js/speedtest.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/pi-hole/js/speedtest.js b/scripts/pi-hole/js/speedtest.js index e718f17f8..ebaab8c68 100644 --- a/scripts/pi-hole/js/speedtest.js +++ b/scripts/pi-hole/js/speedtest.js @@ -131,7 +131,7 @@ function formatDate(itemdate, results) { if (last.diff(first, "hours") >= 24) output = "Do " + output; } - return moment(itemdate).utcOffset(moment().utcOffset()).format(output); + return moment(new Date(itemdate)).utcOffset(moment().utcOffset()).format(output); } function updateSpeedTestData() { From 2a8b9b510a2ceaf5bd677f9778ca3db1ad6f26af Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Tue, 30 Jan 2024 06:09:22 -0500 Subject: [PATCH 045/137] here too --- scripts/pi-hole/js/speedresults.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/pi-hole/js/speedresults.js b/scripts/pi-hole/js/speedresults.js index 1a1b087e2..48b0b2a48 100644 --- a/scripts/pi-hole/js/speedresults.js +++ b/scripts/pi-hole/js/speedresults.js @@ -54,7 +54,7 @@ $(document).ready(function () { .utcOffset(moment().utcOffset()) .format("YYYY-MM-DD HH:mm:ss Z"); } else { - data = moment(data).utcOffset(moment().utcOffset()).format("YYYY-MM-DD HH:mm:ss Z"); + data = moment(new Date(data)).utcOffset(moment().utcOffset()).format("YYYY-MM-DD HH:mm:ss Z"); } } @@ -69,7 +69,7 @@ $(document).ready(function () { .utcOffset(moment().utcOffset()) .format("YYYY-MM-DD HH:mm:ss Z"); } else { - data = moment(data).utcOffset(moment().utcOffset()).format("YYYY-MM-DD HH:mm:ss Z"); + data = moment(new Date(data)).utcOffset(moment().utcOffset()).format("YYYY-MM-DD HH:mm:ss Z"); } } From 90ca5a4f32d61bbdccc9363a6e99f43af4462c57 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Tue, 30 Jan 2024 06:10:03 -0500 Subject: [PATCH 046/137] prettier --- scripts/pi-hole/js/speedresults.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/scripts/pi-hole/js/speedresults.js b/scripts/pi-hole/js/speedresults.js index 48b0b2a48..d6e167743 100644 --- a/scripts/pi-hole/js/speedresults.js +++ b/scripts/pi-hole/js/speedresults.js @@ -54,7 +54,9 @@ $(document).ready(function () { .utcOffset(moment().utcOffset()) .format("YYYY-MM-DD HH:mm:ss Z"); } else { - data = moment(new Date(data)).utcOffset(moment().utcOffset()).format("YYYY-MM-DD HH:mm:ss Z"); + data = moment(new Date(data)) + .utcOffset(moment().utcOffset()) + .format("YYYY-MM-DD HH:mm:ss Z"); } } @@ -69,7 +71,9 @@ $(document).ready(function () { .utcOffset(moment().utcOffset()) .format("YYYY-MM-DD HH:mm:ss Z"); } else { - data = moment(new Date(data)).utcOffset(moment().utcOffset()).format("YYYY-MM-DD HH:mm:ss Z"); + data = moment(new Date(data)) + .utcOffset(moment().utcOffset()) + .format("YYYY-MM-DD HH:mm:ss Z"); } } From 6011e53a97df30bce6ce64a9e44795dfb21fd277 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Tue, 30 Jan 2024 06:46:19 -0500 Subject: [PATCH 047/137] limit unnecessary refreshes --- scripts/pi-hole/js/speedtest.js | 10 ++++++++-- scripts/pi-hole/speedtest/uninstall.sh | 5 +++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/scripts/pi-hole/js/speedtest.js b/scripts/pi-hole/js/speedtest.js index ebaab8c68..e73f815ca 100644 --- a/scripts/pi-hole/js/speedtest.js +++ b/scripts/pi-hole/js/speedtest.js @@ -135,7 +135,10 @@ function formatDate(itemdate, results) { } function updateSpeedTestData() { - const days = localStorage?.getItem("speedtest_days") || "-2"; + const daysIsTheSame = days === localStorage?.getItem("speedtest_days"); + const typeIsTheSame = type === localStorage?.getItem("speedtest_chart_type"); + days = localStorage?.getItem("speedtest_days") || "-2"; + type = localStorage?.getItem("speedtest_chart_type") || "line"; speedlabels = []; downloadspeed = []; uploadspeed = []; @@ -155,13 +158,16 @@ function updateSpeedTestData() { } }); if (speedChart && days === "-2") speedChart.update(); - else { + else if (!daysIsTheSame || !typeIsTheSame || !speedChart) { if (speedChart) speedChart.destroy(); createChart(speedlabels); } }); } +var days = ""; +var type = ""; + $(function () { updateSpeedTestData(); setInterval(function () { diff --git a/scripts/pi-hole/speedtest/uninstall.sh b/scripts/pi-hole/speedtest/uninstall.sh index 4d9ac8ab2..84b207314 100644 --- a/scripts/pi-hole/speedtest/uninstall.sh +++ b/scripts/pi-hole/speedtest/uninstall.sh @@ -42,11 +42,12 @@ download() { local name=$2 local url=$3 local src=${4-} + local branch=${5-master} local dest=$path/$name if [ ! -d $dest ]; then # replicate cd "$path" rm -rf "$name" - git clone --depth=1 "$url" "$name" + git clone --depth=1 -b "$branch" "$url" "$name" setTags "$name" "$src" if [ ! -z "$src" ]; then if [[ "$localTag" == *.* ]] && [[ "$localTag" < "$latestTag" ]]; then @@ -68,7 +69,7 @@ download() { fi git fetch origin -q fi - git reset --hard origin/master + git reset --hard origin/$branch fi git -c advice.detachedHead=false checkout $latestTag From fa6d963d3c4801604e395ab3bab94d1168d108f8 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Tue, 30 Jan 2024 12:53:20 -0500 Subject: [PATCH 048/137] fix reshowing preview and use json --- api_speedtest.php | 26 ++++++++ scripts/pi-hole/js/settings.js | 38 +++++++----- scripts/pi-hole/js/speedtest.js | 22 +++++-- scripts/pi-hole/speedtest/uninstall.sh | 82 +++++++++++++++++++++++--- 4 files changed, 141 insertions(+), 27 deletions(-) diff --git a/api_speedtest.php b/api_speedtest.php index 56cf9790f..70ac27817 100644 --- a/api_speedtest.php +++ b/api_speedtest.php @@ -22,6 +22,7 @@ $cmdStatus = 'systemctl status pihole-speedtest.timer ; systemctl status pihole-speedtest --no-pager -l'; $cmdRun = 'cat /tmp/speedtest.log'; $cmdServersCurl = "curl 'https://c.speedtest.net/speedtest-servers-static.php' --compressed -H 'Upgrade-Insecure-Requests: 1' -H 'DNT: 1' -H 'Sec-GPC: 1'"; +$cmdServersJSON = "curl 'https://www.speedtest.net/api/js/servers' --compressed -H 'Upgrade-Insecure-Requests: 1' -H 'Sec-GPC: 1'"; if ($auth) { if (isset($_GET['hasSpeedTestBackup'])) { @@ -51,6 +52,9 @@ if (isset($_GET['curlClosestServers'])) { $data = array_merge($data, curlServers($cmdServersCurl)); } + if (isset($_GET['JSONClosestServers'])) { + $data = array_merge($data, JSONServers($cmdServersJSON)); + } } function hasSpeedTestBackup($dbSpeedtestOld) @@ -269,3 +273,25 @@ function curlServers($cmdServersCurl) return array('data' => implode("\n", $serverList)); } } + +function JSONServers($cmdServersJSON) +{ + $array = speedtestExecute($cmdServersJSON); + $jsonContent = $array['data']; + + if ($jsonContent === false) { + return array('error' => 'Error fetching JSON'); + } else { + $json = json_decode($jsonContent); + if ($json === false) { + return array('error' => 'Error parsing JSON'); + } + + $serverList = array(); + foreach ($json as $server) { + $serverList[] = str_pad($server->id, 5, ' ', STR_PAD_LEFT).') '.$server->sponsor.' ('.$server->name.', '.$server->cc.') [Distance '.$server->distance.']'; + } + + return array('data' => implode("\n", $serverList)); + } +} diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index 6c3a148bf..38b7692ba 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -512,7 +512,6 @@ $(function () { pre.style.maxWidth = "100%"; pre.style.overflowX = "auto"; pre.style.whiteSpace = "pre"; - pre.style.overflowWrap = "normal"; pre.style.marginTop = "1vw"; return pre; }; @@ -529,20 +528,20 @@ $(function () { if (status) { const scheduleStatusPattern = /pihole-speedtest\.timer.*?Active:\s+(\w+)/s; const triggerPattern = /Trigger:.*?;\s*([\d\s\w]+)\s+left/s; - //const lastRunPattern = /pihole-speedtest\.service.*?Active: inactive \(dead\) since.*?;\s+([\w\s]+ago).*?pihole-speedtest\.service: (\w+)/s; const scheduleStatusMatch = status.match(scheduleStatusPattern); const triggerMatch = status.match(triggerPattern); - //const lastRunMatch = status.match(lastRunPattern); - - const scheduleStatusText = scheduleStatusMatch ? scheduleStatusMatch[1] : "off"; - triggerText = speedtestTest.attr("value") - ? " awaiting confirmation" - : triggerMatch - ? ` in ${triggerMatch[1]}` - : " active"; - //const lastRunText = lastRunMatch ? ` ${lastRunMatch[1]} ago, it ${lastRunMatch[2].toLowerCase()}` : ' never'; - pre = codeBlock(`Schedule is ${scheduleStatusText}\nNext run is${triggerText}`); //\nLast run was${lastRunText}`); + + const scheduleStatusText = scheduleStatusMatch ? scheduleStatusMatch[1] : "missing"; + if (!speedtestTest.attr("value")) { + if (triggerMatch) { + triggerText = ` in ${triggerMatch[1]}`; + } else if (scheduleStatusText === "active") { + triggerText = " running"; + } + } + + pre = codeBlock(`Schedule is ${scheduleStatusText}\nNext run is${triggerText}`); } speedtestStatus.find("pre").remove(); @@ -555,10 +554,15 @@ $(function () { const previewChart = preview => { if (!preview) { + localStorage.setItem("speedtest_preview_hidden", "true"); + localStorage.setItem("speedtest_preview_shown", "false"); speedtestChartPreview.find("div").remove(); } else { let speedtestdays = speedtestDays.val(); const speedtestcharttype = speedtestChartType.val(); + localStorage.setItem("speedtest_days", speedtestdays); + localStorage.setItem("speedtest_chart_type", speedtestcharttype); + localStorage.setItem("speedtest_preview_shown", "true"); if (speedtestdays === "1") { speedtestdays = "24 hours"; @@ -575,6 +579,8 @@ $(function () { const boxBodyDiv = document.createElement("div"); const chartDiv = document.createElement("div"); const canvas = document.createElement("canvas"); + const overlayDiv = document.createElement("div"); + const i = document.createElement("i"); colDiv.className = "col-md-12"; boxDiv.className = "box"; @@ -589,6 +595,9 @@ $(function () { chartDiv.style.height = "180px"; canvas.id = "speedOverTimeChart"; canvas.setAttribute("value", speedtestcharttype); + overlayDiv.className = "overlay"; + overlayDiv.id = "speedOverTimeChartOverlay"; + i.className = "fa fa-sync fa-spin"; colDiv.style.marginTop = "1vw"; @@ -598,6 +607,8 @@ $(function () { boxDiv.append(boxBodyDiv); boxBodyDiv.append(chartDiv); chartDiv.append(canvas); + chartDiv.append(overlayDiv); + overlayDiv.append(i); speedtestChartPreview.find("div").remove(); speedtestChartPreview.append(colDiv); @@ -641,7 +652,7 @@ $(function () { speedtestServerBtn.text("Hide servers"); } else { $.ajax({ - url: "api.php?curlClosestServers", + url: "api.php?JSONClosestServers", dataType: "json", }) .done(function (data) { @@ -757,7 +768,6 @@ $(function () { type = type ? (type === "bar" ? "line" : "bar") : "bar"; speedtestChartType.attr("value", type); localStorage.setItem("speedtest_chart_type", type); - // Call check messages to make new setting effective checkMessages(); previewChart(speedtestChartPreview.find("div").length > 0); diff --git a/scripts/pi-hole/js/speedtest.js b/scripts/pi-hole/js/speedtest.js index e73f815ca..14b761ef3 100644 --- a/scripts/pi-hole/js/speedtest.js +++ b/scripts/pi-hole/js/speedtest.js @@ -137,8 +137,10 @@ function formatDate(itemdate, results) { function updateSpeedTestData() { const daysIsTheSame = days === localStorage?.getItem("speedtest_days"); const typeIsTheSame = type === localStorage?.getItem("speedtest_chart_type"); + const beenHidden = localStorage?.getItem("speedtest_preview_hidden") === "true"; days = localStorage?.getItem("speedtest_days") || "-2"; type = localStorage?.getItem("speedtest_chart_type") || "line"; + speedlabels = []; downloadspeed = []; uploadspeed = []; @@ -157,10 +159,22 @@ function updateSpeedTestData() { serverPing.push(parseFloat(packet.server_ping)); } }); - if (speedChart && days === "-2") speedChart.update(); - else if (!daysIsTheSame || !typeIsTheSame || !speedChart) { - if (speedChart) speedChart.destroy(); - createChart(speedlabels); + if (speedChart && (!daysIsTheSame || !typeIsTheSame || beenHidden) && days !== "-2") { + speedChart.destroy(); + speedChart = null; + } + + if (!speedChart || beenHidden) { + localStorage.setItem( + "speedtest_preview_hidden", + !localStorage?.getItem("speedtest_preview_shown") + ); + createChart(); + } + + if (speedChart) { + speedChart.update(); + $("#speedOverTimeChartOverlay").css("display", "none"); } }); } diff --git a/scripts/pi-hole/speedtest/uninstall.sh b/scripts/pi-hole/speedtest/uninstall.sh index 84b207314..b1dc74af1 100644 --- a/scripts/pi-hole/speedtest/uninstall.sh +++ b/scripts/pi-hole/speedtest/uninstall.sh @@ -1,4 +1,5 @@ #!/bin/bash +LOG_FILE="/var/log/pimod.log" admin_dir=/var/www/html curr_wp=/opt/pihole/webpage.sh @@ -22,6 +23,13 @@ upload real, share_url text );" +help() { + echo "Uninstall Latest Speedtest Mod." + echo "Usage: sudo $0 [db]" + echo "db - flush database (restore for a short while after)" + echo "If no option is specified, the mod will simply be uninstalled." +} + setTags() { local path=${1-} local name=${2-} @@ -128,20 +136,76 @@ uninstall() { } purge() { - rm -rf "$curr_wp".* rm -rf "$admin_dir"*_admin - rm -rf "$curr_db".* - rm -rf "$curr_db"_* rm -rf /opt/mod_pihole + if [ -f /etc/systemd/system/pihole-speedtest.timer ]; then + rm -f /etc/systemd/system/pihole-speedtest.service + rm -f /etc/systemd/system/pihole-speedtest.timer + systemctl daemon-reload + fi + rm -f "$curr_wp".* + rm -f "$curr_db".* + rm -f "$curr_db"_* if isEmpty $curr_db; then rm -f $curr_db fi } -echo "$(date)" -uninstall ${1-} -purge -echo "Done!" -echo "$(date)" -exit 0 +abort() { + echo "Process Aborting..." + + if [ -f $last_wp ]; then + cp $last_wp $curr_wp + chmod +x $curr_wp + rm -f $last_wp + fi + if [ -f $last_db ] && [ ! -f $curr_db ]; then + mv $last_db $curr_db + fi + if [ ! -f $curr_wp ] || ! cat $curr_wp | grep -q SpeedTest; then + purge + fi + if [ -d $admin_dir/admin/.git/refs/remotes/old ]; then + download $admin_dir admin old web + fi + + if (($aborted == 0)); then + pihole restartdns + printf "Please try again or try manually.\n\n$(date)\n" + fi + aborted=1 + exit 1 +} + +commit() { + cd $admin_dir/admin + git remote -v | grep -q "old" && git remote remove old + rm -f $last_wp + pihole restartdns + printf "Done!\n\n$(date)\n" + exit 0 +} + +main() { + printf "Thanks for using Speedtest Mod!\nScript by @ipitio\n\n$(date)\n\n" + local db=${1-} + if [ "$op" == "-h" ] || [ "$op" == "--help" ]; then + help + exit 0 + fi + if [ $EUID != 0 ]; then + sudo "$0" "$@" + exit $? + fi + aborted=0 + set -Eeuo pipefail + trap '[ "$?" -eq "0" ] && commit || abort' EXIT + trap 'abort' INT TERM + + uninstall $db + purge + exit 0 +} + +main "$@" 2>&1 | sudo tee -- "$LOG_FILE" From 1998bae6f9ca74dd667c7535b9ca3805a96d40de Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Wed, 31 Jan 2024 08:48:35 -0500 Subject: [PATCH 049/137] move scripts, remove points --- scripts/pi-hole/js/settings.js | 104 ++++++------ scripts/pi-hole/js/speedtest.js | 19 ++- scripts/pi-hole/php/savesettings.php | 4 - scripts/pi-hole/speedtest/speedtest.sh | 81 ---------- scripts/pi-hole/speedtest/uninstall.sh | 211 ------------------------- settings.php | 4 - 6 files changed, 56 insertions(+), 367 deletions(-) delete mode 100755 scripts/pi-hole/speedtest/speedtest.sh delete mode 100644 scripts/pi-hole/speedtest/uninstall.sh diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index 38b7692ba..dafb38227 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -502,20 +502,31 @@ $(function () { speedtestChartType.prop("checked", type === "bar"); localStorage.setItem("speedtest_chart_type", type); - const codeBlock = text => { + const preCode = text => { const pre = document.createElement("pre"); const code = document.createElement("code"); code.textContent = text; code.style.whiteSpace = "pre"; + code.style.overflowWrap = "normal"; pre.append(code); pre.style.width = "100%"; pre.style.maxWidth = "100%"; - pre.style.overflowX = "auto"; + pre.style.maxHeight = "500px"; + pre.style.overflow = "auto"; pre.style.whiteSpace = "pre"; pre.style.marginTop = "1vw"; return pre; }; + const codeBlock = (element, text, button, output) => { + if (element.find("pre").length > 0) { + element.find("pre code").text(text); + } else { + button.text("Hide " + output); + element.append(preCode(text)); + } + }; + const serviceStatus = () => { $.ajax({ url: "api.php?getSpeedTestStatus", @@ -524,7 +535,7 @@ $(function () { .done(function (data) { const status = data?.data; let triggerText = speedtestTest.attr("value") ? " awaiting confirmation" : " disabled"; - let pre = codeBlock("Schedule is inactive\nNext run is" + triggerText); + let statusText = "Schedule is inactive\nNext run is" + triggerText; if (status) { const scheduleStatusPattern = /pihole-speedtest\.timer.*?Active:\s+(\w+)/s; const triggerPattern = /Trigger:.*?;\s*([\d\s\w]+)\s+left/s; @@ -541,11 +552,10 @@ $(function () { } } - pre = codeBlock(`Schedule is ${scheduleStatusText}\nNext run is${triggerText}`); + statusText = `Schedule is ${scheduleStatusText}\nNext run is${triggerText}`; } - speedtestStatus.find("pre").remove(); - speedtestStatus.append(pre); + codeBlock(speedtestStatus, statusText, speedtestStatusBtn, "status"); }) .fail(function () { speedtestStatusBtn.text("Failed to get status"); @@ -559,9 +569,9 @@ $(function () { speedtestChartPreview.find("div").remove(); } else { let speedtestdays = speedtestDays.val(); - const speedtestcharttype = speedtestChartType.val(); + const speedtestcharttype = speedtestChartType.attr("value"); localStorage.setItem("speedtest_days", speedtestdays); - localStorage.setItem("speedtest_chart_type", speedtestcharttype); + localStorage.setItem("speedtest_chart_type", type); localStorage.setItem("speedtest_preview_shown", "true"); if (speedtestdays === "1") { @@ -583,6 +593,7 @@ $(function () { const i = document.createElement("i"); colDiv.className = "col-md-12"; + colDiv.style.marginTop = "1vw"; boxDiv.className = "box"; boxDiv.id = "queries-over-time"; boxHeaderDiv.className = "box-header with-border"; @@ -594,13 +605,11 @@ $(function () { chartDiv.style.width = "100%"; chartDiv.style.height = "180px"; canvas.id = "speedOverTimeChart"; - canvas.setAttribute("value", speedtestcharttype); + canvas.setAttribute("value", type); overlayDiv.className = "overlay"; overlayDiv.id = "speedOverTimeChartOverlay"; i.className = "fa fa-sync fa-spin"; - colDiv.style.marginTop = "1vw"; - colDiv.append(boxDiv); boxDiv.append(boxHeaderDiv); boxHeaderDiv.append(h3); @@ -624,70 +633,45 @@ $(function () { }) .done(function (data) { const log = data?.data; - speedtestLogBtn.text("Hide output"); - if (speedtestLog.find("pre").length > 0) { - speedtestLog.find("pre code").text(log); - } else if (log) { - const pre = codeBlock(log); - speedtestLog.find("p").remove(); - speedtestLog.append(pre); + if (log) { + codeBlock(speedtestLog, log, speedtestLogBtn, "log"); + } else { + speedtestLogBtn.text("Failed to get log"); } }) .fail(function () { - speedtestLogBtn.text("No log found"); + speedtestLogBtn.text("Failed to get log"); }); }; - const closestServers = () => { + const closestServers = cmds => { + const tryNextCmd = () => { + if (cmds.length === 1) { + speedtestServerBtn.text("Failed to display servers"); + if (speedtestServerCtr.find("p").length === 0) { + speedtestServerCtr.append( + `

Please download the results: XML | JSON

` + ); + } + } else { + closestServers(cmds.slice(1)); + } + }; + $.ajax({ - url: "api.php?getClosestServers", + url: `api.php?${cmds[0]}`, dataType: "json", }) .done(function (data) { const serversInfo = data?.data; if (serversInfo) { - const pre = codeBlock(serversInfo); - speedtestServerCtr.find("p").remove(); - speedtestServerCtr.append(pre); - speedtestServerBtn.text("Hide servers"); + codeBlock(speedtestServerCtr, serversInfo, speedtestServerBtn, "servers"); } else { - $.ajax({ - url: "api.php?JSONClosestServers", - dataType: "json", - }) - .done(function (data) { - const serversInfo = data?.data; - if (serversInfo) { - const pre = codeBlock(serversInfo); - speedtestServerCtr.find("p").remove(); - speedtestServerCtr.append(pre); - speedtestServerBtn.text("Hide servers"); - } else { - speedtestServerBtn.text("Failed to get servers"); - if (speedtestServerCtr.find("p").length === 0) { - speedtestServerCtr.append( - `

You can also open this XML file to see them

` - ); - } - } - }) - .fail(function () { - speedtestServerBtn.text("Failed to get servers"); - if (speedtestServerCtr.find("p").length === 0) { - speedtestServerCtr.append( - `

You can also open this XML file to see them

` - ); - } - }); + tryNextCmd(); } }) .fail(function () { - speedtestServerBtn.text("Failed to get servers"); - if (speedtestServerCtr.find("p").length === 0) { - speedtestServerCtr.append( - `

You can also open this XML file to see them

` - ); - } + tryNextCmd(); }); }; @@ -825,7 +809,7 @@ $(function () { speedtestServerBtn.text("Show closest servers"); } else { speedtestServerBtn.text("Retrieving servers..."); - closestServers(); + closestServers(["JSONClosestServers", "getClosestServers", "curlClosestServers"]); } }); diff --git a/scripts/pi-hole/js/speedtest.js b/scripts/pi-hole/js/speedtest.js index 14b761ef3..ac18dc7df 100644 --- a/scripts/pi-hole/js/speedtest.js +++ b/scripts/pi-hole/js/speedtest.js @@ -36,27 +36,27 @@ function createChart() { data: downloadspeed, backgroundColor: "rgba(0, 123, 255, 0.5)", borderColor: "rgba(0, 123, 255, 1)", - borderWidth: 1, - cubicInterpolationMode: "monotone", + borderWidth: 3, yAxisID: "y-axis-1", + tension: 0.4, }, { label: "Mbps Upload", data: uploadspeed, backgroundColor: "rgba(40, 167, 69, 0.5)", borderColor: "rgba(40, 167, 69, 1)", - borderWidth: 1, - cubicInterpolationMode: "monotone", + borderWidth: 3, yAxisID: "y-axis-1", + tension: 0.4, }, { label: "ms Ping", data: serverPing, backgroundColor: "rgba(108, 117, 125, 0.5)", borderColor: "rgba(108, 117, 125, 1)", - borderWidth: 1, - cubicInterpolationMode: "monotone", + borderWidth: 3, yAxisID: "y-axis-2", + tension: 0.4, }, ], }, @@ -113,6 +113,11 @@ function createChart() { position: "right", }, }, + elements: { + point: { + radius: 0, + }, + }, }, }); } @@ -186,5 +191,5 @@ $(function () { updateSpeedTestData(); setInterval(function () { updateSpeedTestData(); - }, 6000); + }, 1000); }); diff --git a/scripts/pi-hole/php/savesettings.php b/scripts/pi-hole/php/savesettings.php index 6e2809a27..7d2581c68 100644 --- a/scripts/pi-hole/php/savesettings.php +++ b/scripts/pi-hole/php/savesettings.php @@ -583,10 +583,6 @@ function addStaticDHCPLease($mac, $ip, $hostname) break; case 'speedtest': - if (isset($_POST['speedtestmode'])) { - pihole_execute('-a -sm '.trim($_POST['speedtestmode'])); - } - if (isset($_POST['speedtestschedule'])) { pihole_execute('-a -s '.trim($_POST['speedtestschedule'])); } diff --git a/scripts/pi-hole/speedtest/speedtest.sh b/scripts/pi-hole/speedtest/speedtest.sh deleted file mode 100755 index fddea98b6..000000000 --- a/scripts/pi-hole/speedtest/speedtest.sh +++ /dev/null @@ -1,81 +0,0 @@ -#!/bin/bash -FILE=/tmp/speedtest.log -readonly setupVars="/etc/pihole/setupVars.conf" -serverid=$(grep 'SPEEDTEST_SERVER' ${setupVars} | cut -d '=' -f2) -start=$(date +"%Y-%m-%d %H:%M:%S %Z") - -speedtest() { - if grep -q official <<< "$(/usr/bin/speedtest --version)"; then - if [[ -z "${serverid}" ]]; then - /usr/bin/speedtest --accept-gdpr --accept-license -f json-pretty - else - /usr/bin/speedtest -s $serverid --accept-gdpr --accept-license -f json-pretty - fi - else - if [[ -z "${serverid}" ]]; then - /usr/bin/speedtest --json --share --secure - else - /usr/bin/speedtest -s $serverid --json --share --secure - fi - fi -} - -internet() { - stop=$(date +"%Y-%m-%d %H:%M:%S %Z") - res="$(<$FILE)" - server_name=$(jq -r '.server.name' <<< "$res") - server_dist=0 - - if grep -q official <<< "$(/usr/bin/speedtest --version)"; then - download=$(jq -r '.download.bandwidth' <<< "$res" | awk '{$1=$1*8/1000/1000; print $1;}' | sed 's/,/./g') - upload=$(jq -r '.upload.bandwidth' <<< "$res" | awk '{$1=$1*8/1000/1000; print $1;}' | sed 's/,/./g') - isp=$(jq -r '.isp' <<< "$res") - server_ip=$(jq -r '.server.ip' <<< "$res") - from_ip=$(jq -r '.interface.externalIp' <<< "$res") - server_ping=$(jq -r '.ping.latency' <<< "$res") - share_url=$(jq -r '.result.url' <<< "$res") - else - download=$(jq -r '.download' <<< "$res" | awk '{$1=$1/1000/1000; print $1;}' | sed 's/,/./g') - upload=$(jq -r '.upload' <<< "$res" | awk '{$1=$1/1000/1000; print $1;}' | sed 's/,/./g') - isp=$(jq -r '.client.isp' <<< "$res") - server_ip=$(jq -r '.server.host' <<< "$res") - from_ip=$(jq -r '.client.ip' <<< "$res") - server_ping=$(jq -r '.ping' <<< "$res") - share_url=$(jq -r '.share' <<< "$res") - fi - - sep="\t" - quote="" - opts= - sep="$quote$sep$quote" - printf "$quote$start$sep$stop$sep$isp$sep$from_ip$sep$server_name$sep$server_dist$sep$server_ping$sep$download$sep$upload$sep$share_url$quote\n" - sqlite3 /etc/pihole/speedtest.db "insert into speedtest values (NULL, '${start}', '${stop}', '${isp}', '${from_ip}', '${server_name}', ${server_dist}, ${server_ping}, ${download}, ${upload}, '${share_url}');" - exit 0 -} - -nointernet(){ - stop=$(date +"%Y-%m-%d %H:%M:%S %Z") - echo "No Internet" - sqlite3 /etc/pihole/speedtest.db "insert into speedtest values (NULL, '${start}', '${stop}', 'No Internet', '-', '-', 0, 0, 0, 0, '#');" - exit 1 -} - -tryagain(){ - if apt-cache policy speedtest-cli | grep -q 'Installed: (none)'; then - apt-get install -y speedtest-cli speedtest- - else - apt-get install -y speedtest speedtest-cli- - fi - speedtest > "$FILE" && internet || nointernet -} - -main() { - if [ $EUID != 0 ]; then - sudo "$0" "$@" - exit $? - fi - echo "Test has been initiated, please wait." - speedtest > "$FILE" && internet || tryagain -} - -main diff --git a/scripts/pi-hole/speedtest/uninstall.sh b/scripts/pi-hole/speedtest/uninstall.sh deleted file mode 100644 index b1dc74af1..000000000 --- a/scripts/pi-hole/speedtest/uninstall.sh +++ /dev/null @@ -1,211 +0,0 @@ -#!/bin/bash -LOG_FILE="/var/log/pimod.log" - -admin_dir=/var/www/html -curr_wp=/opt/pihole/webpage.sh -last_wp=$curr_wp.old -org_wp=$curr_wp.org - -curr_db=/etc/pihole/speedtest.db -last_db=$curr_db.old -db_table="speedtest" -create_table="create table if not exists $db_table ( -id integer primary key autoincrement, -start_time integer, -stop_time text, -from_server text, -from_ip text, -server text, -server_dist real, -server_ping real, -download real, -upload real, -share_url text -);" - -help() { - echo "Uninstall Latest Speedtest Mod." - echo "Usage: sudo $0 [db]" - echo "db - flush database (restore for a short while after)" - echo "If no option is specified, the mod will simply be uninstalled." -} - -setTags() { - local path=${1-} - local name=${2-} - if [ ! -z "$path" ]; then - cd "$path" - git fetch origin -q - git fetch --tags -f -q - latestTag=$(git describe --tags $(git rev-list --tags --max-count=1)) - fi - if [ ! -z "$name" ]; then - localTag=$(pihole -v | grep "$name" | cut -d ' ' -f 6) - [ "$localTag" == "HEAD" ] && localTag=$(pihole -v | grep "$name" | cut -d ' ' -f 7) - fi -} - -download() { - local path=$1 - local name=$2 - local url=$3 - local src=${4-} - local branch=${5-master} - local dest=$path/$name - if [ ! -d $dest ]; then # replicate - cd "$path" - rm -rf "$name" - git clone --depth=1 -b "$branch" "$url" "$name" - setTags "$name" "$src" - if [ ! -z "$src" ]; then - if [[ "$localTag" == *.* ]] && [[ "$localTag" < "$latestTag" ]]; then - latestTag=$localTag - git fetch --unshallow - fi - fi - else # replace - setTags $dest - if [ ! -z "$src" ]; then - if [ "$url" != "old" ]; then - git config --global --add safe.directory "$dest" - git remote -v | grep -q "old" || git remote rename origin old - git remote -v | grep -q "origin" && git remote remove origin - git remote add origin $url - else - git remote remove origin - git remote rename old origin - fi - git fetch origin -q - fi - git reset --hard origin/$branch - fi - - git -c advice.detachedHead=false checkout $latestTag - cd .. -} - -isEmpty() { - db=$1 - if [ -f $db ]; then - if ! sqlite3 "$db" "select * from $db_table limit 1;" >/dev/null 2>&1 || [ -z "$(sqlite3 "$db" "select * from $db_table limit 1;")" ]; then - return 0 - fi - fi - return 1 -} - -manageHistory() { - if [ "${1-}" == "db" ]; then - if [ -f $curr_db ] && ! isEmpty $curr_db; then - if [ -z "${2-}" ]; then - echo "Flushing Database..." - mv -f $curr_db $last_db - fi - elif [ -f $last_db ]; then - echo "Restoring Database..." - mv -f $last_db $curr_db - fi - echo "Configuring Database..." - sqlite3 "$curr_db" "$create_table" - fi -} - -uninstall() { - if [ -f $curr_wp ] && cat $curr_wp | grep -q SpeedTest; then - echo "Restoring Pi-hole..." - - if [ ! -f $org_wp ]; then - if [ ! -d /opt/org_pihole ]; then - download /opt org_pihole https://github.com/pi-hole/pi-hole Pi-hole - fi - cd /opt - cp org_pihole/advanced/Scripts/webpage.sh $org_wp - rm -rf org_pihole - fi - - pihole -a -su - download /var/www/html admin https://github.com/pi-hole/AdminLTE web - if [ ! -f $last_wp ]; then - cp $curr_wp $last_wp - fi - cp $org_wp $curr_wp - chmod +x $curr_wp - fi - - manageHistory ${1-} -} - -purge() { - rm -rf "$admin_dir"*_admin - rm -rf /opt/mod_pihole - if [ -f /etc/systemd/system/pihole-speedtest.timer ]; then - rm -f /etc/systemd/system/pihole-speedtest.service - rm -f /etc/systemd/system/pihole-speedtest.timer - systemctl daemon-reload - fi - - rm -f "$curr_wp".* - rm -f "$curr_db".* - rm -f "$curr_db"_* - if isEmpty $curr_db; then - rm -f $curr_db - fi -} - -abort() { - echo "Process Aborting..." - - if [ -f $last_wp ]; then - cp $last_wp $curr_wp - chmod +x $curr_wp - rm -f $last_wp - fi - if [ -f $last_db ] && [ ! -f $curr_db ]; then - mv $last_db $curr_db - fi - if [ ! -f $curr_wp ] || ! cat $curr_wp | grep -q SpeedTest; then - purge - fi - if [ -d $admin_dir/admin/.git/refs/remotes/old ]; then - download $admin_dir admin old web - fi - - if (($aborted == 0)); then - pihole restartdns - printf "Please try again or try manually.\n\n$(date)\n" - fi - aborted=1 - exit 1 -} - -commit() { - cd $admin_dir/admin - git remote -v | grep -q "old" && git remote remove old - rm -f $last_wp - pihole restartdns - printf "Done!\n\n$(date)\n" - exit 0 -} - -main() { - printf "Thanks for using Speedtest Mod!\nScript by @ipitio\n\n$(date)\n\n" - local db=${1-} - if [ "$op" == "-h" ] || [ "$op" == "--help" ]; then - help - exit 0 - fi - if [ $EUID != 0 ]; then - sudo "$0" "$@" - exit $? - fi - aborted=0 - set -Eeuo pipefail - trap '[ "$?" -eq "0" ] && commit || abort' EXIT - trap 'abort' INT TERM - - uninstall $db - purge - exit 0 -} - -main "$@" 2>&1 | sudo tee -- "$LOG_FILE" diff --git a/settings.php b/settings.php index ed2fc8e57..7614ceb93 100644 --- a/settings.php +++ b/settings.php @@ -1507,7 +1507,6 @@ $speedtestshedule = false; $speedtestdays = 'official'; $speedtestserver = ''; -$speedtestmode = 'python'; $speedtestcharttype = 'line'; if (isset($setupVars['SPEEDTESTSCHEDULE'])) { @@ -1519,9 +1518,6 @@ if (isset($setupVars['SPEEDTEST_SERVER'])) { $speedtestserver = $setupVars['SPEEDTEST_SERVER']; } -if (isset($setupVars['SPEEDTEST_MODE'])) { - $speedtestmode = $setupVars['SPEEDTEST_MODE']; -} if (isset($setupVars['SPEEDTEST_CHART_TYPE'])) { $speedtestcharttype = $setupVars['SPEEDTEST_CHART_TYPE']; } From 6945bac3ba73ec892d4fca016651a4e7f184ad3b Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Wed, 31 Jan 2024 08:50:31 -0500 Subject: [PATCH 050/137] rm var --- scripts/pi-hole/js/settings.js | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index dafb38227..da4222535 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -569,7 +569,6 @@ $(function () { speedtestChartPreview.find("div").remove(); } else { let speedtestdays = speedtestDays.val(); - const speedtestcharttype = speedtestChartType.attr("value"); localStorage.setItem("speedtest_days", speedtestdays); localStorage.setItem("speedtest_chart_type", type); localStorage.setItem("speedtest_preview_shown", "true"); From 6bf07e2aa8793b388c5d6f657ef806479fb24c63 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Wed, 31 Jan 2024 09:11:56 -0500 Subject: [PATCH 051/137] higher --- scripts/pi-hole/js/speedtest.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/pi-hole/js/speedtest.js b/scripts/pi-hole/js/speedtest.js index ac18dc7df..b21551b0e 100644 --- a/scripts/pi-hole/js/speedtest.js +++ b/scripts/pi-hole/js/speedtest.js @@ -191,5 +191,5 @@ $(function () { updateSpeedTestData(); setInterval(function () { updateSpeedTestData(); - }, 1000); + }, 6000); }); From c6b7789193e9564583bc9fe586e5bf88c4a2d302 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Thu, 1 Feb 2024 04:05:56 -0500 Subject: [PATCH 052/137] fix unselect server --- scripts/pi-hole/php/savesettings.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/pi-hole/php/savesettings.php b/scripts/pi-hole/php/savesettings.php index 7d2581c68..c2c95e5a4 100644 --- a/scripts/pi-hole/php/savesettings.php +++ b/scripts/pi-hole/php/savesettings.php @@ -593,7 +593,7 @@ function addStaticDHCPLease($mac, $ip, $hostname) } } - if (isset($_POST['speedtestserver']) && is_numeric($_POST['speedtestserver'])) { + if (!isset($_POST['speedtestserver']) || is_numeric($_POST['speedtestserver'])) { pihole_execute('-a -ss '.trim($_POST['speedtestserver'])); } From 61aec6d6b53eaecdc867038798fec6259388cb29 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Thu, 1 Feb 2024 04:10:46 -0500 Subject: [PATCH 053/137] actually fix --- scripts/pi-hole/php/savesettings.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/pi-hole/php/savesettings.php b/scripts/pi-hole/php/savesettings.php index c2c95e5a4..1839c8b14 100644 --- a/scripts/pi-hole/php/savesettings.php +++ b/scripts/pi-hole/php/savesettings.php @@ -593,7 +593,7 @@ function addStaticDHCPLease($mac, $ip, $hostname) } } - if (!isset($_POST['speedtestserver']) || is_numeric($_POST['speedtestserver'])) { + if (isset($_POST['speedtestserver']) && is_numeric($_POST['speedtestserver'] || empty($_POST['speedtestserver']))) { pihole_execute('-a -ss '.trim($_POST['speedtestserver'])); } From a6a4784feeb53f75f8edf58786e38f71af67c661 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Thu, 1 Feb 2024 04:18:15 -0500 Subject: [PATCH 054/137] autoselect for invalid data --- scripts/pi-hole/php/savesettings.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/pi-hole/php/savesettings.php b/scripts/pi-hole/php/savesettings.php index 1839c8b14..7c0861493 100644 --- a/scripts/pi-hole/php/savesettings.php +++ b/scripts/pi-hole/php/savesettings.php @@ -593,8 +593,10 @@ function addStaticDHCPLease($mac, $ip, $hostname) } } - if (isset($_POST['speedtestserver']) && is_numeric($_POST['speedtestserver'] || empty($_POST['speedtestserver']))) { + if (isset($_POST['speedtestserver']) && is_numeric($_POST['speedtestserver'])) { pihole_execute('-a -ss '.trim($_POST['speedtestserver'])); + } else { + pihole_execute('-a -ss auto'); } if (isset($_POST['speedtestdays'])) { From cc16c9d2856024391d0b76c59acf7e42b6453e57 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Thu, 1 Feb 2024 04:22:19 -0500 Subject: [PATCH 055/137] separate condition --- scripts/pi-hole/php/savesettings.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/pi-hole/php/savesettings.php b/scripts/pi-hole/php/savesettings.php index 7c0861493..9cbffe901 100644 --- a/scripts/pi-hole/php/savesettings.php +++ b/scripts/pi-hole/php/savesettings.php @@ -595,7 +595,9 @@ function addStaticDHCPLease($mac, $ip, $hostname) if (isset($_POST['speedtestserver']) && is_numeric($_POST['speedtestserver'])) { pihole_execute('-a -ss '.trim($_POST['speedtestserver'])); - } else { + } + + if (isset($_POST['speedtestserver']) && empty($_POST['speedtestserver'])) { pihole_execute('-a -ss auto'); } From 3ba1355fda616620a6d1ec721bcf06da6949d78a Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Sun, 4 Feb 2024 22:29:54 -0500 Subject: [PATCH 056/137] show error in block --- scripts/pi-hole/js/settings.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index da4222535..48206ce63 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -558,7 +558,9 @@ $(function () { codeBlock(speedtestStatus, statusText, speedtestStatusBtn, "status"); }) .fail(function () { - speedtestStatusBtn.text("Failed to get status"); + const triggerText = speedtestTest.attr("value") ? " awaiting confirmation" : " unknown"; + const statusText = "Failed to get schedule\nNext run is " + triggerText; + codeBlock(speedtestStatus, statusText, speedtestStatusBtn, "status"); }); }; @@ -635,11 +637,11 @@ $(function () { if (log) { codeBlock(speedtestLog, log, speedtestLogBtn, "log"); } else { - speedtestLogBtn.text("Failed to get log"); + codeBlock(speedtestLog, "The log is empty", speedtestLogBtn, "log"); } }) .fail(function () { - speedtestLogBtn.text("Failed to get log"); + codeBlock(speedtestLog, "Failed to get log", speedtestLogBtn, "log"); }); }; @@ -664,6 +666,7 @@ $(function () { .done(function (data) { const serversInfo = data?.data; if (serversInfo) { + speedtestServerCtr.find("p").remove(); codeBlock(speedtestServerCtr, serversInfo, speedtestServerBtn, "servers"); } else { tryNextCmd(); @@ -808,6 +811,7 @@ $(function () { speedtestServerBtn.text("Show closest servers"); } else { speedtestServerBtn.text("Retrieving servers..."); + speedtestServerCtr.find("p").remove(); closestServers(["JSONClosestServers", "getClosestServers", "curlClosestServers"]); } }); From baa4c5f90d56f5057ed4afa50ad8d4fa729ef3fb Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Sun, 4 Feb 2024 23:33:33 -0500 Subject: [PATCH 057/137] extra check for db backup --- api_speedtest.php | 10 ++++++++-- scripts/pi-hole/js/settings.js | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/api_speedtest.php b/api_speedtest.php index 70ac27817..948c23752 100644 --- a/api_speedtest.php +++ b/api_speedtest.php @@ -59,9 +59,15 @@ function hasSpeedTestBackup($dbSpeedtestOld) { - $data = file_exists($dbSpeedtestOld); + $exists = file_exists($dbSpeedtestOld); - return array('data' => $data); + if ($exists) { + $data = getAllSpeedTestData($dbSpeedtestOld); + } else { + $data = array(); + } + + return array('data' => !empty($data) && !empty($data['data']) ? true : false); } function getAllSpeedTestData($dbSpeedtest) diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index 48206ce63..56b8c38f2 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -559,7 +559,7 @@ $(function () { }) .fail(function () { const triggerText = speedtestTest.attr("value") ? " awaiting confirmation" : " unknown"; - const statusText = "Failed to get schedule\nNext run is " + triggerText; + const statusText = "Failed to get schedule\nNext run is" + triggerText; codeBlock(speedtestStatus, statusText, speedtestStatusBtn, "status"); }); }; From 08ff5ea408750a0b12687d778438449ac95565dd Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Mon, 5 Feb 2024 06:32:55 -0500 Subject: [PATCH 058/137] support cron script --- api_speedtest.php | 2 +- index.php | 6 +++--- scripts/pi-hole/js/settings.js | 35 ++++++++++++++++++---------------- 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/api_speedtest.php b/api_speedtest.php index 948c23752..08c9d23de 100644 --- a/api_speedtest.php +++ b/api_speedtest.php @@ -19,7 +19,7 @@ $cmdLog = 'cat /var/log/pimod.log'; $cmdServers = 'speedtest -h | grep -q official && sudo speedtest -L || speedtest --list'; -$cmdStatus = 'systemctl status pihole-speedtest.timer ; systemctl status pihole-speedtest --no-pager -l'; +$cmdStatus = "[[ -f /opt/pihole/speedtestmod/schedule_check.sh ]] && value=$(grep 'interval_seconds=' /opt/pihole/speedtestmod/schedule_check.sh | cut -d'=' -f2) || systemctl status pihole-speedtest.timer"; $cmdRun = 'cat /tmp/speedtest.log'; $cmdServersCurl = "curl 'https://c.speedtest.net/speedtest-servers-static.php' --compressed -H 'Upgrade-Insecure-Requests: 1' -H 'DNT: 1' -H 'Sec-GPC: 1'"; $cmdServersJSON = "curl 'https://www.speedtest.net/api/js/servers' --compressed -H 'Upgrade-Insecure-Requests: 1' -H 'Sec-GPC: 1'"; diff --git a/index.php b/index.php index b695af3c8..3b1016a3f 100644 --- a/index.php +++ b/index.php @@ -136,11 +136,11 @@
>
-
- -
+
+ +
diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index 56b8c38f2..042ff0a28 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -537,20 +537,23 @@ $(function () { let triggerText = speedtestTest.attr("value") ? " awaiting confirmation" : " disabled"; let statusText = "Schedule is inactive\nNext run is" + triggerText; if (status) { - const scheduleStatusPattern = /pihole-speedtest\.timer.*?Active:\s+(\w+)/s; - const triggerPattern = /Trigger:.*?;\s*([\d\s\w]+)\s+left/s; - - const scheduleStatusMatch = status.match(scheduleStatusPattern); - const triggerMatch = status.match(triggerPattern); - - const scheduleStatusText = scheduleStatusMatch ? scheduleStatusMatch[1] : "missing"; - if (!speedtestTest.attr("value")) { - if (triggerMatch) { - triggerText = ` in ${triggerMatch[1]}`; - } else if (scheduleStatusText === "active") { - triggerText = " running"; + if (status.match(/^\d+$/)) { + triggerText = ` in ${status}s`; + } else { + const scheduleStatusPattern = /pihole-speedtest\.timer.*?Active:\s+(\w+)/s; + const triggerPattern = /Trigger:.*?;\s*([\d\s\w]+)\s+left/s; + + const scheduleStatusMatch = status.match(scheduleStatusPattern); + const triggerMatch = status.match(triggerPattern); + + const scheduleStatusText = scheduleStatusMatch ? scheduleStatusMatch[1] : "missing"; + if (!speedtestTest.attr("value")) { + if (triggerMatch) { + triggerText = ` in ${triggerMatch[1]}`; + } else if (scheduleStatusText === "active") { + triggerText = " running"; + } } - } statusText = `Schedule is ${scheduleStatusText}\nNext run is${triggerText}`; } @@ -613,12 +616,12 @@ $(function () { colDiv.append(boxDiv); boxDiv.append(boxHeaderDiv); - boxHeaderDiv.append(h3); boxDiv.append(boxBodyDiv); + boxDiv.append(overlayDiv); + boxHeaderDiv.append(h3); boxBodyDiv.append(chartDiv); - chartDiv.append(canvas); - chartDiv.append(overlayDiv); overlayDiv.append(i); + chartDiv.append(canvas); speedtestChartPreview.find("div").remove(); speedtestChartPreview.append(colDiv); From 13a9b0353b056be3b1e4c368a18d7679596d216d Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Tue, 6 Feb 2024 17:12:31 -0500 Subject: [PATCH 059/137] fix docker --- api_speedtest.php | 67 ++++++++++++++++++++++++---- scripts/pi-hole/js/settings.js | 15 ++++--- scripts/pi-hole/js/speedresults.js | 16 ++----- scripts/pi-hole/js/speedtest.js | 22 +++++---- scripts/pi-hole/php/savesettings.php | 2 +- settings.php | 9 ++-- 6 files changed, 87 insertions(+), 44 deletions(-) diff --git a/api_speedtest.php b/api_speedtest.php index 08c9d23de..0346de9af 100644 --- a/api_speedtest.php +++ b/api_speedtest.php @@ -10,7 +10,7 @@ exit('Direct call to api_PHP.php is not allowed!'); } -// $data = array(); +date_default_timezone_set(exec('date +%Z')); $dbSpeedtest = '/etc/pihole/speedtest.db'; $dbSpeedtestOld = '/etc/pihole/speedtest.db.old'; @@ -19,10 +19,24 @@ $cmdLog = 'cat /var/log/pimod.log'; $cmdServers = 'speedtest -h | grep -q official && sudo speedtest -L || speedtest --list'; -$cmdStatus = "[[ -f /opt/pihole/speedtestmod/schedule_check.sh ]] && value=$(grep 'interval_seconds=' /opt/pihole/speedtestmod/schedule_check.sh | cut -d'=' -f2) || systemctl status pihole-speedtest.timer"; +if (file_exists('/opt/pihole/speedtestmod/schedule_check.sh')) { + $remaining_seconds = getRemainingTime(); + if ($remaining_seconds < 0) { + $cmdStatus = 'echo ""'; + } else { + $remaining_date = sprintf('%dd %dh %dmin %ds', $remaining_seconds / 86400, $remaining_seconds / 3600 % 24, $remaining_seconds / 60 % 60, $remaining_seconds % 60); + $remaining_date = preg_replace('/0d |0h |0min /', '', $remaining_date); // remove 0d 0h 0min + $remaining_date = preg_replace('/\s(\d+s)/', '', $remaining_date); // remove seconds if not needed + $cmdStatus = 'echo '.$remaining_date; + } +} elseif (!file_exists('/bin/systemctl')) { + $cmdStatus = 'echo ""'; +} else { + $cmdStatus = 'systemctl status pihole-speedtest.timer'; +} $cmdRun = 'cat /tmp/speedtest.log'; $cmdServersCurl = "curl 'https://c.speedtest.net/speedtest-servers-static.php' --compressed -H 'Upgrade-Insecure-Requests: 1' -H 'DNT: 1' -H 'Sec-GPC: 1'"; -$cmdServersJSON = "curl 'https://www.speedtest.net/api/js/servers' --compressed -H 'Upgrade-Insecure-Requests: 1' -H 'Sec-GPC: 1'"; +$cmdServersJSON = "curl 'https://www.speedtest.net/api/js/servers' --compressed -H 'Upgrade-Insecure-Requests: 1' -H 'DNT: 1' -H 'Sec-GPC: 1'"; if ($auth) { if (isset($_GET['hasSpeedTestBackup'])) { @@ -138,15 +152,25 @@ function getSpeedTestData($dbSpeedtest, $durationdays = '1') // return array("status"=>"success"); } + $dataFromSpeedDB = getLastSpeedtestResult($dbSpeedtest); + $tz = new DateTimeZone('UTC'); + if (isset($dataFromSpeedDB[0]['start_time'])) { + $tz = new DateTimeZone(substr($dataFromSpeedDB[0]['start_time'], -1)); + } + $curdate = date('Y-m-d H:i:s'); + $curdate = new DateTime(); + $curdate->setTimezone($tz); $date = new DateTime(); + $date->setTimezone($tz); $date->modify('-'.$durationdays.' day'); - $start_date = $date->format('Y-m-d H:i:s'); + $curdate = $curdate->format('Y-m-d H:i:s Z'); + $start_date = $date->format('Y-m-d H:i:s Z'); if ($durationdays == -1) { $sql = 'SELECT * from speedtest order by id asc'; } else { - $sql = "SELECT * from speedtest where start_time between '{$start_date}' and '{$curdate}' order by id asc;"; + $sql = "SELECT * from speedtest where start_time between '{$start_date}' and '{$curdate}' order by id asc"; } $dbResults = $db->query($sql); @@ -167,15 +191,19 @@ function getSpeedTestData($dbSpeedtest, $durationdays = '1') function getSpeedData($dbSpeedtest, $durationdays = '-2') { - global $log, $setupVars; + global $setupVars; if (isset($setupVars['SPEEDTEST_CHART_DAYS']) && $durationdays == '-2') { - $dataFromSpeedDB = getSpeedTestData($dbSpeedtest, $setupVars['SPEEDTEST_CHART_DAYS']); + $durationdays = $setupVars['SPEEDTEST_CHART_DAYS']; } else { $durationdays = (int) $durationdays < -1 ? '1' : $durationdays; - $dataFromSpeedDB = getSpeedTestData($dbSpeedtest, $durationdays); } - return $dataFromSpeedDB; + $data = getSpeedTestData($dbSpeedtest, $durationdays); + if (isset($data['error'])) { + return array(); + } + + return $data; } if (!empty($_GET['csv-export'])) { @@ -301,3 +329,24 @@ function JSONServers($cmdServersJSON) return array('data' => implode("\n", $serverList)); } } + +function getRemainingTime() +{ + $interval_seconds = speedtestExecute("grep 'interval_seconds=' /opt/pihole/speedtestmod/schedule_check.sh | cut -d'=' -f2")['data']; + $interval_seconds = (int) $interval_seconds; + + $last_run_time = -1; + if (file_exists('/etc/pihole/last_speedtest')) { + $last_run_time = file_get_contents('/etc/pihole/last_speedtest'); + $last_run_time = (int) $last_run_time; + } + + if ($last_run_time == -1) { + return -1; + } + + $time = time(); + $remaining_time = $interval_seconds - ($time - $last_run_time); + + return $remaining_time; +} diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index 042ff0a28..5f2b0b414 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -534,11 +534,14 @@ $(function () { }) .done(function (data) { const status = data?.data; + let scheduleStatusText = "inactive"; let triggerText = speedtestTest.attr("value") ? " awaiting confirmation" : " disabled"; - let statusText = "Schedule is inactive\nNext run is" + triggerText; if (status) { - if (status.match(/^\d+$/)) { - triggerText = ` in ${status}s`; + if (!status.includes("timer")) { + scheduleStatusText = "active"; + if (!speedtestTest.attr("value")) { + triggerText = status === "0s" ? " running" : ` in ${status}`; + } } else { const scheduleStatusPattern = /pihole-speedtest\.timer.*?Active:\s+(\w+)/s; const triggerPattern = /Trigger:.*?;\s*([\d\s\w]+)\s+left/s; @@ -546,7 +549,7 @@ $(function () { const scheduleStatusMatch = status.match(scheduleStatusPattern); const triggerMatch = status.match(triggerPattern); - const scheduleStatusText = scheduleStatusMatch ? scheduleStatusMatch[1] : "missing"; + scheduleStatusText = scheduleStatusMatch ? scheduleStatusMatch[1] : "missing"; if (!speedtestTest.attr("value")) { if (triggerMatch) { triggerText = ` in ${triggerMatch[1]}`; @@ -554,10 +557,10 @@ $(function () { triggerText = " running"; } } - - statusText = `Schedule is ${scheduleStatusText}\nNext run is${triggerText}`; + } } + const statusText = `Schedule is ${scheduleStatusText}\nNext run is${triggerText}`; codeBlock(speedtestStatus, statusText, speedtestStatusBtn, "status"); }) .fail(function () { diff --git a/scripts/pi-hole/js/speedresults.js b/scripts/pi-hole/js/speedresults.js index d6e167743..5065209b5 100644 --- a/scripts/pi-hole/js/speedresults.js +++ b/scripts/pi-hole/js/speedresults.js @@ -50,13 +50,9 @@ $(document).ready(function () { render: function (data, type, _full, _meta) { if (type === "display") { if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) { - data = moment(data, "YYYY-MM-DD HH:mm:ss Z") - .utcOffset(moment().utcOffset()) - .format("YYYY-MM-DD HH:mm:ss Z"); + data = moment(data, "YYYY-MM-DD HH:mm:ss Z").utcOffset(moment().utcOffset()); } else { - data = moment(new Date(data)) - .utcOffset(moment().utcOffset()) - .format("YYYY-MM-DD HH:mm:ss Z"); + data = moment(new Date(data).toISOString()); } } @@ -67,13 +63,9 @@ $(document).ready(function () { render: function (data, type, _full, _meta) { if (type === "display") { if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) { - data = moment(data, "YYYY-MM-DD HH:mm:ss Z") - .utcOffset(moment().utcOffset()) - .format("YYYY-MM-DD HH:mm:ss Z"); + data = moment(data, "YYYY-MM-DD HH:mm:ss Z").utcOffset(moment().utcOffset()); } else { - data = moment(new Date(data)) - .utcOffset(moment().utcOffset()) - .format("YYYY-MM-DD HH:mm:ss Z"); + data = moment(new Date(data).toISOString()); } } diff --git a/scripts/pi-hole/js/speedtest.js b/scripts/pi-hole/js/speedtest.js index b21551b0e..49bb16fa5 100644 --- a/scripts/pi-hole/js/speedtest.js +++ b/scripts/pi-hole/js/speedtest.js @@ -115,7 +115,7 @@ function createChart() { }, elements: { point: { - radius: 0, + radius: speedlabels.length > 1 ? 0 : 6, }, }, }, @@ -123,20 +123,18 @@ function createChart() { } function formatDate(itemdate, results) { + let output = "HH:mm"; if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) { - return moment(itemdate, "YYYY-MM-DD HH:mm:ss Z") - .utcOffset(moment().utcOffset()) - .format("HH:mm"); + return moment(itemdate, "YYYY-MM-DD HH:mm:ss Z").utcOffset(moment().utcOffset()).format(output); } - let output = "HH:mm"; - if (results.length > 1) { - const first = moment(results[0].start_time, "YYYY-MM-DD HH:mm:ss Z"); - const last = moment(results.at(-1).start_time, "YYYY-MM-DD HH:mm:ss Z"); - if (last.diff(first, "hours") >= 24) output = "Do " + output; + const first = moment(results[0].start_time, "YYYY-MM-DD HH:mm:ss Z"); + const last = moment(results.at(-1).start_time, "YYYY-MM-DD HH:mm:ss Z"); + if (last.diff(first, "hours") > 24) { + output = "Do HH:mm"; } - return moment(new Date(itemdate)).utcOffset(moment().utcOffset()).format(output); + return moment(new Date(itemdate).toISOString()).format(output); } function updateSpeedTestData() { @@ -155,7 +153,7 @@ function updateSpeedTestData() { url: "api.php?getSpeedData=" + days, dataType: "json", }).done(function (results) { - results.forEach(function (packet) { + results?.forEach(function (packet) { // console.log(speedlabels.indexOf(formatDate(packet.start_time))); if (speedlabels.indexOf(formatDate(packet.start_time, results)) === -1) { speedlabels.push(formatDate(packet.start_time, results)); @@ -164,7 +162,7 @@ function updateSpeedTestData() { serverPing.push(parseFloat(packet.server_ping)); } }); - if (speedChart && (!daysIsTheSame || !typeIsTheSame || beenHidden) && days !== "-2") { + if (speedChart && (!daysIsTheSame || !typeIsTheSame || beenHidden)) { speedChart.destroy(); speedChart = null; } diff --git a/scripts/pi-hole/php/savesettings.php b/scripts/pi-hole/php/savesettings.php index 9cbffe901..6cd8fe0ba 100644 --- a/scripts/pi-hole/php/savesettings.php +++ b/scripts/pi-hole/php/savesettings.php @@ -584,7 +584,7 @@ function addStaticDHCPLease($mac, $ip, $hostname) case 'speedtest': if (isset($_POST['speedtestschedule'])) { - pihole_execute('-a -s '.trim($_POST['speedtestschedule'])); + pihole_execute('-a -s '.trim($_POST['speedtestschedule']), true); } if (isset($_POST['clearspeedtests'])) { diff --git a/settings.php b/settings.php index 7614ceb93..49bf6ec74 100644 --- a/settings.php +++ b/settings.php @@ -1546,6 +1546,7 @@
Hours
- + Health check
@@ -1587,7 +1588,7 @@ class="form-control"
- + Bar graph
/> @@ -1616,7 +1617,7 @@ class="form-control"
- + Mod the Mod

For the adventurous

@@ -1661,7 +1662,7 @@ class="form-control"
- From 5ddf4a7a04ce92dc34d9605f1df3677a9fdddccd Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Thu, 8 Feb 2024 04:17:03 -0500 Subject: [PATCH 069/137] update chart --- scripts/pi-hole/js/speedtest.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/scripts/pi-hole/js/speedtest.js b/scripts/pi-hole/js/speedtest.js index 7cc370bf9..26e18ffa3 100644 --- a/scripts/pi-hole/js/speedtest.js +++ b/scripts/pi-hole/js/speedtest.js @@ -39,6 +39,8 @@ function createChart() { borderWidth: 3, yAxisID: "y-axis-1", tension: 0.4, + pointHitRadius: 5, + pointHoverRadius: 5, }, { label: "Mbps Upload", @@ -48,6 +50,8 @@ function createChart() { borderWidth: 3, yAxisID: "y-axis-1", tension: 0.4, + pointHitRadius: 5, + pointHoverRadius: 5, }, { label: "ms Ping", @@ -57,6 +61,8 @@ function createChart() { borderWidth: 3, yAxisID: "y-axis-2", tension: 0.4, + pointHitRadius: 5, + pointHoverRadius: 5, }, ], }, @@ -176,6 +182,10 @@ function updateSpeedTestData() { } if (speedChart) { + speedChart.data.labels = speedlabels; + speedChart.data.datasets[0].data = downloadspeed; + speedChart.data.datasets[1].data = uploadspeed; + speedChart.data.datasets[2].data = serverPing; speedChart.update(); $("#speedOverTimeChartOverlay").css("display", "none"); } From 8387c675f3e0ecd02810c1c648bf2fa646cd9e29 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Thu, 8 Feb 2024 04:28:46 -0500 Subject: [PATCH 070/137] fix regex --- api_speedtest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api_speedtest.php b/api_speedtest.php index 8cc4424a6..53dc5c96c 100644 --- a/api_speedtest.php +++ b/api_speedtest.php @@ -25,7 +25,7 @@ $cmdStatus = 'echo ""'; } else { $remaining_date = sprintf('%dd %dh %dmin %ds', $remaining_seconds / 86400, $remaining_seconds / 3600 % 24, $remaining_seconds / 60 % 60, $remaining_seconds % 60); - $remaining_date = preg_replace('/0d |0h |0min /', '', $remaining_date); // remove 0d 0h 0min + $remaining_date = preg_replace('/^0d |(^|(?<= ))0h |(^|(?<= ))0min /', '', $remaining_date); // remove 0d 0h 0min $remaining_date = preg_replace('/\s(\d+s)/', '', $remaining_date); // remove seconds if not needed $cmdStatus = 'echo '.$remaining_date; } From adea096ca77bf21ddfa043f64c894ae1aa44b43e Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Thu, 8 Feb 2024 04:54:10 -0500 Subject: [PATCH 071/137] min 0 --- api_speedtest.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/api_speedtest.php b/api_speedtest.php index 53dc5c96c..954c8a942 100644 --- a/api_speedtest.php +++ b/api_speedtest.php @@ -332,8 +332,5 @@ function getRemainingTime() return 0; } - $time = time(); - $remaining_time = $interval_seconds - ($time - $last_run_time); - - return $remaining_time; + return min(0, $interval_seconds - (time() - $last_run_time)); } From 3f584c42e6811ebbc3fe4a41c08c721adb29c732 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Thu, 8 Feb 2024 04:55:56 -0500 Subject: [PATCH 072/137] max --- api_speedtest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api_speedtest.php b/api_speedtest.php index 954c8a942..973ebe404 100644 --- a/api_speedtest.php +++ b/api_speedtest.php @@ -332,5 +332,5 @@ function getRemainingTime() return 0; } - return min(0, $interval_seconds - (time() - $last_run_time)); + return max(0, $interval_seconds - (time() - $last_run_time)); } From 0abdfbede0fdc76dcec0024160c458520a368e9d Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Thu, 8 Feb 2024 05:20:22 -0500 Subject: [PATCH 073/137] pretty --- api_speedtest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api_speedtest.php b/api_speedtest.php index 973ebe404..f413c5c92 100644 --- a/api_speedtest.php +++ b/api_speedtest.php @@ -309,14 +309,14 @@ function getRemainingTime() { $interval_seconds = speedtestExecute("grep 'interval_seconds=' /opt/pihole/speedtestmod/schedule_check.sh | cut -d'=' -f2")['data']; - # if interval_seconds is "nan", then schedule has never been set + // if interval_seconds is "nan", then schedule has never been set if (strpos($interval_seconds, 'nan') !== false) { return -1; } $interval_seconds = (int) $interval_seconds; - # if interval_seconds is less than 0, then schedule is disabled + // if interval_seconds is less than 0, then schedule is disabled if ($interval_seconds < 0) { return -1; } @@ -327,7 +327,7 @@ function getRemainingTime() $last_run_time = (int) $last_run_time; } - # if last_run_time is -1, then speedtest has never been run + // if last_run_time is -1, then speedtest has never been run if ($last_run_time == -1) { return 0; } From 1e43f2c6646b0155ab4b3a33f04e9576c9a15d5a Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Thu, 8 Feb 2024 14:57:53 -0500 Subject: [PATCH 074/137] use utc again --- api_speedtest.php | 14 ++++---------- scripts/pi-hole/js/speedresults.js | 8 ++------ scripts/pi-hole/js/speedtest.js | 8 ++++---- 3 files changed, 10 insertions(+), 20 deletions(-) diff --git a/api_speedtest.php b/api_speedtest.php index f413c5c92..55ed7d2a5 100644 --- a/api_speedtest.php +++ b/api_speedtest.php @@ -135,17 +135,11 @@ function getSpeedTestData($dbSpeedtest, $durationdays = '1') if ($durationdays == -1) { $sql = 'SELECT * from speedtest order by id asc'; } else { - $lastrun = getLastSpeedtestResult($dbSpeedtest); - if (empty($lastrun)) { - return array(); - } - - $system_tz = new DateTimeZone(end(explode(' ', $lastrun[0]['start_time']))); - $curdate = new DateTime('now', $system_tz); - $daysago = new DateTime('now', $system_tz); + $curdate = new DateTime('now', new DateTimeZone('UTC')); + $daysago = new DateTime('now', new DateTimeZone('UTC')); $daysago->modify('-'.$durationdays.' day'); - $daysago = $daysago->format('Y-m-d H:i:s'); - $curdate = $curdate->format('Y-m-d H:i:s'); + $curdate = $curdate->format('Y-m-d\TH:i'); + $daysago = $daysago->format('Y-m-d\TH:i'); $sql = "SELECT * from speedtest where start_time between '{$daysago}' and '{$curdate}' order by id asc"; } diff --git a/scripts/pi-hole/js/speedresults.js b/scripts/pi-hole/js/speedresults.js index c90a267c8..c13951757 100644 --- a/scripts/pi-hole/js/speedresults.js +++ b/scripts/pi-hole/js/speedresults.js @@ -49,9 +49,7 @@ $(document).ready(function () { { render: function (data, type, _full, _meta) { if (type === "display") { - data = /^((?!chrome|android).)*safari/i.test(navigator.userAgent) - ? moment(data, "YYYY-MM-DD HH:mm:ss Z").utcOffset(moment().utcOffset()) - : moment(new Date(data).toISOString()); + moment().utc(data).local(); } return data; @@ -60,9 +58,7 @@ $(document).ready(function () { { render: function (data, type, _full, _meta) { if (type === "display") { - data = /^((?!chrome|android).)*safari/i.test(navigator.userAgent) - ? moment(data, "YYYY-MM-DD HH:mm:ss Z").utcOffset(moment().utcOffset()) - : moment(new Date(data).toISOString()); + moment().utc(data).local(); } return data; diff --git a/scripts/pi-hole/js/speedtest.js b/scripts/pi-hole/js/speedtest.js index 26e18ffa3..cc41224db 100644 --- a/scripts/pi-hole/js/speedtest.js +++ b/scripts/pi-hole/js/speedtest.js @@ -131,16 +131,16 @@ function createChart() { function formatDate(itemdate, results) { let output = "HH:mm"; if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) { - return moment(itemdate, "YYYY-MM-DD HH:mm:ss Z").utcOffset(moment().utcOffset()).format(output); + return moment().utc(itemdate).local().format(output); } - const first = moment(results[0].start_time, "YYYY-MM-DD HH:mm:ss Z"); - const last = moment(results.at(-1).start_time, "YYYY-MM-DD HH:mm:ss Z"); + const first = moment(results[0].start_time); + const last = moment(results.at(-1).start_time); if (last.diff(first, "hours") > 24) { output = "Do HH:mm"; } - return moment(new Date(itemdate).toISOString()).format(output); + return moment().utc(itemdate).local().format(output); } function updateSpeedTestData() { From f7da7d847f7da94c3ffe147421ebdb6e0c8c395b Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Thu, 8 Feb 2024 15:10:27 -0500 Subject: [PATCH 075/137] moment --- scripts/pi-hole/js/speedresults.js | 4 ++-- scripts/pi-hole/js/speedtest.js | 8 ++------ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/scripts/pi-hole/js/speedresults.js b/scripts/pi-hole/js/speedresults.js index c13951757..f5349708f 100644 --- a/scripts/pi-hole/js/speedresults.js +++ b/scripts/pi-hole/js/speedresults.js @@ -49,7 +49,7 @@ $(document).ready(function () { { render: function (data, type, _full, _meta) { if (type === "display") { - moment().utc(data).local(); + moment.utc(data).local(); } return data; @@ -58,7 +58,7 @@ $(document).ready(function () { { render: function (data, type, _full, _meta) { if (type === "display") { - moment().utc(data).local(); + moment.utc(data).local(); } return data; diff --git a/scripts/pi-hole/js/speedtest.js b/scripts/pi-hole/js/speedtest.js index cc41224db..81b4c992a 100644 --- a/scripts/pi-hole/js/speedtest.js +++ b/scripts/pi-hole/js/speedtest.js @@ -129,18 +129,14 @@ function createChart() { } function formatDate(itemdate, results) { - let output = "HH:mm"; - if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) { - return moment().utc(itemdate).local().format(output); - } - const first = moment(results[0].start_time); const last = moment(results.at(-1).start_time); + let output = "HH:mm"; if (last.diff(first, "hours") > 24) { output = "Do HH:mm"; } - return moment().utc(itemdate).local().format(output); + return moment.utc(itemdate).local().format(output); } function updateSpeedTestData() { From 38ec7fd6b02fae2e7d230f0e2aec409830cafe9f Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Thu, 8 Feb 2024 15:17:48 -0500 Subject: [PATCH 076/137] safari --- scripts/pi-hole/js/speedtest.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/pi-hole/js/speedtest.js b/scripts/pi-hole/js/speedtest.js index 81b4c992a..91fc3b0ba 100644 --- a/scripts/pi-hole/js/speedtest.js +++ b/scripts/pi-hole/js/speedtest.js @@ -130,7 +130,7 @@ function createChart() { function formatDate(itemdate, results) { const first = moment(results[0].start_time); - const last = moment(results.at(-1).start_time); + const last = moment(results[results.length - 1].start_time); let output = "HH:mm"; if (last.diff(first, "hours") > 24) { output = "Do HH:mm"; From 38e01c91de80f822baff7303d13f84b4280f5f94 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Thu, 8 Feb 2024 15:21:37 -0500 Subject: [PATCH 077/137] format --- scripts/pi-hole/js/speedresults.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/pi-hole/js/speedresults.js b/scripts/pi-hole/js/speedresults.js index f5349708f..724982f73 100644 --- a/scripts/pi-hole/js/speedresults.js +++ b/scripts/pi-hole/js/speedresults.js @@ -49,7 +49,7 @@ $(document).ready(function () { { render: function (data, type, _full, _meta) { if (type === "display") { - moment.utc(data).local(); + moment.utc(data).local().format(); } return data; @@ -58,7 +58,7 @@ $(document).ready(function () { { render: function (data, type, _full, _meta) { if (type === "display") { - moment.utc(data).local(); + moment.utc(data).local().format(); } return data; From 2f069d5ebba9d369061ace187f1d91beb14e84ca Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Thu, 8 Feb 2024 15:29:45 -0500 Subject: [PATCH 078/137] safarii --- scripts/pi-hole/js/speedtest.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/pi-hole/js/speedtest.js b/scripts/pi-hole/js/speedtest.js index 91fc3b0ba..d0a8028b3 100644 --- a/scripts/pi-hole/js/speedtest.js +++ b/scripts/pi-hole/js/speedtest.js @@ -129,9 +129,13 @@ function createChart() { } function formatDate(itemdate, results) { + let output = "HH:mm"; + if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) { + return moment.utc(itemdate).local().format(output); + } + const first = moment(results[0].start_time); const last = moment(results[results.length - 1].start_time); - let output = "HH:mm"; if (last.diff(first, "hours") > 24) { output = "Do HH:mm"; } From f289cab63baec1e48bb036e6291cce4b0b3e462a Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Thu, 8 Feb 2024 15:38:49 -0500 Subject: [PATCH 079/137] reformat --- scripts/pi-hole/js/speedresults.js | 4 ++-- scripts/pi-hole/js/speedtest.js | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/scripts/pi-hole/js/speedresults.js b/scripts/pi-hole/js/speedresults.js index 724982f73..0a32371c8 100644 --- a/scripts/pi-hole/js/speedresults.js +++ b/scripts/pi-hole/js/speedresults.js @@ -49,7 +49,7 @@ $(document).ready(function () { { render: function (data, type, _full, _meta) { if (type === "display") { - moment.utc(data).local().format(); + moment.utc(data, "YYYY-MM-DDTHH:mm").local().format(); } return data; @@ -58,7 +58,7 @@ $(document).ready(function () { { render: function (data, type, _full, _meta) { if (type === "display") { - moment.utc(data).local().format(); + moment.utc(data, "YYYY-MM-DDTHH:mm").local().format(); } return data; diff --git a/scripts/pi-hole/js/speedtest.js b/scripts/pi-hole/js/speedtest.js index d0a8028b3..2dae06205 100644 --- a/scripts/pi-hole/js/speedtest.js +++ b/scripts/pi-hole/js/speedtest.js @@ -131,16 +131,16 @@ function createChart() { function formatDate(itemdate, results) { let output = "HH:mm"; if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) { - return moment.utc(itemdate).local().format(output); + return moment.utc(itemdate, "YYYY-MM-DDTHH:mm").local().format(output); } - const first = moment(results[0].start_time); - const last = moment(results[results.length - 1].start_time); + const first = moment(results[0].start_time, "YYYY-MM-DDTHH:mm"); + const last = moment(results[results.length - 1].start_time, "YYYY-MM-DDTHH:mm"); if (last.diff(first, "hours") > 24) { output = "Do HH:mm"; } - return moment.utc(itemdate).local().format(output); + return moment.utc(itemdate, "YYYY-MM-DDTHH:mm").local().format(output); } function updateSpeedTestData() { @@ -168,7 +168,7 @@ function updateSpeedTestData() { serverPing.push(parseFloat(packet.server_ping)); } }); - if (speedChart && (!daysIsTheSame || !typeIsTheSame || beenHidden) && days !== "-2") { + if (speedChart && (!daysIsTheSame || !typeIsTheSame || beenHidden)) { speedChart.destroy(); speedChart = null; } From 83664badb956bf4d65751ff3f34a2fdce5fbca25 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Thu, 8 Feb 2024 16:43:34 -0500 Subject: [PATCH 080/137] seconds --- api_speedtest.php | 4 ++-- scripts/pi-hole/js/speedresults.js | 4 ++-- scripts/pi-hole/js/speedtest.js | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/api_speedtest.php b/api_speedtest.php index 55ed7d2a5..fd1dade55 100644 --- a/api_speedtest.php +++ b/api_speedtest.php @@ -138,8 +138,8 @@ function getSpeedTestData($dbSpeedtest, $durationdays = '1') $curdate = new DateTime('now', new DateTimeZone('UTC')); $daysago = new DateTime('now', new DateTimeZone('UTC')); $daysago->modify('-'.$durationdays.' day'); - $curdate = $curdate->format('Y-m-d\TH:i'); - $daysago = $daysago->format('Y-m-d\TH:i'); + $curdate = $curdate->format('Y-m-d\TH:i:s'); + $daysago = $daysago->format('Y-m-d\TH:i:s'); $sql = "SELECT * from speedtest where start_time between '{$daysago}' and '{$curdate}' order by id asc"; } diff --git a/scripts/pi-hole/js/speedresults.js b/scripts/pi-hole/js/speedresults.js index 0a32371c8..724982f73 100644 --- a/scripts/pi-hole/js/speedresults.js +++ b/scripts/pi-hole/js/speedresults.js @@ -49,7 +49,7 @@ $(document).ready(function () { { render: function (data, type, _full, _meta) { if (type === "display") { - moment.utc(data, "YYYY-MM-DDTHH:mm").local().format(); + moment.utc(data).local().format(); } return data; @@ -58,7 +58,7 @@ $(document).ready(function () { { render: function (data, type, _full, _meta) { if (type === "display") { - moment.utc(data, "YYYY-MM-DDTHH:mm").local().format(); + moment.utc(data).local().format(); } return data; diff --git a/scripts/pi-hole/js/speedtest.js b/scripts/pi-hole/js/speedtest.js index 2dae06205..bfee47295 100644 --- a/scripts/pi-hole/js/speedtest.js +++ b/scripts/pi-hole/js/speedtest.js @@ -131,16 +131,16 @@ function createChart() { function formatDate(itemdate, results) { let output = "HH:mm"; if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) { - return moment.utc(itemdate, "YYYY-MM-DDTHH:mm").local().format(output); + return moment.utc(itemdate).local().format(output); } - const first = moment(results[0].start_time, "YYYY-MM-DDTHH:mm"); - const last = moment(results[results.length - 1].start_time, "YYYY-MM-DDTHH:mm"); + const first = moment(results.at(0).start_time); + const last = moment(results.at(-1).start_time); if (last.diff(first, "hours") > 24) { output = "Do HH:mm"; } - return moment.utc(itemdate, "YYYY-MM-DDTHH:mm").local().format(output); + return moment.utc(itemdate).local().format(output); } function updateSpeedTestData() { From 4e86e815790e845cd765054333d5be2860e5769b Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Thu, 8 Feb 2024 16:46:41 -0500 Subject: [PATCH 081/137] or days --- scripts/pi-hole/js/speedtest.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/pi-hole/js/speedtest.js b/scripts/pi-hole/js/speedtest.js index bfee47295..8288ec876 100644 --- a/scripts/pi-hole/js/speedtest.js +++ b/scripts/pi-hole/js/speedtest.js @@ -168,7 +168,7 @@ function updateSpeedTestData() { serverPing.push(parseFloat(packet.server_ping)); } }); - if (speedChart && (!daysIsTheSame || !typeIsTheSame || beenHidden)) { + if (speedChart && (!daysIsTheSame || !typeIsTheSame || beenHidden) || days === "-2") { speedChart.destroy(); speedChart = null; } From d28e06c6753b1c57b79da80d40ae8e612d66629f Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Thu, 8 Feb 2024 16:48:29 -0500 Subject: [PATCH 082/137] and not days --- scripts/pi-hole/js/speedtest.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/pi-hole/js/speedtest.js b/scripts/pi-hole/js/speedtest.js index 8288ec876..c31aa54ab 100644 --- a/scripts/pi-hole/js/speedtest.js +++ b/scripts/pi-hole/js/speedtest.js @@ -168,7 +168,7 @@ function updateSpeedTestData() { serverPing.push(parseFloat(packet.server_ping)); } }); - if (speedChart && (!daysIsTheSame || !typeIsTheSame || beenHidden) || days === "-2") { + if (speedChart && (!daysIsTheSame || !typeIsTheSame || beenHidden) && days !== "-2") { speedChart.destroy(); speedChart = null; } From e7340f3ff234ee40b059a307305236814f5c47a2 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Thu, 8 Feb 2024 17:12:01 -0500 Subject: [PATCH 083/137] format --- scripts/pi-hole/js/speedresults.js | 4 ++-- scripts/pi-hole/js/speedtest.js | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/pi-hole/js/speedresults.js b/scripts/pi-hole/js/speedresults.js index 724982f73..13f0264d9 100644 --- a/scripts/pi-hole/js/speedresults.js +++ b/scripts/pi-hole/js/speedresults.js @@ -49,7 +49,7 @@ $(document).ready(function () { { render: function (data, type, _full, _meta) { if (type === "display") { - moment.utc(data).local().format(); + moment.utc(data, "YYYY-MM-DDTHH:mm:ssZ").local().format("YYYY-MM-DDTHH:mmZ"); } return data; @@ -58,7 +58,7 @@ $(document).ready(function () { { render: function (data, type, _full, _meta) { if (type === "display") { - moment.utc(data).local().format(); + moment.utc(data, "YYYY-MM-DDTHH:mm:ssZ").local().format("YYYY-MM-DDTHH:mmZ"); } return data; diff --git a/scripts/pi-hole/js/speedtest.js b/scripts/pi-hole/js/speedtest.js index c31aa54ab..a15151b61 100644 --- a/scripts/pi-hole/js/speedtest.js +++ b/scripts/pi-hole/js/speedtest.js @@ -131,16 +131,16 @@ function createChart() { function formatDate(itemdate, results) { let output = "HH:mm"; if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) { - return moment.utc(itemdate).local().format(output); + return moment.utc(itemdate, "YYYY-MM-DDTHH:mm:ssZ").local().format(output); } - const first = moment(results.at(0).start_time); - const last = moment(results.at(-1).start_time); + const first = moment(results.at(0).start_time, "YYYY-MM-DDTHH:mm:ssZ"); + const last = moment(results.at(-1).start_time, "YYYY-MM-DDTHH:mm:ssZ"); if (last.diff(first, "hours") > 24) { output = "Do HH:mm"; } - return moment.utc(itemdate).local().format(output); + return moment.utc(itemdate, "YYYY-MM-DDTHH:mm:ssZ").local().format(output); } function updateSpeedTestData() { From 02f77e34254b21d3eb781c730752ad3e3be2b565 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Thu, 8 Feb 2024 17:25:21 -0500 Subject: [PATCH 084/137] rfc --- api_speedtest.php | 4 ++-- scripts/pi-hole/js/speedresults.js | 4 ++-- scripts/pi-hole/js/speedtest.js | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/api_speedtest.php b/api_speedtest.php index fd1dade55..244c0667d 100644 --- a/api_speedtest.php +++ b/api_speedtest.php @@ -138,8 +138,8 @@ function getSpeedTestData($dbSpeedtest, $durationdays = '1') $curdate = new DateTime('now', new DateTimeZone('UTC')); $daysago = new DateTime('now', new DateTimeZone('UTC')); $daysago->modify('-'.$durationdays.' day'); - $curdate = $curdate->format('Y-m-d\TH:i:s'); - $daysago = $daysago->format('Y-m-d\TH:i:s'); + $curdate = $curdate->format('Y-m-d H:i:s'); + $daysago = $daysago->format('Y-m-d H:i:s'); $sql = "SELECT * from speedtest where start_time between '{$daysago}' and '{$curdate}' order by id asc"; } diff --git a/scripts/pi-hole/js/speedresults.js b/scripts/pi-hole/js/speedresults.js index 13f0264d9..85835ecaa 100644 --- a/scripts/pi-hole/js/speedresults.js +++ b/scripts/pi-hole/js/speedresults.js @@ -49,7 +49,7 @@ $(document).ready(function () { { render: function (data, type, _full, _meta) { if (type === "display") { - moment.utc(data, "YYYY-MM-DDTHH:mm:ssZ").local().format("YYYY-MM-DDTHH:mmZ"); + moment.utc(data, "YYYY-MM-DD HH:mm:ssZ").local().format("YYYY-MM-DD HH:mmZ"); } return data; @@ -58,7 +58,7 @@ $(document).ready(function () { { render: function (data, type, _full, _meta) { if (type === "display") { - moment.utc(data, "YYYY-MM-DDTHH:mm:ssZ").local().format("YYYY-MM-DDTHH:mmZ"); + moment.utc(data, "YYYY-MM-DD HH:mm:ssZ").local().format("YYYY-MM-DD HH:mmZ"); } return data; diff --git a/scripts/pi-hole/js/speedtest.js b/scripts/pi-hole/js/speedtest.js index a15151b61..ed05b1a22 100644 --- a/scripts/pi-hole/js/speedtest.js +++ b/scripts/pi-hole/js/speedtest.js @@ -131,16 +131,16 @@ function createChart() { function formatDate(itemdate, results) { let output = "HH:mm"; if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) { - return moment.utc(itemdate, "YYYY-MM-DDTHH:mm:ssZ").local().format(output); + return moment.utc(itemdate, "YYYY-MM-DD HH:mm:ssZ").local().format(output); } - const first = moment(results.at(0).start_time, "YYYY-MM-DDTHH:mm:ssZ"); - const last = moment(results.at(-1).start_time, "YYYY-MM-DDTHH:mm:ssZ"); + const first = moment(results.at(0).start_time, "YYYY-MM-DD HH:mm:ssZ"); + const last = moment(results.at(-1).start_time, "YYYY-MM-DD HH:mm:ssZ"); if (last.diff(first, "hours") > 24) { output = "Do HH:mm"; } - return moment.utc(itemdate, "YYYY-MM-DDTHH:mm:ssZ").local().format(output); + return moment.utc(itemdate, "YYYY-MM-DD HH:mm:ssZ").local().format(output); } function updateSpeedTestData() { From 457f814ed652faa0c9080b83c7547b493c142b96 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Thu, 8 Feb 2024 17:58:08 -0500 Subject: [PATCH 085/137] last run text --- api_speedtest.php | 27 --------------------------- scripts/pi-hole/js/settings.js | 17 ++++++++++++++++- 2 files changed, 16 insertions(+), 28 deletions(-) diff --git a/api_speedtest.php b/api_speedtest.php index 244c0667d..fea61e20e 100644 --- a/api_speedtest.php +++ b/api_speedtest.php @@ -48,9 +48,6 @@ if (isset($_GET['getAllSpeedTestData'])) { $data = array_merge($data, getAllSpeedTestData($dbSpeedtest)); } - if (isset($_GET['getLastSpeedtestResult'])) { - $data = array_merge($data, getLastSpeedtestResult($dbSpeedtest)); - } if (isset($_GET['getLatestLog'])) { $data = array_merge($data, speedtestExecute($cmdLog)); } @@ -98,30 +95,6 @@ function getAllSpeedTestData($dbSpeedtest) return array('data' => $newarr); } -function getLastSpeedtestResult($dbSpeedtest) -{ - if (!file_exists($dbSpeedtest)) { - return array(); - } - - $db = new SQLite3($dbSpeedtest); - if (!$db) { - return array(); - } - - $sql = 'SELECT * from speedtest order by id DESC limit 1'; - $dbResults = $db->query($sql); - $dataFromSpeedDB = array(); - if (!empty($dbResults)) { - while ($row = $dbResults->fetchArray(SQLITE3_ASSOC)) { - array_push($dataFromSpeedDB, $row); - } - } - $db->close(); - - return $dataFromSpeedDB; -} - function getSpeedTestData($dbSpeedtest, $durationdays = '1') { if (!file_exists($dbSpeedtest)) { diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index 5f2b0b414..25eae382f 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -560,7 +560,22 @@ $(function () { } } - const statusText = `Schedule is ${scheduleStatusText}\nNext run is${triggerText}`; + var lastRunText = ""; + $.ajax({ + url: "api.php?getLatestRun", + dataType: "json", + }) + .done(function (data) { + const lastRun = data?.data; + if (lastRun) { + lastRunText = `\n\nLatest run:\n${lastRun}`; + } + }) + .fail(function () { + lastRunText = "\n\nFailed to get latest run"; + }); + + const statusText = `Schedule is ${scheduleStatusText}\nNext run is${triggerText}${lastRunText}`; codeBlock(speedtestStatus, statusText, speedtestStatusBtn, "status"); }) .fail(function () { From 6dcf5b42610b320646f2224968ff1477b57ad651 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Thu, 8 Feb 2024 18:07:24 -0500 Subject: [PATCH 086/137] add cond --- scripts/pi-hole/js/settings.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index 25eae382f..c62a474d7 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -560,7 +560,7 @@ $(function () { } } - var lastRunText = ""; + var lastRunText = "Latest run is unknown"; $.ajax({ url: "api.php?getLatestRun", dataType: "json", @@ -569,6 +569,8 @@ $(function () { const lastRun = data?.data; if (lastRun) { lastRunText = `\n\nLatest run:\n${lastRun}`; + } else { + lastRunText = ""; } }) .fail(function () { From 67ef1ee331a21d3e3987bc07e6a100a06d8c2296 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Thu, 8 Feb 2024 18:13:20 -0500 Subject: [PATCH 087/137] another way --- scripts/pi-hole/js/settings.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index c62a474d7..29b67e7a7 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -560,29 +560,29 @@ $(function () { } } - var lastRunText = "Latest run is unknown"; $.ajax({ url: "api.php?getLatestRun", dataType: "json", }) .done(function (data) { const lastRun = data?.data; + let lastRunText = ""; if (lastRun) { lastRunText = `\n\nLatest run:\n${lastRun}`; - } else { - lastRunText = ""; } + const statusText = `Schedule is ${scheduleStatusText}\nNext run is${triggerText}${lastRunText}`; + codeBlock(speedtestStatus, statusText, speedtestStatusBtn, "status"); }) .fail(function () { - lastRunText = "\n\nFailed to get latest run"; + const lastRunText = "\nFailed to get latest run"; + const statusText = `Schedule is ${scheduleStatusText}\nNext run is${triggerText}${lastRunText}`; + codeBlock(speedtestStatus, statusText, speedtestStatusBtn, "status"); }); - - const statusText = `Schedule is ${scheduleStatusText}\nNext run is${triggerText}${lastRunText}`; - codeBlock(speedtestStatus, statusText, speedtestStatusBtn, "status"); }) .fail(function () { const triggerText = speedtestTest.attr("value") ? " awaiting confirmation" : " unknown"; - const statusText = "Failed to get schedule\nNext run is" + triggerText; + const lastRunText = "\nFailed to get latest run"; + const statusText = "Failed to get schedule\nNext run is" + triggerText + lastRunText; codeBlock(speedtestStatus, statusText, speedtestStatusBtn, "status"); }); }; From 1c82bf4c6a6ad37f1f7bed484efae389574fb113 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Thu, 8 Feb 2024 18:41:13 -0500 Subject: [PATCH 088/137] add seconds until the next minute --- scripts/pi-hole/js/settings.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index 29b67e7a7..bd41937a7 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -540,6 +540,14 @@ $(function () { if (!status.includes("timer")) { scheduleStatusText = "active"; if (!speedtestTest.attr("value")) { + const triggerPattern = /(\d+s)/; + const triggerMatch = status.match(triggerPattern); + + const now = new Date(); + const secondsUntilNextMinute = 60 - now.getSeconds(); + const statusSeconds = parseInt(triggerMatch[0].replace("s", "")); + const status = statusSeconds > secondsUntilNextMinute ? `${statusSeconds - secondsUntilNextMinute}s` : "0s"; + triggerText = status === "0s" ? " running" : ` in ${status}`; } } else { From 45a41a5b772b9534d9ce71858159d26b3e57804d Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Thu, 8 Feb 2024 18:44:51 -0500 Subject: [PATCH 089/137] typo --- scripts/pi-hole/js/settings.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index bd41937a7..ea0a408cf 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -546,9 +546,9 @@ $(function () { const now = new Date(); const secondsUntilNextMinute = 60 - now.getSeconds(); const statusSeconds = parseInt(triggerMatch[0].replace("s", "")); - const status = statusSeconds > secondsUntilNextMinute ? `${statusSeconds - secondsUntilNextMinute}s` : "0s"; + const statusText = statusSeconds > secondsUntilNextMinute ? `${statusSeconds - secondsUntilNextMinute}s` : "0s"; - triggerText = status === "0s" ? " running" : ` in ${status}`; + triggerText = statusText === "0s" ? " running" : ` in ${status}`; } } else { const scheduleStatusPattern = /pihole-speedtest\.timer.*?Active:\s+(\w+)/s; From 0f71f7596afd4e37f274b77aadaa505a8dd4b233 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Thu, 8 Feb 2024 20:46:10 -0500 Subject: [PATCH 090/137] remainder --- scripts/pi-hole/js/settings.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index ea0a408cf..c22c38902 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -543,11 +543,13 @@ $(function () { const triggerPattern = /(\d+s)/; const triggerMatch = status.match(triggerPattern); - const now = new Date(); - const secondsUntilNextMinute = 60 - now.getSeconds(); - const statusSeconds = parseInt(triggerMatch[0].replace("s", "")); - const statusText = statusSeconds > secondsUntilNextMinute ? `${statusSeconds - secondsUntilNextMinute}s` : "0s"; - + let statusText = status; + if (triggerMatch) { + const now = new Date(); + const secondsUntilNextMinute = 60 - now.getSeconds(); + const statusSeconds = parseInt(triggerMatch[0].replace("s", "")); + statusText = statusSeconds > secondsUntilNextMinute ? `${statusSeconds - secondsUntilNextMinute}s` : "0s"; + } triggerText = statusText === "0s" ? " running" : ` in ${status}`; } } else { From 6ca138005ade899e6e9ee5b5ec2c39fd9537f2c3 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Thu, 8 Feb 2024 21:18:52 -0500 Subject: [PATCH 091/137] ensure log --- api_speedtest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api_speedtest.php b/api_speedtest.php index fea61e20e..3e24ba9f6 100644 --- a/api_speedtest.php +++ b/api_speedtest.php @@ -17,7 +17,7 @@ $setupVars = parse_ini_file('/etc/pihole/setupVars.conf'); -$cmdLog = 'cat /var/log/pimod.log'; +$cmdLog = '[[ -f /var/log/pimod.log ]] && cat /var/log/pimod.log || echo ""'; $cmdServers = 'speedtest -h | grep -q official && sudo speedtest -L || speedtest --list'; if (file_exists('/opt/pihole/speedtestmod/schedule_check.sh')) { $remaining_seconds = getRemainingTime(); @@ -34,7 +34,7 @@ } else { $cmdStatus = 'systemctl status pihole-speedtest.timer'; } -$cmdRun = 'cat /tmp/speedtest.log'; +$cmdRun = '[[ -f /var/log/pihole/speedtest.log ]] && cat /var/log/pihole/speedtest.log || echo ""'; $cmdServersCurl = "curl 'https://c.speedtest.net/speedtest-servers-static.php' --compressed -H 'Upgrade-Insecure-Requests: 1' -H 'DNT: 1' -H 'Sec-GPC: 1'"; $cmdServersJSON = "curl 'https://www.speedtest.net/api/js/servers' --compressed -H 'Upgrade-Insecure-Requests: 1' -H 'DNT: 1' -H 'Sec-GPC: 1'"; From e748320520c804aba78488795f532a687505112d Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Thu, 8 Feb 2024 21:27:09 -0500 Subject: [PATCH 092/137] use Date --- scripts/pi-hole/js/speedtest.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/pi-hole/js/speedtest.js b/scripts/pi-hole/js/speedtest.js index ed05b1a22..fc2a8a840 100644 --- a/scripts/pi-hole/js/speedtest.js +++ b/scripts/pi-hole/js/speedtest.js @@ -140,7 +140,7 @@ function formatDate(itemdate, results) { output = "Do HH:mm"; } - return moment.utc(itemdate, "YYYY-MM-DD HH:mm:ssZ").local().format(output); + return moment(new Date(itemdate).toISOString()).format(output); } function updateSpeedTestData() { From ad0d7d233b244cb46fdad74e65c4503062197362 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Fri, 9 Feb 2024 00:28:47 -0500 Subject: [PATCH 093/137] verbiage --- scripts/pi-hole/js/settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index c22c38902..ec626b0e4 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -550,7 +550,7 @@ $(function () { const statusSeconds = parseInt(triggerMatch[0].replace("s", "")); statusText = statusSeconds > secondsUntilNextMinute ? `${statusSeconds - secondsUntilNextMinute}s` : "0s"; } - triggerText = statusText === "0s" ? " running" : ` in ${status}`; + triggerText = statusText === "0s" ? " queued" : ` in ${status}`; } } else { const scheduleStatusPattern = /pihole-speedtest\.timer.*?Active:\s+(\w+)/s; From a0fd45a6ff3081b2ffe921cf56416c84b3395866 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Fri, 9 Feb 2024 00:34:37 -0500 Subject: [PATCH 094/137] I can pipe from inside --- api_speedtest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api_speedtest.php b/api_speedtest.php index 3e24ba9f6..4339dc0e0 100644 --- a/api_speedtest.php +++ b/api_speedtest.php @@ -34,7 +34,7 @@ } else { $cmdStatus = 'systemctl status pihole-speedtest.timer'; } -$cmdRun = '[[ -f /var/log/pihole/speedtest.log ]] && cat /var/log/pihole/speedtest.log || echo ""'; +$cmdRun = '[[ -f /tmp/speedtest.log ]] && cat /tmp/speedtest.log || [[ -f /var/log/pihole/speedtest.log ]] && cat /var/log/pihole/speedtest.log || echo ""'; $cmdServersCurl = "curl 'https://c.speedtest.net/speedtest-servers-static.php' --compressed -H 'Upgrade-Insecure-Requests: 1' -H 'DNT: 1' -H 'Sec-GPC: 1'"; $cmdServersJSON = "curl 'https://www.speedtest.net/api/js/servers' --compressed -H 'Upgrade-Insecure-Requests: 1' -H 'DNT: 1' -H 'Sec-GPC: 1'"; From ebe5feea21836a75ed23ee124f8bae5ae224409f Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Fri, 9 Feb 2024 00:41:15 -0500 Subject: [PATCH 095/137] inline if --- api_speedtest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api_speedtest.php b/api_speedtest.php index 4339dc0e0..af911c5c9 100644 --- a/api_speedtest.php +++ b/api_speedtest.php @@ -34,7 +34,7 @@ } else { $cmdStatus = 'systemctl status pihole-speedtest.timer'; } -$cmdRun = '[[ -f /tmp/speedtest.log ]] && cat /tmp/speedtest.log || [[ -f /var/log/pihole/speedtest.log ]] && cat /var/log/pihole/speedtest.log || echo ""'; +$cmdRun = 'if [[ -f /tmp/speedtest.log ]]; then cat /tmp/speedtest.log elif [[ -f /var/log/pihole/speedtest.log ]]; then cat /var/log/pihole/speedtest.log else echo "" fi'; $cmdServersCurl = "curl 'https://c.speedtest.net/speedtest-servers-static.php' --compressed -H 'Upgrade-Insecure-Requests: 1' -H 'DNT: 1' -H 'Sec-GPC: 1'"; $cmdServersJSON = "curl 'https://www.speedtest.net/api/js/servers' --compressed -H 'Upgrade-Insecure-Requests: 1' -H 'DNT: 1' -H 'Sec-GPC: 1'"; From 16dcc64bc9c00ecefcd2295a15e88326eef21946 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Fri, 9 Feb 2024 00:59:35 -0500 Subject: [PATCH 096/137] use pipe --- api_speedtest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api_speedtest.php b/api_speedtest.php index af911c5c9..abed78561 100644 --- a/api_speedtest.php +++ b/api_speedtest.php @@ -17,7 +17,7 @@ $setupVars = parse_ini_file('/etc/pihole/setupVars.conf'); -$cmdLog = '[[ -f /var/log/pimod.log ]] && cat /var/log/pimod.log || echo ""'; +$cmdLog = '[[ -f /tmp/pimod.log ]] && cat /tmp/pimod.log || { [[ -f /var/log/pimod.log ]] && cat /var/log/pimod.log || echo ""; }'; $cmdServers = 'speedtest -h | grep -q official && sudo speedtest -L || speedtest --list'; if (file_exists('/opt/pihole/speedtestmod/schedule_check.sh')) { $remaining_seconds = getRemainingTime(); @@ -34,7 +34,7 @@ } else { $cmdStatus = 'systemctl status pihole-speedtest.timer'; } -$cmdRun = 'if [[ -f /tmp/speedtest.log ]]; then cat /tmp/speedtest.log elif [[ -f /var/log/pihole/speedtest.log ]]; then cat /var/log/pihole/speedtest.log else echo "" fi'; +$cmdRun = '[[ -f /tmp/speedtest.log ]] && cat /tmp/speedtest.log || { [[ -f /var/log/pihole/speedtest.log ]] && cat /var/log/pihole/speedtest.log || echo ""; }'; $cmdServersCurl = "curl 'https://c.speedtest.net/speedtest-servers-static.php' --compressed -H 'Upgrade-Insecure-Requests: 1' -H 'DNT: 1' -H 'Sec-GPC: 1'"; $cmdServersJSON = "curl 'https://www.speedtest.net/api/js/servers' --compressed -H 'Upgrade-Insecure-Requests: 1' -H 'DNT: 1' -H 'Sec-GPC: 1'"; From 3e948c9a940e92b8896fc5a706ad81acd5307410 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Fri, 9 Feb 2024 02:17:22 -0500 Subject: [PATCH 097/137] move log --- api_speedtest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api_speedtest.php b/api_speedtest.php index abed78561..46fdb0359 100644 --- a/api_speedtest.php +++ b/api_speedtest.php @@ -17,7 +17,7 @@ $setupVars = parse_ini_file('/etc/pihole/setupVars.conf'); -$cmdLog = '[[ -f /tmp/pimod.log ]] && cat /tmp/pimod.log || { [[ -f /var/log/pimod.log ]] && cat /var/log/pimod.log || echo ""; }'; +$cmdLog = '[[ -f /tmp/pimod.log ]] && cat /tmp/pimod.log || { [[ -f /var/log/pihole/mod.log ]] && cat /var/log/pihole/mod.log || echo ""; }'; $cmdServers = 'speedtest -h | grep -q official && sudo speedtest -L || speedtest --list'; if (file_exists('/opt/pihole/speedtestmod/schedule_check.sh')) { $remaining_seconds = getRemainingTime(); From d29de6d1b52612b96588cd35a65d02da59ff500f Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Fri, 9 Feb 2024 04:04:03 -0500 Subject: [PATCH 098/137] data = --- scripts/pi-hole/js/speedresults.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/pi-hole/js/speedresults.js b/scripts/pi-hole/js/speedresults.js index 85835ecaa..f34c070d2 100644 --- a/scripts/pi-hole/js/speedresults.js +++ b/scripts/pi-hole/js/speedresults.js @@ -49,7 +49,7 @@ $(document).ready(function () { { render: function (data, type, _full, _meta) { if (type === "display") { - moment.utc(data, "YYYY-MM-DD HH:mm:ssZ").local().format("YYYY-MM-DD HH:mmZ"); + data = moment.utc(data, "YYYY-MM-DD HH:mm:ssZ").local().format("YYYY-MM-DD HH:mmZ"); } return data; @@ -58,7 +58,7 @@ $(document).ready(function () { { render: function (data, type, _full, _meta) { if (type === "display") { - moment.utc(data, "YYYY-MM-DD HH:mm:ssZ").local().format("YYYY-MM-DD HH:mmZ"); + data = moment.utc(data, "YYYY-MM-DD HH:mm:ssZ").local().format("YYYY-MM-DD HH:mmZ"); } return data; From bff39b5b42fcd16479e2cbdfff01d4bd96bed035 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Fri, 9 Feb 2024 04:58:34 -0500 Subject: [PATCH 099/137] pretty --- scripts/pi-hole/js/settings.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index ec626b0e4..984a5e425 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -548,7 +548,10 @@ $(function () { const now = new Date(); const secondsUntilNextMinute = 60 - now.getSeconds(); const statusSeconds = parseInt(triggerMatch[0].replace("s", "")); - statusText = statusSeconds > secondsUntilNextMinute ? `${statusSeconds - secondsUntilNextMinute}s` : "0s"; + statusText = + statusSeconds > secondsUntilNextMinute + ? `${statusSeconds - secondsUntilNextMinute}s` + : "0s"; } triggerText = statusText === "0s" ? " queued" : ` in ${status}`; } From 5296259f3aaed4b99f9f68dcde0d2457c918440b Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Sun, 11 Feb 2024 00:27:32 -0500 Subject: [PATCH 100/137] int --- api_speedtest.php | 2 +- scripts/pi-hole/js/settings.js | 4 +++- scripts/pi-hole/js/speedtest.js | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/api_speedtest.php b/api_speedtest.php index 46fdb0359..c41e2ccaf 100644 --- a/api_speedtest.php +++ b/api_speedtest.php @@ -105,7 +105,7 @@ function getSpeedTestData($dbSpeedtest, $durationdays = '1') return array(); } - if ($durationdays == -1) { + if ((int) $durationdays == -1) { $sql = 'SELECT * from speedtest order by id asc'; } else { $curdate = new DateTime('now', new DateTimeZone('UTC')); diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index 984a5e425..f969683e0 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -547,12 +547,13 @@ $(function () { if (triggerMatch) { const now = new Date(); const secondsUntilNextMinute = 60 - now.getSeconds(); - const statusSeconds = parseInt(triggerMatch[0].replace("s", "")); + const statusSeconds = parseInt(triggerMatch[0].replace("s", ""), 10); statusText = statusSeconds > secondsUntilNextMinute ? `${statusSeconds - secondsUntilNextMinute}s` : "0s"; } + triggerText = statusText === "0s" ? " queued" : ` in ${status}`; } } else { @@ -583,6 +584,7 @@ $(function () { if (lastRun) { lastRunText = `\n\nLatest run:\n${lastRun}`; } + const statusText = `Schedule is ${scheduleStatusText}\nNext run is${triggerText}${lastRunText}`; codeBlock(speedtestStatus, statusText, speedtestStatusBtn, "status"); }) diff --git a/scripts/pi-hole/js/speedtest.js b/scripts/pi-hole/js/speedtest.js index fc2a8a840..3dc60d2b4 100644 --- a/scripts/pi-hole/js/speedtest.js +++ b/scripts/pi-hole/js/speedtest.js @@ -168,12 +168,12 @@ function updateSpeedTestData() { serverPing.push(parseFloat(packet.server_ping)); } }); - if (speedChart && (!daysIsTheSame || !typeIsTheSame || beenHidden) && days !== "-2") { + if (speedChart && (!daysIsTheSame || !typeIsTheSame || beenHidden)) { speedChart.destroy(); speedChart = null; } - if (!speedChart || beenHidden) { + if (!speedChart) { localStorage.setItem( "speedtest_preview_hidden", !localStorage?.getItem("speedtest_preview_shown") From 755b07a45413b2b3e71ca228223186197c5d4c30 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Sun, 11 Feb 2024 00:41:00 -0500 Subject: [PATCH 101/137] extra utc? --- scripts/pi-hole/js/speedresults.js | 4 ++-- scripts/pi-hole/js/speedtest.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/pi-hole/js/speedresults.js b/scripts/pi-hole/js/speedresults.js index f34c070d2..2ca1a5781 100644 --- a/scripts/pi-hole/js/speedresults.js +++ b/scripts/pi-hole/js/speedresults.js @@ -49,7 +49,7 @@ $(document).ready(function () { { render: function (data, type, _full, _meta) { if (type === "display") { - data = moment.utc(data, "YYYY-MM-DD HH:mm:ssZ").local().format("YYYY-MM-DD HH:mmZ"); + data = moment(data, "YYYY-MM-DD HH:mm:ssZ").local().format("YYYY-MM-DD HH:mmZ"); } return data; @@ -58,7 +58,7 @@ $(document).ready(function () { { render: function (data, type, _full, _meta) { if (type === "display") { - data = moment.utc(data, "YYYY-MM-DD HH:mm:ssZ").local().format("YYYY-MM-DD HH:mmZ"); + data = moment(data, "YYYY-MM-DD HH:mm:ssZ").local().format("YYYY-MM-DD HH:mmZ"); } return data; diff --git a/scripts/pi-hole/js/speedtest.js b/scripts/pi-hole/js/speedtest.js index 3dc60d2b4..013aaade2 100644 --- a/scripts/pi-hole/js/speedtest.js +++ b/scripts/pi-hole/js/speedtest.js @@ -181,7 +181,7 @@ function updateSpeedTestData() { createChart(); } - if (speedChart) { + if (speedChart && speedChart.data.labels.join() !== speedlabels.join()) { speedChart.data.labels = speedlabels; speedChart.data.datasets[0].data = downloadspeed; speedChart.data.datasets[1].data = uploadspeed; From 3df07e9e669f18d4e71e8b2553962caa03aa95fd Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Sun, 11 Feb 2024 00:53:00 -0500 Subject: [PATCH 102/137] Parse the date as UTC but ignore the time zone conversion by using moment.utc --- scripts/pi-hole/js/speedresults.js | 4 ++-- scripts/pi-hole/js/speedtest.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/pi-hole/js/speedresults.js b/scripts/pi-hole/js/speedresults.js index 2ca1a5781..542dd1687 100644 --- a/scripts/pi-hole/js/speedresults.js +++ b/scripts/pi-hole/js/speedresults.js @@ -49,7 +49,7 @@ $(document).ready(function () { { render: function (data, type, _full, _meta) { if (type === "display") { - data = moment(data, "YYYY-MM-DD HH:mm:ssZ").local().format("YYYY-MM-DD HH:mmZ"); + data = moment.utc(data, "YYYY-MM-DD HH:mm:ss").format("YYYY-MM-DD HH:mm"); } return data; @@ -58,7 +58,7 @@ $(document).ready(function () { { render: function (data, type, _full, _meta) { if (type === "display") { - data = moment(data, "YYYY-MM-DD HH:mm:ssZ").local().format("YYYY-MM-DD HH:mmZ"); + data = moment.utc(data, "YYYY-MM-DD HH:mm:ss").format("YYYY-MM-DD HH:mm"); } return data; diff --git a/scripts/pi-hole/js/speedtest.js b/scripts/pi-hole/js/speedtest.js index 013aaade2..acff782ba 100644 --- a/scripts/pi-hole/js/speedtest.js +++ b/scripts/pi-hole/js/speedtest.js @@ -199,5 +199,5 @@ $(function () { updateSpeedTestData(); setInterval(function () { updateSpeedTestData(); - }, 6000); + }, 1000); }); From 3f0d3ecbec3c10ebed529752e3d8240d5066add8 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Sun, 11 Feb 2024 01:03:49 -0500 Subject: [PATCH 103/137] with local() --- scripts/pi-hole/js/speedresults.js | 4 ++-- scripts/pi-hole/js/speedtest.js | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/scripts/pi-hole/js/speedresults.js b/scripts/pi-hole/js/speedresults.js index 542dd1687..f316c077c 100644 --- a/scripts/pi-hole/js/speedresults.js +++ b/scripts/pi-hole/js/speedresults.js @@ -49,7 +49,7 @@ $(document).ready(function () { { render: function (data, type, _full, _meta) { if (type === "display") { - data = moment.utc(data, "YYYY-MM-DD HH:mm:ss").format("YYYY-MM-DD HH:mm"); + data = moment.utc(data, "YYYY-MM-DD HH:mm:ss").local().format("YYYY-MM-DD HH:mm Z"); } return data; @@ -58,7 +58,7 @@ $(document).ready(function () { { render: function (data, type, _full, _meta) { if (type === "display") { - data = moment.utc(data, "YYYY-MM-DD HH:mm:ss").format("YYYY-MM-DD HH:mm"); + data = moment.utc(data, "YYYY-MM-DD HH:mm:ss").local().format("YYYY-MM-DD HH:mm Z"); } return data; diff --git a/scripts/pi-hole/js/speedtest.js b/scripts/pi-hole/js/speedtest.js index acff782ba..80bdc3315 100644 --- a/scripts/pi-hole/js/speedtest.js +++ b/scripts/pi-hole/js/speedtest.js @@ -187,8 +187,9 @@ function updateSpeedTestData() { speedChart.data.datasets[1].data = uploadspeed; speedChart.data.datasets[2].data = serverPing; speedChart.update(); - $("#speedOverTimeChartOverlay").css("display", "none"); } + + $("#speedOverTimeChartOverlay").css("display", "none"); }); } From 482ba9ccea7711d424e77aaded246cd62eebdebf Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Sun, 11 Feb 2024 01:36:56 -0500 Subject: [PATCH 104/137] log --- scripts/pi-hole/js/speedresults.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/scripts/pi-hole/js/speedresults.js b/scripts/pi-hole/js/speedresults.js index f316c077c..258ad5621 100644 --- a/scripts/pi-hole/js/speedresults.js +++ b/scripts/pi-hole/js/speedresults.js @@ -48,17 +48,18 @@ $(document).ready(function () { null, { render: function (data, type, _full, _meta) { + console.log("utc: " + data); if (type === "display") { - data = moment.utc(data, "YYYY-MM-DD HH:mm:ss").local().format("YYYY-MM-DD HH:mm Z"); + data = moment.utc(data, "YYYY-MM-DD HH:mm:ss").local().format("YYYY-MM-DD HH:mmZ"); } - + console.log("local: " + data); return data; }, }, { render: function (data, type, _full, _meta) { if (type === "display") { - data = moment.utc(data, "YYYY-MM-DD HH:mm:ss").local().format("YYYY-MM-DD HH:mm Z"); + data = moment.utc(data, "YYYY-MM-DD HH:mm:ss").local().format("YYYY-MM-DD HH:mmZ"); } return data; From d4394039572450d1265b3034ae226d42fb38b0a9 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Sun, 11 Feb 2024 01:49:20 -0500 Subject: [PATCH 105/137] no log --- scripts/pi-hole/js/speedresults.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/scripts/pi-hole/js/speedresults.js b/scripts/pi-hole/js/speedresults.js index 258ad5621..512a21e15 100644 --- a/scripts/pi-hole/js/speedresults.js +++ b/scripts/pi-hole/js/speedresults.js @@ -48,9 +48,8 @@ $(document).ready(function () { null, { render: function (data, type, _full, _meta) { - console.log("utc: " + data); if (type === "display") { - data = moment.utc(data, "YYYY-MM-DD HH:mm:ss").local().format("YYYY-MM-DD HH:mmZ"); + data = moment.utc(data, "YYYY-MM-DD HH:mm:ss").local().format(); } console.log("local: " + data); return data; @@ -59,7 +58,7 @@ $(document).ready(function () { { render: function (data, type, _full, _meta) { if (type === "display") { - data = moment.utc(data, "YYYY-MM-DD HH:mm:ss").local().format("YYYY-MM-DD HH:mmZ"); + data = moment.utc(data, "YYYY-MM-DD HH:mm:ss").local().format(); } return data; From 25634a0ab0f8e736a57a6deb19d9ded9624faa93 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Sun, 11 Feb 2024 01:52:22 -0500 Subject: [PATCH 106/137] pretty --- scripts/pi-hole/js/speedresults.js | 2 +- scripts/pi-hole/js/speedtest.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/pi-hole/js/speedresults.js b/scripts/pi-hole/js/speedresults.js index 512a21e15..9160d7aed 100644 --- a/scripts/pi-hole/js/speedresults.js +++ b/scripts/pi-hole/js/speedresults.js @@ -51,7 +51,7 @@ $(document).ready(function () { if (type === "display") { data = moment.utc(data, "YYYY-MM-DD HH:mm:ss").local().format(); } - console.log("local: " + data); + return data; }, }, diff --git a/scripts/pi-hole/js/speedtest.js b/scripts/pi-hole/js/speedtest.js index 80bdc3315..424b311ef 100644 --- a/scripts/pi-hole/js/speedtest.js +++ b/scripts/pi-hole/js/speedtest.js @@ -181,7 +181,7 @@ function updateSpeedTestData() { createChart(); } - if (speedChart && speedChart.data.labels.join() !== speedlabels.join()) { + if (speedChart && speedChart.data.labels.join(",") !== speedlabels.join(",")) { speedChart.data.labels = speedlabels; speedChart.data.datasets[0].data = downloadspeed; speedChart.data.datasets[1].data = uploadspeed; From 80183345ac7fd9890418e30f2dff1496b81b9e71 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Sun, 11 Feb 2024 02:11:28 -0500 Subject: [PATCH 107/137] no animation --- scripts/pi-hole/js/speedtest.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/pi-hole/js/speedtest.js b/scripts/pi-hole/js/speedtest.js index 424b311ef..353cd18c2 100644 --- a/scripts/pi-hole/js/speedtest.js +++ b/scripts/pi-hole/js/speedtest.js @@ -131,7 +131,7 @@ function createChart() { function formatDate(itemdate, results) { let output = "HH:mm"; if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) { - return moment.utc(itemdate, "YYYY-MM-DD HH:mm:ssZ").local().format(output); + return moment.utc(itemdate, "YYYY-MM-DD HH:mm:ss").local().format(output); } const first = moment(results.at(0).start_time, "YYYY-MM-DD HH:mm:ssZ"); @@ -140,7 +140,7 @@ function formatDate(itemdate, results) { output = "Do HH:mm"; } - return moment(new Date(itemdate).toISOString()).format(output); + return moment.utc(itemdate, "YYYY-MM-DD HH:mm:ss").local().format(output); } function updateSpeedTestData() { @@ -186,7 +186,7 @@ function updateSpeedTestData() { speedChart.data.datasets[0].data = downloadspeed; speedChart.data.datasets[1].data = uploadspeed; speedChart.data.datasets[2].data = serverPing; - speedChart.update(); + speedChart.update("none"); } $("#speedOverTimeChartOverlay").css("display", "none"); From 6e41e6c728f55ae70887895a39d9c9ce3061d2c3 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Sun, 11 Feb 2024 02:16:39 -0500 Subject: [PATCH 108/137] why join --- scripts/pi-hole/js/speedtest.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/pi-hole/js/speedtest.js b/scripts/pi-hole/js/speedtest.js index 353cd18c2..54da7927b 100644 --- a/scripts/pi-hole/js/speedtest.js +++ b/scripts/pi-hole/js/speedtest.js @@ -181,12 +181,12 @@ function updateSpeedTestData() { createChart(); } - if (speedChart && speedChart.data.labels.join(",") !== speedlabels.join(",")) { + if (speedChart && speedChart.data.labels !== speedlabels) { speedChart.data.labels = speedlabels; speedChart.data.datasets[0].data = downloadspeed; speedChart.data.datasets[1].data = uploadspeed; speedChart.data.datasets[2].data = serverPing; - speedChart.update("none"); + speedChart.update(); } $("#speedOverTimeChartOverlay").css("display", "none"); From 7c7dd0f5a30326357e0a60c86f929c94fe6923b4 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Sun, 11 Feb 2024 02:20:39 -0500 Subject: [PATCH 109/137] if days --- scripts/pi-hole/js/speedtest.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/pi-hole/js/speedtest.js b/scripts/pi-hole/js/speedtest.js index 54da7927b..f620fce85 100644 --- a/scripts/pi-hole/js/speedtest.js +++ b/scripts/pi-hole/js/speedtest.js @@ -168,7 +168,7 @@ function updateSpeedTestData() { serverPing.push(parseFloat(packet.server_ping)); } }); - if (speedChart && (!daysIsTheSame || !typeIsTheSame || beenHidden)) { + if (speedChart && (!daysIsTheSame || !typeIsTheSame || beenHidden) && days !== "-2") { speedChart.destroy(); speedChart = null; } From 24110b15bd3086f7d92d56bed0f3a74732e20d0e Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Sun, 11 Feb 2024 03:30:05 -0500 Subject: [PATCH 110/137] verbiage --- scripts/pi-hole/js/settings.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index f969683e0..b056ef49c 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -580,23 +580,23 @@ $(function () { }) .done(function (data) { const lastRun = data?.data; - let lastRunText = ""; + let lastRunText = "Latest run is unavailable"; if (lastRun) { - lastRunText = `\n\nLatest run:\n${lastRun}`; + lastRunText = `Latest run:\n\n${lastRun}`; } - const statusText = `Schedule is ${scheduleStatusText}\nNext run is${triggerText}${lastRunText}`; + const statusText = `Schedule is ${scheduleStatusText}\nNext run is${triggerText}\n${lastRunText}`; codeBlock(speedtestStatus, statusText, speedtestStatusBtn, "status"); }) .fail(function () { - const lastRunText = "\nFailed to get latest run"; + const lastRunText = "\nLatest run is unavailable"; const statusText = `Schedule is ${scheduleStatusText}\nNext run is${triggerText}${lastRunText}`; codeBlock(speedtestStatus, statusText, speedtestStatusBtn, "status"); }); }) .fail(function () { const triggerText = speedtestTest.attr("value") ? " awaiting confirmation" : " unknown"; - const lastRunText = "\nFailed to get latest run"; + const lastRunText = "\nLatest run is unavailable"; const statusText = "Failed to get schedule\nNext run is" + triggerText + lastRunText; codeBlock(speedtestStatus, statusText, speedtestStatusBtn, "status"); }); From b0aca364c93aed9bc14303ae698db81203079a08 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Sun, 11 Feb 2024 04:30:48 -0500 Subject: [PATCH 111/137] add help --- scripts/pi-hole/js/settings.js | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index b056ef49c..75d03ebad 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -673,13 +673,24 @@ $(function () { .done(function (data) { const log = data?.data; if (log) { + speedtestLog.find("p").remove(); codeBlock(speedtestLog, log, speedtestLogBtn, "log"); } else { codeBlock(speedtestLog, "The log is empty", speedtestLogBtn, "log"); } }) .fail(function () { - codeBlock(speedtestLog, "Failed to get log", speedtestLogBtn, "log"); + codeBlock( + speedtestLog, + "tmux a -t pimod; cat /var/log/pihole/mod.log", + speedtestLogBtn, + "log" + ); + if (speedtestLogCtr.find("p").length === 0) { + speedtestLogCtr.append( + `

Failed to get process output. Use the above command to get it.

` + ); + } }); }; @@ -834,8 +845,10 @@ $(function () { speedtestLogBtn.on("click", function () { const log = speedtestLog.find("pre"); - if (log.length > 0) { + const info = speedtestLog.find("p"); + if (log.length > 0 || info.length > 0) { log.remove(); + info.remove(); speedtestLogBtn.text("Show latest log"); } else { latestLog(); From ca480e6eb08e8d4f83948bc0b558edccd87c0bdf Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Sun, 11 Feb 2024 04:45:54 -0500 Subject: [PATCH 112/137] make it double --- scripts/pi-hole/js/settings.js | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index 75d03ebad..aaab5746c 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -597,7 +597,7 @@ $(function () { .fail(function () { const triggerText = speedtestTest.attr("value") ? " awaiting confirmation" : " unknown"; const lastRunText = "\nLatest run is unavailable"; - const statusText = "Failed to get schedule\nNext run is" + triggerText + lastRunText; + const statusText = "Schedule is unavailable\nNext run is" + triggerText + lastRunText; codeBlock(speedtestStatus, statusText, speedtestStatusBtn, "status"); }); }; @@ -676,7 +676,17 @@ $(function () { speedtestLog.find("p").remove(); codeBlock(speedtestLog, log, speedtestLogBtn, "log"); } else { - codeBlock(speedtestLog, "The log is empty", speedtestLogBtn, "log"); + codeBlock( + speedtestLog, + "tmux a -t pimod; cat /var/log/pihole/mod.log", + speedtestLogBtn, + "log" + ); + if (speedtestLogCtr.find("p").length === 0) { + speedtestLogCtr.append( + `

Use this command to get the log while I look for it

` + ); + } } }) .fail(function () { @@ -688,7 +698,7 @@ $(function () { ); if (speedtestLogCtr.find("p").length === 0) { speedtestLogCtr.append( - `

Failed to get process output. Use the above command to get it.

` + `

Use this command to get the log while I look for it

` ); } }); @@ -886,6 +896,17 @@ $(function () { latestLog(); } + // if speedtestLogCtr has a p element, cycle through ellipsis + const info = speedtestLogCtr.find("p"); + if (info.length > 0) { + const text = info.text(); + if (text.includes("...")) { + info.text(text.replace(/\.{3}/, ".")); + } else { + info.text(text + "."); + } + } + canRestore(); }, 1000); }); From dc44d91b877c7b1f98a966c71e3d000aa928b4a8 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Sun, 11 Feb 2024 04:48:53 -0500 Subject: [PATCH 113/137] typo --- scripts/pi-hole/js/settings.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index aaab5746c..4927392e1 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -682,8 +682,8 @@ $(function () { speedtestLogBtn, "log" ); - if (speedtestLogCtr.find("p").length === 0) { - speedtestLogCtr.append( + if (speedtestLog.find("p").length === 0) { + speedtestLog.append( `

Use this command to get the log while I look for it

` ); } @@ -696,8 +696,8 @@ $(function () { speedtestLogBtn, "log" ); - if (speedtestLogCtr.find("p").length === 0) { - speedtestLogCtr.append( + if (speedtestLog.find("p").length === 0) { + speedtestLog.append( `

Use this command to get the log while I look for it

` ); } @@ -896,8 +896,8 @@ $(function () { latestLog(); } - // if speedtestLogCtr has a p element, cycle through ellipsis - const info = speedtestLogCtr.find("p"); + // if speedtestLog has a p element, cycle through ellipsis + const info = speedtestLog.find("p"); if (info.length > 0) { const text = info.text(); if (text.includes("...")) { From f2ce4b0d500de4b25133b6be29c365217831e272 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Sun, 11 Feb 2024 04:52:08 -0500 Subject: [PATCH 114/137] . --- scripts/pi-hole/js/settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index 4927392e1..8f3ef4bb2 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -901,7 +901,7 @@ $(function () { if (info.length > 0) { const text = info.text(); if (text.includes("...")) { - info.text(text.replace(/\.{3}/, ".")); + info.text(text.replace(/\.{3}/, "")); } else { info.text(text + "."); } From e4bde7ac777089f9c8e97dbd404b5bb78e6d0db3 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Sun, 11 Feb 2024 05:36:07 -0500 Subject: [PATCH 115/137] chart in pre --- scripts/pi-hole/js/settings.js | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index 8f3ef4bb2..35e0f20b2 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -502,10 +502,14 @@ $(function () { speedtestChartType.prop("checked", type === "bar"); localStorage.setItem("speedtest_chart_type", type); - const preCode = text => { + const preCode = content => { const pre = document.createElement("pre"); const code = document.createElement("code"); - code.textContent = text; + if (typeof content === "string") { + code.textContent = content; + } else { + code.append(content); + } code.style.whiteSpace = "pre"; code.style.overflowWrap = "normal"; pre.append(code); @@ -606,7 +610,7 @@ $(function () { if (!preview) { localStorage.setItem("speedtest_preview_hidden", "true"); localStorage.setItem("speedtest_preview_shown", "false"); - speedtestChartPreview.find("div").remove(); + speedtestChartPreview.find("pre").remove(); } else { let speedtestdays = speedtestDays.val(); localStorage.setItem("speedtest_days", speedtestdays); @@ -635,6 +639,7 @@ $(function () { colDiv.style.marginTop = "1vw"; boxDiv.className = "box"; boxDiv.id = "queries-over-time"; + boxDiv.style.marginBottom = "0"; boxHeaderDiv.className = "box-header with-border"; h3.className = "box-title"; h3.textContent = `Speedtest results over last ${speedtestdays}`; @@ -658,8 +663,8 @@ $(function () { overlayDiv.append(i); chartDiv.append(canvas); - speedtestChartPreview.find("div").remove(); - speedtestChartPreview.append(colDiv); + speedtestChartPreview.find("pre").remove(); + speedtestChartPreview.append(preCode(colDiv)); } speedtestChartPreviewBtn.text(preview ? "Hide preview" : "Show chart preview"); @@ -804,7 +809,7 @@ $(function () { speedtestDays.attr("value", speedtestDays.val()); if (speedtestDays.val()) { localStorage.setItem("speedtest_days", speedtestDays.val()); - previewChart(speedtestChartPreview.find("div").length > 0); + previewChart(speedtestChartPreview.find("pre").length > 0); } }); @@ -815,7 +820,7 @@ $(function () { localStorage.setItem("speedtest_chart_type", type); // Call check messages to make new setting effective checkMessages(); - previewChart(speedtestChartPreview.find("div").length > 0); + previewChart(speedtestChartPreview.find("pre").length > 0); }); speedtestChartTypeSave.on("click", function () { @@ -823,7 +828,7 @@ $(function () { }); speedtestChartPreviewBtn.on("click", function () { - previewChart(speedtestChartPreview.find("div").length === 0); + previewChart(speedtestChartPreview.find("pre").length === 0); }); speedtestUpdate.on("click", function () { From 06398569523a2643af415889751a67921dda4c5c Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Sun, 11 Feb 2024 05:38:50 -0500 Subject: [PATCH 116/137] nvm --- scripts/pi-hole/js/settings.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index 35e0f20b2..153878b1d 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -610,7 +610,7 @@ $(function () { if (!preview) { localStorage.setItem("speedtest_preview_hidden", "true"); localStorage.setItem("speedtest_preview_shown", "false"); - speedtestChartPreview.find("pre").remove(); + speedtestChartPreview.find("div").remove(); } else { let speedtestdays = speedtestDays.val(); localStorage.setItem("speedtest_days", speedtestdays); @@ -639,7 +639,6 @@ $(function () { colDiv.style.marginTop = "1vw"; boxDiv.className = "box"; boxDiv.id = "queries-over-time"; - boxDiv.style.marginBottom = "0"; boxHeaderDiv.className = "box-header with-border"; h3.className = "box-title"; h3.textContent = `Speedtest results over last ${speedtestdays}`; @@ -663,8 +662,8 @@ $(function () { overlayDiv.append(i); chartDiv.append(canvas); - speedtestChartPreview.find("pre").remove(); - speedtestChartPreview.append(preCode(colDiv)); + speedtestChartPreview.find("div").remove(); + speedtestChartPreview.append(colDiv); } speedtestChartPreviewBtn.text(preview ? "Hide preview" : "Show chart preview"); @@ -809,7 +808,7 @@ $(function () { speedtestDays.attr("value", speedtestDays.val()); if (speedtestDays.val()) { localStorage.setItem("speedtest_days", speedtestDays.val()); - previewChart(speedtestChartPreview.find("pre").length > 0); + previewChart(speedtestChartPreview.find("div").length > 0); } }); @@ -820,7 +819,7 @@ $(function () { localStorage.setItem("speedtest_chart_type", type); // Call check messages to make new setting effective checkMessages(); - previewChart(speedtestChartPreview.find("pre").length > 0); + previewChart(speedtestChartPreview.find("div").length > 0); }); speedtestChartTypeSave.on("click", function () { @@ -828,7 +827,7 @@ $(function () { }); speedtestChartPreviewBtn.on("click", function () { - previewChart(speedtestChartPreview.find("pre").length === 0); + previewChart(speedtestChartPreview.find("div").length === 0); }); speedtestUpdate.on("click", function () { From 6cf6f0fb80b8a47a5b69ad0b3e4878dcb0e7246a Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Sun, 11 Feb 2024 05:53:04 -0500 Subject: [PATCH 117/137] recheck for servers --- scripts/pi-hole/js/settings.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index 153878b1d..d9d06fb48 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -722,6 +722,10 @@ $(function () { } }; + if (cmds.length === 0) { + cmds = ["JSONClosestServers", "getClosestServers", "curlClosestServers"]; + } + $.ajax({ url: `api.php?${cmds[0]}`, dataType: "json", @@ -876,8 +880,7 @@ $(function () { speedtestServerBtn.text("Show closest servers"); } else { speedtestServerBtn.text("Retrieving servers..."); - speedtestServerCtr.find("p").remove(); - closestServers(["JSONClosestServers", "getClosestServers", "curlClosestServers"]); + closestServers(); } }); @@ -911,6 +914,10 @@ $(function () { } } + if (speedtestServerCtr.find("p").length > 0) { + closestServers(); + } + canRestore(); }, 1000); }); From 1996ad8aa2360b06d399d9d8255dd25bb2d05efa Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Sun, 11 Feb 2024 07:40:26 -0500 Subject: [PATCH 118/137] pretty --- scripts/pi-hole/js/settings.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index d9d06fb48..8be7379c8 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -510,15 +510,16 @@ $(function () { } else { code.append(content); } + code.style.whiteSpace = "pre"; code.style.overflowWrap = "normal"; - pre.append(code); pre.style.width = "100%"; pre.style.maxWidth = "100%"; pre.style.maxHeight = "500px"; pre.style.overflow = "auto"; pre.style.whiteSpace = "pre"; pre.style.marginTop = "1vw"; + pre.append(code); return pre; }; From f943cda287936c449e7746d014598c754029ee6d Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Sun, 11 Feb 2024 17:04:50 -0500 Subject: [PATCH 119/137] destroy after one point --- scripts/pi-hole/js/speedtest.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/scripts/pi-hole/js/speedtest.js b/scripts/pi-hole/js/speedtest.js index f620fce85..e79b520b0 100644 --- a/scripts/pi-hole/js/speedtest.js +++ b/scripts/pi-hole/js/speedtest.js @@ -168,7 +168,14 @@ function updateSpeedTestData() { serverPing.push(parseFloat(packet.server_ping)); } }); - if (speedChart && (!daysIsTheSame || !typeIsTheSame || beenHidden) && days !== "-2") { + if ( + speedChart && + (!daysIsTheSame || + !typeIsTheSame || + beenHidden || + (type === "line" && speedChart.data?.labels?.length < 2 && speedlabels?.length > 1)) && + days !== "-2" + ) { speedChart.destroy(); speedChart = null; } From cd3386e1b58078dde960836b255317cc38df6b96 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Sun, 11 Feb 2024 17:21:16 -0500 Subject: [PATCH 120/137] fix if --- scripts/pi-hole/js/settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index 8be7379c8..1e8ebfd8f 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -723,7 +723,7 @@ $(function () { } }; - if (cmds.length === 0) { + if (!cmds || cmds.length === 0) { cmds = ["JSONClosestServers", "getClosestServers", "curlClosestServers"]; } From 090e4bfcc9b0ef070311bfdf1ee82b1b4becc0b1 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Tue, 13 Feb 2024 06:41:03 -0500 Subject: [PATCH 121/137] getNumberOfDaysInDB --- api_speedtest.php | 31 +++++++++ scripts/pi-hole/js/settings.js | 111 ++++++++++++++++++++------------- 2 files changed, 97 insertions(+), 45 deletions(-) diff --git a/api_speedtest.php b/api_speedtest.php index c41e2ccaf..f3a7eddf1 100644 --- a/api_speedtest.php +++ b/api_speedtest.php @@ -66,6 +66,9 @@ if (isset($_GET['JSONClosestServers'])) { $data = array_merge($data, JSONServers($cmdServersJSON)); } + if (isset($_GET['getNumberOfDaysInDB'])) { + $data = array_merge($data, getNumberOfDaysInDB($dbSpeedtest)); + } } function hasSpeedTestBackup($dbSpeedtestOld) @@ -301,3 +304,31 @@ function getRemainingTime() return max(0, $interval_seconds - (time() - $last_run_time)); } + +function getNumberOfDaysInDB($dbSpeedtest) +{ + $db = new SQLite3($dbSpeedtest); + if (!$db) { + return array('data' => 0); + } + + $sql = 'SELECT start_time from speedtest order by id asc'; + $dbResults = $db->query($sql); + $dataFromSpeedDB = array(); + if (!empty($dbResults)) { + while ($row = $dbResults->fetchArray(SQLITE3_ASSOC)) { + array_push($dataFromSpeedDB, $row); + } + } + $db->close(); + + if (empty($dataFromSpeedDB)) { + return array('data' => 0); + } + + $first_date = new DateTime($dataFromSpeedDB[0]['start_time']); + $last_date = new DateTime($dataFromSpeedDB[count($dataFromSpeedDB) - 1]['start_time']); + $diff = $first_date->diff($last_date); + + return array('data' => $diff->days + 1); +} diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index 1e8ebfd8f..1b4cddfc5 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -607,6 +607,48 @@ $(function () { }); }; + const drawChart = (days, type) => { + const colDiv = document.createElement("div"); + const boxDiv = document.createElement("div"); + const boxHeaderDiv = document.createElement("div"); + const h3 = document.createElement("h3"); + const boxBodyDiv = document.createElement("div"); + const chartDiv = document.createElement("div"); + const canvas = document.createElement("canvas"); + const overlayDiv = document.createElement("div"); + const i = document.createElement("i"); + + colDiv.className = "col-md-12"; + colDiv.style.marginTop = "1vw"; + boxDiv.className = "box"; + boxDiv.id = "queries-over-time"; + boxHeaderDiv.className = "box-header with-border"; + h3.className = "box-title"; + h3.textContent = `Speedtest results over last ${days}`; + boxBodyDiv.className = "box-body"; + chartDiv.className = "chart"; + chartDiv.style.position = "relative"; + chartDiv.style.width = "100%"; + chartDiv.style.height = "180px"; + canvas.id = "speedOverTimeChart"; + canvas.setAttribute("value", type); + overlayDiv.className = "overlay"; + overlayDiv.id = "speedOverTimeChartOverlay"; + i.className = "fa fa-sync fa-spin"; + + colDiv.append(boxDiv); + boxDiv.append(boxHeaderDiv); + boxDiv.append(boxBodyDiv); + boxDiv.append(overlayDiv); + boxHeaderDiv.append(h3); + boxBodyDiv.append(chartDiv); + overlayDiv.append(i); + chartDiv.append(canvas); + + speedtestChartPreview.find("div").remove(); + speedtestChartPreview.append(colDiv); + }; + const previewChart = preview => { if (!preview) { localStorage.setItem("speedtest_preview_hidden", "true"); @@ -618,53 +660,32 @@ $(function () { localStorage.setItem("speedtest_chart_type", type); localStorage.setItem("speedtest_preview_shown", "true"); - if (speedtestdays === "1") { - speedtestdays = "24 hours"; - } else if (speedtestdays === "-1") { - speedtestdays = "however many days"; - } else { - speedtestdays += " days"; - } + $.ajax({ + url: "api.php?getNumberOfDaysInDB", + dataType: "json", + }) + .done(function (data) { + if (speedtestdays === "1") { + speedtestdays = "24 hours"; + } else if (speedtestdays === "-1") { + speedtestdays = data ? data.data : "however many days"; + } else { + speedtestdays += " days"; + } - const colDiv = document.createElement("div"); - const boxDiv = document.createElement("div"); - const boxHeaderDiv = document.createElement("div"); - const h3 = document.createElement("h3"); - const boxBodyDiv = document.createElement("div"); - const chartDiv = document.createElement("div"); - const canvas = document.createElement("canvas"); - const overlayDiv = document.createElement("div"); - const i = document.createElement("i"); - - colDiv.className = "col-md-12"; - colDiv.style.marginTop = "1vw"; - boxDiv.className = "box"; - boxDiv.id = "queries-over-time"; - boxHeaderDiv.className = "box-header with-border"; - h3.className = "box-title"; - h3.textContent = `Speedtest results over last ${speedtestdays}`; - boxBodyDiv.className = "box-body"; - chartDiv.className = "chart"; - chartDiv.style.position = "relative"; - chartDiv.style.width = "100%"; - chartDiv.style.height = "180px"; - canvas.id = "speedOverTimeChart"; - canvas.setAttribute("value", type); - overlayDiv.className = "overlay"; - overlayDiv.id = "speedOverTimeChartOverlay"; - i.className = "fa fa-sync fa-spin"; - - colDiv.append(boxDiv); - boxDiv.append(boxHeaderDiv); - boxDiv.append(boxBodyDiv); - boxDiv.append(overlayDiv); - boxHeaderDiv.append(h3); - boxBodyDiv.append(chartDiv); - overlayDiv.append(i); - chartDiv.append(canvas); + drawChart(speedtestdays, type); + }) + .fail(function () { + if (speedtestdays === "1") { + speedtestdays = "24 hours"; + } else if (speedtestdays === "-1") { + speedtestdays = "however many days"; + } else { + speedtestdays += " days"; + } - speedtestChartPreview.find("div").remove(); - speedtestChartPreview.append(colDiv); + drawChart(speedtestdays, type); + }); } speedtestChartPreviewBtn.text(preview ? "Hide preview" : "Show chart preview"); From 33784b824bebafed2d4082e68cd2f2f4316a5dc4 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Tue, 13 Feb 2024 06:54:01 -0500 Subject: [PATCH 122/137] fix text --- api_speedtest.php | 2 +- scripts/pi-hole/js/settings.js | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/api_speedtest.php b/api_speedtest.php index f3a7eddf1..8a9856acf 100644 --- a/api_speedtest.php +++ b/api_speedtest.php @@ -327,7 +327,7 @@ function getNumberOfDaysInDB($dbSpeedtest) } $first_date = new DateTime($dataFromSpeedDB[0]['start_time']); - $last_date = new DateTime($dataFromSpeedDB[count($dataFromSpeedDB) - 1]['start_time']); + $last_date = new DateTime('now', new DateTimeZone('UTC')); $diff = $first_date->diff($last_date); return array('data' => $diff->days + 1); diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index 1b4cddfc5..ac2cce52f 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -665,10 +665,12 @@ $(function () { dataType: "json", }) .done(function (data) { + if (speedtestdays === "-1") { + speedtestdays = data ? data.data : "however many"; + } + if (speedtestdays === "1") { speedtestdays = "24 hours"; - } else if (speedtestdays === "-1") { - speedtestdays = data ? data.data : "however many days"; } else { speedtestdays += " days"; } @@ -676,10 +678,12 @@ $(function () { drawChart(speedtestdays, type); }) .fail(function () { + if (speedtestdays === "-1") { + speedtestdays = "however many"; + } + if (speedtestdays === "1") { speedtestdays = "24 hours"; - } else if (speedtestdays === "-1") { - speedtestdays = "however many days"; } else { speedtestdays += " days"; } From 0788b8ce51be6692210281c328de24b4cfc8ba69 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Tue, 13 Feb 2024 12:02:35 -0500 Subject: [PATCH 123/137] getNumberOfDaysInDB on the dashboard too --- scripts/pi-hole/php/header_authenticated.php | 40 +++++++++++++++++--- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/scripts/pi-hole/php/header_authenticated.php b/scripts/pi-hole/php/header_authenticated.php index fb8dcf5f6..4de326ad9 100644 --- a/scripts/pi-hole/php/header_authenticated.php +++ b/scripts/pi-hole/php/header_authenticated.php @@ -149,6 +149,35 @@ function getTemperature() // Get memory usage $memory_usage = getMemUsage(); +$dbSpeedtest = '/etc/pihole/speedtest.db'; +function getNumberOfDaysInDB($dbSpeedtest) +{ + $db = new SQLite3($dbSpeedtest); + if (!$db) { + return 0; + } + + $sql = 'SELECT start_time from speedtest order by id asc'; + $dbResults = $db->query($sql); + $dataFromSpeedDB = array(); + if (!empty($dbResults)) { + while ($row = $dbResults->fetchArray(SQLITE3_ASSOC)) { + array_push($dataFromSpeedDB, $row); + } + } + $db->close(); + + if (empty($dataFromSpeedDB)) { + return 0; + } + + $first_date = new DateTime($dataFromSpeedDB[0]['start_time']); + $last_date = new DateTime('now', new DateTimeZone('UTC')); + $diff = $first_date->diff($last_date); + + return $diff->days + 1; +} + $speedtestschedule = false; $speedtestdays = ''; $speedtestcharttype = 'line'; @@ -156,15 +185,16 @@ function getTemperature() $speedtestschedule = $setupVars['SPEEDTESTSCHEDULE']; } if (isset($setupVars['SPEEDTEST_CHART_DAYS'])) { - if ($setupVars['SPEEDTEST_CHART_DAYS'] > 1) { - $speedtestdays = $setupVars['SPEEDTEST_CHART_DAYS'].' days'; + if ($setupVars['SPEEDTEST_CHART_DAYS'] == -1) { + $speedtestdays = getNumberOfDaysInDB($dbSpeedtest).' days'; } if ($setupVars['SPEEDTEST_CHART_DAYS'] == 1) { $speedtestdays = '24 hours'; + } else { + $speedtestdays = $setupVars['SPEEDTEST_CHART_DAYS'].' days'; } - if ($setupVars['SPEEDTEST_CHART_DAYS'] == -1) { - $speedtestdays = 'however many days'; - } + + } if (isset($setupVars['SPEEDTEST_CHART_TYPE'])) { $speedtestcharttype = $setupVars['SPEEDTEST_CHART_TYPE']; From 2773fb8125ff3571b3948fcecf44fc85c7c1a657 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Tue, 13 Feb 2024 12:11:20 -0500 Subject: [PATCH 124/137] else if --- scripts/pi-hole/php/header_authenticated.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/pi-hole/php/header_authenticated.php b/scripts/pi-hole/php/header_authenticated.php index 4de326ad9..6d8b76182 100644 --- a/scripts/pi-hole/php/header_authenticated.php +++ b/scripts/pi-hole/php/header_authenticated.php @@ -187,8 +187,7 @@ function getNumberOfDaysInDB($dbSpeedtest) if (isset($setupVars['SPEEDTEST_CHART_DAYS'])) { if ($setupVars['SPEEDTEST_CHART_DAYS'] == -1) { $speedtestdays = getNumberOfDaysInDB($dbSpeedtest).' days'; - } - if ($setupVars['SPEEDTEST_CHART_DAYS'] == 1) { + } else if ($setupVars['SPEEDTEST_CHART_DAYS'] == 1) { $speedtestdays = '24 hours'; } else { $speedtestdays = $setupVars['SPEEDTEST_CHART_DAYS'].' days'; From 62e3de20ec80335e04c9f1af84cbc592ff65470a Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Tue, 13 Feb 2024 12:29:10 -0500 Subject: [PATCH 125/137] verbiage --- scripts/pi-hole/js/settings.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index ac2cce52f..d408bfd7c 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -714,7 +714,7 @@ $(function () { ); if (speedtestLog.find("p").length === 0) { speedtestLog.append( - `

Use this command to get the log while I look for it

` + `

Use this command to get the log while the connection is reestablished

` ); } } @@ -728,7 +728,7 @@ $(function () { ); if (speedtestLog.find("p").length === 0) { speedtestLog.append( - `

Use this command to get the log while I look for it

` + `

Use this command to get the log while the connection is reestablished

` ); } }); From b8bfbf69bf7ab7d607f67453ed7bf14a7355b898 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Tue, 13 Feb 2024 13:16:29 -0500 Subject: [PATCH 126/137] get log from volume --- api_speedtest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api_speedtest.php b/api_speedtest.php index 8a9856acf..76b57866d 100644 --- a/api_speedtest.php +++ b/api_speedtest.php @@ -34,7 +34,7 @@ } else { $cmdStatus = 'systemctl status pihole-speedtest.timer'; } -$cmdRun = '[[ -f /tmp/speedtest.log ]] && cat /tmp/speedtest.log || { [[ -f /var/log/pihole/speedtest.log ]] && cat /var/log/pihole/speedtest.log || echo ""; }'; +$cmdRun = '[[ -f /tmp/speedtest.log ]] && cat /tmp/speedtest.log || { [[ -f /etc/pihole/speedtest.log ]] && cat /etc/pihole/speedtest.log || echo ""; }'; $cmdServersCurl = "curl 'https://c.speedtest.net/speedtest-servers-static.php' --compressed -H 'Upgrade-Insecure-Requests: 1' -H 'DNT: 1' -H 'Sec-GPC: 1'"; $cmdServersJSON = "curl 'https://www.speedtest.net/api/js/servers' --compressed -H 'Upgrade-Insecure-Requests: 1' -H 'DNT: 1' -H 'Sec-GPC: 1'"; From 474c741a27a73006360f6b75eefa628d682399c8 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Tue, 13 Feb 2024 13:43:05 -0500 Subject: [PATCH 127/137] pretty --- scripts/pi-hole/php/header_authenticated.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/scripts/pi-hole/php/header_authenticated.php b/scripts/pi-hole/php/header_authenticated.php index 6d8b76182..1990795cf 100644 --- a/scripts/pi-hole/php/header_authenticated.php +++ b/scripts/pi-hole/php/header_authenticated.php @@ -187,13 +187,11 @@ function getNumberOfDaysInDB($dbSpeedtest) if (isset($setupVars['SPEEDTEST_CHART_DAYS'])) { if ($setupVars['SPEEDTEST_CHART_DAYS'] == -1) { $speedtestdays = getNumberOfDaysInDB($dbSpeedtest).' days'; - } else if ($setupVars['SPEEDTEST_CHART_DAYS'] == 1) { + } elseif ($setupVars['SPEEDTEST_CHART_DAYS'] == 1) { $speedtestdays = '24 hours'; } else { $speedtestdays = $setupVars['SPEEDTEST_CHART_DAYS'].' days'; } - - } if (isset($setupVars['SPEEDTEST_CHART_TYPE'])) { $speedtestcharttype = $setupVars['SPEEDTEST_CHART_TYPE']; From b5dacaf6dab8953880869c850405110c39208317 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Tue, 13 Feb 2024 17:38:52 -0500 Subject: [PATCH 128/137] getStatusCmd --- api_speedtest.php | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/api_speedtest.php b/api_speedtest.php index 76b57866d..eec772c06 100644 --- a/api_speedtest.php +++ b/api_speedtest.php @@ -19,21 +19,6 @@ $cmdLog = '[[ -f /tmp/pimod.log ]] && cat /tmp/pimod.log || { [[ -f /var/log/pihole/mod.log ]] && cat /var/log/pihole/mod.log || echo ""; }'; $cmdServers = 'speedtest -h | grep -q official && sudo speedtest -L || speedtest --list'; -if (file_exists('/opt/pihole/speedtestmod/schedule_check.sh')) { - $remaining_seconds = getRemainingTime(); - if ($remaining_seconds < 0) { - $cmdStatus = 'echo ""'; - } else { - $remaining_date = sprintf('%dd %dh %dmin %ds', $remaining_seconds / 86400, $remaining_seconds / 3600 % 24, $remaining_seconds / 60 % 60, $remaining_seconds % 60); - $remaining_date = preg_replace('/^0d |(^|(?<= ))0h |(^|(?<= ))0min /', '', $remaining_date); // remove 0d 0h 0min - $remaining_date = preg_replace('/\s(\d+s)/', '', $remaining_date); // remove seconds if not needed - $cmdStatus = 'echo '.$remaining_date; - } -} elseif (!file_exists('/bin/systemctl')) { - $cmdStatus = 'echo ""'; -} else { - $cmdStatus = 'systemctl status pihole-speedtest.timer'; -} $cmdRun = '[[ -f /tmp/speedtest.log ]] && cat /tmp/speedtest.log || { [[ -f /etc/pihole/speedtest.log ]] && cat /etc/pihole/speedtest.log || echo ""; }'; $cmdServersCurl = "curl 'https://c.speedtest.net/speedtest-servers-static.php' --compressed -H 'Upgrade-Insecure-Requests: 1' -H 'DNT: 1' -H 'Sec-GPC: 1'"; $cmdServersJSON = "curl 'https://www.speedtest.net/api/js/servers' --compressed -H 'Upgrade-Insecure-Requests: 1' -H 'DNT: 1' -H 'Sec-GPC: 1'"; @@ -55,7 +40,7 @@ $data = array_merge($data, getServers($cmdServers)); } if (isset($_GET['getSpeedTestStatus'])) { - $data = array_merge($data, speedtestExecute($cmdStatus)); + $data = array_merge($data, speedtestExecute(getStatusCmd())); } if (isset($_GET['getLatestRun'])) { $data = array_merge($data, speedtestExecute($cmdRun)); @@ -332,3 +317,21 @@ function getNumberOfDaysInDB($dbSpeedtest) return array('data' => $diff->days + 1); } + +function getStatusCmd() +{ + $cmdStatus = 'echo ""'; + if (file_exists('/opt/pihole/speedtestmod/schedule_check.sh')) { + $remaining_seconds = getRemainingTime(); + if ($remaining_seconds >= 0) { + $remaining_date = sprintf('%dd %dh %dmin %ds', $remaining_seconds / 86400, $remaining_seconds / 3600 % 24, $remaining_seconds / 60 % 60, $remaining_seconds % 60); + $remaining_date = preg_replace('/^0d |(^|(?<= ))0h |(^|(?<= ))0min /', '', $remaining_date); // remove 0d 0h 0min + $remaining_date = preg_replace('/\s(\d+s)/', '', $remaining_date); // remove seconds if not needed + $cmdStatus = 'echo '.$remaining_date; + } + } elseif (file_exists('/bin/systemctl')) { + $cmdStatus = 'systemctl status pihole-speedtest.timer'; + } + + return $cmdStatus; +} From 9bafaa920643aeda29c235e8cc4b247747951905 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Tue, 13 Feb 2024 17:52:56 -0500 Subject: [PATCH 129/137] dedup code --- scripts/pi-hole/js/settings.js | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index d408bfd7c..9d17a4834 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -608,6 +608,16 @@ $(function () { }; const drawChart = (days, type) => { + if (days === "-1") { + days = "however many"; + } + + if (days === "1") { + days = "24 hours"; + } else { + days += " days"; + } + const colDiv = document.createElement("div"); const boxDiv = document.createElement("div"); const boxHeaderDiv = document.createElement("div"); @@ -665,29 +675,9 @@ $(function () { dataType: "json", }) .done(function (data) { - if (speedtestdays === "-1") { - speedtestdays = data ? data.data : "however many"; - } - - if (speedtestdays === "1") { - speedtestdays = "24 hours"; - } else { - speedtestdays += " days"; - } - - drawChart(speedtestdays, type); + drawChart(speedtestdays === "-1" && data ? data.data : speedtestdays, type); }) .fail(function () { - if (speedtestdays === "-1") { - speedtestdays = "however many"; - } - - if (speedtestdays === "1") { - speedtestdays = "24 hours"; - } else { - speedtestdays += " days"; - } - drawChart(speedtestdays, type); }); } From 20f2165b1696bded013d11325afa36cca1d66e3f Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Tue, 13 Feb 2024 17:58:03 -0500 Subject: [PATCH 130/137] pretty --- scripts/pi-hole/js/settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index 9d17a4834..ebd619aa0 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -665,7 +665,7 @@ $(function () { localStorage.setItem("speedtest_preview_shown", "false"); speedtestChartPreview.find("div").remove(); } else { - let speedtestdays = speedtestDays.val(); + const speedtestdays = speedtestDays.val(); localStorage.setItem("speedtest_days", speedtestdays); localStorage.setItem("speedtest_chart_type", type); localStorage.setItem("speedtest_preview_shown", "true"); From 9ded01a7bb78b005dbba494590690b23fec6e3ee Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Mon, 19 Feb 2024 04:09:26 -0500 Subject: [PATCH 131/137] count from now --- api_speedtest.php | 2 +- scripts/pi-hole/js/speedtest.js | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/api_speedtest.php b/api_speedtest.php index eec772c06..71fab7a07 100644 --- a/api_speedtest.php +++ b/api_speedtest.php @@ -18,7 +18,7 @@ $setupVars = parse_ini_file('/etc/pihole/setupVars.conf'); $cmdLog = '[[ -f /tmp/pimod.log ]] && cat /tmp/pimod.log || { [[ -f /var/log/pihole/mod.log ]] && cat /var/log/pihole/mod.log || echo ""; }'; -$cmdServers = 'speedtest -h | grep -q official && sudo speedtest -L || speedtest --list'; +$cmdServers = 'speedtest -h | grep -q official && sudo speedtest -L || speedtest --secure --list'; $cmdRun = '[[ -f /tmp/speedtest.log ]] && cat /tmp/speedtest.log || { [[ -f /etc/pihole/speedtest.log ]] && cat /etc/pihole/speedtest.log || echo ""; }'; $cmdServersCurl = "curl 'https://c.speedtest.net/speedtest-servers-static.php' --compressed -H 'Upgrade-Insecure-Requests: 1' -H 'DNT: 1' -H 'Sec-GPC: 1'"; $cmdServersJSON = "curl 'https://www.speedtest.net/api/js/servers' --compressed -H 'Upgrade-Insecure-Requests: 1' -H 'DNT: 1' -H 'Sec-GPC: 1'"; diff --git a/scripts/pi-hole/js/speedtest.js b/scripts/pi-hole/js/speedtest.js index e79b520b0..5e65b8a17 100644 --- a/scripts/pi-hole/js/speedtest.js +++ b/scripts/pi-hole/js/speedtest.js @@ -135,8 +135,7 @@ function formatDate(itemdate, results) { } const first = moment(results.at(0).start_time, "YYYY-MM-DD HH:mm:ssZ"); - const last = moment(results.at(-1).start_time, "YYYY-MM-DD HH:mm:ssZ"); - if (last.diff(first, "hours") > 24) { + if (moment.utc().diff(first, "hours") > 24) { output = "Do HH:mm"; } From ca5a063aba5e9cb6f85147e307734272e6611af7 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Tue, 27 Feb 2024 21:01:36 -0500 Subject: [PATCH 132/137] support librespeed (#89) * extra checks * typos * support librespeed * dont filter * redirect * words --- api_speedtest.php | 10 +++++++- scripts/pi-hole/js/settings.js | 44 ++++++++++++++++++++------------- scripts/pi-hole/js/speedtest.js | 11 +++++++-- settings.php | 4 +-- 4 files changed, 47 insertions(+), 22 deletions(-) diff --git a/api_speedtest.php b/api_speedtest.php index 71fab7a07..c66046aff 100644 --- a/api_speedtest.php +++ b/api_speedtest.php @@ -18,7 +18,7 @@ $setupVars = parse_ini_file('/etc/pihole/setupVars.conf'); $cmdLog = '[[ -f /tmp/pimod.log ]] && cat /tmp/pimod.log || { [[ -f /var/log/pihole/mod.log ]] && cat /var/log/pihole/mod.log || echo ""; }'; -$cmdServers = 'speedtest -h | grep -q official && sudo speedtest -L || speedtest --secure --list'; +$cmdServers = '/usr/bin/speedtest -h | grep -q official && sudo /usr/bin/speedtest -L || /usr/bin/speedtest --secure --list 2>&1'; $cmdRun = '[[ -f /tmp/speedtest.log ]] && cat /tmp/speedtest.log || { [[ -f /etc/pihole/speedtest.log ]] && cat /etc/pihole/speedtest.log || echo ""; }'; $cmdServersCurl = "curl 'https://c.speedtest.net/speedtest-servers-static.php' --compressed -H 'Upgrade-Insecure-Requests: 1' -H 'DNT: 1' -H 'Sec-GPC: 1'"; $cmdServersJSON = "curl 'https://www.speedtest.net/api/js/servers' --compressed -H 'Upgrade-Insecure-Requests: 1' -H 'DNT: 1' -H 'Sec-GPC: 1'"; @@ -54,6 +54,9 @@ if (isset($_GET['getNumberOfDaysInDB'])) { $data = array_merge($data, getNumberOfDaysInDB($dbSpeedtest)); } + if (isset($_GET['isLibrespeed'])) { + $data = array_merge($data, array('data' => isLibrespeed())); + } } function hasSpeedTestBackup($dbSpeedtestOld) @@ -335,3 +338,8 @@ function getStatusCmd() return $cmdStatus; } + +function isLibrespeed() +{ + return strpos(speedtestExecute('/usr/bin/speedtest --version')['data'], 'LibreSpeed') !== false; +} diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index ebd619aa0..92e85d85e 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -739,25 +739,35 @@ $(function () { }; if (!cmds || cmds.length === 0) { - cmds = ["JSONClosestServers", "getClosestServers", "curlClosestServers"]; - } - - $.ajax({ - url: `api.php?${cmds[0]}`, - dataType: "json", - }) - .done(function (data) { - const serversInfo = data?.data; - if (serversInfo) { - speedtestServerCtr.find("p").remove(); - codeBlock(speedtestServerCtr, serversInfo, speedtestServerBtn, "servers"); + $.ajax({ + url: `api.php?isLibrespeed`, + dataType: "json", + }).done(function (data) { + const librespeed = data?.data; + if (librespeed) { + closestServers(["getClosestServers"]); } else { - tryNextCmd(); + closestServers(["JSONClosestServers", "getClosestServers", "curlClosestServers"]); } - }) - .fail(function () { - tryNextCmd(); }); + } else { + $.ajax({ + url: `api.php?${cmds[0]}`, + dataType: "json", + }) + .done(function (data) { + const serversInfo = data?.data; + if (serversInfo) { + speedtestServerCtr.find("p").remove(); + codeBlock(speedtestServerCtr, serversInfo, speedtestServerBtn, "servers"); + } else { + tryNextCmd(); + } + }) + .fail(function () { + tryNextCmd(); + }); + } }; const hasBackup = callback => { @@ -893,7 +903,7 @@ $(function () { const closestServersList = speedtestServerCtr.find("pre"); if (closestServersList.length > 0) { closestServersList.remove(); - speedtestServerBtn.text("Show closest servers"); + speedtestServerBtn.text("Show available servers"); } else { speedtestServerBtn.text("Retrieving servers..."); closestServers(); diff --git a/scripts/pi-hole/js/speedtest.js b/scripts/pi-hole/js/speedtest.js index 5e65b8a17..b4c52cda1 100644 --- a/scripts/pi-hole/js/speedtest.js +++ b/scripts/pi-hole/js/speedtest.js @@ -24,7 +24,9 @@ var serverPing = []; function createChart() { var gridColor = getCSSval("graphs-grid", "background-color"); var ticksColor = getCSSval("graphs-ticks", "color"); - var speedChartctx = document.getElementById("speedOverTimeChart")?.getContext("2d"); + const chartElement = document.getElementById("speedOverTimeChart"); + if (chartElement === null || chartElement === undefined) return; + var speedChartctx = chartElement.getContext("2d"); if (speedChartctx === null || speedChartctx === undefined) return; speedChart = new Chart(speedChartctx, { type: getGraphType(1), @@ -187,7 +189,12 @@ function updateSpeedTestData() { createChart(); } - if (speedChart && speedChart.data.labels !== speedlabels) { + if ( + speedChart && + speedChart !== null && + speedChart !== undefined && + speedChart.data.labels !== speedlabels + ) { speedChart.data.labels = speedlabels; speedChart.data.datasets[0].data = downloadspeed; speedChart.data.datasets[1].data = uploadspeed; diff --git a/settings.php b/settings.php index 3e8fbe25a..770adf05c 100644 --- a/settings.php +++ b/settings.php @@ -1638,7 +1638,7 @@ class="form-control"
-

If you know better

+

To be tried first

id

- +

From f69d4596dbf0b4b92578e377e3820815a708711c Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Thu, 29 Feb 2024 01:09:10 -0500 Subject: [PATCH 133/137] show mod version (#90) * check version --- api_speedtest.php | 18 +++++++-- scripts/pi-hole/js/settings.js | 51 +++++++++++++++++--------- scripts/pi-hole/php/footer.php | 5 +++ scripts/pi-hole/php/update_checker.php | 27 +++++++++++++- settings.php | 2 +- 5 files changed, 79 insertions(+), 24 deletions(-) diff --git a/api_speedtest.php b/api_speedtest.php index c66046aff..6c905d4b4 100644 --- a/api_speedtest.php +++ b/api_speedtest.php @@ -54,8 +54,8 @@ if (isset($_GET['getNumberOfDaysInDB'])) { $data = array_merge($data, getNumberOfDaysInDB($dbSpeedtest)); } - if (isset($_GET['isLibrespeed'])) { - $data = array_merge($data, array('data' => isLibrespeed())); + if (isset($_GET['whichSpeedtest'])) { + $data = array_merge($data, array('data' => whichSpeedtest())); } } @@ -339,7 +339,17 @@ function getStatusCmd() return $cmdStatus; } -function isLibrespeed() +function whichSpeedtest() { - return strpos(speedtestExecute('/usr/bin/speedtest --version')['data'], 'LibreSpeed') !== false; + if (!file_exists('/usr/bin/speedtest')) { + return 'official'; + } + $version = speedtestExecute('/usr/bin/speedtest --version')['data']; + if (strpos($version, 'LibreSpeed') !== false) { + return 'LibreSpeed'; + } elseif (strpos($version, 'Python') !== false) { + return 'sivel\'s'; + } else { + return 'official'; + } } diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index 92e85d85e..9547aed6b 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -532,7 +532,28 @@ $(function () { } }; + const whichSpeedtest = () => { + $.ajax({ + url: "api.php?whichSpeedtest", + dataType: "json", + }) + .done(function (data) { + const speedtest = data?.data; + if (speedtest) { + // set in localStorage for use in other functions + localStorage.setItem("speedtest", speedtest); + } else { + localStorage.setItem("speedtest", "official"); + } + }) + .fail(function () { + localStorage.setItem("speedtest", "unknown"); + }); + }; + const serviceStatus = () => { + whichSpeedtest(); + const speedtestVersion = localStorage.getItem("speedtest") || "unknown"; $.ajax({ url: "api.php?getSpeedTestStatus", dataType: "json", @@ -590,19 +611,19 @@ $(function () { lastRunText = `Latest run:\n\n${lastRun}`; } - const statusText = `Schedule is ${scheduleStatusText}\nNext run is${triggerText}\n${lastRunText}`; + const statusText = `Using ${speedtestVersion} CLI\nSchedule is ${scheduleStatusText}\nNext run is${triggerText}\n${lastRunText}`; codeBlock(speedtestStatus, statusText, speedtestStatusBtn, "status"); }) .fail(function () { - const lastRunText = "\nLatest run is unavailable"; - const statusText = `Schedule is ${scheduleStatusText}\nNext run is${triggerText}${lastRunText}`; + const lastRunText = "Latest run is unavailable"; + const statusText = `Using ${speedtestVersion} CLI\nSchedule is ${scheduleStatusText}\nNext run is${triggerText}\n${lastRunText}`; codeBlock(speedtestStatus, statusText, speedtestStatusBtn, "status"); }); }) .fail(function () { - const triggerText = speedtestTest.attr("value") ? " awaiting confirmation" : " unknown"; - const lastRunText = "\nLatest run is unavailable"; - const statusText = "Schedule is unavailable\nNext run is" + triggerText + lastRunText; + const triggerText = speedtestTest.attr("value") ? "awaiting confirmation" : "unknown"; + const lastRunText = "Latest run is unavailable"; + const statusText = `Using ${speedtestVersion} CLI\nSchedule is unavailable\nNext run is ${triggerText}\n${lastRunText}`; codeBlock(speedtestStatus, statusText, speedtestStatusBtn, "status"); }); }; @@ -739,17 +760,13 @@ $(function () { }; if (!cmds || cmds.length === 0) { - $.ajax({ - url: `api.php?isLibrespeed`, - dataType: "json", - }).done(function (data) { - const librespeed = data?.data; - if (librespeed) { - closestServers(["getClosestServers"]); - } else { - closestServers(["JSONClosestServers", "getClosestServers", "curlClosestServers"]); - } - }); + whichSpeedtest(); + const speedtestVersion = localStorage.getItem("speedtest"); + if (speedtestVersion === "LibreSpeed") { + closestServers(["getClosestServers"]); + } else { + closestServers(["JSONClosestServers", "getClosestServers", "curlClosestServers"]); + } } else { $.ajax({ url: `api.php?${cmds[0]}`, diff --git a/scripts/pi-hole/php/footer.php b/scripts/pi-hole/php/footer.php index 15dd17297..3b03e7dd5 100644 --- a/scripts/pi-hole/php/footer.php +++ b/scripts/pi-hole/php/footer.php @@ -87,6 +87,11 @@ · Update available! +
  • + Speedtest Mod + + · Update available! +
  • diff --git a/scripts/pi-hole/php/update_checker.php b/scripts/pi-hole/php/update_checker.php index cd2ce2586..e6bf0240a 100644 --- a/scripts/pi-hole/php/update_checker.php +++ b/scripts/pi-hole/php/update_checker.php @@ -22,6 +22,10 @@ function checkUpdate($currentVersion, $latestVersion) $web_current = 'N/A'; $web_update = false; + $speedtest_branch = 'master'; + $speedtest_current = 'N/A'; + $speedtest_update = false; + $FTL_current = 'N/A'; $FTL_update = false; @@ -49,6 +53,15 @@ function checkUpdate($currentVersion, $latestVersion) $web_current = explode('-', $versions['WEB_VERSION'])[0]; } + // Get Speedtest Mod branch / version / commit + $speedtest_branch = $versions['SPEEDTEST_BRANCH']; + if ($speedtest_branch !== 'master') { + $speedtest_current = 'vDev'; + $speedtest_commit = $versions['SPEEDTEST_VERSION']; + } else { + $speedtest_current = explode('-', $versions['SPEEDTEST_VERSION'])[0]; + } + // Get Pi-hole FTL (not a git repository) $FTL_branch = $versions['FTL_BRANCH']; if (substr($versions['FTL_VERSION'], 0, 4) === 'vDev') { @@ -68,6 +81,7 @@ function checkUpdate($currentVersion, $latestVersion) // Get data from GitHub $core_latest = $versions['GITHUB_CORE_VERSION']; $web_latest = $versions['GITHUB_WEB_VERSION']; + $speedtest_latest = $versions['GITHUB_SPEEDTEST_VERSION']; $FTL_latest = $versions['GITHUB_FTL_VERSION']; if (isset($versions['GITHUB_DOCKER_VERSION'])) { $docker_latest = $versions['GITHUB_DOCKER_VERSION']; @@ -77,6 +91,7 @@ function checkUpdate($currentVersion, $latestVersion) $core_update = false; $web_update = false; + $speedtest_update = false; $FTL_update = false; // Version comparison @@ -92,6 +107,7 @@ function checkUpdate($currentVersion, $latestVersion) // Components comparison $core_update = checkUpdate($core_current, $core_latest); $web_update = checkUpdate($web_current, $web_latest); + $speedtest_update = checkUpdate($speedtest_current, $speedtest_latest); $FTL_update = checkUpdate($FTL_current, $FTL_latest); // Not a docker container @@ -101,9 +117,10 @@ function checkUpdate($currentVersion, $latestVersion) // URLs for the links $coreUrl = 'https://github.com/pi-hole/pi-hole/releases'; -$webUrl = 'https://github.com/arevindh/AdminLTE/releases'; +$webUrl = 'https://github.com/pi-hole/AdminLTE/releases'; $ftlUrl = 'https://github.com/pi-hole/FTL/releases'; -$dockerUrl = 'https://github.com/arevindh/docker-pi-hole/releases'; +$dockerUrl = 'https://github.com/pi-hole/docker-pi-hole/releases'; +$speedtestUrl = 'https://github.com/arevindh/pihole-speedtest/releases'; // Version strings (encoded to avoid code execution) // If "vDev" show branch/commit, else show link @@ -119,6 +136,12 @@ function checkUpdate($currentVersion, $latestVersion) $webVersionStr = ''.htmlentities($web_current).''; } +if (isset($speedtest_commit)) { + $speedtestVersionStr = htmlentities($speedtest_current.' ('.$speedtest_branch.', '.$speedtest_commit.')'); +} else { + $speedtestVersionStr = ''.htmlentities($speedtest_current).''; +} + if (isset($FTL_commit)) { $ftlVersionStr = htmlentities($FTL_current.' ('.$FTL_branch.', '.$FTL_commit.')'); } else { diff --git a/settings.php b/settings.php index 770adf05c..26698dda1 100644 --- a/settings.php +++ b/settings.php @@ -1618,7 +1618,7 @@ class="form-control"

    Mod the Mod -

    For the adventurous

    +

    With the Script

    From e693f1704599ed95fbe30c33ddd71c2849d2aeb0 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Fri, 1 Mar 2024 02:20:08 -0500 Subject: [PATCH 134/137] fix getting version (#91) --- api_speedtest.php | 20 ++++++++++---------- scripts/pi-hole/php/footer.php | 6 ++++-- scripts/pi-hole/php/update_checker.php | 4 ++-- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/api_speedtest.php b/api_speedtest.php index 6c905d4b4..efacd731c 100644 --- a/api_speedtest.php +++ b/api_speedtest.php @@ -341,15 +341,15 @@ function getStatusCmd() function whichSpeedtest() { - if (!file_exists('/usr/bin/speedtest')) { - return 'official'; - } - $version = speedtestExecute('/usr/bin/speedtest --version')['data']; - if (strpos($version, 'LibreSpeed') !== false) { - return 'LibreSpeed'; - } elseif (strpos($version, 'Python') !== false) { - return 'sivel\'s'; - } else { - return 'official'; + if (file_exists('/usr/bin/speedtest')) { + $version = speedtestExecute('/usr/bin/speedtest --version')['data']; + if (strpos($version, 'LibreSpeed') !== false) { + return 'LibreSpeed'; + } + if (strpos($version, 'Python') !== false) { + return 'sivel\'s'; + } } + + return 'official'; } diff --git a/scripts/pi-hole/php/footer.php b/scripts/pi-hole/php/footer.php index 3b03e7dd5..9cb747875 100644 --- a/scripts/pi-hole/php/footer.php +++ b/scripts/pi-hole/php/footer.php @@ -88,7 +88,7 @@ · Update available!
  • - Speedtest Mod + Speedtest · Update available!
  • @@ -98,7 +98,9 @@ To install updates, replace this old container with a fresh upgraded image. - To install updates, run pihole -up. + To install updates, uninstall Speedtest Mod, run pihole -up, and reinstall Speedtest Mod. The Reinstall button in [ Settings > Speedtest ] can do this for you! + + To install updates, reinstall Speedtest Mod. The Reinstall button in [ Settings > Speedtest ] can do this for you!

    diff --git a/scripts/pi-hole/php/update_checker.php b/scripts/pi-hole/php/update_checker.php index e6bf0240a..e2be46944 100644 --- a/scripts/pi-hole/php/update_checker.php +++ b/scripts/pi-hole/php/update_checker.php @@ -116,8 +116,8 @@ function checkUpdate($currentVersion, $latestVersion) } // URLs for the links -$coreUrl = 'https://github.com/pi-hole/pi-hole/releases'; -$webUrl = 'https://github.com/pi-hole/AdminLTE/releases'; +$coreUrl = 'https://github.com/arevindh/pi-hole/releases'; +$webUrl = 'https://github.com/arevindh/AdminLTE/releases'; $ftlUrl = 'https://github.com/pi-hole/FTL/releases'; $dockerUrl = 'https://github.com/pi-hole/docker-pi-hole/releases'; $speedtestUrl = 'https://github.com/arevindh/pihole-speedtest/releases'; From 959769f9f648f81c2ec4a06f81972cf3adbc634e Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Tue, 5 Mar 2024 01:45:55 -0500 Subject: [PATCH 135/137] update the updating instructions (#92) * update update instructions --- scripts/pi-hole/php/footer.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/pi-hole/php/footer.php b/scripts/pi-hole/php/footer.php index 9cb747875..b4033ec90 100644 --- a/scripts/pi-hole/php/footer.php +++ b/scripts/pi-hole/php/footer.php @@ -98,9 +98,9 @@ To install updates, replace this old container with a fresh upgraded image. - To install updates, uninstall Speedtest Mod, run pihole -up, and reinstall Speedtest Mod. The Reinstall button in [ Settings > Speedtest ] can do this for you! + To install updates, uninstall Speedtest Mod, run pihole -up if there's a new official Pi-hole release, and reinstall Speedtest Mod. The Reinstall button in [Settings/Speedtest] can do this for you! - To install updates, reinstall Speedtest Mod. The Reinstall button in [ Settings > Speedtest ] can do this for you! + To install updates, reinstall Speedtest Mod. The Reinstall button in [Settings/Speedtest] can do this for you!

    From 91bd55ec481f70c92814b7ff3159c91e7ba683ac Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Sat, 9 Mar 2024 13:08:49 -0500 Subject: [PATCH 136/137] workaround ookla error (#93) * avoid calling ookla's cli to get version --- api_speedtest.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/api_speedtest.php b/api_speedtest.php index efacd731c..6a61c2fc9 100644 --- a/api_speedtest.php +++ b/api_speedtest.php @@ -342,10 +342,18 @@ function getStatusCmd() function whichSpeedtest() { if (file_exists('/usr/bin/speedtest')) { + $officialInstalled = speedtestExecute('SKIP_MOD=true ; . /opt/pihole/speedtestmod/mod.sh ; notInstalled speedtest && echo "false" || echo "true"')['data']; + + if ($officialInstalled === 'true') { + return 'official'; + } + $version = speedtestExecute('/usr/bin/speedtest --version')['data']; + if (strpos($version, 'LibreSpeed') !== false) { return 'LibreSpeed'; } + if (strpos($version, 'Python') !== false) { return 'sivel\'s'; } From a6373e2b6a94c62862d8062d3306a165afdf32e0 Mon Sep 17 00:00:00 2001 From: ipitio <21136719+ipitio@users.noreply.github.com> Date: Wed, 13 Mar 2024 21:43:30 -0400 Subject: [PATCH 137/137] fix ookla and sql issues (#94) * suppress error * redirect stderr * redirect both * bypass ookla * forgot var * add sc * check if file exists * account for no table --- api_speedtest.php | 10 +++++++--- img/st-chart.png | Bin 0 -> 254536 bytes img/st-pref.png | Bin 0 -> 109957 bytes scripts/pi-hole/php/header_authenticated.php | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) create mode 100644 img/st-chart.png create mode 100644 img/st-pref.png diff --git a/api_speedtest.php b/api_speedtest.php index 6a61c2fc9..5d6f6c717 100644 --- a/api_speedtest.php +++ b/api_speedtest.php @@ -92,7 +92,7 @@ function getSpeedTestData($dbSpeedtest, $durationdays = '1') return array(); } $db = new SQLite3($dbSpeedtest); - if (!$db) { + if (!$db || !$db->querySingle('SELECT count(*) FROM sqlite_master WHERE type="table" AND name="speedtest"')) { return array(); } @@ -265,7 +265,11 @@ function JSONServers($cmdServersJSON) function getRemainingTime() { - $interval_seconds = speedtestExecute("grep 'interval_seconds=' /opt/pihole/speedtestmod/schedule_check.sh | cut -d'=' -f2")['data']; + $interval_seconds = -1; + + if (file_exists('/opt/pihole/speedtestmod/schedule_check.sh')) { + $interval_seconds = speedtestExecute("grep 'interval_seconds=' /opt/pihole/speedtestmod/schedule_check.sh | cut -d'=' -f2")['data']; + } // if interval_seconds is "nan", then schedule has never been set if (strpos($interval_seconds, 'nan') !== false) { @@ -296,7 +300,7 @@ function getRemainingTime() function getNumberOfDaysInDB($dbSpeedtest) { $db = new SQLite3($dbSpeedtest); - if (!$db) { + if (!$db || !$db->querySingle('SELECT count(*) FROM sqlite_master WHERE type="table" AND name="speedtest"')) { return array('data' => 0); } diff --git a/img/st-chart.png b/img/st-chart.png new file mode 100644 index 0000000000000000000000000000000000000000..c038da2245d612cab7b56047954e0c45623e5e40 GIT binary patch literal 254536 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

    dngrKC%OEAg|n?fz_C92TfXqxdb8}K|>-T(Nf_! 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 z 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

    >fK`p6md@B8h2_L!~O@CnY?I? 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 X=VVE?&m6PeUmw73>S+|+uzB)-9w?ke literal 0 HcmV?d00001 diff --git a/img/st-pref.png b/img/st-pref.png new file mode 100644 index 0000000000000000000000000000000000000000..e09fa940e743e2a90eb492fcf5d8069a237f332f GIT binary patch literal 109957 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ÛlVNu@off$9mbN(IZtQEa}Z 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 J@WPFI{|Qy6eUktH literal 0 HcmV?d00001 diff --git a/scripts/pi-hole/php/header_authenticated.php b/scripts/pi-hole/php/header_authenticated.php index 1990795cf..1a66cbbdd 100644 --- a/scripts/pi-hole/php/header_authenticated.php +++ b/scripts/pi-hole/php/header_authenticated.php @@ -153,7 +153,7 @@ function getTemperature() function getNumberOfDaysInDB($dbSpeedtest) { $db = new SQLite3($dbSpeedtest); - if (!$db) { + if (!$db || !$db->querySingle('SELECT count(*) FROM sqlite_master WHERE type="table" AND name="speedtest"')) { return 0; }