Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions wled00/data/update.htm
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,55 @@
<head>
<meta content='width=device-width' name='viewport'>
<title>WLED Update</title>
<script src="common.js" type="text/javascript"></script>
<script>
function B() { window.history.back(); }
function U() { document.getElementById("uf").style.display="none";document.getElementById("msg").style.display="block"; }
async function autoUpdate() {
const btn = gId('autoUpdBtn');
const status = gId('autoUpdStatus');
btn.disabled = true;
status.textContent = '';
try {
status.textContent = 'Fetching device info...';
const infoResp = await fetch(getURL('/json/info'));
if (!infoResp.ok) throw new Error('Failed to fetch device info');
Comment on lines +17 to +18
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="wled00/data/update.htm"

echo "== File header (first 60 lines) =="
sed -n '1,60p' "$FILE" | nl -ba | sed -e 's/^/ /'

echo
echo "== Lines 1-120 with line numbers (to find autoUpdate and fetches) =="
sed -n '1,140p' "$FILE" | nl -ba | sed -e 's/^/ /'

echo
echo "== Search for fetch usage =="
rg -n "fetch\(" "$FILE" || true

echo
echo "== Show autoUpdate function block (best-effort) =="
# Print around the first occurrence of autoUpdate
LINE_AUTO=$(rg -n "autoUpdate" "$FILE" | head -n 1 | cut -d: -f1 || true)
if [ -n "${LINE_AUTO:-}" ]; then
  START=$((LINE_AUTO-40))
  END=$((LINE_AUTO+180))
  START=$((START<1?1:START))
  sed -n "${START},${END}p" "$FILE" | nl -ba | sed -e 's/^/ /'
fi

echo
echo "== Check for any AbortController/timeout wrapper already present =="
rg -n "AbortController|signal:|timeoutMs|fetchWithTimeout" "$FILE" || true

echo
echo "== Check for WLEDMM markers near additions =="
rg -n "//\s*WLEDMM" "$FILE" || true

echo
echo "== Show nearby lines where // WLEDMM might be missing (lines 1-140) for context =="
sed -n '1,140p' "$FILE" | nl -ba | sed -e 's/^/ /'

Repository: MoonModules/WLED-MM

Length of output: 159


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="wled00/data/update.htm"

echo "== File header (first 80 lines) =="
sed -n '1,80p' "$FILE" | cat -n

echo
echo "== Lines 1-220 with line numbers (find autoUpdate/fetches) =="
sed -n '1,220p' "$FILE" | cat -n

echo
echo "== Search for fetch usage =="
rg -n "fetch\(" "$FILE" || true

echo
echo "== Search for AbortController/timeout wrapper =="
rg -n "AbortController|fetchWithTimeout|timeoutMs|signal:" "$FILE" || true

echo
echo "== Search for WLEDMM markers =="
rg -n "//\s*WLEDMM" "$FILE" || true

echo
echo "== Show full autoUpdate block vicinity (around first autoUpdate) =="
LINE_AUTO=$(rg -n "autoUpdate" "$FILE" | head -n 1 | cut -d: -f1 || true)
if [ -n "${LINE_AUTO:-}" ]; then
  START=$((LINE_AUTO-60))
  END=$((LINE_AUTO+260))
  START=$((START<1?1:START))
  sed -n "${START},${END}p" "$FILE" | cat -n
fi

Repository: MoonModules/WLED-MM

Length of output: 12732


Add fetch timeouts (AbortController) to prevent the Auto update flow from hanging
autoUpdate() has three fetch() calls with no timeout, so the button can remain disabled indefinitely on a stalled network request (device info, GitHub latest release, and firmware download).

  • Add an AbortController-based timeout wrapper and use it for all fetch() calls in autoUpdate().
  • Add the required // WLEDMM marker(s) for the WLED-MM-specific JS additions in this file (there are no // WLEDMM comments in the inline <script>).
Suggested patch
 		async function autoUpdate() {
 			const btn = gId('autoUpdBtn');
 			const status = gId('autoUpdStatus');
+			const fetchWithTimeout = async (url, options = {}, timeoutMs = 15000) => {
+				const controller = new AbortController();
+				const t = setTimeout(() => controller.abort(), timeoutMs);
+				try {
+					return await fetch(url, {...options, signal: controller.signal});
+				} finally {
+					clearTimeout(t);
+				}
+			};
 			btn.disabled = true;
 			status.textContent = '';
 			try {
 				status.textContent = 'Fetching device info...';
-				const infoResp = await fetch(getURL('/json/info'));
+				const infoResp = await fetchWithTimeout(getURL('/json/info'));
 				if (!infoResp.ok) throw new Error('Failed to fetch device info');
 				const info = await infoResp.json();
@@
-				const ghResp = await fetch(`https://api.github.com/repos/${info.repo}/releases/latest`);
+				const ghResp = await fetchWithTimeout(`https://api.github.com/repos/${info.repo}/releases/latest`);
@@
-				const fwResp = await fetch(fwUrl);
+				const fwResp = await fetchWithTimeout(fwUrl);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@wled00/data/update.htm` around lines 17 - 18, autoUpdate() currently performs
three fetch() calls (info fetch via getURL('/json/info'), GitHub latest release
fetch, and firmware download) with no timeout; add a small AbortController-based
helper (e.g., fetchWithTimeout or timeoutFetch) that accepts a URL, options, and
a timeout ms, uses AbortController to cancel the request after the timeout, and
throws on timeout, then replace the direct fetch(...) calls inside autoUpdate()
with this helper; mark your additions with the required // WLEDMM comment(s) in
the inline <script> (place a // WLEDMM above the helper and another where you
call the helper in autoUpdate() so WLED-MM-specific changes are clearly
identifiable).

const info = await infoResp.json();
if (!info || !info.repo || info.repo === 'unknown') {
status.textContent = 'No release repository available for this build.';
btn.disabled = false;
return;
}
status.textContent = 'Checking GitHub for latest release...';
const ghResp = await fetch(`https://api.github.com/repos/${info.repo}/releases/latest`);
if (!ghResp.ok) throw new Error(`GitHub API error: ${ghResp.status}`);
const ghRelease = await ghResp.json();
const releaseVer = ghRelease.tag_name.replace(/^v/, '');
const assetSuffix = `_${releaseVer}_${info.release}.bin`;
const asset = ghRelease.assets.find(a => a.name.endsWith(assetSuffix));
if (!asset) {
const available = ghRelease.assets.map(a => a.name).join(', ');
Comment on lines +29 to +33
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot Jun 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show referenced section with line numbers
nl -ba wled00/data/update.htm | sed -n '1,120p'

# Show around the cited lines (29-70 in file are likely different due to offsets; also search exact snippet)
rg -n "ghRelease\.tag_name|assetSuffix|ghRelease\.assets\.find|info\.release" wled00/data/update.htm

# Extract larger surrounding context near matches
rg -n "ghRelease\.tag_name" wled00/data/update.htm | cut -d: -f1 | head -n 5 | while read -r ln; do
  start=$((ln-30)); end=$((ln+80))
  echo "---- context around line $ln ($start-$end) ----"
  nl -ba wled00/data/update.htm | sed -n "${start},${end}p"
done

# Find where ghRelease is fetched/parsed and whether there is any schema guard
rg -n "fetch\\(|api\\.github|releases|json\\(|tag_name|assets" wled00/data/update.htm

# Show any catch blocks / error messaging
rg -n "catch\\(|show.*status|status.*msg|alert\\(|toast\\(|error" wled00/data/update.htm

Repository: MoonModules/WLED-MM

Length of output: 106


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Basic sanity: list file exists
ls -la wled00/data/update.htm

# Print first ~160 lines with line numbers via awk
awk '{printf "%6d\t%s\n", NR, $0}' wled00/data/update.htm | sed -n '1,160p'

# Print around the referenced snippet area by searching for the exact line fragments
# (no nl needed)
awk '{if ($0 ~ /ghRelease\.tag_name/ || $0 ~ /assetSuffix/ || $0 ~ /ghRelease\.assets\.find/ || $0 ~ /info\.release/) print NR "\t" $0}' wled00/data/update.htm

# Get broader context around where ghRelease.tag_name appears
ln=$(awk '/ghRelease\.tag_name/ {print NR; exit}' wled00/data/update.htm)
start=$((ln-60)); end=$((ln+120))
awk -v s="$start" -v e="$end" '{if(NR>=s && NR<=e) printf "%6d\t%s\n", NR, $0}' wled00/data/update.htm

# Find where ghRelease is constructed/assigned
rg -n --context 3 "ghRelease\s*=" wled00/data/update.htm || true
rg -n --context 3 "tag_name|assets" wled00/data/update.htm || true

# Find where info.release is created/assigned
rg -n --context 3 "info\.release" wled00/data/update.htm || true

# Find relevant error handling paths
rg -n --context 3 "catch\\(|show.*status|status.*msg|alert\\(|toast\\(|error" wled00/data/update.htm || true

Repository: MoonModules/WLED-MM

Length of output: 10817


Harden GitHub release parsing before using release fields
In autoUpdate() the code assumes ghRelease.tag_name is a string and ghRelease.assets is an array, and it uses info.release without checking it exists. If the GitHub/device payload shape differs, tag_name.replace(...) / assets.find(...) / assets.map(...) can throw and fall into the generic Auto-update failed: ... message; add guards for tag_name, assets (array), and info.release and set a targeted status.textContent when any are missing/invalid.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@wled00/data/update.htm` around lines 29 - 33, In autoUpdate(), guard use of
ghRelease.tag_name, ghRelease.assets and info.release before calling .replace,
.find or .map: check typeof ghRelease.tag_name === 'string' before computing
releaseVer, ensure Array.isArray(ghRelease.assets) before searching/mapping
assets, and verify info.release is present (non-empty string) before building
assetSuffix; for each missing/invalid condition set a clear status.textContent
(e.g., "Missing release tag", "No assets array in release", "Missing device
info.release") and early-return to avoid the generic error path.

Copy link
Copy Markdown
Collaborator

@softhack007 softhack007 Jun 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please suggest some minimal code that would ensure that the code catches unexpected response content.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@softhack007, here are minimal inline guards to insert right after the two await *.json() calls:

// After:  const info = await infoResp.json();
if (!info || typeof info.release !== 'string' || !info.release) {
    status.textContent = 'Missing device release info (info.release).';
    btn.disabled = false;
    return;
}
// After:  const ghRelease = await ghResp.json();
if (typeof ghRelease.tag_name !== 'string' || !ghRelease.tag_name) {
    status.textContent = 'Missing release tag from GitHub response.';
    btn.disabled = false;
    return;
}
if (!Array.isArray(ghRelease.assets)) {
    status.textContent = 'No assets array in GitHub release response.';
    btn.disabled = false;
    return;
}

That gives a targeted, readable error for each unexpected shape instead of falling through to the generic Auto-update failed: TypeError message. The existing !info.repo guard already covers the repo field, so no change is needed there.


🧠 Learnings used
Learnt from: CR
Repo: MoonModules/WLED-MM PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-04-14T23:39:33.591Z
Learning: Applies to wled00/**/*.{h,hpp,cpp,js},wled00/data/**/*.{html,css,js} : Repository language is English — suggest translations for non-English content

Learnt from: CR
Repo: MoonModules/WLED-MM PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-04-14T23:39:33.590Z
Learning: Applies to wled00/html_*.h,wled00/js_*.h : Never edit or commit auto-generated header files `wled00/html_*.h` and `wled00/js_*.h` — these are generated from `wled00/data/` during the build process

Learnt from: CR
Repo: MoonModules/WLED-MM PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-14T23:40:02.031Z
Learning: Applies to wled00/html_*.h wled00/js_*.h : Never edit or commit auto-generated `wled00/html_*.h` or `wled00/js_*.h` files

Learnt from: CR
Repo: MoonModules/WLED-MM PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-14T23:40:02.031Z
Learning: Applies to wled00/data/**/*.{html,js,css} : Run `npm run build` after editing web UI files to regenerate C++ headers

Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 353
File: .github/web.instructions.md:8-9
Timestamp: 2026-04-02T14:17:35.133Z
Learning: In the WLED web UI files under wled00/data (HTML and JavaScript: wled00/data/**/*.{htm,html,js}), indent with tabs. Do not enforce spaces in these files.

status.textContent = `Firmware not found (*${assetSuffix}). Available: ${available}`;
btn.disabled = false;
return;
}
status.textContent = `Downloading ${asset.name} (${Math.round(asset.size/1024)} KB)...`;
const fwUrl = asset.browser_download_url.replace(/^https?:\/\/[^/]+/, 'https://download.wled.me');
const fwResp = await fetch(fwUrl);
if (!fwResp.ok) throw new Error(`Download failed: ${fwResp.status}`);
const blob = await fwResp.blob();
const file = new File([blob], asset.name, {type: 'application/octet-stream'});
const dt = new DataTransfer();
dt.items.add(file);
document.querySelector('input[name="update"]').files = dt.files;
status.textContent = `Downloaded ${asset.name}. Uploading to device...`;
U();
gId('uf').submit();
} catch(e) {
status.textContent = 'Auto-update failed: ' + e.message;
btn.disabled = false;
}
}
function GetV() {/*injected values here*/}
</script>
<style>
Expand All @@ -19,6 +65,9 @@ <h2>MoonMod WLED Software Update</h2>
Installed version: <span class="sip">WLEDMM ##VERSION##</span><br> <!--WLEDMM: show bin name-->
Download the latest release: <a href="https://github.com/MoonModules/WLED-MM/releases/latest" target="_blank" rel="noopener noreferrer">
<img src="https://img.shields.io/github/release/MoonModules/WLED-MM.svg?style=flat-square"></a><br>
<button type="button" id="autoUpdBtn" onclick="autoUpdate()">Auto update</button><br>
<span id="autoUpdStatus" style="display:block;margin-top:4px;font-size:13px;"></span>
<hr>
<input type='file' name='update' required><br> <!--should have accept='.bin', but it prevents file upload from android app-->
<button type="submit">Update!</button><br>
<button type="button" onclick="B()">Back</button>
Expand Down
Loading