update 2026-07-28 01:38:11

This commit is contained in:
action
2026-07-28 01:38:11 +08:00
parent 20b786ab77
commit 25d570431d
6243 changed files with 2850542 additions and 0 deletions
+24
View File
@@ -0,0 +1,24 @@
# SPDX-License-Identifier: GPL-3.0-only
#
# Copyright (C) 2021-2026 sirpdboy <herboy2008@gmail.com>
#
# This is free software, licensed under the Apache License, Version 2.0 .
#
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-ddns-go
PKG_VERSION:=1.6.8
PKG_RELEASE:=20260323
PKG_MAINTAINER:=sirpdboy <herboy2008@gmail.com>
PKG_CONFIG_DEPENDS:=
LUCI_TITLE:=LuCI Support for Dynamic ddns-go Client
LUCI_DEPENDS:=+ddns-go
LUCI_PKGARCH:=all
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature
@@ -0,0 +1,388 @@
/* Copyright (C) 2022-2026 sirpdboy herboy2008@gmail.com*/
'use strict';
'require view';
'require fs';
'require ui';
'require uci';
'require form';
'require poll';
'require rpc';
const getDDNSGoInfo = rpc.declare({
object: 'luci.ddns-go',
method: 'get_ver',
expect: { 'ver': {} }
});
const getUpdateInfo = rpc.declare({
object: 'luci.ddns-go',
method: 'last_update',
expect: { 'update': {} }
});
const updateMessageMap = {
'Already the latest version': _('Already the latest version'),
'New version available': _('New version available'),
'Update successful': _('Update successful'),
'Download update failed': _('Download update failed'),
'Update check failed': _('Update check failed'),
'Update status unknown': _('Update status unknown')
};
async function checkProcess() {
try {
const pidofRes = await fs.exec('/bin/pidof', ['ddns-go']);
if (pidofRes.code === 0) {
return {
running: true,
pid: pidofRes.stdout.trim()
};
}
} catch (err) {
}
try {
const psRes = await fs.exec('/bin/ps', ['-C', 'ddns-go', '-o', 'pid=']);
const pid = psRes.stdout.trim();
return {
running: pid !== '',
pid: pid || null
};
} catch (err) {
return { running: false, pid: null };
}
}
function getVersionInfo() {
return L.resolveDefault(getDDNSGoInfo(), {}).then(function(result) {
return result || {};
}).catch(function(error) {
console.error('Failed to get version:', error);
return {};
});
}
function checkUpdateStatus() {
return L.resolveDefault(getUpdateInfo(), {}).then(function(result) {
return result || {};
}).catch(function(error) {
console.error('Failed to get update info:', error);
return {};
});
}
function extractPortNumber(portValue) {
if (!portValue) return '9876';
if (portValue.includes(':')) {
var parts = portValue.split(':');
return parts[parts.length - 1];
}
return portValue;
}
function renderStatus(isRunning, listen_port, noweb, version) {
var statusText = isRunning ? _('RUNNING') : _('NOT RUNNING');
var color = isRunning ? 'green' : 'red';
var icon = isRunning ? '✓' : '✗';
var versionText = version ? `v${version}` : '';
var html = String.format(
'<em><span style="color:%s">%s <strong>%s %s - %s</strong></span></em>',
color, icon, _('DDNS-Go'), versionText, statusText
);
if (isRunning) {
html += String.format('&#160;<a class="btn cbi-button" href="http://%s:%s" target="_blank">%s</a>',
window.location.hostname, listen_port, _('Open Web Interface'));
}
return html;
}
function renderUpdateStatus(updateInfo) {
if (!updateInfo || !updateInfo.status) {
return '<span style="color:orange"> ⚠ ' + _('Update status unknown') + '</span>';
}
var status = updateInfo.status;
var message = updateInfo.message || '';
for (let [en, zh] of Object.entries(updateMessageMap)) {
if (message.includes(en)) {
message = message.replace(en, zh);
break;
}
}
switch(status) {
case 'updated':
return String.format('<span style="color:green">✓ %s</span>', message);
case 'update_available':
return String.format('<span style="color:blue">↻ %s</span>', message);
case 'latest':
return String.format('<span style="color:green">✓ %s</span>', message);
case 'download_failed':
case 'check_failed':
return String.format('<span style="color:red">✗ %s</span>', message);
default:
return String.format('<span style="color:orange">? %s</span>', message);
}
}
return view.extend({
load: function() {
return Promise.all([
uci.load('ddns-go')
]);
},
handleResetPassword: async function () {
try {
ui.showModal(_('Resetting Password'), [
E('p', { 'class': 'spinning' }, _('Resetting admin username and password, please wait...'))
]);
const result = await fs.exec('/usr/bin/ddns-go', ['-resetPassword', 'admin12345', '-c', '/etc/ddns-go/ddns-go-config.yaml']);
const configFile = '/etc/ddns-go/ddns-go-config.yaml';
const readResult = await fs.read(configFile);
if (readResult && readResult.trim() !== '') {
let configContent = readResult;
configContent = configContent.replace(/(username:\s*).*/g, '$1admin');
if (!configContent.includes('user:')) {
configContent += '\nuser:\n username: admin\n password: $2a$10$G1xO1cVUYtSpPYwV/Jk3l.u7PxLUxo03wntWG6VA9BxAftNWfZEhK';
}
await fs.write(configFile, configContent);
}
ui.hideModal();
if (result.code === 0) {
ui.showModal(_('Username and Password Reset Successful'), [
E('p', _('Username: admin, Password: admin12345')),
E('p', _('You need to restart DDNS-Go service for the changes to take effect.')),
E('div', { 'class': 'right' }, [
E('button', {
'class': 'btn cbi-button cbi-button-positive',
'click': ui.createHandlerFn(this, function() {
ui.hideModal();
this.handleRestartService();
})
}, _('Restart Service Now')),
' ',
E('button', {
'class': 'btn cbi-button cbi-button-neutral',
'click': ui.hideModal
}, _('Restart Later'))
])
]);
} else {
ui.showModal(_('Partial Reset'), [
E('p', _('DDNS-Go command reset may have failed, but configuration file has been updated.')),
E('p', _('Username: admin, Password: admin12345')),
E('p', _('You may need to restart DDNS-Go service manually.')),
E('div', { 'class': 'right' }, [
E('button', {
'class': 'btn cbi-button cbi-button-positive',
'click': ui.createHandlerFn(this, function() {
ui.hideModal();
this.handleRestartService();
})
}, _('Restart Service Now')),
' ',
E('button', {
'class': 'btn cbi-button cbi-button-neutral',
'click': ui.hideModal
}, _('Close'))
])
]);
}
} catch (error) {
ui.hideModal();
alert(_('ERROR:') + '\n' + _('Reset username/password failed:') + '\n' + error.message);
}
},
handleRestartService: async function() {
try {
// const enabledValue = document.querySelector('input[name="cfg001c48.enabled"]')?.checked ? '1' : '0';
const enabledValue = document.querySelectorAll('input[id="widget.cbid.ddns-go.config.enabled"]')?.checked ? '1' : '0';
await uci.set('ddns-go', 'config', 'enabled', enabledValue);
await uci.save('ddns-go');
await uci.apply();
await fs.exec('/etc/init.d/ddns-go', ['stop']);
await new Promise(resolve => setTimeout(resolve, 1000));
if (enabledValue === '1') {
await fs.exec('/etc/init.d/ddns-go', ['start']);
}
alert(_('SUCCESS:') + '\n' + _('DDNS-Go service restarted successfully'));
if (window.statusPoll) {
window.statusPoll();
}
} catch (error) {
alert(_('ERROR:') + '\n' + _('Failed to restart service:') + '\n' + error.message);
}
},
handleUpdate: async function () {
try {
var updateView = document.getElementById('update_status');
if (updateView) {
updateView.innerHTML = '<span class="spinning"></span> ' + _('Updating, please wait...');
}
const updateInfo = await checkUpdateStatus();
if (updateView) {
updateView.innerHTML = renderUpdateStatus(updateInfo);
}
if (updateInfo.update_successful || updateInfo.status === 'updated') {
if (window.statusPoll) {
window.statusPoll();
}
setTimeout(() => {
var updateView = document.getElementById('update_status');
if (updateView) {
getVersionInfo().then(function(versionInfo) {
var version = versionInfo.version || '';
updateView.innerHTML = String.format('<span style="color:green">✓ %s v%s</span>',
_('Current Version'), version);
});
}
}, 3000);
}
} catch (error) {
console.error('Update failed:', error);
var updateView = document.getElementById('update_status');
if (updateView) {
updateView.innerHTML = '<span style="color:red">✗ ' + _('Update failed') + '</span>';
setTimeout(() => {
getVersionInfo().then(function(versionInfo) {
var version = versionInfo.version || '';
updateView.innerHTML = String.format('<span>%s v%s</span>',
_('Current Version'), version);
});
}, 5000);
}
}
},
render: function(data) {
var m, s, o;
var portValue = uci.get('ddns-go', 'config', 'port') || '[::]:9876';
var listen_port = extractPortNumber(portValue);
var noweb = uci.get('ddns-go', 'config', 'noweb') || '0';
m = new form.Map('ddns-go', _('DDNS-GO'),
_('DDNS-GO automatically obtains your public IPv4 or IPv6 address and resolves it to the corresponding domain name service.'));
s = m.section(form.TypedSection);
s.anonymous = true;
s.render = function() {
var statusView = E('p', { id: 'control_status' },
'<span class="spinning"></span> ' + _('Checking status...'));
window.statusPoll = function() {
return Promise.all([
checkProcess(),
getVersionInfo()
]).then(function(results) {
var [processInfo, versionInfo] = results;
var version = versionInfo.version || '';
statusView.innerHTML = renderStatus(processInfo.running, listen_port, noweb, version);
}).catch(function(err) {
console.error('Status check failed:', err);
statusView.innerHTML = '<span style="color:orange">⚠ ' + _('Status check error') + '</span>';
});
};
poll.add(window.statusPoll, 5);
return E('div', { class: 'cbi-section', id: 'status_bar' }, [
statusView
]);
};
s = m.section(form.NamedSection, 'config', 'basic');
o = s.option(form.Flag, 'enabled', _('Enable'));
o.default = o.disabled;
o.rmempty = false;
o = s.option(form.Value, 'port', _('Listen port'));
o.default = '9876';
o.rmempty = false;
o.datatype = 'string';
o.description = _('Port number (1-65535)');
o = s.option(form.Value, 'time', _('Update interval (seconds)'));
o.default = '300';
o.datatype = 'range(60,86400)';
o.description = _('Update interval in seconds (60-86400)');
o = s.option(form.Value, 'ctimes', _('Provider comparison interval'));
o.default = '5';
o.datatype = 'range(1,60)';
o.description = _('Number of times to compare with service provider (1-60)');
o = s.option(form.Value, 'skipverify', _('Skip verifying certificates'));
o.default = '0';
o.value('0', _('No'));
o.value('1', _('Yes'));
o = s.option(form.Value, 'dns', _('Specify DNS resolution server'));
o.value('223.5.5.5', _('Ali DNS 223.5.5.5'));
o.value('223.6.6.6', _('Ali DNS 223.6.6.6'));
o.value('119.29.29.29', _('Tencent DNS 119.29.29.29'));
o.value('1.1.1.1', _('CloudFlare DNS 1.1.1.1'));
o.value('8.8.8.8', _('Google DNS 8.8.8.8'));
o.value('8.8.4.4', _('Google DNS 8.8.4.4'));
o.datatype = 'ipaddr';
o = s.option(form.Flag, 'noweb', _('Do not start web services'));
o.default = '0';
o.rmempty = false;
o = s.option(form.Value, 'delay', _('Delayed Start (seconds)'));
o.default = '60';
o = s.option(form.Button, '_newpassword', _('Reset account password'));
o.inputtitle = _('Reset');
o.inputstyle = 'apply';
o.onclick = L.bind(this.handleResetPassword, this, data);
o = s.option(form.Button, '_update', _('Check Update'));
o.inputtitle = _('Check');
o.inputstyle = 'apply';
o.onclick = L.bind(this.handleUpdate, this, data);
o = s.option(form.DummyValue, '_update_status', _('Current Version'));
o.rawhtml = true;
var currentVersion = '';
getVersionInfo().then(function(versionInfo) {
currentVersion = versionInfo.version || '';
var updateView = document.getElementById('update_status');
if (updateView) {
updateView.innerHTML = String.format('<span>v%s</span>', currentVersion);
}
});
o.cfgvalue = function() {
return E('div', { style: 'margin: 5px 0;' }, [
E('span', { id: 'update_status' },
currentVersion ? String.format('v%s', currentVersion) : _('Loading...'))
]);
};
return m.render();
}
});
@@ -0,0 +1,96 @@
/* Copyright (C) 2022-2026 sirpdboy herboy2008@gmail.com */
'use strict';
'require view';
'require fs';
'require ui';
'require uci';
'require form';
'require poll';
return view.extend({
load: function() {
return uci.load('ddns-go');
},
checkRunning: function() {
return fs.exec('/bin/pidof', ['ddns-go']).then(function(pidRes) {
if (pidRes.code === 0) return { isRunning: true };
return fs.exec('/bin/ash', ['-c', 'ps | grep -q "[d]dns-go"']).then(function(grepRes) {
return { isRunning: grepRes.code === 0 };
});
});
},
render: function() {
var self = this;
return this.checkRunning().then(function(checkResult) {
var isRunning = checkResult.isRunning;
var port = uci.get('ddns-go', 'config', 'port') || '[::]:9876';
var noweb = uci.get('ddns-go', 'config', 'noweb');
port = port.split(':').pop();
var container = E('div');
if (!isRunning || noweb === '1') {
if (!isRunning) {
var message = _('DDNS-GO Service Not Running');
}
if (noweb === '1') {
var message = _('DDNS-GO Web Interface Disabled');
}
container.appendChild(E('div', {
style: 'text-align: center; padding: 2em;'
}, [
E('img', {
src: 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMjQiIGhlaWdodD0iMTAyNCIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCI+PHBhdGggZmlsbD0iI2RmMDAwMCIgZD0iTTk0Mi40MjEgMjM0LjYyNGw4MC44MTEtODAuODExLTE1My4wNDUtMTUzLjA0NS04MC44MTEgODAuODExYy03OS45NTctNTEuNjI3LTE3NS4xNDctODEuNTc5LTI3Ny4zNzYtODEuNTc5LTI4Mi43NTIgMC01MTIgMjI5LjI0OC01MTIgNTEyIDAgMTAyLjIyOSAyOS45NTIgMTk3LjQxOSA4MS41NzkgMjc3LjM3NmwtODAuODExIDgwLjgxMSAxNTMuMDQ1IDE1My4wNDUgODAuODExLTgwLjgxMWM3OS45NTcgNTEuNjI3IDE3NS4xNDcgODEuNTc5IDI3Ny4zNzYgODEuNTc5IDI4Mi43NTIgMCA1MTItMjI5LjI0OCA1MTItNTEyIDAtMTAyLjIyOS0yOS45NTItMTk3LjQxOS04MS41NzktMjc3LjM3NnpNMTk0Ljk0NCA1MTJjMC0xNzUuMTA0IDE0MS45NTItMzE3LjA1NiAzMTcuMDU2LTMxNy4wNTYgNDggMCA5My40ODMgMTAuNjY3IDEzNC4yMjkgMjkuNzgxbC00MjEuNTQ3IDQyMS41NDdjLTE5LjA3Mi00MC43ODktMjkuNzM5LTg2LjI3Mi0yOS43MzktMTM0LjI3MnpNNTEyIDgyOS4wNTZjLTQ4IDAtOTMuNDgzLTEwLjY2Ny0xMzQuMjI5LTI5Ljc4MWw0MjEuNTQ3LTQyMS41NDdjMTkuMDcyIDQwLjc4OSAyOS43ODEgODYuMjcyIDI5Ljc4MSAxMzQuMjI5LTAuMDQzIDE3NS4xNDctMTQxLjk5NSAzMTcuMDk5LTMxNy4wOTkgMzE3LjA5OXoiLz48L3N2Zz4=',
style: 'width: 100px; height: 100px; margin-bottom: 1em;'
}),
E('h2', {}, message)
]));
} else {
var isHttps = window.location.protocol === 'https:';
if (isHttps) {
var buttonContainer = E('div', {
style: 'text-align: center; padding: 2em;'
}, [
E('h2', {}, _('DDNS-GO Control panel')),
E('p', {}, _('Due to browser security policies, the DDNS-GO interface https cannot be embedded directly.')),
E('a', {
href: 'http://' + window.location.hostname + ':' + port,
target: '_blank',
class: 'cbi-button cbi-button-apply',
style: 'display: inline-block; margin-top: 1em; padding: 10px 20px; font-size: 16px; text-decoration: none; color: white;'
}, _('Open Web Interface')),
]);
container.appendChild(buttonContainer);
} else {
var iframe = E('iframe', {
src: 'http://' + window.location.hostname + ':' + port,
style: 'width: 100%; min-height: 100vh; border: none;'
});
container.appendChild(iframe);
}
}
poll.add(function() {
return self.checkRunning().then(function(checkResult) {
var newStatus = checkResult.isRunning;
if (newStatus !== isRunning) {
window.location.reload();
}
});
}, 5);
poll.start();
return container;
});
},
handleSaveApply: null,
handleSave: null,
handleReset: null
});
@@ -0,0 +1,258 @@
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2022-2026 sirpdboy <herboy2008@gmail.com>
*/
'use strict';
'require dom';
'require fs';
'require poll';
'require uci';
'require view';
'require form';
return view.extend({
render: function () {
var css = `
.log-container {
max-height: 1200px;
overflow-y: auto;
border-radius: 3px;
margin-top: 10px;
padding: 5px;
background-color: var(--background-color);
font-family: monospace;
font-size: 12px;
border: 1px solid var(--border-color);
}
.log-line {
padding: 3px 5px;
font-family: monospace;
font-size: 12px;
line-height: 1.4;
border-bottom: 1px solid var(--border-color-light);
white-space: pre-wrap;
word-break: break-all;
}
.log-line:last-child {
border-bottom: none;
}
.log-timestamp {
color: #0066cc;
margin-right: 10px;
font-weight: bold;
}
.log-error {
color: #cc0000;
}
.log-warning {
color: #ff9900;
}
.control-buttons {
margin-bottom: 10px;
display: flex;
gap: 5px;
}
`;
var log_container = E('div', {
'class': 'log-container',
'id': 'log_container',
'style': 'min-height: 200px;'
}, E('div', { 'class': 'log-line' }, _('Loading logs...')));
var lastLogContent = '';
var lastScrollTop = 0;
var isScrolledToTop = true;
function extractDDNSGoMessage(line) {
if (!line || !line.includes('ddns-go')) return null;
var regex = /^(.*?ddns-go.*?):\s*(.*)$/;
var match = line.match(regex);
if (match) {
var timestampMatch = line.match(/^([A-Z][a-z]{2}\s+[A-Z][a-z]{2}\s+\d{1,2}\s+\d{2}:\d{2}:\d{2}\s+\d{4})/);
if (timestampMatch) {
return {
timestamp: timestampMatch[1],
message: match[2]
};
}
}
var selfTimestampMatch = line.match(/(\d{4}\/\d{2}\/\d{2}\s+\d{2}:\d{2}:\d{2})\s+(.*)$/);
if (selfTimestampMatch) {
return {
timestamp: selfTimestampMatch[1],
message: selfTimestampMatch[2]
};
}
return {
timestamp: null,
message: line
};
}
function formatLogLine(line) {
if (!line || line.trim() === '') return null;
var extracted = extractDDNSGoMessage(line);
if (!extracted) return null;
var lineClass = ['log-line'];
if (line.includes('err') || line.includes('ERROR') || line.includes('failed')) {
lineClass.push('log-error');
} else if (line.includes('warn') || line.includes('WARNING')) {
lineClass.push('log-warning');
}
if (extracted.timestamp) {
return E('div', { 'class': lineClass.join(' ') }, [
E('span', { 'class': 'log-timestamp' }, extracted.timestamp + ' '),
E('span', { 'class': 'log-message' }, extracted.message)
]);
} else {
return E('div', { 'class': lineClass.join(' ') }, extracted.message);
}
}
function formatLogContent(logContent) {
if (!logContent || logContent.trim() === '') {
return E('div', { 'class': 'log-line' }, _('No ddns-go logs found.'));
}
var lines = logContent.split('\n');
var formattedLines = [];
for (var i = 0; i < lines.length; i++) {
var line = lines[i].trim();
if (line === '' || line.includes('No ddns-go logs found')) continue;
var formattedLine = formatLogLine(line);
if (formattedLine) {
formattedLines.push(formattedLine);
}
}
if (formattedLines.length === 0) {
return E('div', { 'class': 'log-line' }, _('No ddns-go logs found.'));
}
formattedLines.reverse();
return E('div', {}, formattedLines);
}
function clearLogs(button) {
button.disabled = true;
button.textContent = _('Clearing...');
return fs.exec('/usr/libexec/ddns-go-call', ['clear_logs'])
.then(function(res) {
button.textContent = _('Logs cleared!');
lastLogContent = '';
return fetchLogs();
})
.catch(function(err) {
console.error('Clear logs error:', err);
button.textContent = _('Failed to clear');
})
.finally(function() {
setTimeout(function() {
button.disabled = false;
button.textContent = _('Clear Logs');
}, 2000);
});
}
function fetchLogs() {
return fs.exec('/usr/libexec/ddns-go-call', ['get_logs'])
.then(function(res) {
var logContent = '';
if (res === null || res === undefined) {
logContent = '';
} else if (typeof res === 'string') {
logContent = res;
} else if (res.stdout !== undefined) {
logContent = res.stdout;
} else if (res.data !== undefined) {
logContent = res.data;
} else if (typeof res === 'object') {
logContent = JSON.stringify(res);
}
logContent = logContent.trim();
var lineCount = logContent.split('\n').filter(l =>
l.trim() !== '' && !l.includes('No ddns-go logs found')
).length;
if (logContent !== lastLogContent) {
var formattedLog = formatLogContent(logContent);
var prevScrollHeight = log_container.scrollHeight;
var prevScrollTop = log_container.scrollTop;
dom.content(log_container, formattedLog);
lastLogContent = logContent;
if (!isScrolledToTop) {
var newScrollHeight = log_container.scrollHeight;
var heightDiff = newScrollHeight - prevScrollHeight;
log_container.scrollTop = prevScrollTop + heightDiff;
}
}
return Promise.resolve();
})
.catch(function(err) {
console.error('Log fetch error:', err);
var errorMsg = _('Failed to read logs: %s').format(err.message || 'Resource not found');
dom.content(log_container, E('div', { 'class': 'log-line log-error' }, errorMsg));
return Promise.reject(err);
});
}
var clear_button = E('button', {
'class': 'cbi-button cbi-button-remove',
'click': function(ev) {
ev.preventDefault();
clearLogs(ev.target);
}
}, _('Clear Logs'));
log_container.addEventListener('scroll', function() {
lastScrollTop = this.scrollTop;
isScrolledToTop = this.scrollTop <= 1;
});
setTimeout(fetchLogs, 200);
poll.add(L.bind(function() {
return fetchLogs().catch(function(err) {
console.error('Poll error:', err);
});
}));
poll.start();
return E('div', { 'class': 'cbi-map' }, [
E('style', [css]),
E('div', { 'class': 'cbi-section' }, [
E('div', { 'class': 'control-buttons' }, [ clear_button]),
log_container,
E('small', {}, [
_('Refresh every 5 seconds.').format(L.env.pollinterval),
])
])
]);
},
handleSaveApply: null,
handleSave: null,
handleReset: null
});
+187
View File
@@ -0,0 +1,187 @@
msgid ""
msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Project-Id-Version: ddns-go\n"
msgid "DDNS-GO"
msgstr ""
msgid "DDNS-GO automatically obtains your public IPv4 or IPv6 address and resolves it to the corresponding domain name service."
msgstr ""
msgid "Enable"
msgstr ""
msgid "Listen port"
msgstr ""
msgid "DDNS-GO Service Not Running"
msgstr ""
msgid "DDNS-GO Web Interface Disabled"
msgstr ""
msgid "DDNS-GO Control panel"
msgstr ""
msgid "Update interval(seconds)"
msgstr ""
msgid "Compare with service provider N times intervals"
msgstr ""
msgid "Skip verifying certificates"
msgstr ""
msgid "Specify DNS resolution server"
msgstr ""
msgid "Do not start web services"
msgstr ""
msgid "Delayed Start (seconds)"
msgstr ""
msgid "Reset account password"
msgstr ""
msgid "Check Update"
msgstr ""
msgid "Current Version:"
msgstr ""
msgid "Reset"
msgstr ""
msgid "RUNNING"
msgstr ""
msgid "NOT RUNNING"
msgstr ""
msgid "Open Web Interface"
msgstr ""
msgid "Checking status..."
msgstr ""
msgid "Status check error"
msgstr ""
msgid "Already the latest version"
msgstr ""
msgid "New version available"
msgstr ""
msgid "Update successful"
msgstr ""
msgid "Download update failed"
msgstr ""
msgid "Update check failed"
msgstr ""
msgid "Update status unknown"
msgstr ""
msgid "Update failed"
msgstr ""
msgid "Updating, please wait..."
msgstr ""
msgid "Loading..."
msgstr ""
msgid "Unknown"
msgstr ""
msgid "Failed to load"
msgstr ""
msgid "Log is clean."
msgstr ""
msgid "Clear Logs"
msgstr ""
msgid "Logs cleared!"
msgstr ""
msgid "Refresh every 5 seconds."
msgstr ""
msgid "No ddns-go logs found"
msgstr ""
msgid "Resetting Password"
msgstr ""
msgid "Resetting admin username and password, please wait..."
msgstr ""
msgid "Username and Password Reset Successful"
msgstr ""
msgid "Username: admin, Password: admin12345"
msgstr ""
msgid "You need to restart DDNS-Go service for the changes to take effect."
msgstr ""
msgid "Restart Service Now"
msgstr ""
msgid "Restart Later"
msgstr ""
msgid "Partial Reset"
msgstr ""
msgid "DDNS-Go command reset may have failed, but configuration file has been updated."
msgstr ""
msgid "You may need to restart DDNS-Go service manually."
msgstr ""
msgid "Close"
msgstr ""
msgid "ERROR:"
msgstr ""
msgid "Reset username/password failed:"
msgstr ""
msgid "SUCCESS:"
msgstr ""
msgid "DDNS-Go service restarted successfully"
msgstr ""
msgid "Failed to restart service:"
msgstr ""
msgid "Ali DNS 223.5.5.5"
msgstr ""
msgid "Ali DNS 223.6.6.6"
msgstr ""
msgid "Tencent DNS 119.29.29.29"
msgstr ""
msgid "CloudFlare DNS 1.1.1.1"
msgstr ""
msgid "Google DNS 8.8.8.8"
msgstr ""
msgid "Google DNS 8.8.4.4"
msgstr ""
msgid "Found %d ddns-go entries. Last updated: %s"
msgstr ""
+199
View File
@@ -0,0 +1,199 @@
msgid ""
msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Project-Id-Version: ddns-go\n"
msgid "DDNS-GO"
msgstr "DDNS-GO"
msgid "DDNS-GO automatically obtains your public IPv4 or IPv6 address and resolves it to the corresponding domain name service."
msgstr "DDNS-GO 自动获取您的公网 IPv4 或 IPv6 地址,并解析到对应的域名服务。"
msgid "Enable"
msgstr "启用"
msgid "Listen port"
msgstr "监听端口"
msgid "DDNS-GO Service Not Running"
msgstr "DDNS-GO服务未启用"
msgid "DDNS-GO Web Interface Disabled"
msgstr "DDNS-GO WEB服务禁用"
msgid "DDNS-GO Control panel"
msgstr "DDNS-GO操作台"
msgid "Update interval (seconds)"
msgstr "更新间隔(秒)"
msgid "Provider comparison interval"
msgstr "提供商比较间隔"
msgid "Number of times to compare with service provider (1-60)"
msgstr "与服务提供商间隔比较的次数(1-60)"
msgid "Skip verifying certificates"
msgstr "跳过证书验证"
msgid "Specify DNS resolution server"
msgstr "指定DNS解析服务器"
msgid "Do not start web services"
msgstr "不启动Web服务"
msgid "Delayed Start (seconds)"
msgstr "延迟启动(秒)"
msgid "Reset account password"
msgstr "重置账户密码"
msgid "Check Update"
msgstr "检查更新"
msgid "Port number (1-65535)"
msgstr "端口范围(1-65535)"
msgid "Check"
msgstr "检查"
msgid "Update interval in seconds (60-86400)"
msgstr "更新间隔范围(60-86400)秒"
msgid "Current Version"
msgstr "当前版本"
msgid "Reset"
msgstr "重置"
msgid "RUNNING"
msgstr "运行中"
msgid "NOT RUNNING"
msgstr "未运行"
msgid "Open Web Interface"
msgstr "打开Web界面"
msgid "Checking status..."
msgstr "检查状态中..."
msgid "Status check error"
msgstr "状态检查错误"
msgid "Already the latest version"
msgstr "已是最新版本"
msgid "New version available"
msgstr "有新版本可用"
msgid "Update successful"
msgstr "更新成功"
msgid "Download update failed"
msgstr "下载更新失败"
msgid "Update check failed"
msgstr "检查更新失败"
msgid "Update status unknown"
msgstr "更新状态未知"
msgid "Update failed"
msgstr "更新失败"
msgid "Updating, please wait..."
msgstr "正在更新,请稍候..."
msgid "Loading..."
msgstr "加载中..."
msgid "Unknown"
msgstr "未知"
msgid "Failed to load"
msgstr "加载失败"
msgid "Log is clean."
msgstr "日志清除"
msgid "Clear Logs"
msgstr "清除日志"
msgid "Logs cleared!"
msgstr "已清除"
msgid "Refresh every 5 seconds."
msgstr "每 5 秒刷新"
msgid "No ddns-go logs found"
msgstr "没有日志"
msgid "Resetting Password"
msgstr "重置密码"
msgid "Resetting admin username and password, please wait..."
msgstr "正在重置管理员用户名和密码,请稍候..."
msgid "Username and Password Reset Successful"
msgstr "用户名和密码重置成功"
msgid "Username: admin, Password: admin12345"
msgstr "用户名: admin, 密码: admin12345"
msgid "You need to restart DDNS-Go service for the changes to take effect."
msgstr "您需要重启 DDNS-Go 服务以使更改生效。"
msgid "Restart Service Now"
msgstr "立即重启服务"
msgid "Restart Later"
msgstr "稍后重启"
msgid "Partial Reset"
msgstr "部分重置"
msgid "DDNS-Go command reset may have failed, but configuration file has been updated."
msgstr "DDNS-Go 命令重置可能失败,但配置文件已更新。"
msgid "You may need to restart DDNS-Go service manually."
msgstr "您可能需要手动重启 DDNS-Go 服务。"
msgid "Close"
msgstr "关闭"
msgid "ERROR:"
msgstr "错误:"
msgid "Reset username/password failed:"
msgstr "重置用户名/密码失败:"
msgid "SUCCESS:"
msgstr "成功:"
msgid "DDNS-Go service restarted successfully"
msgstr "DDNS-Go 服务重启成功"
msgid "Failed to restart service:"
msgstr "重启服务失败:"
msgid "Ali DNS 223.5.5.5"
msgstr "阿里 DNS 223.5.5.5"
msgid "Ali DNS 223.6.6.6"
msgstr "阿里 DNS 223.6.6.6"
msgid "Tencent DNS 119.29.29.29"
msgstr "腾讯 DNS 119.29.29.29"
msgid "CloudFlare DNS 1.1.1.1"
msgstr "CloudFlare DNS 1.1.1.1"
msgid "Google DNS 8.8.8.8"
msgstr "谷歌 DNS 8.8.8.8"
msgid "Google DNS 8.8.4.4"
msgstr "谷歌 DNS 8.8.4.4"
msgid "Found %d ddns-go entries. Last updated: %s"
msgstr "找到 %d ddns-go记录,最近时间: %s"
@@ -0,0 +1,8 @@
#!/bin/sh
chmod +x /usr/share/rpcd/ucode/luci.ddns-go
chown root:www-data /usr/libexec/ddns-go-call
chmod 750 /usr/libexec/ddns-go-call
rm -f /tmp/luci-indexcache
/etc/init.d/rpcd restart
exit 0
@@ -0,0 +1,10 @@
#!/bin/sh
case "$1" in
"get_logs")
logread -l 200 2>/dev/null | grep ddns-go || echo "No ddns-go logs found"
;;
"clear_logs")
/etc/init.d/log restart >/dev/null 2>&1
echo "Logs cleared"
;;
esac
@@ -0,0 +1,40 @@
{
"admin/services/ddns-go": {
"title": "DDNS-GO",
"order": 58,
"action": {
"type": "firstchild"
},
"depends": {
"acl": [ "luci-app-ddns-go" ],
"uci": { "ddns-go": true }
}
},
"admin/services/ddns-go/ddns-go": {
"title": "DDNS-GO Control panel",
"order": 10,
"action": {
"type": "view",
"path": "ddns-go/ddns-go"
}
},
"admin/services/ddns-go/config": {
"title": "Base Setting",
"order": 20,
"action": {
"type": "view",
"path": "ddns-go/config"
}
},
"admin/services/ddns-go/log": {
"title": "Log",
"order": 30,
"action": {
"type": "view",
"path": "ddns-go/log"
}
}
}
@@ -0,0 +1,35 @@
{
"luci-app-ddns-go": {
"description": "Grant UCI access for luci-app-ddns-go",
"read": {
"uci": [ "ddns-go" ],
"file": {
"/etc/init.d/ddns-go": [ "exec" ],
"/usr/libexec/ddns-go-call": [ "exec" ],
"/usr/share/rpcd/ucode/luci.ddns-go": [ "exec" ],
"/bin/pidof": [ "exec" ],
"/bin/ps": [ "exec" ],
"/bin/ash": [ "exec" ],
"/etc/ddns-go/ddns-go-config.yaml": [ "read" ],
"/var/log/*": [ "read" ],
"/bin/logread": [ "exec" ]
},
"ubus": {
"rc": [ "*" ],
"service": [ "list" ],
"luci.ddns-go": [ "*" ],
"network.interface.*": [ "status" ],
"network": [ "*" ]
}
},
"write": {
"uci": [ "ddns-go" ],
"file": {
"/etc/ddns-go/ddns-go-config.yaml": [ "write" ]
},
"ubus": {
"luci.ddns-go": [ "*" ]
}
}
}
}
@@ -0,0 +1,134 @@
#!/usr/bin/ucode
/*
* SPDX-License-Identifier: GPL-2.0-only
*
* Copyright (C) 2022-2026 sirpdboy <herboy2008@gmail.com>
*/
'use strict';
import { access, error, lstat, popen, readfile, writefile } from 'fs';
/* Kanged from ucode/luci */
function shellquote(s) {
return `'${replace(s, "'", "'\\''")}'`;
}
function get_current_version() {
if (!access('/usr/bin/ddns-go'))
return null;
const fd = popen('/usr/bin/ddns-go -v');
if (fd) {
let version_output = fd.read('all');
fd.close();
if (!version_output || length(version_output) === 0)
return null;
try {
version_output = replace(trim(version_output), /^v/, '');
return version_output;
} catch(e) {
return null;
}
}
return null;
}
const methods = {
get_ver: {
call: function() {
let current_version = get_current_version();
if (!current_version)
return { ver: {}, error: 'ddns-go not found or version check failed' };
return { ver: { version: current_version } };
}
},
last_update: {
call: function() {
if (!access('/usr/bin/ddns-go'))
return { update: {}, error: 'ddns-go not found' };
let version_before = get_current_version();
const fd = popen('/usr/bin/ddns-go -u');
if (fd) {
let output = fd.read('all');
fd.close();
if (!output || length(output) === 0)
return { update: {}, error: 'empty response' };
try {
output = trim(output);
let update_info = {
raw_output: output,
version_before: version_before,
version_after: null,
has_update: false,
update_successful: false,
current_version: '',
latest_version: '',
status: 'unknown',
message: output
};
update_info.version_after = get_current_version();
if (version_before && update_info.version_after && version_before !== update_info.version_after) {
update_info.update_successful = true;
update_info.has_update = false;
update_info.status = 'updated';
update_info.message = `Update successful: ${version_before} → ${update_info.version_after}`;
}
else if (match(output, /Current version.*is the latest/)) {
update_info.status = 'latest';
update_info.has_update = false;
let version_match = match(output, /v[\d.]+/);
if (version_match) {
update_info.current_version = replace(version_match[0], /^v/, '');
update_info.latest_version = update_info.current_version;
}
update_info.message = 'Already the latest version ' + (update_info.current_version || '');
} else if (match(output, /new version.*available/)) {
update_info.status = 'update_available';
update_info.has_update = true;
let versions = match(output, /v[\d.]+/, 'g');
if (versions && length(versions) >= 2) {
update_info.current_version = replace(versions[0], /^v/, '');
update_info.latest_version = replace(versions[1], /^v/, '');
} else if (version_before) {
update_info.current_version = version_before;
}
update_info.message = 'New version available: ' + (update_info.latest_version || '');
} else if (match(output, /download.*failed/)) {
update_info.status = 'download_failed';
update_info.has_update = false;
update_info.message = 'Download update failed';
} else if (match(output, /check.*failed/)) {
update_info.status = 'check_failed';
update_info.has_update = false;
update_info.message = 'Update check failed';
} else {
update_info.status = 'unknown';
update_info.message = output;
}
return { update: update_info };
} catch(e) {
return { update: {}, error: 'Parse error: ' + e };
}
} else {
return { update: {}, error: 'failed to execute ddns-go command' };
}
}
}
};
return { 'luci.ddns-go': methods };