feat(dns-hosts): replace DynamicList with textarea for 300+ record comfort
This commit is contained in:
@@ -588,7 +588,8 @@ function showImportHostsModal(section_id, optionRef) {
|
||||
if (uiEl && typeof uiEl.getValue === "function") {
|
||||
const v = uiEl.getValue();
|
||||
if (Array.isArray(v)) existingEntries = v;
|
||||
else if (typeof v === "string" && v) existingEntries = [v];
|
||||
else if (typeof v === "string" && v)
|
||||
existingEntries = v.split("\n").map(l => l.trim()).filter(l => l && !l.startsWith("#"));
|
||||
}
|
||||
if (!existingEntries.length) {
|
||||
const uciVal = uci.get(UCI_PACKAGE, section_id, "dns_hosts");
|
||||
@@ -693,7 +694,8 @@ function showImportHostsModal(section_id, optionRef) {
|
||||
if (uiEl && typeof uiEl.getValue === "function") {
|
||||
const v = uiEl.getValue();
|
||||
if (Array.isArray(v)) existingEntries = v;
|
||||
else if (typeof v === "string" && v) existingEntries = [v];
|
||||
else if (typeof v === "string" && v)
|
||||
existingEntries = v.split("\n").map(l => l.trim()).filter(l => l && !l.startsWith("#"));
|
||||
}
|
||||
if (!existingEntries.length) {
|
||||
const uciVal = uci.get(UCI_PACKAGE, section_id, "dns_hosts");
|
||||
@@ -725,7 +727,8 @@ function showImportHostsModal(section_id, optionRef) {
|
||||
|
||||
uci.set(UCI_PACKAGE, section_id, "dns_hosts", finalEntries);
|
||||
if (uiEl && typeof uiEl.setValue === "function") {
|
||||
uiEl.setValue(finalEntries);
|
||||
const _curVal = typeof uiEl.getValue === "function" ? uiEl.getValue() : null;
|
||||
uiEl.setValue(Array.isArray(_curVal) ? finalEntries : finalEntries.join("\n"));
|
||||
}
|
||||
|
||||
ui.hideModal();
|
||||
@@ -933,39 +936,67 @@ function createSettingsContent(section, capabilities) {
|
||||
};
|
||||
|
||||
const dnsHostsOpt = section.option(
|
||||
form.DynamicList,
|
||||
form.TextValue,
|
||||
"dns_hosts",
|
||||
_("Custom DNS Records (Hosts)"),
|
||||
_("Map domains to specific IP addresses (A/AAAA). Format: <code>example.com 192.168.1.100</code>"),
|
||||
_("One record per line: <code>example.com 192.168.1.100</code>. Lines starting with <code>#</code> are ignored."),
|
||||
);
|
||||
dnsHostsOpt.rows = 12;
|
||||
dnsHostsOpt.cfgvalue = function(section_id) {
|
||||
const val = uci.get(UCI_PACKAGE, section_id, "dns_hosts");
|
||||
if (!val) return "";
|
||||
if (Array.isArray(val)) return val.join("\n");
|
||||
return String(val || "");
|
||||
};
|
||||
dnsHostsOpt.write = function(section_id, formvalue) {
|
||||
const lines = String(formvalue || "").split("\n")
|
||||
.map(l => l.trim())
|
||||
.filter(l => l && !l.startsWith("#"));
|
||||
if (lines.length === 0) {
|
||||
uci.unset(UCI_PACKAGE, section_id, "dns_hosts");
|
||||
} else {
|
||||
uci.set(UCI_PACKAGE, section_id, "dns_hosts", lines);
|
||||
}
|
||||
};
|
||||
dnsHostsOpt.remove = function(section_id) {
|
||||
uci.unset(UCI_PACKAGE, section_id, "dns_hosts");
|
||||
};
|
||||
dnsHostsOpt.validate = function(section_id, value) {
|
||||
if (!value) return true;
|
||||
var parts = value.trim().split(/\s+/);
|
||||
if (parts.length !== 2) {
|
||||
return _("Invalid format. Use: domain ip");
|
||||
const lines = value.split("\n");
|
||||
for (const line of lines) {
|
||||
const trimmed = line.trim();
|
||||
if (!trimmed || trimmed.startsWith("#")) continue;
|
||||
const parts = trimmed.split(/\s+/);
|
||||
if (parts.length !== 2) {
|
||||
return _('Invalid line: "%s". Use: domain ip').format(
|
||||
trimmed.length > 40 ? trimmed.substring(0, 40) + "…" : trimmed,
|
||||
);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
const originalRenderDnsHosts = dnsHostsOpt.renderWidget;
|
||||
const _origDnsHostsRenderWidget = dnsHostsOpt.renderWidget;
|
||||
dnsHostsOpt.renderWidget = function(section_id, option_index, cfgvalue) {
|
||||
const node = originalRenderDnsHosts.call(this, section_id, option_index, cfgvalue);
|
||||
const container = E("div", { class: "cbi-value-field-extra" }, [
|
||||
const node = _origDnsHostsRenderWidget.call(this, section_id, option_index, cfgvalue);
|
||||
const ta = node.tagName === "TEXTAREA" ? node : node.querySelector("textarea");
|
||||
if (ta) {
|
||||
ta.style.fontFamily = "monospace";
|
||||
ta.style.fontSize = "0.85rem";
|
||||
ta.style.resize = "vertical";
|
||||
}
|
||||
return E("div", {}, [
|
||||
node,
|
||||
E("div", { style: "margin-top: 8px;" }, [
|
||||
E(
|
||||
"button",
|
||||
{
|
||||
class: "cbi-button cbi-button-action",
|
||||
type: "button",
|
||||
click: ui.createHandlerFn(this, function() {
|
||||
showImportHostsModal(section_id, dnsHostsOpt);
|
||||
}),
|
||||
},
|
||||
_("Import Hosts File"),
|
||||
),
|
||||
E("div", { style: "margin-top:8px;" }, [
|
||||
E("button", {
|
||||
class: "cbi-button cbi-button-action",
|
||||
type: "button",
|
||||
click: ui.createHandlerFn(this, function() {
|
||||
showImportHostsModal(section_id, dnsHostsOpt);
|
||||
}),
|
||||
}, _("Import Hosts File")),
|
||||
]),
|
||||
]);
|
||||
return container;
|
||||
};
|
||||
|
||||
o = section.option(form.ListValue, "dns_strategy", _("DNS Strategy"));
|
||||
|
||||
@@ -1160,8 +1160,8 @@ msgstr "Неверный домен"
|
||||
msgid "Invalid format. Use an IP address or CIDR subnet"
|
||||
msgstr "Неверный формат. Используйте IP-адрес или подсеть CIDR"
|
||||
|
||||
msgid "Invalid format. Use: domain ip"
|
||||
msgstr ""
|
||||
msgid "Invalid line: \"%s\". Use: domain ip"
|
||||
msgstr "Неверный формат строки: \"%s\". Используйте: домен ip"
|
||||
|
||||
msgid "Invalid HY2 URL: insecure must be 0 or 1"
|
||||
msgstr "Неверный URL Hysteria2: параметр insecure должен быть 0 или 1"
|
||||
@@ -1412,8 +1412,8 @@ msgstr "Основной DNS"
|
||||
msgid "Main DNS server. If multiple servers are selected, a timeout switches to a backup."
|
||||
msgstr "Основной DNS-сервер. Если выбрать несколько, то при таймауте произойдёт переключение на резервный."
|
||||
|
||||
msgid "Map domains to specific IP addresses (A/AAAA). Format: <code>example.com 192.168.1.100</code>"
|
||||
msgstr "Сопоставление доменов с IP-адресами (A/AAAA). Формат: <code>example.com 192.168.1.100</code>"
|
||||
msgid "One record per line: <code>example.com 192.168.1.100</code>. Lines starting with <code>#</code> are ignored."
|
||||
msgstr "По одной записи на строку: <code>example.com 192.168.1.100</code>. Строки, начинающиеся с <code>#</code>, игнорируются."
|
||||
|
||||
msgid "MASQUE Access Token"
|
||||
msgstr "Токен доступа MASQUE"
|
||||
|
||||
@@ -1683,8 +1683,8 @@ msgstr ""
|
||||
msgid "Invalid format. Use an IP address or CIDR subnet"
|
||||
msgstr ""
|
||||
|
||||
#: ../luci-app-tachyon/htdocs/luci-static/resources/view/tachyon/settings.js:511
|
||||
msgid "Invalid format. Use: domain ip"
|
||||
#: ../luci-app-tachyon/htdocs/luci-static/resources/view/tachyon/settings.js:953
|
||||
msgid "Invalid line: \"%s\". Use: domain ip"
|
||||
msgstr ""
|
||||
|
||||
#: src/validators/validateHysteriaUrl.ts:104
|
||||
@@ -2035,8 +2035,8 @@ msgstr ""
|
||||
msgid "Main DNS server. If multiple servers are selected, a timeout switches to a backup."
|
||||
msgstr ""
|
||||
|
||||
#: ../luci-app-tachyon/htdocs/luci-static/resources/view/tachyon/settings.js:505
|
||||
msgid "Map domains to specific IP addresses (A/AAAA). Format: <code>example.com 192.168.1.100</code>"
|
||||
#: ../luci-app-tachyon/htdocs/luci-static/resources/view/tachyon/settings.js:939
|
||||
msgid "One record per line: <code>example.com 192.168.1.100</code>. Lines starting with <code>#</code> are ignored."
|
||||
msgstr ""
|
||||
|
||||
#: ../luci-app-tachyon/htdocs/luci-static/resources/view/tachyon/section.js:8029
|
||||
|
||||
Reference in New Issue
Block a user