mirror of
https://github.com/itdoginfo/podkop.git
synced 2025-12-09 04:56:51 +03:00
fix: run prettier for luci app js assets
This commit is contained in:
@@ -5,235 +5,358 @@
|
|||||||
'require view.podkop.main as main';
|
'require view.podkop.main as main';
|
||||||
|
|
||||||
function createAdditionalSection(mainSection) {
|
function createAdditionalSection(mainSection) {
|
||||||
let o = mainSection.tab('additional', _('Additional Settings'));
|
let o = mainSection.tab('additional', _('Additional Settings'));
|
||||||
|
|
||||||
o = mainSection.taboption('additional', form.Flag, 'yacd', _('Yacd enable'), `<a href="${main.getBaseUrl()}:9090/ui" target="_blank">${main.getBaseUrl()}:9090/ui</a>`);
|
o = mainSection.taboption(
|
||||||
o.default = '0';
|
'additional',
|
||||||
o.rmempty = false;
|
form.Flag,
|
||||||
o.ucisection = 'main';
|
'yacd',
|
||||||
|
_('Yacd enable'),
|
||||||
|
`<a href="${main.getBaseUrl()}:9090/ui" target="_blank">${main.getBaseUrl()}:9090/ui</a>`,
|
||||||
|
);
|
||||||
|
o.default = '0';
|
||||||
|
o.rmempty = false;
|
||||||
|
o.ucisection = 'main';
|
||||||
|
|
||||||
o = mainSection.taboption('additional', form.Flag, 'exclude_ntp', _('Exclude NTP'), _('Allows you to exclude NTP protocol traffic from the tunnel'));
|
o = mainSection.taboption(
|
||||||
o.default = '0';
|
'additional',
|
||||||
o.rmempty = false;
|
form.Flag,
|
||||||
o.ucisection = 'main';
|
'exclude_ntp',
|
||||||
|
_('Exclude NTP'),
|
||||||
|
_('Allows you to exclude NTP protocol traffic from the tunnel'),
|
||||||
|
);
|
||||||
|
o.default = '0';
|
||||||
|
o.rmempty = false;
|
||||||
|
o.ucisection = 'main';
|
||||||
|
|
||||||
o = mainSection.taboption('additional', form.Flag, 'quic_disable', _('QUIC disable'), _('For issues with the video stream'));
|
o = mainSection.taboption(
|
||||||
o.default = '0';
|
'additional',
|
||||||
o.rmempty = false;
|
form.Flag,
|
||||||
o.ucisection = 'main';
|
'quic_disable',
|
||||||
|
_('QUIC disable'),
|
||||||
|
_('For issues with the video stream'),
|
||||||
|
);
|
||||||
|
o.default = '0';
|
||||||
|
o.rmempty = false;
|
||||||
|
o.ucisection = 'main';
|
||||||
|
|
||||||
o = mainSection.taboption('additional', form.ListValue, 'update_interval', _('List Update Frequency'), _('Select how often the lists will be updated'));
|
o = mainSection.taboption(
|
||||||
Object.entries(main.UPDATE_INTERVAL_OPTIONS).forEach(([key, label]) => {
|
'additional',
|
||||||
o.value(key, _(label));
|
form.ListValue,
|
||||||
});
|
'update_interval',
|
||||||
o.default = '1d';
|
_('List Update Frequency'),
|
||||||
o.rmempty = false;
|
_('Select how often the lists will be updated'),
|
||||||
o.ucisection = 'main';
|
);
|
||||||
|
Object.entries(main.UPDATE_INTERVAL_OPTIONS).forEach(([key, label]) => {
|
||||||
|
o.value(key, _(label));
|
||||||
|
});
|
||||||
|
o.default = '1d';
|
||||||
|
o.rmempty = false;
|
||||||
|
o.ucisection = 'main';
|
||||||
|
|
||||||
o = mainSection.taboption('additional', form.ListValue, 'dns_type', _('DNS Protocol Type'), _('Select DNS protocol to use'));
|
o = mainSection.taboption(
|
||||||
o.value('doh', _('DNS over HTTPS (DoH)'));
|
'additional',
|
||||||
o.value('dot', _('DNS over TLS (DoT)'));
|
form.ListValue,
|
||||||
o.value('udp', _('UDP (Unprotected DNS)'));
|
'dns_type',
|
||||||
o.default = 'udp';
|
_('DNS Protocol Type'),
|
||||||
o.rmempty = false;
|
_('Select DNS protocol to use'),
|
||||||
o.ucisection = 'main';
|
);
|
||||||
|
o.value('doh', _('DNS over HTTPS (DoH)'));
|
||||||
|
o.value('dot', _('DNS over TLS (DoT)'));
|
||||||
|
o.value('udp', _('UDP (Unprotected DNS)'));
|
||||||
|
o.default = 'udp';
|
||||||
|
o.rmempty = false;
|
||||||
|
o.ucisection = 'main';
|
||||||
|
|
||||||
o = mainSection.taboption('additional', form.Value, 'dns_server', _('DNS Server'), _('Select or enter DNS server address'));
|
o = mainSection.taboption(
|
||||||
Object.entries(main.DNS_SERVER_OPTIONS).forEach(([key, label]) => {
|
'additional',
|
||||||
o.value(key, _(label));
|
form.Value,
|
||||||
});
|
'dns_server',
|
||||||
o.default = '8.8.8.8';
|
_('DNS Server'),
|
||||||
o.rmempty = false;
|
_('Select or enter DNS server address'),
|
||||||
o.ucisection = 'main';
|
);
|
||||||
o.validate = function (section_id, value) {
|
Object.entries(main.DNS_SERVER_OPTIONS).forEach(([key, label]) => {
|
||||||
const validation = main.validateDNS(value);
|
o.value(key, _(label));
|
||||||
|
});
|
||||||
|
o.default = '8.8.8.8';
|
||||||
|
o.rmempty = false;
|
||||||
|
o.ucisection = 'main';
|
||||||
|
o.validate = function (section_id, value) {
|
||||||
|
const validation = main.validateDNS(value);
|
||||||
|
|
||||||
if (validation.valid) {
|
if (validation.valid) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _(validation.message);
|
return _(validation.message);
|
||||||
};
|
};
|
||||||
|
|
||||||
o = mainSection.taboption('additional', form.Value, 'bootstrap_dns_server', _('Bootstrap DNS server'), _('The DNS server used to look up the IP address of an upstream DNS server'));
|
o = mainSection.taboption(
|
||||||
Object.entries(main.BOOTSTRAP_DNS_SERVER_OPTIONS).forEach(([key, label]) => {
|
'additional',
|
||||||
o.value(key, _(label));
|
form.Value,
|
||||||
});
|
'bootstrap_dns_server',
|
||||||
o.default = '77.88.8.8';
|
_('Bootstrap DNS server'),
|
||||||
o.rmempty = false;
|
_(
|
||||||
o.ucisection = 'main';
|
'The DNS server used to look up the IP address of an upstream DNS server',
|
||||||
o.validate = function (section_id, value) {
|
),
|
||||||
const validation = main.validateDNS(value);
|
);
|
||||||
|
Object.entries(main.BOOTSTRAP_DNS_SERVER_OPTIONS).forEach(([key, label]) => {
|
||||||
|
o.value(key, _(label));
|
||||||
|
});
|
||||||
|
o.default = '77.88.8.8';
|
||||||
|
o.rmempty = false;
|
||||||
|
o.ucisection = 'main';
|
||||||
|
o.validate = function (section_id, value) {
|
||||||
|
const validation = main.validateDNS(value);
|
||||||
|
|
||||||
if (validation.valid) {
|
if (validation.valid) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _(validation.message);
|
return _(validation.message);
|
||||||
};
|
};
|
||||||
|
|
||||||
o = mainSection.taboption('additional', form.Value, 'dns_rewrite_ttl', _('DNS Rewrite TTL'), _('Time in seconds for DNS record caching (default: 60)'));
|
o = mainSection.taboption(
|
||||||
o.default = '60';
|
'additional',
|
||||||
o.rmempty = false;
|
form.Value,
|
||||||
o.ucisection = 'main';
|
'dns_rewrite_ttl',
|
||||||
o.validate = function (section_id, value) {
|
_('DNS Rewrite TTL'),
|
||||||
if (!value) {
|
_('Time in seconds for DNS record caching (default: 60)'),
|
||||||
return _('TTL value cannot be empty');
|
);
|
||||||
}
|
o.default = '60';
|
||||||
|
o.rmempty = false;
|
||||||
|
o.ucisection = 'main';
|
||||||
|
o.validate = function (section_id, value) {
|
||||||
|
if (!value) {
|
||||||
|
return _('TTL value cannot be empty');
|
||||||
|
}
|
||||||
|
|
||||||
const ttl = parseInt(value);
|
const ttl = parseInt(value);
|
||||||
if (isNaN(ttl) || ttl < 0) {
|
if (isNaN(ttl) || ttl < 0) {
|
||||||
return _('TTL must be a positive number');
|
return _('TTL must be a positive number');
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
o = mainSection.taboption('additional', form.ListValue, 'config_path', _('Config File Path'), _('Select path for sing-box config file. Change this ONLY if you know what you are doing'));
|
o = mainSection.taboption(
|
||||||
o.value('/etc/sing-box/config.json', 'Flash (/etc/sing-box/config.json)');
|
'additional',
|
||||||
o.value('/tmp/sing-box/config.json', 'RAM (/tmp/sing-box/config.json)');
|
form.ListValue,
|
||||||
o.default = '/etc/sing-box/config.json';
|
'config_path',
|
||||||
o.rmempty = false;
|
_('Config File Path'),
|
||||||
o.ucisection = 'main';
|
_(
|
||||||
|
'Select path for sing-box config file. Change this ONLY if you know what you are doing',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
o.value('/etc/sing-box/config.json', 'Flash (/etc/sing-box/config.json)');
|
||||||
|
o.value('/tmp/sing-box/config.json', 'RAM (/tmp/sing-box/config.json)');
|
||||||
|
o.default = '/etc/sing-box/config.json';
|
||||||
|
o.rmempty = false;
|
||||||
|
o.ucisection = 'main';
|
||||||
|
|
||||||
o = mainSection.taboption('additional', form.Value, 'cache_path', _('Cache File Path'), _('Select or enter path for sing-box cache file. Change this ONLY if you know what you are doing'));
|
o = mainSection.taboption(
|
||||||
o.value('/tmp/sing-box/cache.db', 'RAM (/tmp/sing-box/cache.db)');
|
'additional',
|
||||||
o.value('/usr/share/sing-box/cache.db', 'Flash (/usr/share/sing-box/cache.db)');
|
form.Value,
|
||||||
o.default = '/tmp/sing-box/cache.db';
|
'cache_path',
|
||||||
o.rmempty = false;
|
_('Cache File Path'),
|
||||||
o.ucisection = 'main';
|
_(
|
||||||
o.validate = function (section_id, value) {
|
'Select or enter path for sing-box cache file. Change this ONLY if you know what you are doing',
|
||||||
if (!value) {
|
),
|
||||||
return _('Cache file path cannot be empty');
|
);
|
||||||
}
|
o.value('/tmp/sing-box/cache.db', 'RAM (/tmp/sing-box/cache.db)');
|
||||||
|
o.value(
|
||||||
|
'/usr/share/sing-box/cache.db',
|
||||||
|
'Flash (/usr/share/sing-box/cache.db)',
|
||||||
|
);
|
||||||
|
o.default = '/tmp/sing-box/cache.db';
|
||||||
|
o.rmempty = false;
|
||||||
|
o.ucisection = 'main';
|
||||||
|
o.validate = function (section_id, value) {
|
||||||
|
if (!value) {
|
||||||
|
return _('Cache file path cannot be empty');
|
||||||
|
}
|
||||||
|
|
||||||
if (!value.startsWith('/')) {
|
if (!value.startsWith('/')) {
|
||||||
return _('Path must be absolute (start with /)');
|
return _('Path must be absolute (start with /)');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!value.endsWith('cache.db')) {
|
if (!value.endsWith('cache.db')) {
|
||||||
return _('Path must end with cache.db');
|
return _('Path must end with cache.db');
|
||||||
}
|
}
|
||||||
|
|
||||||
const parts = value.split('/').filter(Boolean);
|
const parts = value.split('/').filter(Boolean);
|
||||||
if (parts.length < 2) {
|
if (parts.length < 2) {
|
||||||
return _('Path must contain at least one directory (like /tmp/cache.db)');
|
return _('Path must contain at least one directory (like /tmp/cache.db)');
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
o = mainSection.taboption('additional', widgets.DeviceSelect, 'iface', _('Source Network Interface'), _('Select the network interface from which the traffic will originate'));
|
o = mainSection.taboption(
|
||||||
o.ucisection = 'main';
|
'additional',
|
||||||
o.default = 'br-lan';
|
widgets.DeviceSelect,
|
||||||
o.noaliases = true;
|
'iface',
|
||||||
o.nobridges = false;
|
_('Source Network Interface'),
|
||||||
o.noinactive = false;
|
_('Select the network interface from which the traffic will originate'),
|
||||||
o.multiple = true;
|
);
|
||||||
o.filter = function (section_id, value) {
|
o.ucisection = 'main';
|
||||||
// Block specific interface names from being selectable
|
o.default = 'br-lan';
|
||||||
const blocked = ['wan', 'phy0-ap0', 'phy1-ap0', 'pppoe-wan'];
|
o.noaliases = true;
|
||||||
if (blocked.includes(value)) {
|
o.nobridges = false;
|
||||||
return false;
|
o.noinactive = false;
|
||||||
}
|
o.multiple = true;
|
||||||
|
o.filter = function (section_id, value) {
|
||||||
|
// Block specific interface names from being selectable
|
||||||
|
const blocked = ['wan', 'phy0-ap0', 'phy1-ap0', 'pppoe-wan'];
|
||||||
|
if (blocked.includes(value)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Try to find the device object by its name
|
// Try to find the device object by its name
|
||||||
const device = this.devices.find(dev => dev.getName() === value);
|
const device = this.devices.find((dev) => dev.getName() === value);
|
||||||
|
|
||||||
// If no device is found, allow the value
|
// If no device is found, allow the value
|
||||||
if (!device) {
|
if (!device) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the type of the device
|
// Check the type of the device
|
||||||
const type = device.getType();
|
const type = device.getType();
|
||||||
|
|
||||||
// Consider any Wi-Fi / wireless / wlan device as invalid
|
// Consider any Wi-Fi / wireless / wlan device as invalid
|
||||||
const isWireless =
|
const isWireless =
|
||||||
type === 'wifi' || type === 'wireless' || type.includes('wlan');
|
type === 'wifi' || type === 'wireless' || type.includes('wlan');
|
||||||
|
|
||||||
// Allow only non-wireless devices
|
// Allow only non-wireless devices
|
||||||
return !isWireless;
|
return !isWireless;
|
||||||
};
|
};
|
||||||
|
|
||||||
o = mainSection.taboption('additional', form.Flag, 'mon_restart_ifaces', _('Interface monitoring'), _('Interface monitoring for bad WAN'));
|
o = mainSection.taboption(
|
||||||
o.default = '0';
|
'additional',
|
||||||
o.rmempty = false;
|
form.Flag,
|
||||||
o.ucisection = 'main';
|
'mon_restart_ifaces',
|
||||||
|
_('Interface monitoring'),
|
||||||
|
_('Interface monitoring for bad WAN'),
|
||||||
|
);
|
||||||
|
o.default = '0';
|
||||||
|
o.rmempty = false;
|
||||||
|
o.ucisection = 'main';
|
||||||
|
|
||||||
o = mainSection.taboption('additional', widgets.NetworkSelect, 'restart_ifaces', _('Interface for monitoring'), _('Select the WAN interfaces to be monitored'));
|
o = mainSection.taboption(
|
||||||
o.ucisection = 'main';
|
'additional',
|
||||||
o.depends('mon_restart_ifaces', '1');
|
widgets.NetworkSelect,
|
||||||
o.multiple = true;
|
'restart_ifaces',
|
||||||
o.filter = function (section_id, value) {
|
_('Interface for monitoring'),
|
||||||
// Reject if the value is in the blocked list ['lan', 'loopback']
|
_('Select the WAN interfaces to be monitored'),
|
||||||
if (['lan', 'loopback'].includes(value)) {
|
);
|
||||||
return false;
|
o.ucisection = 'main';
|
||||||
}
|
o.depends('mon_restart_ifaces', '1');
|
||||||
|
o.multiple = true;
|
||||||
|
o.filter = function (section_id, value) {
|
||||||
|
// Reject if the value is in the blocked list ['lan', 'loopback']
|
||||||
|
if (['lan', 'loopback'].includes(value)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Reject if the value starts with '@' (means it's an alias/reference)
|
// Reject if the value starts with '@' (means it's an alias/reference)
|
||||||
if (value.startsWith('@')) {
|
if (value.startsWith('@')) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise allow it
|
// Otherwise allow it
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
o = mainSection.taboption('additional', form.Value, 'procd_reload_delay', _('Interface Monitoring Delay'), _('Delay in milliseconds before reloading podkop after interface UP'));
|
o = mainSection.taboption(
|
||||||
o.ucisection = 'main';
|
'additional',
|
||||||
o.depends('mon_restart_ifaces', '1');
|
form.Value,
|
||||||
o.default = '2000';
|
'procd_reload_delay',
|
||||||
o.rmempty = false;
|
_('Interface Monitoring Delay'),
|
||||||
o.validate = function (section_id, value) {
|
_('Delay in milliseconds before reloading podkop after interface UP'),
|
||||||
if (!value) {
|
);
|
||||||
return _('Delay value cannot be empty');
|
o.ucisection = 'main';
|
||||||
}
|
o.depends('mon_restart_ifaces', '1');
|
||||||
return true;
|
o.default = '2000';
|
||||||
};
|
o.rmempty = false;
|
||||||
|
o.validate = function (section_id, value) {
|
||||||
|
if (!value) {
|
||||||
|
return _('Delay value cannot be empty');
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
o = mainSection.taboption('additional', form.Flag, 'dont_touch_dhcp', _('Dont touch my DHCP!'), _('Podkop will not change the DHCP config'));
|
o = mainSection.taboption(
|
||||||
o.default = '0';
|
'additional',
|
||||||
o.rmempty = false;
|
form.Flag,
|
||||||
o.ucisection = 'main';
|
'dont_touch_dhcp',
|
||||||
|
_('Dont touch my DHCP!'),
|
||||||
|
_('Podkop will not change the DHCP config'),
|
||||||
|
);
|
||||||
|
o.default = '0';
|
||||||
|
o.rmempty = false;
|
||||||
|
o.ucisection = 'main';
|
||||||
|
|
||||||
o = mainSection.taboption('additional', form.Flag, 'detour', _('Proxy download of lists'), _('Downloading all lists via main Proxy/VPN'));
|
o = mainSection.taboption(
|
||||||
o.default = '0';
|
'additional',
|
||||||
o.rmempty = false;
|
form.Flag,
|
||||||
o.ucisection = 'main';
|
'detour',
|
||||||
|
_('Proxy download of lists'),
|
||||||
|
_('Downloading all lists via main Proxy/VPN'),
|
||||||
|
);
|
||||||
|
o.default = '0';
|
||||||
|
o.rmempty = false;
|
||||||
|
o.ucisection = 'main';
|
||||||
|
|
||||||
// Extra IPs and exclusions (main section)
|
// Extra IPs and exclusions (main section)
|
||||||
o = mainSection.taboption('basic', form.Flag, 'exclude_from_ip_enabled', _('IP for exclusion'), _('Specify local IP addresses that will never use the configured route'));
|
o = mainSection.taboption(
|
||||||
o.default = '0';
|
'basic',
|
||||||
o.rmempty = false;
|
form.Flag,
|
||||||
o.ucisection = 'main';
|
'exclude_from_ip_enabled',
|
||||||
|
_('IP for exclusion'),
|
||||||
|
_('Specify local IP addresses that will never use the configured route'),
|
||||||
|
);
|
||||||
|
o.default = '0';
|
||||||
|
o.rmempty = false;
|
||||||
|
o.ucisection = 'main';
|
||||||
|
|
||||||
o = mainSection.taboption('basic', form.DynamicList, 'exclude_traffic_ip', _('Local IPs'), _('Enter valid IPv4 addresses'));
|
o = mainSection.taboption(
|
||||||
o.placeholder = 'IP';
|
'basic',
|
||||||
o.depends('exclude_from_ip_enabled', '1');
|
form.DynamicList,
|
||||||
o.rmempty = false;
|
'exclude_traffic_ip',
|
||||||
o.ucisection = 'main';
|
_('Local IPs'),
|
||||||
o.validate = function (section_id, value) {
|
_('Enter valid IPv4 addresses'),
|
||||||
// Optional
|
);
|
||||||
if (!value || value.length === 0) {
|
o.placeholder = 'IP';
|
||||||
return true
|
o.depends('exclude_from_ip_enabled', '1');
|
||||||
}
|
o.rmempty = false;
|
||||||
|
o.ucisection = 'main';
|
||||||
|
o.validate = function (section_id, value) {
|
||||||
|
// Optional
|
||||||
|
if (!value || value.length === 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
const validation = main.validateIPV4(value);
|
const validation = main.validateIPV4(value);
|
||||||
|
|
||||||
if (validation.valid) {
|
if (validation.valid) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _(validation.message)
|
return _(validation.message);
|
||||||
};
|
};
|
||||||
|
|
||||||
o = mainSection.taboption('basic', form.Flag, 'socks5', _('Mixed enable'), _('Browser port: 2080'));
|
o = mainSection.taboption(
|
||||||
o.default = '0';
|
'basic',
|
||||||
o.rmempty = false;
|
form.Flag,
|
||||||
o.ucisection = 'main';
|
'socks5',
|
||||||
|
_('Mixed enable'),
|
||||||
|
_('Browser port: 2080'),
|
||||||
|
);
|
||||||
|
o.default = '0';
|
||||||
|
o.rmempty = false;
|
||||||
|
o.ucisection = 'main';
|
||||||
}
|
}
|
||||||
|
|
||||||
return baseclass.extend({
|
return baseclass.extend({
|
||||||
createAdditionalSection
|
createAdditionalSection,
|
||||||
});
|
});
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -15,138 +15,149 @@ let errorPollTimer = null;
|
|||||||
|
|
||||||
// Helper function to fetch errors from the podkop command
|
// Helper function to fetch errors from the podkop command
|
||||||
async function getPodkopErrors() {
|
async function getPodkopErrors() {
|
||||||
return new Promise(resolve => {
|
return new Promise((resolve) => {
|
||||||
safeExec('/usr/bin/podkop', ['check_logs'], 'P0_PRIORITY', result => {
|
safeExec('/usr/bin/podkop', ['check_logs'], 'P0_PRIORITY', (result) => {
|
||||||
if (!result || !result.stdout) return resolve([]);
|
if (!result || !result.stdout) return resolve([]);
|
||||||
|
|
||||||
const logs = result.stdout.split('\n');
|
const logs = result.stdout.split('\n');
|
||||||
const errors = logs.filter(log =>
|
const errors = logs.filter((log) => log.includes('[critical]'));
|
||||||
log.includes('[critical]')
|
|
||||||
);
|
|
||||||
|
|
||||||
resolve(errors);
|
resolve(errors);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show error notification to the user
|
// Show error notification to the user
|
||||||
function showErrorNotification(error, isMultiple = false) {
|
function showErrorNotification(error, isMultiple = false) {
|
||||||
const notificationContent = E('div', { 'class': 'alert-message error' }, [
|
const notificationContent = E('div', { class: 'alert-message error' }, [
|
||||||
E('pre', { 'class': 'error-log' }, error)
|
E('pre', { class: 'error-log' }, error),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
ui.addNotification(null, notificationContent);
|
ui.addNotification(null, notificationContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function for command execution with prioritization
|
// Helper function for command execution with prioritization
|
||||||
function safeExec(command, args, priority, callback, timeout = main.COMMAND_TIMEOUT) {
|
function safeExec(
|
||||||
// Default to highest priority execution if priority is not provided or invalid
|
command,
|
||||||
let schedulingDelay = main.COMMAND_SCHEDULING.P0_PRIORITY;
|
args,
|
||||||
|
priority,
|
||||||
|
callback,
|
||||||
|
timeout = main.COMMAND_TIMEOUT,
|
||||||
|
) {
|
||||||
|
// Default to highest priority execution if priority is not provided or invalid
|
||||||
|
let schedulingDelay = main.COMMAND_SCHEDULING.P0_PRIORITY;
|
||||||
|
|
||||||
// If priority is a string, try to get the corresponding delay value
|
// If priority is a string, try to get the corresponding delay value
|
||||||
if (typeof priority === 'string' && main.COMMAND_SCHEDULING[priority] !== undefined) {
|
if (
|
||||||
schedulingDelay = main.COMMAND_SCHEDULING[priority];
|
typeof priority === 'string' &&
|
||||||
|
main.COMMAND_SCHEDULING[priority] !== undefined
|
||||||
|
) {
|
||||||
|
schedulingDelay = main.COMMAND_SCHEDULING[priority];
|
||||||
|
}
|
||||||
|
|
||||||
|
const executeCommand = async () => {
|
||||||
|
try {
|
||||||
|
const controller = new AbortController();
|
||||||
|
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
||||||
|
|
||||||
|
const result = await Promise.race([
|
||||||
|
fs.exec(command, args),
|
||||||
|
new Promise((_, reject) => {
|
||||||
|
controller.signal.addEventListener('abort', () => {
|
||||||
|
reject(new Error('Command execution timed out'));
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
|
||||||
|
if (callback && typeof callback === 'function') {
|
||||||
|
callback(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(
|
||||||
|
`Command execution failed or timed out: ${command} ${args.join(' ')}`,
|
||||||
|
);
|
||||||
|
const errorResult = { stdout: '', stderr: error.message, error: error };
|
||||||
|
|
||||||
|
if (callback && typeof callback === 'function') {
|
||||||
|
callback(errorResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
return errorResult;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const executeCommand = async () => {
|
if (callback && typeof callback === 'function') {
|
||||||
try {
|
setTimeout(executeCommand, schedulingDelay);
|
||||||
const controller = new AbortController();
|
return;
|
||||||
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
} else {
|
||||||
|
return executeCommand();
|
||||||
const result = await Promise.race([
|
}
|
||||||
fs.exec(command, args),
|
|
||||||
new Promise((_, reject) => {
|
|
||||||
controller.signal.addEventListener('abort', () => {
|
|
||||||
reject(new Error('Command execution timed out'));
|
|
||||||
});
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
|
|
||||||
clearTimeout(timeoutId);
|
|
||||||
|
|
||||||
if (callback && typeof callback === 'function') {
|
|
||||||
callback(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
} catch (error) {
|
|
||||||
console.warn(`Command execution failed or timed out: ${command} ${args.join(' ')}`);
|
|
||||||
const errorResult = { stdout: '', stderr: error.message, error: error };
|
|
||||||
|
|
||||||
if (callback && typeof callback === 'function') {
|
|
||||||
callback(errorResult);
|
|
||||||
}
|
|
||||||
|
|
||||||
return errorResult;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (callback && typeof callback === 'function') {
|
|
||||||
setTimeout(executeCommand, schedulingDelay);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return executeCommand();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for critical errors and show notifications
|
// Check for critical errors and show notifications
|
||||||
async function checkForCriticalErrors() {
|
async function checkForCriticalErrors() {
|
||||||
try {
|
try {
|
||||||
const errors = await getPodkopErrors();
|
const errors = await getPodkopErrors();
|
||||||
|
|
||||||
if (errors && errors.length > 0) {
|
if (errors && errors.length > 0) {
|
||||||
// Filter out errors we've already seen
|
// Filter out errors we've already seen
|
||||||
const newErrors = errors.filter(error => !lastErrorsSet.has(error));
|
const newErrors = errors.filter((error) => !lastErrorsSet.has(error));
|
||||||
|
|
||||||
if (newErrors.length > 0) {
|
if (newErrors.length > 0) {
|
||||||
// On initial check, just store errors without showing notifications
|
// On initial check, just store errors without showing notifications
|
||||||
if (!isInitialCheck) {
|
if (!isInitialCheck) {
|
||||||
// Show each new error as a notification
|
// Show each new error as a notification
|
||||||
newErrors.forEach(error => {
|
newErrors.forEach((error) => {
|
||||||
showErrorNotification(error, newErrors.length > 1);
|
showErrorNotification(error, newErrors.length > 1);
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
// Add new errors to our set of seen errors
|
|
||||||
newErrors.forEach(error => lastErrorsSet.add(error));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// After first check, mark as no longer initial
|
// Add new errors to our set of seen errors
|
||||||
isInitialCheck = false;
|
newErrors.forEach((error) => lastErrorsSet.add(error));
|
||||||
} catch (error) {
|
}
|
||||||
console.error('Error checking for critical messages:', error);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// After first check, mark as no longer initial
|
||||||
|
isInitialCheck = false;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error checking for critical messages:', error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start polling for errors at regular intervals
|
// Start polling for errors at regular intervals
|
||||||
function startErrorPolling() {
|
function startErrorPolling() {
|
||||||
if (errorPollTimer) {
|
if (errorPollTimer) {
|
||||||
clearInterval(errorPollTimer);
|
clearInterval(errorPollTimer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset initial check flag to make sure we show errors
|
// Reset initial check flag to make sure we show errors
|
||||||
isInitialCheck = false;
|
isInitialCheck = false;
|
||||||
|
|
||||||
// Immediately check for errors on start
|
// Immediately check for errors on start
|
||||||
checkForCriticalErrors();
|
checkForCriticalErrors();
|
||||||
|
|
||||||
// Then set up periodic checks
|
// Then set up periodic checks
|
||||||
errorPollTimer = setInterval(checkForCriticalErrors, main.ERROR_POLL_INTERVAL);
|
errorPollTimer = setInterval(
|
||||||
|
checkForCriticalErrors,
|
||||||
|
main.ERROR_POLL_INTERVAL,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop polling for errors
|
// Stop polling for errors
|
||||||
function stopErrorPolling() {
|
function stopErrorPolling() {
|
||||||
if (errorPollTimer) {
|
if (errorPollTimer) {
|
||||||
clearInterval(errorPollTimer);
|
clearInterval(errorPollTimer);
|
||||||
errorPollTimer = null;
|
errorPollTimer = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return baseclass.extend({
|
return baseclass.extend({
|
||||||
startErrorPolling,
|
startErrorPolling,
|
||||||
stopErrorPolling,
|
stopErrorPolling,
|
||||||
checkForCriticalErrors,
|
checkForCriticalErrors,
|
||||||
safeExec
|
safeExec,
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user