v1.0.83: HTML-escape + JSON validation for map fields (onblur + pre-submit)
This commit is contained in:
+1
-1
@@ -6,7 +6,7 @@ from routes.main import bp as main_bp
|
|||||||
from routes.api import bp as api_bp
|
from routes.api import bp as api_bp
|
||||||
from routes.api_test import bp as api_test_bp
|
from routes.api_test import bp as api_test_bp
|
||||||
|
|
||||||
VERSION = "1.0.82"
|
VERSION = "1.0.83"
|
||||||
|
|
||||||
app = Flask(__name__, template_folder="templates", static_folder="static")
|
app = Flask(__name__, template_folder="templates", static_folder="static")
|
||||||
app.config["NUBES_API_ENDPOINT"] = os.getenv("NUBES_API_ENDPOINT", "https://lk-api-gateway-dev.ngcloud.ru/api/v1/svc")
|
app.config["NUBES_API_ENDPOINT"] = os.getenv("NUBES_API_ENDPOINT", "https://lk-api-gateway-dev.ngcloud.ru/api/v1/svc")
|
||||||
|
|||||||
@@ -211,27 +211,39 @@ function showParams(opId,opName){
|
|||||||
const params=data.params||data;
|
const params=data.params||data;
|
||||||
const form=document.getElementById('params-form');
|
const form=document.getElementById('params-form');
|
||||||
const displayNameValue=isCreate?makeCreateDisplayName():'';
|
const displayNameValue=isCreate?makeCreateDisplayName():'';
|
||||||
form.innerHTML=(opName==='create'?`<div class="param-row"><label class="req">displayName</label><span style="font-size:12px;color:var(--muted);white-space:nowrap;">${AUTOTEST_PREFIX}</span><input type="text" id="param-displayname" value="${displayNameValue}" autocomplete="off"></div>`:'')
|
form.innerHTML=(opName==='create'?`<div class="param-row"><label class="req">displayName</label><span style="font-size:12px;color:var(--muted);white-space:nowrap;">${AUTOTEST_PREFIX}</span><input type="text" id="param-displayname" value="${_esc(displayNameValue)}" autocomplete="off"></div>`:'')
|
||||||
+ params.map(p=>{
|
+ params.map(p=>{
|
||||||
const req=p.isRequired;
|
const req=p.isRequired;
|
||||||
const hasDfl=p.defaultValue!==null&&p.defaultValue!==undefined&&p.defaultValue!=='';
|
const hasDfl=p.defaultValue!==null&&p.defaultValue!==undefined&&p.defaultValue!=='';
|
||||||
const labelCls=req?(hasDfl?'req':'req-nodfl'):'';
|
const labelCls=req?(hasDfl?'req':'req-nodfl'):'';
|
||||||
const dfl=hasDfl?p.defaultValue:(req?'':'');
|
const dfl=hasDfl?p.defaultValue:(req?'':'');
|
||||||
const vl=p.valueList;
|
const vl=p.valueList;
|
||||||
|
const isMap=(p.dataType||'').startsWith('map');
|
||||||
let input;
|
let input;
|
||||||
if(vl&&Array.isArray(vl)){
|
if(vl&&Array.isArray(vl)){
|
||||||
input=`<select name="p_${p.svcOperationCfsParamId}" data-type="${p.dataType}">${vl.map(v=>`<option value="${v}" ${v==dfl?'selected':''}>${v}</option>`).join('')}</select>`;
|
input=`<select name="p_${p.svcOperationCfsParamId}" data-type="${_esc(p.dataType)}">${vl.map(v=>`<option value="${_esc(v)}" ${v==dfl?'selected':''}>${_esc(v)}</option>`).join('')}</select>`;
|
||||||
}else if(p.dataType==='boolean'){
|
}else if(p.dataType==='boolean'){
|
||||||
input=`<select name="p_${p.svcOperationCfsParamId}" data-type="boolean"><option value="true" ${dfl==='true'||dfl===true?'selected':''}>true</option><option value="false" ${dfl==='false'||dfl===false?'selected':''}>false</option></select>`;
|
input=`<select name="p_${p.svcOperationCfsParamId}" data-type="boolean"><option value="true" ${dfl==='true'||dfl===true?'selected':''}>true</option><option value="false" ${dfl==='false'||dfl===false?'selected':''}>false</option></select>`;
|
||||||
}else{
|
}else{
|
||||||
input=`<input type="text" name="p_${p.svcOperationCfsParamId}" value="${dfl}" placeholder="${req&&!hasDfl?'обязательно':''}">`;
|
const dt=isMap?'map':'';
|
||||||
|
input=`<input type="text" name="p_${p.svcOperationCfsParamId}" value="${_esc(dfl)}" data-type="${dt}" placeholder="${req&&!hasDfl?'обязательно':''}"${isMap?' onblur="validateJson(this)"':''}>`;
|
||||||
|
if(isMap) input+=`<span class="json-err" style="display:none;color:var(--destructive);font-size:10px;margin-left:4px;"></span>`;
|
||||||
}
|
}
|
||||||
return `<div class="param-row"><label class="${labelCls}">${p.name}</label>${input}</div>`;
|
return `<div class="param-row"><label class="${labelCls}">${_esc(p.name)}</label>${input}</div>`;
|
||||||
}).join('');
|
}).join('');
|
||||||
const btn=document.getElementById('btn-test');
|
const btn=document.getElementById('btn-test');
|
||||||
btn.style.display='inline-flex';
|
btn.style.display='inline-flex';
|
||||||
btn.textContent='Запустить';
|
btn.textContent='Запустить';
|
||||||
btn.onclick=()=>{
|
btn.onclick=()=>{
|
||||||
|
// Проверить ВСЕ map-поля перед отправкой
|
||||||
|
let bad=[];
|
||||||
|
document.querySelectorAll('#params-form [data-type="map"]').forEach(el=>{
|
||||||
|
if(!validateJson(el,true)) bad.push(el);
|
||||||
|
});
|
||||||
|
if(bad.length>0){
|
||||||
|
document.getElementById('test-status').innerHTML=`<span class="badge">Ошибка</span> ${bad.length} полей с невалидным JSON — поправьте и нажмите Запустить снова`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
const pp={};
|
const pp={};
|
||||||
document.querySelectorAll('#params-form [name^="p_"]').forEach(el=>{
|
document.querySelectorAll('#params-form [name^="p_"]').forEach(el=>{
|
||||||
let v=el.value;
|
let v=el.value;
|
||||||
@@ -245,6 +257,30 @@ function showParams(opId,opName){
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// === Хелперы ===
|
||||||
|
|
||||||
|
function _esc(s){
|
||||||
|
/* HTML-экранирование: " → " & → & < → < */
|
||||||
|
return String(s||'').replace(/&/g,'&').replace(/"/g,'"').replace(/</g,'<');
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateJson(el,quiet){
|
||||||
|
/* Проверить что значение в поле — валидный JSON.
|
||||||
|
quiet=true — не менять внешний вид (для batch-проверки перед отправкой). */
|
||||||
|
const errEl=el.parentElement.querySelector('.json-err');
|
||||||
|
const v=el.value.trim();
|
||||||
|
if(!v) return true; // пусто — ок
|
||||||
|
try{ JSON.parse(v); }
|
||||||
|
catch(e){
|
||||||
|
if(!quiet&&errEl){ errEl.style.display='inline'; errEl.textContent='Ошибка JSON: '+e.message; }
|
||||||
|
el.style.borderColor='var(--destructive)';
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(!quiet&&errEl) errEl.style.display='none';
|
||||||
|
el.style.borderColor='';
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
function setFinishedState(statusText, statusClass, statusError, statusDuration, instanceName){
|
function setFinishedState(statusText, statusClass, statusError, statusDuration, instanceName){
|
||||||
const btn=document.getElementById('btn-test');
|
const btn=document.getElementById('btn-test');
|
||||||
btn.disabled=false;
|
btn.disabled=false;
|
||||||
|
|||||||
Reference in New Issue
Block a user