Development
This commit is contained in:
parent
fcdaf09477
commit
f152d82386
11 changed files with 219 additions and 66 deletions
|
|
@ -17,6 +17,43 @@ LEVEL_RANK = {'nothing': 0, 'viewer': 1, 'administrator': 2, 'manager': 3}
|
||||||
|
|
||||||
STANDARD_INPUT_TYPES = {'text', 'password', 'number', 'checkbox', 'select', 'textarea'}
|
STANDARD_INPUT_TYPES = {'text', 'password', 'number', 'checkbox', 'select', 'textarea'}
|
||||||
|
|
||||||
|
_VALIDATION_FLAGS = {
|
||||||
|
'VALIDATION_IPV4_FORMAT': 1,
|
||||||
|
'VALIDATION_IPV6_FORMAT': 2,
|
||||||
|
'VALIDATION_SUBNET': 4,
|
||||||
|
'VALIDATION_ADDRESS': 8,
|
||||||
|
'VALIDATION_MAC': 16,
|
||||||
|
'VALIDATION_URL': 32,
|
||||||
|
'VALIDATION_PORT': 64,
|
||||||
|
'VALIDATION_DASH_NAME': 128,
|
||||||
|
'VALIDATION_NETWORK_NAME': 256,
|
||||||
|
'VALIDATION_DOMAIN_NAME': 512,
|
||||||
|
'VALIDATION_TIME24H': 1024,
|
||||||
|
'VALIDATION_RANGE_INT': 2048,
|
||||||
|
'VALIDATION_ENDPOINT': 4096,
|
||||||
|
'VALIDATION_IPV4_CIDR': 8192,
|
||||||
|
}
|
||||||
|
|
||||||
|
_COMPAT_VALIDATION = {
|
||||||
|
'ipv4': 'VALIDATION_IPV4_FORMAT',
|
||||||
|
'ipv6': 'VALIDATION_IPV6_FORMAT',
|
||||||
|
'ip': 'VALIDATION_IPV4_FORMAT|VALIDATION_IPV6_FORMAT',
|
||||||
|
'ipv4cidr': 'VALIDATION_IPV4_CIDR',
|
||||||
|
'mac': 'VALIDATION_MAC',
|
||||||
|
'url': 'VALIDATION_URL',
|
||||||
|
'port': 'VALIDATION_PORT',
|
||||||
|
'dashname': 'VALIDATION_DASH_NAME',
|
||||||
|
'networkname': 'VALIDATION_NETWORK_NAME',
|
||||||
|
'domainname': 'VALIDATION_DOMAIN_NAME',
|
||||||
|
'time_24h': 'VALIDATION_TIME24H',
|
||||||
|
'vlan_id': 'VALIDATION_RANGE_INT',
|
||||||
|
'positive_int': 'VALIDATION_RANGE_INT',
|
||||||
|
'endpoint': 'VALIDATION_ENDPOINT',
|
||||||
|
'ip_in_subnet': 'VALIDATION_ADDRESS',
|
||||||
|
'address': 'VALIDATION_ADDRESS',
|
||||||
|
'subnet': 'VALIDATION_SUBNET',
|
||||||
|
}
|
||||||
|
|
||||||
# Utilities ===========================================================
|
# Utilities ===========================================================
|
||||||
|
|
||||||
def e(text):
|
def e(text):
|
||||||
|
|
@ -62,6 +99,124 @@ def js_str(value):
|
||||||
return json.dumps(str(value))
|
return json.dumps(str(value))
|
||||||
|
|
||||||
|
|
||||||
|
def parse_validation(s):
|
||||||
|
if not s:
|
||||||
|
return 0
|
||||||
|
resolved = _COMPAT_VALIDATION.get(s, s)
|
||||||
|
result = 0
|
||||||
|
for token in resolved.split('|'):
|
||||||
|
token = token.strip()
|
||||||
|
val = _VALIDATION_FLAGS.get(token)
|
||||||
|
if val is None:
|
||||||
|
print(f'[factory] WARNING: unknown validation token "{token}" in "{s}"', file=sys.stderr)
|
||||||
|
continue
|
||||||
|
result |= val
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def _encode_field_validations(fields):
|
||||||
|
out = []
|
||||||
|
for f in fields:
|
||||||
|
f2 = dict(f)
|
||||||
|
raw = f2.get('validate', '')
|
||||||
|
if not raw and f2.get('input_type') == 'number':
|
||||||
|
raw = 'VALIDATION_RANGE_INT'
|
||||||
|
if raw and isinstance(raw, str):
|
||||||
|
f2['validate'] = parse_validation(raw)
|
||||||
|
out.append(f2)
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def build_big_validate():
|
||||||
|
_JS_NAMES = {
|
||||||
|
'VALIDATION_IPV4_FORMAT': 'F_IPV4',
|
||||||
|
'VALIDATION_IPV6_FORMAT': 'F_IPV6',
|
||||||
|
'VALIDATION_SUBNET': 'F_SUBNET',
|
||||||
|
'VALIDATION_ADDRESS': 'F_ADDR',
|
||||||
|
'VALIDATION_MAC': 'F_MAC',
|
||||||
|
'VALIDATION_URL': 'F_URL',
|
||||||
|
'VALIDATION_PORT': 'F_PORT',
|
||||||
|
'VALIDATION_DASH_NAME': 'F_DASH',
|
||||||
|
'VALIDATION_NETWORK_NAME': 'F_NET',
|
||||||
|
'VALIDATION_DOMAIN_NAME': 'F_DOMAIN',
|
||||||
|
'VALIDATION_TIME24H': 'F_T24H',
|
||||||
|
'VALIDATION_RANGE_INT': 'F_RNGINT',
|
||||||
|
'VALIDATION_ENDPOINT': 'F_ENDPT',
|
||||||
|
'VALIDATION_IPV4_CIDR': 'F_IPV4C',
|
||||||
|
}
|
||||||
|
decls = ''.join(
|
||||||
|
f'var {_JS_NAMES[k]}={_VALIDATION_FLAGS[k]};'
|
||||||
|
for k in _VALIDATION_FLAGS
|
||||||
|
)
|
||||||
|
body = r"""
|
||||||
|
function _ok(){return{ok:true,msg:'',partial:false};}
|
||||||
|
function _par(m){return{ok:false,msg:m||'',partial:true};}
|
||||||
|
function _err(m){return{ok:false,msg:m||'Invalid',partial:false};}
|
||||||
|
function _ipv4(s){
|
||||||
|
if(!s)return'empty';
|
||||||
|
if(/[^0-9.]/.test(s))return'badchar';
|
||||||
|
if(/\.\./.test(s)||s[0]==='.')return'badstruct';
|
||||||
|
var p=s.split('.');
|
||||||
|
if(p.length>4)return'badstruct';
|
||||||
|
for(var i=0;i<p.length;i++){if(!p[i])continue;var n=parseInt(p[i],10);if(isNaN(n)||n>255||String(n)!==p[i])return'badrange';}
|
||||||
|
return(p.length===4&&p.every(function(x){return x!=='';}))? 'ok':'partial';
|
||||||
|
}
|
||||||
|
function _ipv6(s){
|
||||||
|
if(!s)return'empty';
|
||||||
|
if(/[^0-9a-fA-F:]/.test(s))return'badchar';
|
||||||
|
if(/:::/.test(s))return'badstruct';
|
||||||
|
if((s.match(/::/g)||[]).length>1)return'badstruct';
|
||||||
|
var parts=s.split(/::?/);
|
||||||
|
for(var i=0;i<parts.length;i++){if(parts[i].length>4)return'badstruct';}
|
||||||
|
var c=(s.match(/:/g)||[]).length,d=s.indexOf('::')!==-1;
|
||||||
|
if(d&&c>7)return'badstruct';
|
||||||
|
return(c===7&&!d)||d?'ok':'partial';
|
||||||
|
}
|
||||||
|
function _checkFlag(s,flag){
|
||||||
|
if(flag===F_IPV4){var r=_ipv4(s);if(r==='ok')return _ok();if(r==='partial'||r==='empty')return _par('');if(r==='badchar')return _err('Invalid character');if(r==='badrange')return _err('Octet out of range');return _err('Invalid format');}
|
||||||
|
if(flag===F_IPV6){var r=_ipv6(s);if(r==='ok')return _ok();if(r==='partial'||r==='empty')return _par('');if(r==='badchar')return _err('Invalid character');return _err('Invalid format');}
|
||||||
|
if(flag===F_MAC){if(!s)return _par('');if(/[^0-9a-fA-F:]/.test(s))return _err('Invalid character');if(/::/.test(s))return _err('Invalid format');var g=s.split(':');if(g.length>6)return _err('Too many groups');for(var i=0;i<g.length;i++){if(g[i].length>2)return _err('Each group must be exactly 2 hex characters');}return(g.length===6&&g.every(function(x){return x.length===2;}))?_ok():_par('');}
|
||||||
|
if(flag===F_URL){if(!s)return _par('');if(/[^A-Za-z0-9\-._~:/?#\[\]@!$&'()*+,;=%]/.test(s))return _err('Invalid character');var sl=s.toLowerCase();if('https://'.startsWith(sl)||'http://'.startsWith(sl))return _par('');var sep=sl.indexOf('://');if(sep===-1)return _err('Invalid URL format');var scheme=sl.slice(0,sep);if(scheme!=='http'&&scheme!=='https')return _err('Invalid URL format');var after=s.slice(sep+3);if(!after)return _par('');var he=after.search(/[/:?#]/),host=he===-1?after:after.slice(0,he),rest=he===-1?'':after.slice(he);if(!host)return _par('');if(/\.\./.test(host)||host[0]==='.'||host[host.length-1]==='.')return _err('Invalid URL format');var lb=host.split('.');for(var i=0;i<lb.length;i++){if(!/^[a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?$/.test(lb[i]))return _err('Invalid URL format');}if(rest[0]===':'){var pm=rest.slice(1).match(/^\d+/);if(!pm)return _par('');if(parseInt(pm[0])<1||parseInt(pm[0])>65535)return _err('Invalid URL format');}return _ok();}
|
||||||
|
if(flag===F_PORT){if(!s)return _par('');if(/[^0-9]/.test(s))return _err('Digits only');var n=parseInt(s,10);return(n>=1&&n<=65535)?_ok():_err('Must be between 1 and 65535');}
|
||||||
|
if(flag===F_DASH){if(!s)return _par('');if(/[^a-z0-9-]/.test(s))return _err('Lowercase letters, digits and hyphens only');if(s[0]==='-'||/--/.test(s))return _err('No leading, trailing or consecutive hyphens');if(s[s.length-1]==='-')return _par('');return _ok();}
|
||||||
|
if(flag===F_NET){if(!s)return _par('');if(/[^a-zA-Z0-9_-]/.test(s))return _err('Letters, digits, hyphens and underscores only');if(s[0]==='-'||s[0]==='_')return _err('No leading, trailing or consecutive special characters');if(/[-_]{2,}/.test(s))return _err('No leading, trailing or consecutive special characters');if(s[s.length-1]==='-'||s[s.length-1]==='_')return _par('');return _ok();}
|
||||||
|
if(flag===F_DOMAIN){if(!s)return _par('');if(/[^a-zA-Z0-9.-]/.test(s))return _err('Letters, digits, hyphens and dots only');if(s[0]==='.')return _err('Invalid domain format');if(/\.\./.test(s))return _err('Invalid domain format');if(s[s.length-1]==='.')return _par('');var lb=s.split('.');for(var i=0;i<lb.length;i++){var l=lb[i];if(l[0]==='-'||l[l.length-1]==='-')return _err('Invalid domain format');}return _ok();}
|
||||||
|
if(flag===F_T24H){if(!s)return _par('');if(/[^0-9:]/.test(s))return _err('Digits and colon only');if(s.length<5)return _par('');return /^([01]\d|2[0-3]):[0-5]\d$/.test(s)?_ok():_err('Must be HH:MM in 24-hour format (e.g. 02:30)');}
|
||||||
|
if(flag===F_RNGINT){if(s===''||s===null||s===undefined)return _par('');if(/[^0-9]/.test(s))return _err('Digits only');var n=parseInt(s,10);var mn=(arg1!==''&&arg1!=null)?parseInt(arg1,10):0;var mx=(arg2!==''&&arg2!=null)?parseInt(arg2,10):null;if(n<mn||(mx!==null&&n>mx)){if(mn!=null&&mx!==null)return _err('Must be between '+mn+' and '+mx);return mn!=null?_err('Must be >= '+mn):_err('Must be <= '+mx);}return _ok();}
|
||||||
|
if(flag===F_ENDPT){if(!s)return _par('');if(/^[0-9.]+$/.test(s)){var r=_ipv4(s);return r==='ok'?_ok():(r==='partial'||r==='empty')?_par(''):_err('Invalid character');}if(s.indexOf(':')!==-1){var cc=(s.match(/:/g)||[]).length;if(cc>1){if(/:::/.test(s)||(s.match(/::/g)||[]).length>1)return _err('Invalid hostname or IP');if(/[^0-9a-fA-F:.]/.test(s))return _err('Invalid character');var col=s.replace(/[^:]/g,'').length;return(s.indexOf('::')!==-1||col===7)?_ok():_par('');}return _checkFlag(s.slice(0,s.lastIndexOf(':')),F_DOMAIN);}return _checkFlag(s,F_DOMAIN);}
|
||||||
|
if(flag===F_IPV4C){if(!s)return _par('');var slash=s.indexOf('/');if(slash===-1){var r=_ipv4(s);return(r==='ok'||r==='partial'||r==='empty')?_par(''):(r==='badchar'?_err('Invalid character'):r==='badrange'?_err('Octet out of range'):_err('Invalid format'));}var r=_ipv4(s.slice(0,slash));if(r!=='ok')return r==='badchar'?_err('Invalid character'):r==='badrange'?_err('Octet out of range'):_par('');var pfx=s.slice(slash+1);if(!pfx)return _par('');if(/[^0-9]/.test(pfx))return _err('Invalid character');var n=parseInt(pfx,10);return(n>=0&&n<=32)?_ok():_err('Prefix must be 0-32');}
|
||||||
|
if(flag===F_SUBNET){if(!arg1)return _par('');var prefix=parseInt(arg1,10);if(isNaN(prefix)||prefix<1||prefix>30)return _err('Prefix must be 1-30');var r=_ipv4(s);if(r!=='ok')return(r==='partial'||r==='empty')?_par(''):(r==='badchar'?_err('Invalid character'):_err('Invalid format'));var mB=prefix===0?0:((0xFFFFFFFF<<(32-prefix))>>>0);var ipN=s.split('.').reduce(function(a,o){return(a<<8|+o)>>>0;},0);return((ipN&(~mB>>>0))!==0)?_err('Host bits must be zero'):_ok();}
|
||||||
|
if(flag===F_ADDR){var r=_ipv4(s);if(r!=='ok')return(r==='partial'||r==='empty')?_par(''):(r==='badchar'?_err('Invalid character'):_err('Invalid format'));if(!arg1||!arg2)return _par('');var prefix=parseInt(arg1,10);if(isNaN(prefix)||prefix<1||prefix>30)return _par('');if(_ipv4(arg2)!=='ok')return _par('');var mB=prefix===0?0:((0xFFFFFFFF<<(32-prefix))>>>0);var snN=arg2.split('.').reduce(function(a,o){return(a<<8|+o)>>>0;},0);if((snN&(~mB>>>0))!==0)return _par('');var iPts=s.split('.').map(Number),sPts=arg2.split('.').map(Number);var ipN=((iPts[0]<<24)|(iPts[1]<<16)|(iPts[2]<<8)|iPts[3])>>>0,sN=((sPts[0]<<24)|(sPts[1]<<16)|(sPts[2]<<8)|sPts[3])>>>0;if((ipN&mB)!==(sN&mB))return _err('IP not in VLAN subnet');var hM=(~mB)>>>0,netN=(sN&mB)>>>0;if(ipN===netN)return _err('Network address not allowed');if(ipN===(netN|hM)>>>0)return _err('Broadcast address not allowed');return _ok();}
|
||||||
|
return _par('');
|
||||||
|
}
|
||||||
|
function _checkLine(s){
|
||||||
|
var anyPartial=false,firstMsg='';
|
||||||
|
var flags=[F_IPV4,F_IPV6,F_SUBNET,F_ADDR,F_MAC,F_URL,F_PORT,F_DASH,F_NET,F_DOMAIN,F_T24H,F_RNGINT,F_ENDPT,F_IPV4C];
|
||||||
|
for(var i=0;i<flags.length;i++){
|
||||||
|
if(!(validation&flags[i]))continue;
|
||||||
|
var r=_checkFlag(s,flags[i]);
|
||||||
|
if(r.ok)return r;
|
||||||
|
if(r.partial)anyPartial=true;
|
||||||
|
else if(!firstMsg)firstMsg=r.msg;
|
||||||
|
}
|
||||||
|
return anyPartial?_par(''):_err(firstMsg||'Invalid');
|
||||||
|
}
|
||||||
|
var lines=value.split('\n'),hasPartial=false,seen={},hasContent=false;
|
||||||
|
for(var i=0;i<lines.length;i++){
|
||||||
|
var l=lines[i].trim();
|
||||||
|
if(!l)continue;
|
||||||
|
hasContent=true;
|
||||||
|
var r=_checkLine(l);
|
||||||
|
if(!r.ok&&!r.partial)return r;
|
||||||
|
if(!r.ok){hasPartial=true;continue;}
|
||||||
|
if(dedup){if(seen[l])return _err('Duplicate entry');seen[l]=true;}
|
||||||
|
}
|
||||||
|
if(!hasContent)return _par('');
|
||||||
|
if(hasPartial)return _par('');
|
||||||
|
return _ok();"""
|
||||||
|
return f'function bigValidate(value,validation,collisions,dedup,arg1,arg2){{{decls}{body}\n}}'
|
||||||
|
|
||||||
|
|
||||||
def get_worker_id(datasource):
|
def get_worker_id(datasource):
|
||||||
for prefix in ('config:', 'live:'):
|
for prefix in ('config:', 'live:'):
|
||||||
if datasource.startswith(prefix):
|
if datasource.startswith(prefix):
|
||||||
|
|
@ -213,7 +368,7 @@ def build_form_script(field_specs, submit_sel):
|
||||||
for sv, pv in subnet_items:
|
for sv, pv in subnet_items:
|
||||||
lines.append(f' function _chkSubnet() {{')
|
lines.append(f' function _chkSubnet() {{')
|
||||||
lines.append(f' if (!{sv} || !{pv}) return;')
|
lines.append(f' if (!{sv} || !{pv}) return;')
|
||||||
lines.append(f" var res = ipv4SubnetValid({sv}.value.trim(), {pv}.value.trim());")
|
lines.append(f" var res = bigValidate({sv}.value.trim(), {parse_validation('VALIDATION_SUBNET')}, null, false, {pv}.value.trim(), null);")
|
||||||
lines.append(f" setFieldHint({sv}, res.ok ? '' : (res.msg||''), res.ok ? 'ok' : (res.partial ? 'warning' : 'error'));")
|
lines.append(f" setFieldHint({sv}, res.ok ? '' : (res.msg||''), res.ok ? 'ok' : (res.partial ? 'warning' : 'error'));")
|
||||||
lines.append(f' {sv}._valid = res.ok;')
|
lines.append(f' {sv}._valid = res.ok;')
|
||||||
lines.append(f" var dot = {pv}.closest('.form-group').querySelector('.subnet-dotted');")
|
lines.append(f" var dot = {pv}.closest('.form-group').querySelector('.subnet-dotted');")
|
||||||
|
|
@ -480,11 +635,12 @@ def build_field(item, tokens):
|
||||||
f'<option value="{e(o["value"])}"{" selected" if o["value"] == current else ""}>{e(o["label"])}</option>'
|
f'<option value="{e(o["value"])}"{" selected" if o["value"] == current else ""}>{e(o["label"])}</option>'
|
||||||
for o in options
|
for o in options
|
||||||
)
|
)
|
||||||
validate = item.get('validate', '')
|
validate_raw = item.get('validate', '')
|
||||||
depends = item.get('depends', [])
|
depends = item.get('depends', [])
|
||||||
validate_attr = f' data-validate="{e(validate)}"' if validate else ''
|
_vmask = parse_validation(validate_raw) if validate_raw else 0
|
||||||
|
validate_attr = f' data-validate="{_vmask}"' if _vmask else ''
|
||||||
depends_attr = f' data-depends="{e(",".join(depends))}"' if depends else ''
|
depends_attr = f' data-depends="{e(",".join(depends))}"' if depends else ''
|
||||||
if validate:
|
if _vmask:
|
||||||
return (
|
return (
|
||||||
f'<div class="form-group"><label class="form-label">{label}</label>'
|
f'<div class="form-group"><label class="form-label">{label}</label>'
|
||||||
f'<div class="field-wrap"><select name="{name}" class="form-select"{validate_attr}{depends_attr}>{opts_html}</select>'
|
f'<div class="field-wrap"><select name="{name}" class="form-select"{validate_attr}{depends_attr}>{opts_html}</select>'
|
||||||
|
|
@ -500,10 +656,11 @@ def build_field(item, tokens):
|
||||||
if input_type == 'number':
|
if input_type == 'number':
|
||||||
min_attr = f' min="{item["min"]}"' if 'min' in item else ''
|
min_attr = f' min="{item["min"]}"' if 'min' in item else ''
|
||||||
max_attr = f' max="{item["max"]}"' if 'max' in item else ''
|
max_attr = f' max="{item["max"]}"' if 'max' in item else ''
|
||||||
validate = item.get('validate', 'positive_int')
|
validate_raw = item.get('validate', 'VALIDATION_RANGE_INT')
|
||||||
depends = item.get('depends', [])
|
depends = item.get('depends', [])
|
||||||
existing_ids = apply_tokens(item.get('existing_ids', ''), tokens)
|
existing_ids = apply_tokens(item.get('existing_ids', ''), tokens)
|
||||||
validate_attr = f' data-validate="{e(validate)}"'
|
_vmask = parse_validation(validate_raw)
|
||||||
|
validate_attr = f' data-validate="{_vmask}"'
|
||||||
depends_attr = f' data-depends="{e(",".join(depends))}"' if depends else ''
|
depends_attr = f' data-depends="{e(",".join(depends))}"' if depends else ''
|
||||||
existing_attr = f' data-existing-ids="{e(existing_ids)}"' if existing_ids else ''
|
existing_attr = f' data-existing-ids="{e(existing_ids)}"' if existing_ids else ''
|
||||||
dyn_hint_html = '<p class="form-hint field-dyn-hint hidden"></p>'
|
dyn_hint_html = '<p class="form-hint field-dyn-hint hidden"></p>'
|
||||||
|
|
@ -612,11 +769,12 @@ def build_field(item, tokens):
|
||||||
]
|
]
|
||||||
return build_table_picker(name, label, current, picker_rows, headers, summary_config, action_btn_html)
|
return build_table_picker(name, label, current, picker_rows, headers, summary_config, action_btn_html)
|
||||||
|
|
||||||
validate = item.get('validate', '')
|
validate_raw = item.get('validate', '')
|
||||||
depends = item.get('depends', [])
|
depends = item.get('depends', [])
|
||||||
validate_attr = f' data-validate="{e(validate)}"' if validate else ''
|
_vmask = parse_validation(validate_raw) if validate_raw else 0
|
||||||
|
validate_attr = f' data-validate="{_vmask}"' if _vmask else ''
|
||||||
depends_attr = f' data-depends="{e(",".join(depends))}"' if depends else ''
|
depends_attr = f' data-depends="{e(",".join(depends))}"' if depends else ''
|
||||||
if validate:
|
if _vmask:
|
||||||
return (
|
return (
|
||||||
f'<div class="form-group"><label class="form-label">{label}</label>'
|
f'<div class="form-group"><label class="form-label">{label}</label>'
|
||||||
f'<div class="field-wrap"><input type="{e(input_type)}" name="{name}" value="{e(value)}"'
|
f'<div class="field-wrap"><input type="{e(input_type)}" name="{name}" value="{e(value)}"'
|
||||||
|
|
@ -640,7 +798,8 @@ def build_editable_list(item, tokens):
|
||||||
add_lbl = e(apply_tokens(item.get('add_label', 'Add'), tokens))
|
add_lbl = e(apply_tokens(item.get('add_label', 'Add'), tokens))
|
||||||
hint = e(apply_tokens(item.get('hint', ''), tokens))
|
hint = e(apply_tokens(item.get('hint', ''), tokens))
|
||||||
hint_html = f'<p class="form-hint">{hint}</p>' if hint else ''
|
hint_html = f'<p class="form-hint">{hint}</p>' if hint else ''
|
||||||
validate = e(item.get('validate', ''))
|
validate_raw = item.get('validate', '')
|
||||||
|
_vmask = parse_validation(validate_raw) if validate_raw else 0
|
||||||
|
|
||||||
try:
|
try:
|
||||||
items_list = json.loads(apply_tokens(item.get('items', '[]'), tokens))
|
items_list = json.loads(apply_tokens(item.get('items', '[]'), tokens))
|
||||||
|
|
@ -654,7 +813,7 @@ def build_editable_list(item, tokens):
|
||||||
'</div>'
|
'</div>'
|
||||||
for v in items_list
|
for v in items_list
|
||||||
)
|
)
|
||||||
validate_attr = f' data-validate="{validate}"' if validate else ''
|
validate_attr = f' data-validate="{_vmask}"' if _vmask else ''
|
||||||
return (
|
return (
|
||||||
f'<div class="form-group"><label class="form-label">{label}</label>'
|
f'<div class="form-group"><label class="form-label">{label}</label>'
|
||||||
f'<div class="editable-list" data-name="{name}" data-placeholder="{ph}"{validate_attr}>'
|
f'<div class="editable-list" data-name="{name}" data-placeholder="{ph}"{validate_attr}>'
|
||||||
|
|
@ -843,7 +1002,7 @@ def build_table(item, tokens, inherited_req=None):
|
||||||
thead += '<th></th>'
|
thead += '<th></th>'
|
||||||
|
|
||||||
expanded_ra_fields = {
|
expanded_ra_fields = {
|
||||||
i: expand_fields(ra.get('fields', []), tokens)
|
i: _encode_field_validations(expand_fields(ra.get('fields', []), tokens))
|
||||||
for i, ra in enumerate(row_actions)
|
for i, ra in enumerate(row_actions)
|
||||||
if ra.get('method', 'post').lower() == 'inline_edit'
|
if ra.get('method', 'post').lower() == 'inline_edit'
|
||||||
}
|
}
|
||||||
|
|
@ -1174,20 +1333,18 @@ def build_item(item, tokens, inherited_req=None):
|
||||||
f_name = e(f.get('name', ''))
|
f_name = e(f.get('name', ''))
|
||||||
f_placeholder = e(f.get('placeholder', ''))
|
f_placeholder = e(f.get('placeholder', ''))
|
||||||
f_required = 'true' if f.get('required') else 'false'
|
f_required = 'true' if f.get('required') else 'false'
|
||||||
f_validate = f.get('validate', '')
|
f_validate_raw = f.get('validate', '') or f.get('valtype', '')
|
||||||
f_valtype = f.get('valtype', '')
|
f_attrs = f.get('attrs', {})
|
||||||
f_attrs = f.get('attrs', {})
|
_vmask = parse_validation(f_validate_raw) if f_validate_raw else 0
|
||||||
|
|
||||||
attr_str = f' data-field="{f_name}" data-required="{f_required}"'
|
attr_str = f' data-field="{f_name}" data-required="{f_required}"'
|
||||||
if f_validate:
|
if _vmask:
|
||||||
attr_str += f' data-validate="{e(f_validate)}"'
|
attr_str += f' data-validate="{_vmask}"'
|
||||||
if f_valtype:
|
|
||||||
attr_str += f' data-valtype="{e(f_valtype)}"'
|
|
||||||
for ak, av in f_attrs.items():
|
for ak, av in f_attrs.items():
|
||||||
attr_str += f' {e(ak)}="{e(str(av))}"'
|
attr_str += f' {e(ak)}="{e(str(av))}"'
|
||||||
|
|
||||||
inp = f'<input type="text" class="form-input"{attr_str} placeholder="{f_placeholder}"/>'
|
inp = f'<input type="text" class="form-input"{attr_str} placeholder="{f_placeholder}"/>'
|
||||||
if f_validate or f_valtype:
|
if _vmask:
|
||||||
field_inner = (
|
field_inner = (
|
||||||
'<div class="field-wrap">'
|
'<div class="field-wrap">'
|
||||||
+ inp +
|
+ inp +
|
||||||
|
|
@ -1249,11 +1406,12 @@ def build_item(item, tokens, inherited_req=None):
|
||||||
label = e(item.get('label', ''))
|
label = e(item.get('label', ''))
|
||||||
name = e(item.get('name', ''))
|
name = e(item.get('name', ''))
|
||||||
override_name = e(item.get('override_name', name + '_override'))
|
override_name = e(item.get('override_name', name + '_override'))
|
||||||
validate = e(item.get('validate', ''))
|
validate_raw = item.get('validate', '')
|
||||||
validate_attr = f' data-validate-lines="{validate}"' if validate else ''
|
_vmask = parse_validation(validate_raw) if validate_raw else 0
|
||||||
dyn_hint_html = '<p class="form-hint field-dyn-hint hidden"></p>' if validate else ''
|
validate_attr = f' data-validate-lines="{_vmask}"' if _vmask else ''
|
||||||
wrap_open = '<div class="field-wrap">' if validate else ''
|
dyn_hint_html = '<p class="form-hint field-dyn-hint hidden"></p>' if _vmask else ''
|
||||||
wrap_close = '</div>' if validate else ''
|
wrap_open = '<div class="field-wrap">' if _vmask else ''
|
||||||
|
wrap_close = '</div>' if _vmask else ''
|
||||||
hint = e(apply_tokens(item.get('hint', ''), tokens))
|
hint = e(apply_tokens(item.get('hint', ''), tokens))
|
||||||
hint_html = f'<p class="form-hint">{hint}</p>' if hint else ''
|
hint_html = f'<p class="form-hint">{hint}</p>' if hint else ''
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -108,12 +108,12 @@
|
||||||
{
|
{
|
||||||
"col": "hostname",
|
"col": "hostname",
|
||||||
"input_type": "text",
|
"input_type": "text",
|
||||||
"validate": "networkname"
|
"validate": "VALIDATION_NETWORK_NAME"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"col": "mac",
|
"col": "mac",
|
||||||
"input_type": "text",
|
"input_type": "text",
|
||||||
"validate": "mac"
|
"validate": "VALIDATION_MAC"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"col": "ip",
|
"col": "ip",
|
||||||
|
|
@ -171,7 +171,7 @@
|
||||||
"label": "Hostname",
|
"label": "Hostname",
|
||||||
"name": "hostname",
|
"name": "hostname",
|
||||||
"input_type": "text",
|
"input_type": "text",
|
||||||
"validate": "networkname",
|
"validate": "VALIDATION_NETWORK_NAME",
|
||||||
"placeholder": "e.g. nas"
|
"placeholder": "e.g. nas"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -179,7 +179,7 @@
|
||||||
"label": "MAC Address",
|
"label": "MAC Address",
|
||||||
"name": "mac",
|
"name": "mac",
|
||||||
"input_type": "text",
|
"input_type": "text",
|
||||||
"validate": "mac",
|
"validate": "VALIDATION_MAC",
|
||||||
"placeholder": "e.g. aa:bb:cc:dd:ee:ff"
|
"placeholder": "e.g. aa:bb:cc:dd:ee:ff"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@
|
||||||
{
|
{
|
||||||
"col": "name",
|
"col": "name",
|
||||||
"input_type": "text",
|
"input_type": "text",
|
||||||
"validate": "dashname"
|
"validate": "VALIDATION_DASH_NAME"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"col": "description",
|
"col": "description",
|
||||||
|
|
@ -63,7 +63,7 @@
|
||||||
{
|
{
|
||||||
"col": "url",
|
"col": "url",
|
||||||
"input_type": "text",
|
"input_type": "text",
|
||||||
"validate": "url"
|
"validate": "VALIDATION_URL"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
@ -92,7 +92,7 @@
|
||||||
"label": "Name",
|
"label": "Name",
|
||||||
"name": "name",
|
"name": "name",
|
||||||
"input_type": "text",
|
"input_type": "text",
|
||||||
"validate": "dashname",
|
"validate": "VALIDATION_DASH_NAME",
|
||||||
"placeholder": "e.g. steven-black"
|
"placeholder": "e.g. steven-black"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -114,7 +114,7 @@
|
||||||
"label": "Source URL",
|
"label": "Source URL",
|
||||||
"name": "url",
|
"name": "url",
|
||||||
"input_type": "text",
|
"input_type": "text",
|
||||||
"validate": "url",
|
"validate": "VALIDATION_URL",
|
||||||
"placeholder": "https://..."
|
"placeholder": "https://..."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -172,7 +172,7 @@
|
||||||
"label": "Daily Refresh Time",
|
"label": "Daily Refresh Time",
|
||||||
"name": "daily_execute_time_24hr_local",
|
"name": "daily_execute_time_24hr_local",
|
||||||
"input_type": "text",
|
"input_type": "text",
|
||||||
"validate": "time_24h",
|
"validate": "VALIDATION_TIME24H",
|
||||||
"value": "%GENERAL_DAILY_EXECUTE_TIME%",
|
"value": "%GENERAL_DAILY_EXECUTE_TIME%",
|
||||||
"placeholder": "e.g. 02:30",
|
"placeholder": "e.g. 02:30",
|
||||||
"hint": "24-hour local time for the daily blocklist refresh."
|
"hint": "24-hour local time for the daily blocklist refresh."
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@
|
||||||
"name": "upstream_servers",
|
"name": "upstream_servers",
|
||||||
"item_placeholder": "e.g. 1.1.1.1",
|
"item_placeholder": "e.g. 1.1.1.1",
|
||||||
"add_label": "Add Provider",
|
"add_label": "Add Provider",
|
||||||
"validate": "ipv4",
|
"validate": "VALIDATION_IPV4_FORMAT|VALIDATION_IPV6_FORMAT",
|
||||||
"hint": "DNS resolvers queried for external hostnames. Supports IPv4 and IPv6.",
|
"hint": "DNS resolvers queried for external hostnames. Supports IPv4 and IPv6.",
|
||||||
"items": "%DNS_UPSTREAM_SERVERS_JSON%"
|
"items": "%DNS_UPSTREAM_SERVERS_JSON%"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -54,12 +54,12 @@
|
||||||
{
|
{
|
||||||
"col": "host",
|
"col": "host",
|
||||||
"input_type": "text",
|
"input_type": "text",
|
||||||
"validate": "domainname"
|
"validate": "VALIDATION_DOMAIN_NAME"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"col": "ip",
|
"col": "ip",
|
||||||
"input_type": "text",
|
"input_type": "text",
|
||||||
"validate": "ipv4"
|
"validate": "VALIDATION_IPV4_FORMAT"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"col": "enabled",
|
"col": "enabled",
|
||||||
|
|
@ -100,7 +100,7 @@
|
||||||
"label": "Hostname",
|
"label": "Hostname",
|
||||||
"name": "host",
|
"name": "host",
|
||||||
"input_type": "text",
|
"input_type": "text",
|
||||||
"validate": "domainname",
|
"validate": "VALIDATION_DOMAIN_NAME",
|
||||||
"placeholder": "e.g. server.home.local"
|
"placeholder": "e.g. server.home.local"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -108,7 +108,7 @@
|
||||||
"label": "Resolves To",
|
"label": "Resolves To",
|
||||||
"name": "ip",
|
"name": "ip",
|
||||||
"input_type": "text",
|
"input_type": "text",
|
||||||
"validate": "ipv4",
|
"validate": "VALIDATION_IPV4_FORMAT",
|
||||||
"placeholder": "e.g. 192.168.1.100"
|
"placeholder": "e.g. 192.168.1.100"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -124,7 +124,7 @@
|
||||||
"label": "Source",
|
"label": "Source",
|
||||||
"name": "src_ip_or_subnet",
|
"name": "src_ip_or_subnet",
|
||||||
"input_type": "text",
|
"input_type": "text",
|
||||||
"validate": "ipv4cidr",
|
"validate": "VALIDATION_IPV4_CIDR",
|
||||||
"placeholder": "e.g. 192.168.20.0/24"
|
"placeholder": "e.g. 192.168.20.0/24"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -132,7 +132,7 @@
|
||||||
"label": "Destination",
|
"label": "Destination",
|
||||||
"name": "dst_ip_or_subnet",
|
"name": "dst_ip_or_subnet",
|
||||||
"input_type": "text",
|
"input_type": "text",
|
||||||
"validate": "ipv4",
|
"validate": "VALIDATION_IPV4_FORMAT",
|
||||||
"placeholder": "e.g. 192.168.10.100"
|
"placeholder": "e.g. 192.168.10.100"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -140,7 +140,7 @@
|
||||||
"label": "Dest Port",
|
"label": "Dest Port",
|
||||||
"name": "dst_port",
|
"name": "dst_port",
|
||||||
"input_type": "text",
|
"input_type": "text",
|
||||||
"validate": "port",
|
"validate": "VALIDATION_PORT",
|
||||||
"placeholder": "e.g. 8009"
|
"placeholder": "e.g. 8009"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -142,7 +142,7 @@
|
||||||
"input_type": "number",
|
"input_type": "number",
|
||||||
"min": 1,
|
"min": 1,
|
||||||
"max": 4094,
|
"max": 4094,
|
||||||
"validate": "vlan_id",
|
"validate": "VALIDATION_RANGE_INT",
|
||||||
"existing_ids": "%EXISTING_VLAN_IDS_JSON%",
|
"existing_ids": "%EXISTING_VLAN_IDS_JSON%",
|
||||||
"hint": "Unique integer 1-4094. Sets the 802.1Q tag and interface name."
|
"hint": "Unique integer 1-4094. Sets the 802.1Q tag and interface name."
|
||||||
},
|
},
|
||||||
|
|
@ -151,7 +151,7 @@
|
||||||
"label": "VLAN Name",
|
"label": "VLAN Name",
|
||||||
"name": "name",
|
"name": "name",
|
||||||
"input_type": "text",
|
"input_type": "text",
|
||||||
"validate": "dashname",
|
"validate": "VALIDATION_DASH_NAME",
|
||||||
"hint": "Lowercase letters, digits, hyphens. E.g. iot"
|
"hint": "Lowercase letters, digits, hyphens. E.g. iot"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -184,7 +184,7 @@
|
||||||
{
|
{
|
||||||
"label": "IP Address",
|
"label": "IP Address",
|
||||||
"name": "ip",
|
"name": "ip",
|
||||||
"valtype": "address",
|
"validate": "VALIDATION_ADDRESS",
|
||||||
"attrs": {
|
"attrs": {
|
||||||
"data-dep-subnet": "[name='subnet']",
|
"data-dep-subnet": "[name='subnet']",
|
||||||
"data-dep-mask": ".subnet-prefix-input"
|
"data-dep-mask": ".subnet-prefix-input"
|
||||||
|
|
@ -200,7 +200,7 @@
|
||||||
{
|
{
|
||||||
"label": "Hostname",
|
"label": "Hostname",
|
||||||
"name": "hostname",
|
"name": "hostname",
|
||||||
"validate": "networkname",
|
"validate": "VALIDATION_NETWORK_NAME",
|
||||||
"placeholder": "Optional"
|
"placeholder": "Optional"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
@ -223,7 +223,7 @@
|
||||||
"label": "DNS Server(s)",
|
"label": "DNS Server(s)",
|
||||||
"name": "dns_server",
|
"name": "dns_server",
|
||||||
"override_name": "dns_server_override",
|
"override_name": "dns_server_override",
|
||||||
"validate": "ip_in_subnet",
|
"validate": "VALIDATION_ADDRESS",
|
||||||
"hint": "DNS server(s) advertised to clients via DHCP."
|
"hint": "DNS server(s) advertised to clients via DHCP."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -231,7 +231,7 @@
|
||||||
"label": "NTP Server(s)",
|
"label": "NTP Server(s)",
|
||||||
"name": "ntp_server",
|
"name": "ntp_server",
|
||||||
"override_name": "ntp_server_override",
|
"override_name": "ntp_server_override",
|
||||||
"validate": "ip_in_subnet",
|
"validate": "VALIDATION_ADDRESS",
|
||||||
"hint": "NTP server(s) advertised to clients via DHCP."
|
"hint": "NTP server(s) advertised to clients via DHCP."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -239,7 +239,7 @@
|
||||||
"label": "Domain",
|
"label": "Domain",
|
||||||
"name": "dhcp_domain",
|
"name": "dhcp_domain",
|
||||||
"input_type": "text",
|
"input_type": "text",
|
||||||
"validate": "networkname",
|
"validate": "VALIDATION_NETWORK_NAME",
|
||||||
"value": "lan",
|
"value": "lan",
|
||||||
"hint": "Local domain name advertised to clients via DHCP (e.g. lan, home.arpa, corp). Avoid \"local\" per RFC 6762."
|
"hint": "Local domain name advertised to clients via DHCP (e.g. lan, home.arpa, corp). Avoid \"local\" per RFC 6762."
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -131,7 +131,7 @@
|
||||||
"label": "MAC Address",
|
"label": "MAC Address",
|
||||||
"name": "mac",
|
"name": "mac",
|
||||||
"input_type": "text",
|
"input_type": "text",
|
||||||
"validate": "mac",
|
"validate": "VALIDATION_MAC",
|
||||||
"value": "",
|
"value": "",
|
||||||
"hint": "Factory default: none"
|
"hint": "Factory default: none"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -124,7 +124,7 @@
|
||||||
"label": "Ext Port",
|
"label": "Ext Port",
|
||||||
"name": "dest_port",
|
"name": "dest_port",
|
||||||
"input_type": "text",
|
"input_type": "text",
|
||||||
"validate": "port",
|
"validate": "VALIDATION_PORT",
|
||||||
"placeholder": "e.g. 25565"
|
"placeholder": "e.g. 25565"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -132,7 +132,7 @@
|
||||||
"label": "NAT IP",
|
"label": "NAT IP",
|
||||||
"name": "nat_ip",
|
"name": "nat_ip",
|
||||||
"input_type": "text",
|
"input_type": "text",
|
||||||
"validate": "ipv4",
|
"validate": "VALIDATION_IPV4_FORMAT",
|
||||||
"placeholder": "e.g. 192.168.1.50"
|
"placeholder": "e.g. 192.168.1.50"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -140,7 +140,7 @@
|
||||||
"label": "NAT Port",
|
"label": "NAT Port",
|
||||||
"name": "nat_port",
|
"name": "nat_port",
|
||||||
"input_type": "text",
|
"input_type": "text",
|
||||||
"validate": "port",
|
"validate": "VALIDATION_PORT",
|
||||||
"placeholder": "e.g. 25565"
|
"placeholder": "e.g. 25565"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@
|
||||||
{
|
{
|
||||||
"col": "name",
|
"col": "name",
|
||||||
"input_type": "text",
|
"input_type": "text",
|
||||||
"validate": "dashname"
|
"validate": "VALIDATION_DASH_NAME"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"col": "split_tunnel",
|
"col": "split_tunnel",
|
||||||
|
|
@ -141,7 +141,7 @@
|
||||||
"label": "Name",
|
"label": "Name",
|
||||||
"name": "peer_name",
|
"name": "peer_name",
|
||||||
"input_type": "text",
|
"input_type": "text",
|
||||||
"validate": "dashname",
|
"validate": "VALIDATION_DASH_NAME",
|
||||||
"placeholder": "e.g. laptop",
|
"placeholder": "e.g. laptop",
|
||||||
"hint": "Friendly name for this peer."
|
"hint": "Friendly name for this peer."
|
||||||
},
|
},
|
||||||
|
|
@ -157,7 +157,7 @@
|
||||||
"label": "Assigned IP",
|
"label": "Assigned IP",
|
||||||
"name": "peer_ip",
|
"name": "peer_ip",
|
||||||
"input_type": "text",
|
"input_type": "text",
|
||||||
"validate": "ipv4",
|
"validate": "VALIDATION_IPV4_FORMAT",
|
||||||
"placeholder": "e.g. 192.168.40.2",
|
"placeholder": "e.g. 192.168.40.2",
|
||||||
"hint": "Static IP assigned to this peer within the VPN subnet."
|
"hint": "Static IP assigned to this peer within the VPN subnet."
|
||||||
},
|
},
|
||||||
|
|
@ -219,7 +219,7 @@
|
||||||
"label": "Server Endpoint",
|
"label": "Server Endpoint",
|
||||||
"name": "vpn_server_endpoint",
|
"name": "vpn_server_endpoint",
|
||||||
"input_type": "text",
|
"input_type": "text",
|
||||||
"validate": "endpoint",
|
"validate": "VALIDATION_ENDPOINT",
|
||||||
"value": "%VPN_SERVER_ENDPOINT%",
|
"value": "%VPN_SERVER_ENDPOINT%",
|
||||||
"placeholder": "e.g. vpn.example.com",
|
"placeholder": "e.g. vpn.example.com",
|
||||||
"hint": "Publicly reachable hostname or IP of this server, embedded in client config files."
|
"hint": "Publicly reachable hostname or IP of this server, embedded in client config files."
|
||||||
|
|
@ -229,7 +229,7 @@
|
||||||
"label": "Domain",
|
"label": "Domain",
|
||||||
"name": "vpn_domain",
|
"name": "vpn_domain",
|
||||||
"input_type": "text",
|
"input_type": "text",
|
||||||
"validate": "dashname",
|
"validate": "VALIDATION_DASH_NAME",
|
||||||
"value": "%VPN_DOMAIN%",
|
"value": "%VPN_DOMAIN%",
|
||||||
"placeholder": "e.g. local",
|
"placeholder": "e.g. local",
|
||||||
"hint": "DNS search domain pushed to VPN clients."
|
"hint": "DNS search domain pushed to VPN clients."
|
||||||
|
|
@ -239,7 +239,7 @@
|
||||||
"label": "DNS Override",
|
"label": "DNS Override",
|
||||||
"name": "vpn_dns_server",
|
"name": "vpn_dns_server",
|
||||||
"input_type": "text",
|
"input_type": "text",
|
||||||
"validate": "ipv4",
|
"validate": "VALIDATION_IPV4_FORMAT",
|
||||||
"value": "%VPN_DNS_SERVER%",
|
"value": "%VPN_DNS_SERVER%",
|
||||||
"placeholder": "Leave blank to use gateway IP (%VPN_GATEWAY%)",
|
"placeholder": "Leave blank to use gateway IP (%VPN_GATEWAY%)",
|
||||||
"hint": "Explicit DNS server pushed to peers. Defaults to the gateway IP."
|
"hint": "Explicit DNS server pushed to peers. Defaults to the gateway IP."
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ from factory import LEVEL_RANK, e, client_level, passes, build_items, build_snap
|
||||||
PAGES_DIR = os.path.join(APP_DIR, 'pages')
|
PAGES_DIR = os.path.join(APP_DIR, 'pages')
|
||||||
NAVBAR_FILE = os.path.join(APP_DIR, 'navbar.json')
|
NAVBAR_FILE = os.path.join(APP_DIR, 'navbar.json')
|
||||||
CSS_FILE = os.path.join(DATA_DIR, 'styles.css')
|
CSS_FILE = os.path.join(DATA_DIR, 'styles.css')
|
||||||
VALIDATION_FILE = os.path.join(DATA_DIR, 'validation.js')
|
|
||||||
COMMON_JS_FILE = os.path.join(DATA_DIR, 'common.js')
|
COMMON_JS_FILE = os.path.join(DATA_DIR, 'common.js')
|
||||||
BLOCKLISTS_DIR = os.path.join(CONFIGS_DIR, 'blocklists')
|
BLOCKLISTS_DIR = os.path.join(CONFIGS_DIR, 'blocklists')
|
||||||
HEALTH_FILE = os.path.join(CONFIGS_DIR, '.health')
|
HEALTH_FILE = os.path.join(CONFIGS_DIR, '.health')
|
||||||
|
|
@ -1076,11 +1075,7 @@ def build_nav_item(item, active_view, level, in_dropdown=False, inherited_req=No
|
||||||
# Inline JavaScript =================================================
|
# Inline JavaScript =================================================
|
||||||
|
|
||||||
def _inline_js(page_name=None):
|
def _inline_js(page_name=None):
|
||||||
try:
|
big_validate_js = factory.build_big_validate()
|
||||||
with open(VALIDATION_FILE) as f:
|
|
||||||
val_js = f.read()
|
|
||||||
except Exception:
|
|
||||||
val_js = ''
|
|
||||||
try:
|
try:
|
||||||
with open(COMMON_JS_FILE) as f:
|
with open(COMMON_JS_FILE) as f:
|
||||||
app_js = f.read()
|
app_js = f.read()
|
||||||
|
|
@ -1094,7 +1089,7 @@ def _inline_js(page_name=None):
|
||||||
page_js = f.read()
|
page_js = f.read()
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
return val_js + '\n' + app_js + ('\n' + page_js if page_js else '')
|
return big_validate_js + '\n' + app_js + ('\n' + page_js if page_js else '')
|
||||||
|
|
||||||
|
|
||||||
# Routes ============================================================
|
# Routes ============================================================
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue