Skip to content

Commit f19d29c

Browse files
authored
add json validation to file inputs in UI and minify before upload (#5248)
* also updated edit.htm to do the same
1 parent 1031e70 commit f19d29c

2 files changed

Lines changed: 28 additions & 22 deletions

File tree

wled00/data/common.js

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -137,16 +137,26 @@ function showToast(text, error = false) {
137137
x.style.animation = 'none';
138138
timeout = setTimeout(function(){ x.className = x.className.replace("show", ""); }, 2900);
139139
}
140-
function uploadFile(fileObj, name) {
140+
async function uploadFile(fileObj, name, callback) {
141+
let file = fileObj.files?.[0]; // get first file, "?"" = optional chaining in case no file is selected
142+
if (!file) { callback?.(false); return; }
143+
if (/\.json$/i.test(name)) { // same as name.toLowerCase().endsWith('.json')
144+
try {
145+
const minified = JSON.stringify(JSON.parse(await file.text())); // validate and minify JSON
146+
file = new Blob([minified], { type: file.type || "application/json" });
147+
} catch (err) {
148+
if (!confirm("JSON invalid. Continue?")) { callback?.(false); return; }
149+
// proceed with original file if invalid but user confirms
150+
}
151+
}
141152
var req = new XMLHttpRequest();
142-
req.addEventListener('load', function(){showToast(this.responseText,this.status >= 400)});
143-
req.addEventListener('error', function(e){showToast(e.stack,true);});
153+
req.addEventListener('load', function(){showToast(this.responseText,this.status >= 400); if(callback) callback(this.status < 400);});
154+
req.addEventListener('error', function(e){showToast("Upload failed",true); if(callback) callback(false);});
144155
req.open("POST", "/upload");
145156
var formData = new FormData();
146-
formData.append("data", fileObj.files[0], name);
157+
formData.append("data", file, name);
147158
req.send(formData);
148159
fileObj.value = '';
149-
return false;
150160
}
151161
// connect to WebSocket, use parent WS or open new, callback function gets passed the new WS object
152162
function connectWs(onOpen) {

wled00/data/edit.htm

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@
134134
});
135135
});
136136
});
137+
137138
var QueuedRequester = function(){ this.q=[]; this.r=false; this.x=null; }
138139
QueuedRequester.prototype = {
139140
_request: function(req){
@@ -432,7 +433,7 @@
432433
// Check filename from text field or current file
433434
var pathField = gId("filepath");
434435
var filename = (pathField && pathField.value) ? pathField.value : currentFile;
435-
aceEditor.session.setMode(filename && filename.toLowerCase().endsWith('.json') ? "ace/mode/json" : "ace/mode/text");
436+
aceEditor.session.setMode(filename && (/\.json$/i.test(filename)) ? "ace/mode/json" : "ace/mode/text"); // same as filename.toLowerCase().endsWith('.json')
436437
}
437438

438439
// Try to initialize Ace editor if available
@@ -488,7 +489,7 @@
488489
var filename = pathField ? pathField.value : currentFile;
489490
var border = "2px solid #333";
490491

491-
if (filename && filename.toLowerCase().endsWith('.json')) {
492+
if (filename && (/\.json$/i.test(filename))) { // same as filename.toLowerCase().endsWith('.json')
492493
try {
493494
JSON.parse(ta.value);
494495
} catch(e) {
@@ -499,23 +500,19 @@
499500
};
500501

501502
function saveFile(filename,data){
502-
var finalData = data;
503-
// Minify JSON files before upload
504-
if (filename.toLowerCase().endsWith('.json')) {
503+
var outdata = data;
504+
if (/\.json$/i.test(filename)) { // same as filename.toLowerCase().endsWith('.json')
505505
try {
506-
finalData = JSON.stringify(JSON.parse(data));
506+
outdata = JSON.stringify(JSON.parse(data)); // validate and minify
507507
} catch(e) {
508-
alert("Invalid JSON! Please fix syntax.");
508+
alert("Invalid JSON! Please fix.");
509509
return;
510510
}
511511
}
512-
var fd=new FormData();
513-
fd.append("file",new Blob([finalData],{type:"text/plain"}),filename);
514-
req.add("POST","/upload",fd,function(st,resp){
515-
if (st!=200) alert("ERROR "+st+": "+resp);
516-
else {
517-
showToast("File saved");
512+
uploadFile({files: [new Blob([outdata], {type:"text/plain"})]}, filename, function(s) {
513+
if(s) {
518514
refreshTree();
515+
loadFile(filename); // (re)load if saved successfully to update formating or show file content
519516
}
520517
});
521518
}
@@ -526,9 +523,9 @@
526523
gId("preview").style.display="none";
527524
gId("editor").style.display="flex";
528525
if (st==200) {
529-
if (filename.toLowerCase().endsWith('.json')) {
526+
if ((/\.json$/i.test(filename))) { // same as filename.toLowerCase().endsWith('.json')
530527
try {
531-
setContent(filename.toLowerCase().includes('ledmap') ? prettyLedmap(resp) : JSON.stringify(JSON.parse(resp), null, 2));
528+
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)
532529
} catch(e) {
533530
setContent(resp);
534531
}
@@ -555,8 +552,7 @@
555552
}
556553
if (!fn.startsWith("/")) fn = "/" + fn;
557554
currentFile = fn; // Update current file
558-
saveFile(fn, getContent());
559-
loadFile(fn);
555+
saveFile(fn, getContent())
560556
},
561557
loadText:function(fn){
562558
currentFile=fn;

0 commit comments

Comments
 (0)