Compare commits

...

31 Commits

Author SHA1 Message Date
itdoginfo
98797d93b1 v0.3.29 2025-03-17 13:16:38 +03:00
itdoginfo
66c6e998a2 #38 #46 2025-03-17 13:14:37 +03:00
itdoginfo
3d9f82b571 Merge pull request #65 from itdoginfo/chore/fakeip-method
feat: add diagnostics functionality only in tab
2025-03-14 12:33:52 +03:00
Ivan K
38d082e236 feat: add diagnostics functionality only in tab 2025-03-14 09:50:28 +03:00
itdoginfo
9f5abcae6d v0.3.28 2025-03-13 19:34:52 +03:00
itdoginfo
7836d2c6ec Fix 2025-03-13 19:32:57 +03:00
itdoginfo
f46c934c59 Test 2025-03-13 19:30:17 +03:00
itdoginfo
23ed10d393 Added check version in Makefile 2025-03-13 19:28:31 +03:00
itdoginfo
26488baad3 Merge pull request #64 from itdoginfo/chore/fakeip-method
fix: fix enable/disable functionality to podkop service
2025-03-13 19:05:49 +03:00
Ivan K
c79016e456 feat: add createInitActionButton function to ButtonFactory 2025-03-13 10:35:48 +03:00
Ivan K
884bbfee42 fix: remove unused button creation code 2025-03-13 10:32:34 +03:00
Ivan K
1263b9b1b8 fix: fix enable/disable functionality to podkop service 2025-03-13 10:00:37 +03:00
Ivan K
23203fd7a1 feat: add createSystemButton function to ButtonFactory and flush cache button 2025-03-13 00:10:18 +03:00
itdoginfo
25c887a952 v0.3.27 2025-03-12 17:20:35 +03:00
itdoginfo
e7a3c7adf1 Merge pull request #63 from itdoginfo/chore/fakeip-method
feat: update DNS checks and improve FakeIP status reporting
2025-03-12 17:02:18 +03:00
Ivan K
3e96b9a1af feat: update DNS checks and improve FakeIP status reporting 2025-03-12 16:20:59 +03:00
itdoginfo
251f94cb88 v0.3.26 2025-03-12 14:59:03 +03:00
itdoginfo
44936c698e Merge pull request #62 from itdoginfo/chore/fakeip-method
feat: add CLI check for FakeIP functionality and update status display
2025-03-12 14:57:41 +03:00
Ivan K
0faaca12fc сhore: remove tabs 2025-03-12 14:54:56 +03:00
Ivan K
c6d1f05916 feat: add CLI check for FakeIP functionality and update status display 2025-03-11 19:14:21 +03:00
itdoginfo
57554d518b v0.3.25 2025-03-11 18:39:30 +03:00
itdoginfo
09d761956c Some fixes 2025-03-11 18:39:18 +03:00
itdoginfo
ada807fec3 v0.3.24 2025-03-07 14:46:45 +03:00
itdoginfo
b28a5f1293 New default TTL=60, DOH=8.8.8.8 2025-03-07 14:46:22 +03:00
itdoginfo
2332eae5ff Added dns and github checker. JSON file for custom URL lists 2025-03-07 14:45:36 +03:00
itdoginfo
a755b6661d Merge pull request #59 from itdoginfo/feat/multiple-mixed-inbounds
Add support for multiple mixed inbounds with unique ports
2025-03-07 13:10:32 +03:00
Nikita Skryabin
567ce52253 feat: add support for multiple mixed inbounds with unique ports 2025-03-06 22:54:25 +03:00
Nikita Skryabin
b736360b66 fix: ensure routing rule for mixed-in is always applied 2025-03-06 21:55:40 +03:00
itdoginfo
3b2a7ba8af Create /usr/bin/podkop 2025-03-05 01:08:30 +03:00
itdoginfo
c96de62d96 v0.3.22 2025-03-04 13:36:43 +03:00
itdoginfo
14b7fbe4f7 Fix cidr for all_traffic+exclude 2025-03-04 13:36:20 +03:00
11 changed files with 2390 additions and 1924 deletions

View File

