add json validation to file inputs in UI and minify before upload (#5248)
Some checks failed
WLED CI / wled_build (push) Has been cancelled
Deploy Nightly / wled_build (push) Has been cancelled
Close stale issues and PRs / stale (push) Has been cancelled
Deploy Nightly / Deploy nightly (push) Has been cancelled

* also updated edit.htm to do the same
This commit is contained in:
Damian Schneider 2026-01-30 08:18:17 +01:00 committed by GitHub
parent 1031e70d70
commit f19d29cd64
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 28 additions and 22 deletions

View File

@ -137,16 +137,26 @@ function showToast(text, error = false) {
x.style.animation = 'none'; x.style.animation = 'none';
timeout = setTimeout(function(){ x.className = x.className.replace("show", ""); }, 2900); timeout = setTimeout(function(){ x.className = x.className.replace("show", ""); }, 2900);
} }
function uploadFile(fileObj, name) { async function uploadFile(fileObj, name, callback) {
let file = fileObj.files?.[0]; // get first file, "?"" = optional chaining in case no file is selected
if (!file) { callback?.(false); return; }
if (/\.json$/i.test(name)) { // same as name.toLowerCase().endsWith('.json')
try {
const minified = JSON.stringify(JSON.parse(await file.text())); // validate and minify JSON
file = new Blob([minified], { type: file.type || "application/json" });
} catch (err) {
if (!confirm("JSON invalid. Continue?")) { callback?.(false); return; }
// proceed with original file if invalid but user confirms
}
}
var req = new XMLHttpRequest(); var req = new XMLHttpRequest();
req.addEventListener('load', function(){showToast(this.responseText,this.status >= 400)}); req.addEventListener('load', function(){showToast(this.responseText,this.status >= 400); if(callback) callback(this.status < 400);});
req.addEventListener('error', function(e){showToast(e.stack,true);}); req.addEventListener('error', function(e){showToast("Upload failed",true); if(callback) callback(false);});
req.open("POST", "/upload"); req.open("POST", "/upload");
var formData = new FormData(); var formData = new FormData();
formData.append("data", fileObj.files[0], name); formData.append("data", file, name);
req.send(formData); req.send(formData);
fileObj.value = ''; fileObj.value = '';
return false;
} }
// connect to WebSocket, use parent WS or open new, callback function gets passed the new WS object // connect to WebSocket, use parent WS or open new, callback function gets passed the new WS object
function connectWs(onOpen) { function connectWs(onOpen) {

View File

@ -134,6 +134,7 @@ loadFiles('common.js', -1, () => {
}); });
}); });
}); });
var QueuedRequester = function(){ this.q=[]; this.r=false; this.x=null; } var QueuedRequester = function(){ this.q=[]; this.r=false; this.x=null; }
QueuedRequester.prototype = { QueuedRequester.prototype = {
_request: function(req){ _request: function(req){
@ -432,7 +433,7 @@ function createEditor(element,file){
// Check filename from text field or current file // Check filename from text field or current file
var pathField = gId("filepath"); var pathField = gId("filepath");
var filename = (pathField && pathField.value) ? pathField.value : currentFile; var filename = (pathField && pathField.value) ? pathField.value : currentFile;
aceEditor.session.setMode(filename && filename.toLowerCase().endsWith('.json') ? "ace/mode/json" : "ace/mode/text"); aceEditor.session.setMode(filename && (/\.json$/i.test(filename)) ? "ace/mode/json" : "ace/mode/text"); // same as filename.toLowerCase().endsWith('.json')
} }
// Try to initialize Ace editor if available // Try to initialize Ace editor if available
@ -488,7 +489,7 @@ function createEditor(element,file){
var filename = pathField ? pathField.value : currentFile; var filename = pathField ? pathField.value : currentFile;
var border = "2px solid #333"; var border = "2px solid #333";
if (filename && filename.toLowerCase().endsWith('.json')) { if (filename && (/\.json$/i.test(filename))) { // same as filename.toLowerCase().endsWith('.json')
try { try {
JSON.parse(ta.value); JSON.parse(ta.value);
} catch(e) { } catch(e) {
@ -499,23 +500,19 @@ function createEditor(element,file){
}; };
function saveFile(filename,data){ function saveFile(filename,data){
var finalData = data; var outdata = data;
// Minify JSON files before upload if (/\.json$/i.test(filename)) { // same as filename.toLowerCase().endsWith('.json')
if (filename.toLowerCase().endsWith('.json')) {
try { try {
finalData = JSON.stringify(JSON.parse(data)); outdata = JSON.stringify(JSON.parse(data)); // validate and minify
} catch(e) { } catch(e) {
alert("Invalid JSON! Please fix syntax."); alert("Invalid JSON! Please fix.");
return; return;
} }
} }
var fd=new FormData(); uploadFile({files: [new Blob([outdata], {type:"text/plain"})]}, filename, function(s) {
fd.append("file",new Blob([finalData],{type:"text/plain"}),filename); if(s) {
req.add("POST","/upload",fd,function(st,resp){
if (st!=200) alert("ERROR "+st+": "+resp);
else {
showToast("File saved");
refreshTree(); refreshTree();
loadFile(filename); // (re)load if saved successfully to update formating or show file content
} }
}); });
} }
@ -526,9 +523,9 @@ function createEditor(element,file){
gId("preview").style.display="none"; gId("preview").style.display="none";
gId("editor").style.display="flex"; gId("editor").style.display="flex";
if (st==200) { if (st==200) {
if (filename.toLowerCase().endsWith('.json')) { if ((/\.json$/i.test(filename))) { // same as filename.toLowerCase().endsWith('.json')
try { try {
setContent(filename.toLowerCase().includes('ledmap') ? prettyLedmap(resp) : JSON.stringify(JSON.parse(resp), null, 2)); setContent(/ledmap/i.test(filename) ? prettyLedmap(resp) : JSON.stringify(JSON.parse(resp), null, 2)); // pretty-print ledmap files (i.e. if file name includes "ledmap" case-insensitive)
} catch(e) { } catch(e) {
setContent(resp); setContent(resp);
} }
@ -555,8 +552,7 @@ function createEditor(element,file){
} }
if (!fn.startsWith("/")) fn = "/" + fn; if (!fn.startsWith("/")) fn = "/" + fn;
currentFile = fn; // Update current file currentFile = fn; // Update current file
saveFile(fn, getContent()); saveFile(fn, getContent())
loadFile(fn);
}, },
loadText:function(fn){ loadText:function(fn){
currentFile=fn; currentFile=fn;