// The Install page: asks the project's Gitea for the latest release, and gives ESP Web Tools a // manifest for its factory image, built here, so the page offers a new release as soon as it exists // and nothing is copied to this site (docs/milestones/W1.md, Q180). Gitea sits behind Caddy, which // allows this origin to read the release API and the downloads. (function () { var box = document.getElementById("flasher"); if (!box) return; var $ = function (id) { return document.getElementById(id); }; var status = $("flasher-status"); var apiOrigin = new URL(box.dataset.api).origin; var downloads = box.dataset.releasesPath; // A network error (which is also what a browser makes of a missing CORS header) and a release this // page won't offer are different things to tell the visitor. function fail(error) { var unreachable = error instanceof TypeError; status.textContent = (unreachable ? "Couldn't reach the release server (" + error.message + ")" : "This page can't offer the latest release (" + error.message + ")") + ". Use the esptool steps below, or take the file from the downloads page."; status.className = "msg"; } function human(bytes) { return bytes >= 1048576 ? (bytes / 1048576).toFixed(2) + " MB" : Math.round(bytes / 1024) + " KB"; } // Only a download from the project's own server, for this repository, is ever handed to the flasher. function trusted(url) { try { var u = new URL(url); return u.origin === apiOrigin && u.pathname.indexOf(downloads) === 0 && u.pathname.indexOf("..") < 0 && !u.search; } catch (e) { return false; } } fetch(box.dataset.api + "/releases/latest", { headers: { Accept: "application/json" } }) .then(function (r) { if (!r.ok) throw new Error("answer " + r.status); return r.json(); }) .then(function (rel) { var assets = rel.assets || []; var bin = assets.filter(function (a) { return /-factory\.bin$/.test(a.name); })[0]; if (!bin) throw new Error("the latest release has no factory image"); if (!trusted(bin.browser_download_url)) throw new Error("its download isn't on the project's server"); var sums = assets.filter(function (a) { return a.name === "SHA256SUMS" && trusted(a.browser_download_url); })[0]; $("f-version").textContent = rel.tag_name; $("f-size").textContent = human(bin.size); if (rel.html_url) $("f-link").href = rel.html_url; var manifest = { name: "roro9stack", version: rel.tag_name, builds: [{ chipFamily: "ESP32-S3", parts: [{ path: bin.browser_download_url, offset: 0 }] }] }; var url = URL.createObjectURL(new Blob([JSON.stringify(manifest)], { type: "application/json" })); var button = $("f-button"); button.setAttribute("manifest", url); button.hidden = false; $("flasher-info").hidden = false; status.textContent = "Latest release: " + rel.tag_name + "."; if (!sums) { $("f-sha").textContent = "not published"; return; } return fetch(sums.browser_download_url) .then(function (r) { return r.ok ? r.text() : ""; }) .then(function (text) { var line = text.split("\n").filter(function (l) { return l.indexOf(bin.name) >= 0; })[0]; $("f-sha").textContent = line ? line.split(/\s+/)[0] : "not listed"; }) .catch(function () { $("f-sha").textContent = "unavailable"; }); }) .catch(fail); })();