'use strict'; 'require form'; 'require fs'; 'require rpc'; 'require uci'; 'require ui'; 'require view'; const RESOLV_FILE = '/tmp/resolv.conf.d/resolv.conf.auto'; const LOGERR_CMD = "logread -e 'unbound.*error.*ssl library'"; const HELP_URL = 'https://github.com/openwrt/packages/blob/master/net/unbound/files/README.md'; let section_enabled = false; let daemon_enabled = false; const callRcInit = rpc.declare({ object: 'rc', method: 'init', params: [ 'name', 'action' ], expect: { result: false } }); return view.extend({ load() { return Promise.all([ uci.load('unbound'), fs.read(RESOLV_FILE).catch(() => ''), fs.exec(LOGERR_CMD).catch(() => ''), ]).then(([_, resolv, logerr]) => ({ resolvContent: resolv || '', logerr: logerr ? logerr.trim().slice(-250) : null, })); }, render({resolvContent, logerr}) { const m = new form.Map('unbound'); daemon_enabled = uci.get('unbound', 'ub_main', 'enabled'); if (logerr) { ui.addTimeLimitedNotification(null, E('p', _('Note: SSL/TLS library is missing an API. Please review syslog. >> logread ... ') + logerr), 7000, 'warning'); } const s = m.section(form.TableSection, 'zone', _('Zones'), _('Organize directed forward, stub, and authoritative zones' + ' (help).'.format(HELP_URL))); s.anonymous = false; s.addremove = true; s.sortable = true; s.extedit = L.url(`admin/services/unbound/zone-details/%s`); const ztype = s.option(form.DummyValue, 'zone_type', _('Type')); ztype.rawhtml = true; ztype.cfgvalue = function(section_id) { const type = uci.get('unbound', section_id, 'zone_type'); const tls = uci.get('unbound', section_id, 'tls_upstream'); if (type?.includes('forward')) return tls === '1' ? _('Forward TLS') : _('Forward'); if (type?.includes('stub')) return _('Recurse'); if (type?.includes('auth')) return _('AXFR'); return _('Undefined'); }; const zones = s.option(form.DummyValue, 'zone_name', _('Zones')); zones.rawhtml = true; zones.cfgvalue = function(section_id) { const type = uci.get('unbound', section_id, 'zone_type'); const raw = uci.get('unbound', section_id, 'zone_name'); const list = Array.isArray(raw) ? raw : raw?.split(/\s+/) || []; if (list.length === 0) return '(empty)'; const html = list.map(name => `${name === '.' ? _('(root)') : name}` ).join(', '); if (type?.includes('forward')) return _('accept upstream results for ') + html; if (type?.includes('stub')) return _('select recursion for ') + html; if (type?.includes('auth')) return _('prefetch zone files for ') + html; return _('unknown action for ') + html; }; const servers = s.option(form.DummyValue, 'server', _('Servers')); servers.rawhtml = true; servers.cfgvalue = function(section_id) { const servers = uci.get('unbound', section_id, 'server'); const url = uci.get('unbound', section_id, 'url_dir'); const type = uci.get('unbound', section_id, 'zone_type'); const tls = uci.get('unbound', section_id, 'tls_upstream'); const tlsName = uci.get('unbound', section_id, 'tls_index'); const useResolv = uci.get('unbound', section_id, 'resolv_conf'); const list = Array.isArray(servers) ? servers : servers?.split(/\s+/) || []; let out = list.map(s => `${s}`).join(', '); if (out) out = _('use nameservers ') + out; if (tls === '1' && tlsName) out += _(' with default certificate for %s'.format(tlsName)); if (type?.includes('auth') && url) out += _(', and try %s'.format(url)); else if (!type?.includes('auth') && url) out += _('download from %s'.format(url)); if (type?.includes('forward') && useResolv === '1') { const lines = resolvContent.split('\n'); const resolvNameservers = []; for (let i = 0; i < lines.length; i++) { if (lines[i].match(/^\s*nameserver\b/)) { resolvNameservers.push(`${lines[i].match(/^\s*nameserver\b(.*)$/)[1]}`); } } if (!out && resolvNameservers.length > 0) out = _('use %s nameservers '.format(RESOLV_FILE)) + resolvNameservers.join(', '); else if (resolvNameservers.length > 0) out += _(', and %s entries '.format(RESOLV_FILE)) + resolvNameservers.join(', '); } return out || '(empty)'; }; s.option(form.Flag, 'fallback', _('Fallback')).rmempty = false; const ena = s.option(form.Flag, 'enabled', _('Enable')) ena.rmempty = false; ena.load = (section_id) => { let _temp = uci.get('unbound', section_id, 'enabled'); if (_temp === '1') section_enabled = true; } return m.render(); }, handleSaveApply(ev, mode) { const cmd = daemon_enabled ? 'reload' : 'stop'; const Fn = L.bind(() => { if (section_enabled) callRcInit('unbound', cmd); document.removeEventListener('uci-applied', Fn); }); document.addEventListener('uci-applied', Fn); this.super('handleSaveApply', [ev, mode]); } });