feat(dns-hosts): replace DynamicList with textarea for 300+ record comfort

This commit is contained in:
Dushnilin
2026-07-25 10:13:33 +03:00
parent 15d722162d
commit 1ea9cc354e
3 changed files with 63 additions and 32 deletions
@@ -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"));
+4 -4
View File
@@ -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"
+4 -4
View File
@@ -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