mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-13 20:08:05 +08:00
393 lines
9.8 KiB
JavaScript
393 lines
9.8 KiB
JavaScript
/*
|
|
* SPDX-License-Identifier: GPL-2.0-only
|
|
*
|
|
* Copyright (C) 2022-2025 ImmortalWrt.org
|
|
*/
|
|
|
|
'use strict';
|
|
'require dom';
|
|
'require form';
|
|
'require fs';
|
|
'require poll';
|
|
'require rpc';
|
|
'require uci';
|
|
'require ui';
|
|
'require view';
|
|
|
|
'require homeproxy as hp';
|
|
|
|
/* Thanks to luci-app-aria2 */
|
|
const css = ' \
|
|
#log_textarea { \
|
|
padding: 10px; \
|
|
text-align: left; \
|
|
} \
|
|
#log_textarea pre { \
|
|
padding: .5rem; \
|
|
word-break: break-all; \
|
|
margin: 0; \
|
|
} \
|
|
.description { \
|
|
background-color: #33ccff; \
|
|
} \
|
|
.homeproxy-connect-grid { \
|
|
display: grid; \
|
|
grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));\
|
|
gap: 12px; \
|
|
} \
|
|
.homeproxy-connect-card { \
|
|
display: grid; \
|
|
grid-template-columns: 44px 1fr; \
|
|
gap: 10px; \
|
|
align-items: center; \
|
|
min-height: 64px; \
|
|
padding: 8px 10px; \
|
|
border-radius: 6px; \
|
|
background: #fff; \
|
|
box-shadow: 0 1px 4px rgba(0, 0, 0, .08);\
|
|
} \
|
|
.homeproxy-connect-icon { \
|
|
display: flex; \
|
|
align-items: center; \
|
|
justify-content: center; \
|
|
width: 38px; \
|
|
height: 38px; \
|
|
border-radius: 50%; \
|
|
color: #fff; \
|
|
font-weight: 700; \
|
|
font-size: 15px; \
|
|
} \
|
|
.homeproxy-connect-info { \
|
|
min-height: 42px; \
|
|
padding: 7px 12px; \
|
|
border-radius: 8px; \
|
|
background: #e9edf2; \
|
|
} \
|
|
.homeproxy-connect-title { \
|
|
color: #7d8aa0; \
|
|
font-weight: 700; \
|
|
font-size: 13px; \
|
|
line-height: 1.2; \
|
|
} \
|
|
.homeproxy-connect-result { \
|
|
margin-top: 2px; \
|
|
color: #ff4d35; \
|
|
font-weight: 700; \
|
|
font-size: 13px; \
|
|
cursor: pointer; \
|
|
line-height: 1.2; \
|
|
}';
|
|
|
|
const hp_dir = '/var/run/homeproxy';
|
|
|
|
function getConnStat(o, site) {
|
|
const callConnStat = rpc.declare({
|
|
object: 'luci.homeproxy',
|
|
method: 'connection_check',
|
|
params: ['site'],
|
|
expect: { '': {} }
|
|
});
|
|
|
|
o.default = E('div', { 'style': 'cbi-value-field' }, [
|
|
E('button', {
|
|
'class': 'btn cbi-button cbi-button-action',
|
|
'click': ui.createHandlerFn(this, () => {
|
|
return L.resolveDefault(callConnStat(site), {}).then((ret) => {
|
|
let ele = o.default.firstElementChild.nextElementSibling;
|
|
if (ret.result) {
|
|
ele.style.setProperty('color', 'green');
|
|
ele.innerHTML = _('passed');
|
|
} else {
|
|
ele.style.setProperty('color', 'red');
|
|
ele.innerHTML = _('failed');
|
|
}
|
|
});
|
|
})
|
|
}, [ _('Check') ]),
|
|
' ',
|
|
E('strong', { 'style': 'color:gray' }, _('unchecked')),
|
|
]);
|
|
}
|
|
|
|
function renderConnCards() {
|
|
const callConnStat = rpc.declare({
|
|
object: 'luci.homeproxy',
|
|
method: 'connection_check',
|
|
params: ['site'],
|
|
expect: { '': {} }
|
|
});
|
|
|
|
const items = [
|
|
{ site: 'baidu', title: _('Baidu Connection'), icon: 'B', color: '#ff7a00' },
|
|
{ site: 'google', title: _('Google Connection'), icon: 'G', color: '#4285f4' },
|
|
{ site: 'github', title: _('GitHub Connection'), icon: 'GH', color: '#7b3bb3' },
|
|
{ site: 'youtube', title: _('YouTube Connection'), icon: 'YT', color: '#ff0033' }
|
|
];
|
|
|
|
return E('div', { 'class': 'homeproxy-connect-grid' }, items.map((item) => {
|
|
let result = E('div', {
|
|
'class': 'homeproxy-connect-result',
|
|
'click': ui.createHandlerFn(this, () => {
|
|
result.textContent = _('Checking...');
|
|
result.style.setProperty('color', '#f0ad4e');
|
|
|
|
return L.resolveDefault(callConnStat(item.site), {}).then((ret) => {
|
|
if (ret.result && ret.latency != null) {
|
|
result.style.setProperty('color', 'green');
|
|
result.textContent = _('%s ms').format(ret.latency);
|
|
} else {
|
|
result.style.setProperty('color', '#ff4d35');
|
|
result.textContent = _('failed');
|
|
}
|
|
});
|
|
})
|
|
}, [ _('Click to check') ]);
|
|
|
|
return E('div', { 'class': 'homeproxy-connect-card' }, [
|
|
E('div', {
|
|
'class': 'homeproxy-connect-icon',
|
|
'style': 'background:%s'.format(item.color)
|
|
}, [ item.icon ]),
|
|
E('div', { 'class': 'homeproxy-connect-info' }, [
|
|
E('div', { 'class': 'homeproxy-connect-title' }, [ item.title ]),
|
|
result
|
|
])
|
|
]);
|
|
}));
|
|
}
|
|
|
|
function getResVersion(o, type) {
|
|
const callResVersion = rpc.declare({
|
|
object: 'luci.homeproxy',
|
|
method: 'resources_get_version',
|
|
params: ['type'],
|
|
expect: { '': {} }
|
|
});
|
|
|
|
const callResUpdate = rpc.declare({
|
|
object: 'luci.homeproxy',
|
|
method: 'resources_update',
|
|
params: ['type'],
|
|
expect: { '': {} }
|
|
});
|
|
|
|
return L.resolveDefault(callResVersion(type), {}).then((res) => {
|
|
let spanTemp = E('div', { 'style': 'cbi-value-field' }, [
|
|
E('button', {
|
|
'class': 'btn cbi-button cbi-button-action',
|
|
'click': ui.createHandlerFn(this, () => {
|
|
return L.resolveDefault(callResUpdate(type), {}).then((res) => {
|
|
switch (res.status) {
|
|
case 0:
|
|
o.description = _('Successfully updated.');
|
|
break;
|
|
case 1:
|
|
o.description = _('Update failed.');
|
|
break;
|
|
case 2:
|
|
o.description = _('Already in updating.');
|
|
break;
|
|
case 3:
|
|
o.description = _('Already at the latest version.');
|
|
break;
|
|
default:
|
|
o.description = _('Unknown error.');
|
|
break;
|
|
}
|
|
|
|
return o.map.reset();
|
|
});
|
|
})
|
|
}, [ _('Check update') ]),
|
|
' ',
|
|
E('strong', { 'style': (res.error ? 'color:red' : 'color:green') },
|
|
[ res.error ? 'not found' : res.version ]
|
|
),
|
|
]);
|
|
|
|
o.default = spanTemp;
|
|
});
|
|
}
|
|
|
|
function getRuntimeLog(o, name, _option_index, section_id, _in_table) {
|
|
const filename = o.option.split('_')[1];
|
|
|
|
let section, log_level_el;
|
|
switch (filename) {
|
|
case 'homeproxy':
|
|
section = null;
|
|
break;
|
|
case 'sing-box-c':
|
|
section = 'config';
|
|
break;
|
|
case 'sing-box-s':
|
|
section = 'server';
|
|
break;
|
|
}
|
|
|
|
if (section) {
|
|
const selected = uci.get('homeproxy', section, 'log_level') || 'warn';
|
|
const choices = {
|
|
trace: _('Trace'),
|
|
debug: _('Debug'),
|
|
info: _('Info'),
|
|
warn: _('Warn'),
|
|
error: _('Error'),
|
|
fatal: _('Fatal'),
|
|
panic: _('Panic')
|
|
};
|
|
|
|
log_level_el = E('select', {
|
|
'id': o.cbid(section_id),
|
|
'class': 'cbi-input-select',
|
|
'style': 'margin-left: 4px; width: 6em;',
|
|
'change': ui.createHandlerFn(this, (ev) => {
|
|
uci.set('homeproxy', section, 'log_level', ev.target.value);
|
|
return o.map.save(null, true).then(() => {
|
|
ui.changes.apply(true);
|
|
});
|
|
})
|
|
});
|
|
|
|
Object.keys(choices).forEach((v) => {
|
|
log_level_el.appendChild(E('option', {
|
|
'value': v,
|
|
'selected': (v === selected) ? '' : null
|
|
}, [ choices[v] ]));
|
|
});
|
|
}
|
|
|
|
const callLogClean = rpc.declare({
|
|
object: 'luci.homeproxy',
|
|
method: 'log_clean',
|
|
params: ['type'],
|
|
expect: { '': {} }
|
|
});
|
|
|
|
const log_textarea = E('div', { 'id': 'log_textarea' },
|
|
E('img', {
|
|
'src': L.resource('icons/loading.svg'),
|
|
'alt': _('Loading'),
|
|
'style': 'vertical-align:middle'
|
|
}, _('Collecting data...'))
|
|
);
|
|
|
|
let log;
|
|
poll.add(L.bind(() => {
|
|
return fs.read_direct(String.format('%s/%s.log', hp_dir, filename), 'text')
|
|
.then((res) => {
|
|
log = E('pre', { 'wrap': 'pre' }, [
|
|
res.trim() || _('Log is empty.')
|
|
]);
|
|
|
|
dom.content(log_textarea, log);
|
|
}).catch((err) => {
|
|
if (err.toString().includes('NotFoundError'))
|
|
log = E('pre', { 'wrap': 'pre' }, [
|
|
_('Log file does not exist.')
|
|
]);
|
|
else
|
|
log = E('pre', { 'wrap': 'pre' }, [
|
|
_('Unknown error: %s').format(err)
|
|
]);
|
|
|
|
dom.content(log_textarea, log);
|
|
});
|
|
}));
|
|
|
|
return E([
|
|
E('style', [ css ]),
|
|
E('div', {'class': 'cbi-map'}, [
|
|
E('h3', {'name': 'content', 'style': 'align-items: center; display: flex;'}, [
|
|
_('%s log').format(name),
|
|
log_level_el || '',
|
|
E('button', {
|
|
'class': 'btn cbi-button cbi-button-action',
|
|
'style': 'margin-left: 4px;',
|
|
'click': ui.createHandlerFn(this, () => {
|
|
return L.resolveDefault(callLogClean(filename), {});
|
|
})
|
|
}, [ _('Clean log') ])
|
|
]),
|
|
E('div', {'class': 'cbi-section'}, [
|
|
log_textarea,
|
|
E('div', {'style': 'text-align:right'},
|
|
E('small', {}, _('Refresh every %s seconds.').format(L.env.pollinterval))
|
|
)
|
|
])
|
|
])
|
|
]);
|
|
}
|
|
|
|
return view.extend({
|
|
render() {
|
|
let m, s, o;
|
|
|
|
hp.installCloseButtonText();
|
|
|
|
m = new form.Map('homeproxy');
|
|
|
|
s = m.section(form.NamedSection, 'config', 'homeproxy', _('Connection check'));
|
|
s.anonymous = true;
|
|
|
|
o = s.option(form.DummyValue, '_connection_check');
|
|
o.rawhtml = true;
|
|
o.render = renderConnCards;
|
|
|
|
s = m.section(form.NamedSection, 'config', 'homeproxy', _('Resources management'));
|
|
s.anonymous = true;
|
|
|
|
o = s.option(form.DummyValue, '_china_ip4_version', _('China IPv4 list version'));
|
|
o.cfgvalue = L.bind(getResVersion, this, o, 'china_ip4');
|
|
o.rawhtml = true;
|
|
|
|
o = s.option(form.DummyValue, '_china_ip6_version', _('China IPv6 list version'));
|
|
o.cfgvalue = L.bind(getResVersion, this, o, 'china_ip6');
|
|
o.rawhtml = true;
|
|
|
|
o = s.option(form.DummyValue, '_china_list_version', _('China list version'));
|
|
o.cfgvalue = L.bind(getResVersion, this, o, 'china_list');
|
|
o.rawhtml = true;
|
|
|
|
o = s.option(form.DummyValue, '_gfw_list_version', _('GFW list version'));
|
|
o.cfgvalue = L.bind(getResVersion, this, o, 'gfw_list');
|
|
o.rawhtml = true;
|
|
|
|
o = s.option(form.Value, 'github_token', _('GitHub token'));
|
|
o.password = true;
|
|
o.renderWidget = function() {
|
|
let node = form.Value.prototype.renderWidget.apply(this, arguments);
|
|
|
|
(node.querySelector('.control-group') || node).appendChild(E('button', {
|
|
'class': 'cbi-button cbi-button-apply',
|
|
'title': _('Save'),
|
|
'click': ui.createHandlerFn(this, () => {
|
|
return this.map.save(null, true).then(() => {
|
|
ui.changes.apply(true);
|
|
});
|
|
}, this.option)
|
|
}, [ _('Save') ]));
|
|
|
|
return node;
|
|
}
|
|
|
|
s = m.section(form.NamedSection, 'config', 'homeproxy');
|
|
s.anonymous = true;
|
|
|
|
o = s.option(form.DummyValue, '_homeproxy_logview');
|
|
o.render = L.bind(getRuntimeLog, this, o, _('HomeProxy'));
|
|
|
|
o = s.option(form.DummyValue, '_sing-box-c_logview');
|
|
o.render = L.bind(getRuntimeLog, this, o, _('sing-box client'));
|
|
|
|
o = s.option(form.DummyValue, '_sing-box-s_logview');
|
|
o.render = L.bind(getRuntimeLog, this, o, _('sing-box server'));
|
|
|
|
return m.render();
|
|
},
|
|
|
|
handleSaveApply: null,
|
|
handleSave: null,
|
|
handleReset: null
|
|
});
|