Skip to content
Merged
Show file tree
Hide file tree
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
2 changes: 1 addition & 1 deletion wled00/data/settings_sec.htm
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ <h2>Security & Update Setup</h2>
</div>
<div class="sec" id="OTA">
<h3>Software Update</h3>
<button type="button" onclick="U()">Manual OTA Update</button><br>
<button type="button" onclick="U()">Update WLED</button><br>
<div id="aOTA">Enable ArduinoOTA: <input type="checkbox" name="AO"></div>
Only allow update from same network/WiFi: <input type="checkbox" name="SU"><br>
<i class="warn">&#9888; If you are using multiple VLANs (i.e. IoT or guest network) either set PIN or disable this option.<br>
Expand Down
76 changes: 69 additions & 7 deletions wled00/data/update.htm
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<script>
function B() { window.history.back(); }
var cnfr = false;
var deviceInfo = null;
function cR() {
if (!cnfr) {
var bt = gId('rev');
Expand All @@ -22,15 +23,30 @@
fetch(getURL('/json/info'))
.then(response => response.json())
.then(data => {
deviceInfo = data;
document.querySelector('.installed-version').textContent = `${data.brand} ${data.ver} (${data.vid})`;
const repoUrl = data.repo && data.repo !== "unknown" ? "https://github.com/" + data.repo + "/releases/latest" : null;
document.querySelector('.release-name').textContent = data.release;
// assemble update URL and update release badge
if (data.repo && data.repo !== "unknown") {
const repoUrl = "https://github.com/" + data.repo + "/releases/latest";
if (repoUrl) {
const badgeUrl = "https://img.shields.io/github/release/" + data.repo + ".svg?style=flat-square";
document.querySelector('.release-repo').href = repoUrl;
document.querySelector('.release-badge').src = badgeUrl;
toggle('release-download'); // only show release download item after receiving a valid data.repo
// fetch latest release from GitHub to build direct bin download link
Comment on lines +31 to +36
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Hide the “Checking…” placeholder in the successful path.

After a valid repo is detected, Line 35 reveals #release-download but #Norelease-download remains visible, so stale status text can stay on screen.

Proposed fix
-						toggle('release-download'); // only show release download item after receiving a valid data.repo
+						gId('Norelease-download').classList.add("hide");
+						gId('release-download').classList.remove("hide"); // explicit show

Also applies to: 138-139

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@wled00/data/update.htm` around lines 31 - 36, After detecting a valid repo
(when repoUrl is truthy) the code shows the release download UI via
toggle('release-download') but does not hide the stale "Checking…" placeholder
(`#Norelease-download`), leaving old status text visible; update the successful
path to also hide or toggle off the no-release element (the DOM node with id or
selector for the "no release" placeholder) immediately after setting
document.querySelector('.release-repo').href and
document.querySelector('.release-badge').src (i.e., alongside the existing
toggle('release-download') call), and apply the same change in the duplicate
handling near lines 138–139 so the no-release placeholder is always hidden on
success.

fetch(`https://api.github.com/repos/${data.repo}/releases/latest`)
.then(r => r.json())
.then(gh => {
const ver = gh.tag_name.replace(/^v/, '');
const suffix = `_${ver}_${data.release}.bin`;
const asset = gh.assets.find(a => a.name.endsWith(suffix));
if (asset) {
const binUrl = asset.browser_download_url.replace(/^https?:\/\/[^/]+/, 'https://download.wled.me');
const el = document.querySelector('.release-name');
el.innerHTML = `<a href="${binUrl}">${data.release}</a>`;
}
})
.catch(() => {}); // silently ignore if GitHub API unavailable
} else {
gId('Norelease-download').classList.add("hide"); // repo invalid -> hide everything
}
Expand Down Expand Up @@ -60,6 +76,48 @@
gId('bootupd').classList.add("hide");
toggle('upd');
}
async function autoUpdate() {
const btn = gId('autoUpdBtn');
const status = gId('autoUpdStatus');
btn.disabled = true;
status.textContent = '';
try {
const info = deviceInfo;
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(', ');
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}`);
Comment on lines +105 to +107
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Unconditional URL rewrite breaks auto-update for non-official repos

At Line 91, replacing every release asset host with https://download.wled.me can 404 for builds whose info.repo is not wled/WLED, even when GitHub returned a valid browser_download_url.

Proposed fix
-				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 githubUrl = asset.browser_download_url;
+				const useMirror = (info.repo || '').toLowerCase() === 'wled/wled';
+				const fwUrl = useMirror
+					? githubUrl.replace(/^https?:\/\/[^/]+/, 'https://download.wled.me')
+					: githubUrl;
+				let fwResp = await fetch(fwUrl);
+				if (!fwResp.ok && useMirror) fwResp = await fetch(githubUrl); // mirror fallback
+				if (!fwResp.ok) throw new Error(`Download failed: ${fwResp.status}`);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@wled00/data/update.htm` around lines 91 - 93, The unconditional rewrite of
asset.browser_download_url to 'https://download.wled.me' (fwUrl) causes 404s for
non-official repos; change the logic that builds fwUrl so it only rewrites the
host for official WLED releases (e.g., when info.repo === 'wled/WLED' or another
explicit check for the official repo), otherwise use the original
asset.browser_download_url as-is; update the code around fwUrl and subsequent
fetch (fwResp) to conditionally replace the host based on info.repo (or parsed
hostname) so non-official release assets are fetched from their original URL.

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...`;
hideforms();
gId('upd').submit();
} catch(e) {
status.textContent = 'Auto-update failed: ' + e.message;
btn.disabled = false;
}
}
</script>
<style>
@import url("style.css");
Expand All @@ -77,18 +135,22 @@ <h2>WLED Software Update</h2>
<p>
Installed version: <span class="sip installed-version">Loading...</span><br>
Release: <span class="sip release-name">Loading...</span><br>
<span id="Norelease-download">Latest binary: Checking...<br></span>
<span id="Norelease-download">Latest release: Checking...<br></span>
<span id="release-download" class="hide">
Download the latest binary: <a class="release-repo" href="https://github.com/wled/WLED/releases/latest" target="_blank" rel="noopener noreferrer"
Latest version: <a class="release-repo" href="https://github.com/wled/WLED/releases/latest" target="_blank" rel="noopener noreferrer"
style="vertical-align: text-bottom; display: inline-flex;">
<img class="release-badge" alt="badge"></a><br> <!-- start with an empty placeholder, src will be filled after fetching /json/info -->
<img class="release-badge" alt="View latest"></a><br>
<button type="button" id="autoUpdBtn" onclick="autoUpdate()">Auto update</button>
<span id="autoUpdStatus" style="display:block;margin-top:4px;font-size:13px;"></span>
</span>
</p>
<hr class="sml">
<h3>Manual upload</h3>
<input type="hidden" name="skipValidation" value="" id="sV">
<input type='file' name='update' required><br> <!--should have accept='.bin', but it prevents file upload from android app-->
<input type='checkbox' onchange="sV.value=checked?1:''" id="skipValidation">
<label for='skipValidation'>Ignore firmware validation</label><br>
<button type="submit">Update WLED!</button><br>
<button type="submit">Upload</button><br>
<span id="rev">
<hr class="sml">
<button type="button" onclick="cR()">Revert update</button><br>
Expand All @@ -107,4 +169,4 @@ <h2>Bootloader Update</h2>
<div id="Noupd" class="hide sec"><b>Updating...</b><br>Please do not close or refresh the page :)</div>
<p><button id="backbtn" type="button" onclick="B()">Back</button></p>
</body>
</html>
</html>
Loading