@@ -11,6 +11,22 @@ jobs:
steps:
- uses: actions/checkout@v4.2.1
- name: Check version match
run: |
PODKOP_VERSION=$(grep '^PKG_VERSION:=' podkop/Makefile | cut -d '=' -f 2)
LUCI_APP_PODKOP_VERSION=$(grep '^PKG_VERSION:=' luci-app-podkop/Makefile | cut -d '=' -f 2)
TAG_VERSION=${GITHUB_REF#refs/tags/v}
echo "Podkop version: $PODKOP_VERSION"
echo "Luci-app-podkop version: $LUCI_APP_PODKOP_VERSION"
echo "Tag version: $TAG_VERSION"
if [ "$PODKOP_VERSION" != "$TAG_VERSION" ] || [ "$LUCI_APP_PODKOP_VERSION" != "$TAG_VERSION" ]; then
echo "Error: Version mismatch"
exit 1
fi
- name: Build and push
uses: docker/build-push-action@v6.9.0
with:

View File

@@ -74,16 +74,19 @@ Luci: Services/podkop
**Custom subnets enable** - Добавить подсети или IP-адреса. Для подсетей задать маску.
# Известные баги
- [ ] Не отрабатывает service podkop stop, если podkop запущен и не может, к пример, зарезолвить домен с сломанным DNS
- [ ] Update list из remote url domain не удаляет старые домены. А добавляет новые. Для подсетей тоже самое скорее всего. Пересоздавать ruleset?
- [x] Не отрабатывает service podkop stop, если podkop запущен и не может, к пример, зарезолвить домен с сломанным DNS
- [x] Update list из remote url domain не удаляет старые домены. А добавляет новые. Для подсетей тоже самое скорее всего. Пересоздавать ruleset?
# ToDo
Этот раздел не означает задачи, которые нужно брать и делать. Это общий список хотелок. Если вы хотите помочь, пожалуйста, спросите сначала в телеграмме.
- [ ] Проверка, что версия в makefile совпадает с тегом
- [ ] Диагностика: Proxy check completed successfully предположительно не показывает IP, если вернулся это IPv6.
- [x] Проверка, что версия в makefile совпадает с тегом
- [ ] Сделать галку запрещающую подкопу редачить dhcp. Допилить в исключение вместе с пустыми полями proxy и vpn
- [ ] Обработка ошибки `sing-box[9345]: FATAL[0000] start service: initialize DNS rule[2]: rule-set not found: main`. Когда не задана строка\интерфейс
- [x] Обработка ошибки `sing-box[9345]: FATAL[0000] start service: initialize DNS rule[2]: rule-set not found: main`. Когда не задана строка\интерфейс
- [x] Проверка `/etc/resolv.conf` на наличие DNS-серверов
- [x] Отслеживание интерфейса wan в sing-box
- [ ] Рестарт сервиса без рестарта dnsmasq
- [ ] `ash: can't kill pid 9848: No such process` при обновлении
Низкий приоритет
- [ ] Галочка, которая режет доступ к doh серверам

View File

@@ -1,7 +1,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-podkop
PKG_VERSION:=0.3.21
PKG_VERSION:=0.3.29
PKG_RELEASE:=1
LUCI_TITLE:=LuCI podkop app

View File

@@ -41,18 +41,15 @@ function formatDiagnosticOutput(output) {
.replace(/\r/g, '\n');
}
function getNetworkInterfaces(o, section_id) {
const excludeInterfaces = ['br-lan', 'eth0', 'eth1', 'wan', 'phy0-ap0', 'phy1-ap0', 'pppoe-wan'];
function getNetworkInterfaces(o, section_id, excludeInterfaces = []) {
return network.getDevices().then(devices => {
// Reset the options by creating a new keylist
o.keylist = [];
o.vallist = [];
devices.forEach(device => {
if (device.dev && device.dev.name) {
const deviceName = device.dev.name;
if (!excludeInterfaces.includes(deviceName) && !/^lan\d+$/.test(deviceName)) {
if (!excludeInterfaces.includes(deviceName)) {
o.value(deviceName, deviceName);
}
}
@@ -243,11 +240,17 @@ function createConfigSection(section, map, network) {
}
};
o = s.taboption('basic', form.Flag, 'ss_uot', _('Shadowsocks UDP over TCP'), _('Apply for SS2022'));
o.default = '0';
o.depends('mode', 'proxy');
o.rmempty = false;
o.ucisection = 'main';
o = s.taboption('basic', form.ListValue, 'interface', _('Network Interface'), _('Select network interface for VPN connection'));
o.depends('mode', 'vpn');
o.ucisection = s.section;
o.load = function (section_id) {
return getNetworkInterfaces(this, section_id).then(() => {
return getNetworkInterfaces(this, section_id, ['br-lan', 'eth0', 'eth1', 'wan', 'phy0-ap0', 'phy1-ap0', 'pppoe-wan']).then(() => {
return this.super('load', section_id);
});
};
@@ -589,9 +592,9 @@ const createModalContent = (title, content) => {
};
const showConfigModal = async (command, title) => {
const res = await safeExec('/etc/init.d/podkop', [command]);
const res = await safeExec('/usr/bin/podkop', [command]);
const formattedOutput = formatDiagnosticOutput(res.stdout || _('No output'));
ui.showModal(_(title), createModalContent(title, formattedOutput));
ui.showModal(_(title), createModalContent(_(title), formattedOutput));
};
// Button Factory
@@ -605,6 +608,16 @@ const ButtonFactory = {
},
createActionButton: function (config) {
return this.createButton({
label: config.label,
additionalClass: `cbi-button-${config.type || ''}`,
onClick: () => safeExec('/usr/bin/podkop', [config.action])
.then(() => config.reload && location.reload()),
style: config.style
});
},
createInitActionButton: function (config) {
return this.createButton({
label: config.label,
additionalClass: `cbi-button-${config.type || ''}`,
@@ -650,32 +663,25 @@ const createStatusPanel = (title, status, buttons) => {
};
// Update the status section creation
let createStatusSection = function (podkopStatus, singboxStatus, podkop, luci, singbox, system, fakeipStatus) {
let createStatusSection = function (podkopStatus, singboxStatus, podkop, luci, singbox, system, fakeipStatus, fakeipCLIStatus) {
return E('div', { 'class': 'cbi-section' }, [
E('h3', {}, _('Service Status')),
E('div', { 'class': 'table', style: 'display: flex; gap: 20px;' }, [
// Podkop Status Panel
createStatusPanel('Podkop Status', podkopStatus, [
podkopStatus.running ?
ButtonFactory.createActionButton({
label: 'Stop Podkop',
type: 'remove',
action: 'stop',
reload: true
}) :
ButtonFactory.createActionButton({
label: 'Start Podkop',
type: 'apply',
action: 'start',
reload: true
}),
ButtonFactory.createActionButton({
label: podkopStatus.running ? 'Stop Podkop' : 'Start Podkop',
type: podkopStatus.running ? 'remove' : 'apply',
action: podkopStatus.running ? 'stop' : 'start',
reload: true
}),
ButtonFactory.createActionButton({
label: 'Restart Podkop',
type: 'apply',
action: 'restart',
reload: true
}),
ButtonFactory.createActionButton({
ButtonFactory.createInitActionButton({
label: podkopStatus.enabled ? 'Disable Podkop' : 'Enable Podkop',
type: podkopStatus.enabled ? 'remove' : 'apply',
action: podkopStatus.enabled ? 'disable' : 'enable',
@@ -712,36 +718,54 @@ let createStatusSection = function (podkopStatus, singboxStatus, podkop, luci, s
})
]),
// FakeIP Status Panel with dynamic status
createStatusPanel('FakeIP Status', {
running: fakeipStatus.state === 'working',
status: fakeipStatus.message
}, [
// FakeIP Status Panel with both browser and router checks
createStatusPanel(_('FakeIP Status'), null, [
E('div', { style: 'margin-bottom: 10px;' }, [
E('div', { style: 'margin-bottom: 5px;' }, [
E('span', { style: `color: ${fakeipStatus.color}` }, [
fakeipStatus.state === 'working' ? '✔' : fakeipStatus.state === 'not_working' ? '✘' : '!',
' ',
fakeipStatus.state === 'working' ? _('works in browser') : _('not works in browser')
])
]),
E('div', {}, [
E('span', { style: `color: ${fakeipCLIStatus.color}` }, [
fakeipCLIStatus.state === 'working' ? '✔' : fakeipCLIStatus.state === 'not_working' ? '✘' : '!',
' ',
fakeipCLIStatus.state === 'working' ? _('works on router') : _('not works on router')
])
])
]),
ButtonFactory.createModalButton({
label: 'Check NFT Rules',
label: _('Check NFT Rules'),
command: 'check_nft',
title: 'NFT Rules'
title: _('NFT Rules')
}),
ButtonFactory.createModalButton({
label: 'Check DNSMasq',
label: _('Check DNSMasq'),
command: 'check_dnsmasq',
title: 'DNSMasq Configuration'
title: _('DNSMasq Configuration')
}),
ButtonFactory.createModalButton({
label: 'Update Lists',
label: _('Update Lists'),
command: 'list_update',
title: 'Lists Update Results'
title: _('Lists Update Results')
}),
ButtonFactory.createModalButton({
label: _('Check Router FakeIP'),
command: 'check_fakeip',
title: _('FakeIP Router Check')
})
]),
// Version Information Panel
createStatusPanel('Version Information', null, [
createStatusPanel(_('Version Information'), null, [
E('div', { 'style': 'margin-top: 10px; font-family: monospace; white-space: pre-wrap;' }, [
E('strong', {}, 'Podkop: '), podkop.stdout ? podkop.stdout.trim() : _('Unknown'), '\n',
E('strong', {}, 'LuCI App: '), luci.stdout ? luci.stdout.trim() : _('Unknown'), '\n',
E('strong', {}, 'Sing-box: '), singbox.stdout ? singbox.stdout.trim() : _('Unknown'), '\n',
E('strong', {}, 'OpenWrt Version: '), system.stdout ? system.stdout.split('\n')[1].trim() : _('Unknown'), '\n',
E('strong', {}, 'Device Model: '), system.stdout ? system.stdout.split('\n')[4].trim() : _('Unknown')
E('strong', {}, _('Podkop: ')), podkop.stdout ? podkop.stdout.trim() : _('Unknown'), '\n',
E('strong', {}, _('LuCI App: ')), luci.stdout ? luci.stdout.trim() : _('Unknown'), '\n',
E('strong', {}, _('Sing-box: ')), singbox.stdout ? singbox.stdout.trim() : _('Unknown'), '\n',
E('strong', {}, _('OpenWrt Version: ')), system.stdout ? system.stdout.split('\n')[1].trim() : _('Unknown'), '\n',
E('strong', {}, _('Device Model: ')), system.stdout ? system.stdout.split('\n')[4].trim() : _('Unknown')
])
])
])
@@ -751,9 +775,6 @@ let createStatusSection = function (podkopStatus, singboxStatus, podkop, luci, s
return view.extend({
async render() {
document.head.insertAdjacentHTML('beforeend', `
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<style>
.cbi-value {
margin-bottom: 10px !important;
@@ -787,7 +808,7 @@ return view.extend({
// Additional Settings Tab (main section)
let o = mainSection.tab('additional', _('Additional Settings'));
o = mainSection.taboption('additional', form.Flag, 'yacd', _('Yacd enable'), _('http://openwrt.lan:9090/ui'));
o = mainSection.taboption('additional', form.Flag, 'yacd', _('Yacd enable'), _('<a href="http://openwrt.lan:9090/ui" target="_blank">openwrt.lan:9090/ui</a>'));
o.default = '0';
o.rmempty = false;
o.ucisection = 'main';
@@ -827,7 +848,7 @@ return view.extend({
o.value('dns.adguard-dns.com', 'AdGuard Default (dns.adguard-dns.com)');
o.value('unfiltered.adguard-dns.com', 'AdGuard Unfiltered (unfiltered.adguard-dns.com)');
o.value('family.adguard-dns.com', 'AdGuard Family (family.adguard-dns.com)');
o.default = '1.1.1.1';
o.default = '8.8.8.8';
o.rmempty = false;
o.ucisection = 'main';
o.validate = function (section_id, value) {
@@ -856,7 +877,7 @@ return view.extend({
};
o = mainSection.taboption('additional', form.Value, 'dns_rewrite_ttl', _('DNS Rewrite TTL'), _('Time in seconds for DNS record caching (default: 600)'));
o.default = '600';
o.default = '60';
o.rmempty = false;
o.ucisection = 'main';
o.validate = function (section_id, value) {
@@ -899,6 +920,15 @@ return view.extend({
return true;
};
o = mainSection.taboption('additional', form.MultiValue, 'iface', _('Source Network Interface'), _('Select the network interface from which the traffic will originate'));
o.ucisection = 'main';
o.default = 'br-lan';
o.load = function (section_id) {
return getNetworkInterfaces(this, section_id, ['wan', 'phy0-ap0', 'phy1-ap0', 'pppoe-wan']).then(() => {
return this.super('load', section_id);
});
};
// 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.default = '0';
@@ -932,7 +962,39 @@ return view.extend({
o = mainSection.taboption('diagnostics', form.DummyValue, '_status');
o.rawhtml = true;
o.cfgvalue = () => E('div', { id: 'diagnostics-status' }, _('Loading diagnostics...'));
o.cfgvalue = () => E('div', {
id: 'diagnostics-status',
'style': 'cursor: pointer;'
}, _('Click to load diagnostics...'));
let diagnosticsUpdateTimer = null;
function startDiagnosticsUpdates() {
if (diagnosticsUpdateTimer) {
clearInterval(diagnosticsUpdateTimer);
}
const container = document.getElementById('diagnostics-status');
if (container) {
container.innerHTML = _('Loading diagnostics...');
}
updateDiagnostics();
diagnosticsUpdateTimer = setInterval(updateDiagnostics, 10000);
}
function stopDiagnosticsUpdates() {
if (diagnosticsUpdateTimer) {
clearInterval(diagnosticsUpdateTimer);
diagnosticsUpdateTimer = null;
}
// Reset the loading state when stopping updates
const container = document.getElementById('diagnostics-status');
if (container) {
container.removeAttribute('data-loading');
}
}
function checkFakeIP() {
const createStatus = (state, message, color) => ({
@@ -943,7 +1005,7 @@ return view.extend({
return new Promise(async (resolve) => {
try {
const singboxStatusResult = await safeExec('/etc/init.d/podkop', ['get_sing_box_status']);
const singboxStatusResult = await safeExec('/usr/bin/podkop', ['get_sing_box_status']);
const singboxStatus = JSON.parse(singboxStatusResult.stdout || '{"running":0,"dns_configured":0}');
if (!singboxStatus.running) {
@@ -957,29 +1019,58 @@ return view.extend({
const timeoutId = setTimeout(() => controller.abort(), 10000);
try {
const response = await fetch('http://httpbin.org/ip', { signal: controller.signal });
const text = await response.text();
const response = await fetch('https://fakeip.tech-domain.club/check', { signal: controller.signal });
const data = await response.json();
clearTimeout(timeoutId);
if (text.includes('Cannot GET /ip')) {
if (data.fakeip === true) {
return resolve(createStatus('working', 'working', 'SUCCESS'));
}
if (text.includes('"origin":')) {
} else {
return resolve(createStatus('not_working', 'not working', 'ERROR'));
}
return resolve(createStatus('error', 'check error', 'WARNING'));
} catch (fetchError) {
clearTimeout(timeoutId);
const message = fetchError.name === 'AbortError' ? 'timeout' : 'check error';
return resolve(createStatus('error', message, 'WARNING'));
}
} catch (error) {
console.error('Error in checkFakeIP:', error);
return resolve(createStatus('error', 'check error', 'WARNING'));
}
});
}
function checkFakeIPCLI() {
const createStatus = (state, message, color) => ({
state,
message: _(message),
color: STATUS_COLORS[color]
});
return new Promise(async (resolve) => {
try {
const singboxStatusResult = await safeExec('/usr/bin/podkop', ['get_sing_box_status']);
const singboxStatus = JSON.parse(singboxStatusResult.stdout || '{"running":0,"dns_configured":0}');
if (!singboxStatus.running) {
return resolve(createStatus('not_working', 'sing-box not running', 'ERROR'));
}
if (!singboxStatus.dns_configured) {
return resolve(createStatus('not_working', 'DNS not configured', 'ERROR'));
}
const result = await safeExec('nslookup', ['-timeout=2', 'fakeip.tech-domain.club', '127.0.0.42']);
if (result.stdout && result.stdout.includes('198.18')) {
return resolve(createStatus('working', 'working on router', 'SUCCESS'));
} else {
return resolve(createStatus('not_working', 'not working on router', 'ERROR'));
}
} catch (error) {
return resolve(createStatus('error', 'CLI check error', 'WARNING'));
}
});
}
async function updateDiagnostics() {
try {
const [
@@ -989,15 +1080,17 @@ return view.extend({
luci,
singbox,
system,
fakeipStatus
fakeipStatus,
fakeipCLIStatus
] = await Promise.all([
safeExec('/etc/init.d/podkop', ['get_status']),
safeExec('/etc/init.d/podkop', ['get_sing_box_status']),
safeExec('/etc/init.d/podkop', ['show_version']),
safeExec('/etc/init.d/podkop', ['show_luci_version']),
safeExec('/etc/init.d/podkop', ['show_sing_box_version']),
safeExec('/etc/init.d/podkop', ['show_system_info']),
checkFakeIP()
safeExec('/usr/bin/podkop', ['get_status']),
safeExec('/usr/bin/podkop', ['get_sing_box_status']),
safeExec('/usr/bin/podkop', ['show_version']),
safeExec('/usr/bin/podkop', ['show_luci_version']),
safeExec('/usr/bin/podkop', ['show_sing_box_version']),
safeExec('/usr/bin/podkop', ['show_system_info']),
checkFakeIP(),
checkFakeIPCLI()
]);
const parsedPodkopStatus = JSON.parse(podkopStatus.stdout || '{"running":0,"enabled":0,"status":"unknown"}');
@@ -1006,7 +1099,7 @@ return view.extend({
const container = document.getElementById('diagnostics-status');
if (!container) return;
const statusSection = createStatusSection(parsedPodkopStatus, parsedSingboxStatus, podkop, luci, singbox, system, fakeipStatus);
const statusSection = createStatusSection(parsedPodkopStatus, parsedSingboxStatus, podkop, luci, singbox, system, fakeipStatus, fakeipCLIStatus);
container.innerHTML = '';
container.appendChild(statusSection);
@@ -1017,8 +1110,15 @@ return view.extend({
fakeipStatus.message
]).outerHTML;
}
const fakeipCLIElement = document.getElementById('fakeip-cli-status');
if (fakeipCLIElement) {
fakeipCLIElement.innerHTML = E('span', { 'style': `color: ${fakeipCLIStatus.color}` }, [
fakeipCLIStatus.state === 'working' ? '✔ ' : fakeipCLIStatus.state === 'not_working' ? '✘ ' : '! ',
fakeipCLIStatus.message
]).outerHTML;
}
} catch (e) {
console.error('Error updating diagnostics:', e);
const container = document.getElementById('diagnostics-status');
if (container) {
container.innerHTML = E('div', { 'class': 'alert-message warning' }, [
@@ -1030,61 +1130,6 @@ return view.extend({
}
}
function startPeriodicUpdates(titleDiv) {
let updateTimer = null;
let isVisible = !document.hidden;
let versionText = _('Podkop');
let versionReceived = false;
const updateStatus = async () => {
try {
if (!versionReceived) {
const version = await safeExec('/etc/init.d/podkop', ['show_version'], 2000);
if (version.stdout) {
versionText = _('Podkop') + ' v' + version.stdout.trim();
versionReceived = true;
}
}
const singboxStatusResult = await safeExec('/etc/init.d/podkop', ['get_sing_box_status']);
const singboxStatus = JSON.parse(singboxStatusResult.stdout || '{"running":0,"dns_configured":0}');
const fakeipStatus = await checkFakeIP();
titleDiv.textContent = versionText + (!singboxStatus.running || !singboxStatus.dns_configured === 'not_working' ? ' (not working)' : '');
await updateDiagnostics();
} catch (error) {
console.warn('Failed to update status:', error);
titleDiv.textContent = versionText + ' (not working)';
}
};
const toggleUpdates = (visible) => {
if (visible) {
updateStatus();
if (!updateTimer) {
updateTimer = setInterval(updateStatus, 10000);
}
} else if (updateTimer) {
clearInterval(updateTimer);
updateTimer = null;
}
};
document.addEventListener('visibilitychange', () => {
isVisible = !document.hidden;
toggleUpdates(isVisible);
});
toggleUpdates(isVisible);
window.addEventListener('unload', () => {
if (updateTimer) {
clearInterval(updateTimer);
}
});
}
// Extra Section
const extraSection = m.section(form.TypedSection, 'extra', _('Extra configurations'));
extraSection.anonymous = false;
@@ -1097,8 +1142,47 @@ return view.extend({
const titleDiv = E('h2', { 'class': 'cbi-map-title' }, _('Podkop'));
node.insertBefore(titleDiv, node.firstChild);
setTimeout(() => {
const diagnosticsContainer = document.getElementById('diagnostics-status');
if (diagnosticsContainer) {
diagnosticsContainer.addEventListener('click', function () {
if (!this.hasAttribute('data-loading')) {
this.setAttribute('data-loading', 'true');
startDiagnosticsUpdates();
}
});
}
const tabs = node.querySelectorAll('.cbi-tabmenu');
if (tabs.length > 0) {
tabs[0].addEventListener('click', function (e) {
const tab = e.target.closest('.cbi-tab');
if (tab) {
const tabName = tab.getAttribute('data-tab');
if (tabName === 'diagnostics') {
const container = document.getElementById('diagnostics-status');
if (container && !container.hasAttribute('data-loading')) {
container.setAttribute('data-loading', 'true');
startDiagnosticsUpdates();
}
} else {
stopDiagnosticsUpdates();
}
}
});
const activeTab = tabs[0].querySelector('.cbi-tab[data-tab="diagnostics"]');
if (activeTab) {
const container = document.getElementById('diagnostics-status');
if (container && !container.hasAttribute('data-loading')) {
container.setAttribute('data-loading', 'true');
startDiagnosticsUpdates();
}
}
}
}, 100);
node.classList.add('fade-in');
startPeriodicUpdates(titleDiv);
return node;
});

View File

@@ -722,4 +722,31 @@ msgid "stopped but enabled"
msgstr "остановлен, но активирован"
msgid "stopped & disabled"
msgstr "остановлен и деактивирован"
msgstr "остановлен и деактивирован"
msgid "works in browser"
msgstr "работает в браузере"
msgid "works on router"
msgstr "работает на роутере"
msgid "Check Router FakeIP"
msgstr "Проверить FakeIP на роутере"
msgid "FakeIP Router Check"
msgstr "Проверка FakeIP на роутере"
msgid "FakeIP CLI Check"
msgstr "Проверка FakeIP через CLI"
msgid "FakeIP CLI Check Results"
msgstr "Результаты проверки FakeIP через CLI"
msgid "not works in browser"
msgstr "не работает в браузере"
msgid "not works on router"
msgstr "не работает на роутере"
msgid "Diagnostics"
msgstr "Диагностика"

View File

@@ -1076,4 +1076,31 @@ msgid "stopped but enabled"
msgstr ""
msgid "stopped & disabled"
msgstr ""
msgid "works in browser"
msgstr ""
msgid "works on router"
msgstr ""
msgid "Check Router FakeIP"
msgstr ""
msgid "FakeIP Router Check"
msgstr ""
msgid "FakeIP CLI Check"
msgstr ""
msgid "FakeIP CLI Check Results"
msgstr ""
msgid "not works in browser"
msgstr ""
msgid "not works on router"
msgstr ""
msgid "Diagnostics"
msgstr ""

View File

@@ -5,6 +5,9 @@
"file": {
"/etc/init.d/podkop": [
"exec"
],
"/usr/bin/podkop": [
"exec"
]
},
"ubus": {

View File

@@ -1,7 +1,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=podkop
PKG_VERSION:=0.3.21
PKG_VERSION:=0.3.29
PKG_RELEASE:=1
PKG_MAINTAINER:=ITDog <podkop@itdog.info>
@@ -49,6 +49,9 @@ define Package/podkop/install
$(INSTALL_DIR) $(1)/etc/config
$(INSTALL_CONF) ./files/etc/config/podkop $(1)/etc/config/podkop
$(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_BIN) ./files/usr/bin/podkop $(1)/usr/bin/podkop
endef
$(eval $(call BuildPackage,podkop))

View File

@@ -32,6 +32,8 @@ config main 'main'
option dont_touch_dhcp '0'
option update_interval '1d'
option dns_type 'doh'
option dns_server '1.1.1.1'
option dns_rewrite_ttl '600'
option cache_file '/tmp/cache.db'
option dns_server '8.8.8.8'
option dns_rewrite_ttl '60'
option cache_file '/tmp/cache.db'
list iface 'br-lan'
option ss_uot '0'

File diff suppressed because it is too large Load Diff

2083
podkop/files/usr/bin/podkop Executable file

File diff suppressed because it is too large Load Diff