🤞 Sync 2026-11-05 19:25:07

This commit is contained in:
github-actions[bot] committed 2026-11-05 19:25:07 +08:00
1 parent 9c1c52b048
commit 45b207395d
63 files changed
+4401 -3078

No files matched your search

+2 -1
View File
@@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=ariang
PKG_VERSION:=1.3.15
PKG_RELEASE:=6
PKG_RELEASE:=7
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).zip
PKG_SOURCE_URL:=https://github.com/mayswind/AriaNg/releases/download/$(PKG_VERSION)
@@ -40,6 +40,7 @@ define Package/ariang-nginx
$(Package/ariang/default)
EXTRA_DEPENDS:=nginx$(if $(CONFIG_USE_APK), (>=0))
TITLE += for nginx webserver
CONFLICTS:=ariang
endef
define Build/Compile
+3 -3
View File
@@ -5,11 +5,11 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=honk
PKG_VERSION:=2026.11.04~88d470c
PKG_RELEASE:=30
PKG_VERSION:=2026.11.05~9e31cc0
PKG_RELEASE:=31
HONK_SRC_URL:=https://github.com/daeuniverse/honk.git
HONK_SRC_VERSION:=88d470c662e0ee7a3de3e67afc6129f67e66c1bf
HONK_SRC_VERSION:=9e31cc0bd28825afdc0e384dc989bc3f076e69cf
PKG_LICENSE:=AGPL-3.0-only
PKG_LICENSE_FILES:=LICENSE
+1 -1
View File
@@ -138,7 +138,7 @@ echo "==> Building honk-core for $RUST_TARGET..."
cargo build --release --locked \
-p honk-core \
--no-default-features \
--features ebpf,rprx,clash-api,native-api,mimalloc \
--features ebpf,rprx,clash-api,native-api,jemalloc \
--target "$RUST_TARGET"
BUILT_BIN="$SRC_DIR/target/$RUST_TARGET/release/honk-core"
+4 -3
View File
@@ -87,9 +87,10 @@ start_service() {
hijack_resolv_conf
procd_open_instance "$CONF"
# mimalloc pre-reserves a 1 GiB arena; VSZ only, but it dwarfs the real
# footprint in ps. MIMALLOC_ARENA_RESERVE=0: grow on demand instead.
procd_set_param env DAE_LOCATION_ASSET="/usr/share/v2ray" MIMALLOC_ARENA_RESERVE="0"
procd_set_param env \
DAE_LOCATION_ASSET="/usr/share/v2ray" \
MIMALLOC_ARENA_RESERVE="0" \
MALLOC_CONF="retain:false"
procd_set_param command "$PROG" --config "$config_file"
procd_set_param netdev "$HONK_HOST_IFACE" "$HONK_PEER_IFACE"
procd_set_param limits core="unlimited"
+2 -2
View File
@@ -1,8 +1,8 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=isp-split
PKG_VERSION:=0.4.0
PKG_RELEASE:=6
PKG_VERSION:=0.4.1
PKG_RELEASE:=7
PKG_LICENSE:=MIT
PKG_LICENSE_FILES:=LICENSE
PKG_MAINTAINER:=jihebube <jihebube@users.noreply.github.com>
+408 -96
View File
@@ -234,7 +234,13 @@ doc_mwan3_is_nft() {
doc_mwan3_table() {
_doc_have nft || return 1
nft list table inet mwan3 >/dev/null 2>&1
nft list table inet mwan3 >/dev/null 2>&1 && return 0
# 3.6 的 nft 端口用 inet;个别固件(Kwrt 兼容层 / iptables-nft 残留)会同时
# 留 ip、ip6 同名表。判据是"表在不在",不能被 family 写死:只认 inet 会让
# ip mwan3 里的 mwan3 自家规则整表落到 legacy 分支(fix38 的误报根因之一)。
nft list table ip mwan3 >/dev/null 2>&1 && return 0
nft list table ip6 mwan3 >/dev/null 2>&1 && return 0
return 1
}
# 版本(P10):opkg 与 apk 双分支;两者都没有 -> 空(不判"漂移",判 unknown)
@@ -568,7 +574,9 @@ doc_p4b_table() {
}
# 写者枚举(检查三/五共用):一次 awk 出 TSV,位运算留给 shell(busybox awk
# 没有 and()/or())。字段:fam tab chain handle field form mask value comment
# 没有 and()/or())。字段:fam tab chain handle field form mask value comment src
# src = RHS 首个操作数的来源:meta / ct / lit / other(老夹具没有这列时为空,
# 求值按"同字段"处理,与 fix38 行为一致)
doc_p4_writers() {
DOC_W=""
[ -n "${DOC_P4:-}" ] || {
@@ -596,7 +604,10 @@ doc_p4_writers() {
if (p == 0) next
field = "mark"
if (index(line, "ct mark set") > 0) {
field = "ctmark"; p = index(line, "ct mark set") + 13
# "ct mark set" 是 11 字符 + 1 个空格 -> 值的起点是 index+12。
# 旧实现写的 +13 会吃掉值的首字符:`ct mark set 0x100` 被读成
# "x100" -> form=other -> ct 写者整条漏掉(fix38 顺带修的解析 bug)。
field = "ctmark"; p = index(line, "ct mark set") + 12
} else if (index(line, "meta mark set") > 0) {
p = index(line, "meta mark set") + 14
} else {
@@ -607,7 +618,14 @@ doc_p4_writers() {
q = index(rest, "# handle ")
if (q > 0) { h = substr(rest, q + 9); rest = trim(substr(rest, 1, q - 1)) }
sub(/[^0-9].*$/, "", h)
form = "other"; mask = "-"; value = "-"
form = "other"; mask = "-"; value = "-"; src = "other"
# RHS 的首个操作数说明"这条规则读的是哪个字段":meta mark / ct mark /
# 字面值。fix39 的掩码求值靠它区分三种形态——跨字段同步(读 A 写 B)、
# 同字段掩码(读 B 写 B)、读向(只写 packet mark,不写 ct mark)。
# 只认行首锚定,避免把 `ct mark & ... != ...` 的条件部分误当来源。
if (rest ~ /^meta mark/) src = "meta"
else if (rest ~ /^ct mark/) src = "ct"
else if (rest ~ /^0x/) src = "lit"
if (index(rest, "&") > 0) {
q = index(rest, "&")
r2 = trim(substr(rest, q + 1))
@@ -627,38 +645,189 @@ doc_p4_writers() {
form = "mask_clear"
}
} else {
split(rest, C, " ")
value = C[1]
form = "set"
# `meta mark set meta mark | 0x100`(OR 立即数):nft 打印的是
# `|`,不是 "or",旧实现只认 & 分支会把它读成 other -> 漏掉。
if (index(rest, "|") > 0) {
q2 = index(rest, "|")
split(trim(substr(rest, q2 + 1)), C, " ")
value = C[1]
form = "or"
} else {
split(rest, C, " ")
value = C[1]
form = "set"
}
}
if (value != "-" && value != "" && substr(value, 1, 2) != "0x") {
form = "other"; value = "-"; mask = "-"
}
if (fam == "" || tab == "") next
printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", fam, tab, chain, (h == "" ? "-" : h), field, form, mask, value, (cm == "" ? "-" : cm)
printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", fam, tab, chain, (h == "" ? "-" : h), field, form, mask, value, (cm == "" ? "-" : cm), src
}')
DOC_W_ROWS=$(printf '%s\n' "$DOC_W" | _doc_cnt)
}
# 归属(检查五 5.2 的顺序,写死):① inet mwan3 + mwan3* 链 -> mwan3;
# ② 同一张表里 comment 含 isp-split -> isp-split;③ 非 inet 表 -> legacy;
# ④ 其余 -> third_party。顺序即优先级。
# 归属(检查三/五共用,顺序即优先级):
# ① 表名是 mwan3(任意 family:inet/ip/ip6 都算)-> 整张表连同全部子链都是
# mwan3 自己的(iface_in_*、policy_*、mwan3_hook、dynamic、mwan3_or_ct_* 等);
# ② 表名是 isp-split 自己的表(isptest / isp_split*)-> isp-split;
# ③ 任何表里链名是 mwan3* / iface_in_* / iface_out_* -> mwan3(表名被改过时兜底);
# ④ comment 含 isp-split -> isp-split;⑤ 非 inet 表 -> legacy(iptables-nft 残留);
# ⑥ 其余 -> third_party。
#
# 红线(fix38):只看"表/链是谁的",不看 mark 的值。mwan3 的线路值
# (0x100/0x200/...,掩码 mmx_mask)与 OAF 的默认 ct mark 0x100 完全撞值,
# 值签名永远不能当归属判据——否则 mwan3 自己的线路打标会被判成第三方 DPI。
_doc_owner_of() { # fam tab chain comment
case "$2" in
mwan3 | mwan3_* | mwan3-*) printf 'mwan3'; return 0 ;;
isp_split | isp_split_* | isp-split | isp-split_* | isptest) printf 'isp-split'; return 0 ;;
esac
case "$3" in
mwan3* | iface_in_* | iface_out_*) printf 'mwan3'; return 0 ;;
esac
case "${4:-}" in
*isp-split*) printf 'isp-split'; return 0 ;;
esac
case "$1" in
inet)
if [ "$2" = "mwan3" ]; then
case "$3" in
mwan3* | "") printf 'mwan3' ;;
*) printf 'isp-split' ;;
esac
return 0
fi
case "${4:-}" in
*isp-split*) printf 'isp-split' ;;
*) printf 'third_party' ;;
inet) printf 'third_party' ;;
*) printf 'legacy' ;;
esac
}
# 值是否整个落在 mmx_mask 位段内(mwan3 线路 mark 家族:0x100/0x200/... 或掩码本身)。
# 只用于"这条写者是不是线路打标形态"的说明与 hint,绝不参与归属判定。
_doc_val_in_mask() { # 0x..
_doc_hx "${1:-}"
[ "$HEX" -ne 0 ] || return 1
[ "$(( HEX & DOC_MMX_MASK_DEC ))" -eq "$HEX" ]
}
# 写向位求值(检查三/五共用,唯一实现):一条写 mark 的规则到底"写了哪些位"。
#
# nft 打印出来的写向有四种形态,旧实现把它们混在一起(mask 之外的位一律算成
# "清掉了位段内的位"),于是 OpenWrt 自带 fwmark 同步的 OUTPUT 规则必然误报:
# ① `ct mark set 0x100`(字面值) 覆盖位 = 值 & mmx_mask
# ② `... set <别的字段> & 0x00ff0000`(跨字段/同步)覆盖位 = mask | orbits
# ③ `... set <同字段> & 0xffffc1ff | 0x100`(清位+置位)写 = orbits,清 = ~mask
# ④ `... set <别的字段>`(整值写,无掩码) 不可判定 -> 保守
# ②的语义:规则只把来源字段的 mask 段搬进目标字段,mask 之外的位由来源字段的
# 当时取值决定(OpenWrt 用 `ct state new` 限定,位段本身与 0x3f00 无重叠),
# 因此判据是"mask | orbits 与 mmx_mask 是否有交集",不是"mask 之外被清了多少位"。
#
# 读向:`meta mark set ct mark & 0x00ff0000` 只写 packet mark、不写 ct mark,
# 进不了 conntrack,C3 不计(断言 a 的 PREROUTING 行)。
_doc_wr_eval() { # <field> <form> <mask> <value> <src>
_wre_f=${1:-}
_wre_form=${2:-other}
_wre_msk=${3:--}
_wre_val=${4:--}
_wre_src=${5:-}
WR_BITS=0 # 写进目标字段的位(可静态求值时)
WR_CLEAR=0 # 被清 0 的位(只有同字段掩码写才算)
WR_KNOWN=1 # 0 = 无法静态求值 -> 调用方按"整段可能被写"保守处理
WR_KIND=""
# 同字段(自引用)?空 src(老夹具/信息缺失)按同字段处理,保持 fix38 行为。
_wre_own=1
case "$_wre_src" in
meta) [ "$_wre_f" = "mark" ] || _wre_own=0 ;;
ct) [ "$_wre_f" = "ctmark" ] || _wre_own=0 ;;
lit | other) _wre_own=0 ;;
esac
case "$_wre_form" in
set)
_doc_hx "$_wre_val"
WR_BITS=$HEX
WR_KIND=literal
;;
or)
case "$_wre_src:$_wre_f" in
ct:mark)
WR_KIND=read_from_ct
;;
meta:ctmark)
# `ct mark set meta mark | 0x100`:没有 & 的整值写,来源位未知
WR_KNOWN=0
WR_KIND=unevaluable
;;
*)
_doc_hx "$_wre_val"
WR_BITS=$HEX
WR_KIND=or_literal
;;
esac
;;
*) printf 'legacy' ;;
mask_set | mask_or | mask_clear)
case "$_wre_src:$_wre_f" in
ct:mark)
WR_KIND=read_from_ct
;;
*)
if [ "$_wre_own" = "1" ]; then
# 同字段掩码写:mask 内是"写进去的位",mask 外是"清掉的位",
# 两半都动了目标字段 -> 取并集(与 fix38 相同的保守口径)
_doc_hx "$_wre_val"
WR_BITS=$HEX
_doc_hx "$_wre_msk"
WR_CLEAR=$(( ~HEX & DOC_MMX_MASK_DEC ))
WR_KIND=same_field_mask
else
# 跨字段掩码写(fwmark 同步):覆盖位 = mask | orbits
_doc_hx "$_wre_msk"
_wre_b=$HEX
[ "$_wre_val" = "-" ] || {
_doc_hx "$_wre_val"
_wre_b=$(( _wre_b | HEX ))
}
WR_BITS=$_wre_b
WR_KIND=mask_expr
fi
;;
esac
;;
*)
case "$_wre_src:$_wre_f" in
ct:mark)
WR_KIND=read_from_ct
;;
ct:ctmark | meta:mark)
# 自赋值(`ct mark set ct mark`):一个位都不改
WR_KIND=self_noop
;;
*)
# 无掩码整值写 / 无法解析的表达式:不知道会写哪些位 -> 保守
WR_KNOWN=0
WR_KIND=unevaluable
;;
esac
;;
esac
}
# 写向规则的 RHS 文本(人读/JSON 里的判定依据,不是判据本身)
_doc_wr_src_expr() { # <src> <field>
case "${1:-}" in
meta) printf 'meta mark' ;;
ct) printf 'ct mark' ;;
lit) printf '' ;;
*)
# src 缺失(老夹具):按目标字段反推自引用,显示才不会出现 "?"
case "${2:-}" in
mark) printf 'meta mark' ;;
ctmark) printf 'ct mark' ;;
*) printf '?' ;;
esac
;;
esac
}
_doc_wr_expr() { # <field> <form> <mask> <value> <src>
case "${2:-}" in
set) printf '%s' "${4:--}" ;;
or) printf '%s | %s' "$(_doc_wr_src_expr "${5:-}" "${1:-}")" "${4:--}" ;;
mask_set | mask_or) printf '%s & %s | %s' "$(_doc_wr_src_expr "${5:-}" "${1:-}")" "${3:--}" "${4:--}" ;;
mask_clear) printf '%s & %s' "$(_doc_wr_src_expr "${5:-}" "${1:-}")" "${3:--}" ;;
*) printf '%s(整值写)' "$(_doc_wr_src_expr "${5:-}" "${1:-}")" ;;
esac
}
@@ -675,6 +844,19 @@ _doc_guess_product() { # fam tab chain
esac
}
# 产品名的展示写法(hint 用;不改变判据)
_doc_product_label() {
case "$1" in
oaf) printf 'OAF' ;;
openclash) printf 'OpenClash' ;;
passwall) printf 'PassWall' ;;
tailscale) printf 'Tailscale' ;;
mihomo) printf 'mihomo' ;;
sing-box) printf 'sing-box' ;;
*) printf '%s' "${1:-}" ;;
esac
}
# P5:ip rule。无掩码规则等价掩码 0xffffffff——不补全会让位段判据全错。
doc_p5_iprule() {
[ "${DOC_P5_DONE:-0}" = "1" ] && return 0
@@ -1227,48 +1409,86 @@ doc_check_mark() {
fi
_c3_writers=""
_c3_writers_n=0
_c3_excluded=""
_c3_excl_n=0
_c3_excl_mw=0
_c3_excl_own=0
_c3_wn=0
_c3_third=0
_c3_overlap=0
_c3_overlap_ordered=0
_c3_overlap_unordered=0
_c3_ct=0
_c3_ct_names=""
_c3_hint=""
_c3_hint_conf="none"
_c3_hint_basis="none"
_c3_outside=""
_c3_outside_n=0
# 第三方重叠写者的明细,供检查五继承
DOC_C3_OVERLAP_NAMES=""
while IFS=" " read -r _w_fam _w_tab _w_chn _w_hnd _w_fld _w_form _w_msk _w_val _w_cm; do
# fix39:掩码表达式求值的判定记录(facts 里给用户看"为什么没报")
_c3_eval="" # 第三方写者的形态/覆盖位/是否重叠
# 「无重叠(已验证)」的 info:按 表/链/位段 去重,最多 4 条
_c3_disjoint=""
_c3_disjoint_keys=" "
_c3_disjoint_n=0
_c3_readn=0
_c3_read_txt=""
_c3_unknown_n=0
_c3_ct_unknown=0
_c3_find=""
_doc_find3() {
_c3_find="${_c3_find}${_c3_find:+,}{\"code\":$(_doc_jstr "$1"),\"verdict\":$(_doc_jstr "$2"),\"detail\":$(_doc_jstr "$3"),\"evidence\":$(_doc_jstr "$4")}"
}
while IFS=" " read -r _w_fam _w_tab _w_chn _w_hnd _w_fld _w_form _w_msk _w_val _w_cm _w_src; do
[ -n "${_w_fam:-}" ] || continue
_c3_wn=$((_c3_wn + 1))
_w_owner=$(_doc_owner_of "$_w_fam" "$_w_tab" "$_w_chn" "$_w_cm")
_w_owned=0
_w_cleared=0
case "$_w_form" in
set)
_doc_hx "$_w_val"
_w_owned=$(( HEX & _M ))
;;
mask_set | mask_or)
_doc_hx "$_w_msk"
_w_mk=$HEX
_doc_hx "$_w_val"
_w_owned=$(( HEX & _M ))
_w_cleared=$(( (~_w_mk) & _M ))
;;
mask_clear)
_doc_hx "$_w_msk"
_w_mk=$HEX
_w_cleared=$(( (~_w_mk) & _M ))
;;
esac
_w_touched=$(( _w_cleared | _w_owned ))
_w_prod=$(_doc_guess_product "$_w_fam" "$_w_tab" "$_w_chn")
if [ "$_c3_wn" -le 40 ]; then
_c3_writers="${_c3_writers}${_c3_writers:+,}{\"fam\":$(_doc_jstr "$_w_fam"),\"table\":$(_doc_jstr "$_w_tab"),\"chain\":$(_doc_jstr "$_w_chn"),\"handle\":$(_doc_jstr "$_w_hnd"),\"field\":$(_doc_jstr "$_w_fld"),\"form\":$(_doc_jstr "$_w_form"),\"mask\":$(_doc_jstr "$([ "$_w_msk" = "-" ] && printf '' || printf '%s' "$_w_msk")"),\"value\":$(_doc_jstr "$([ "$_w_val" = "-" ] && printf '' || printf '%s' "$_w_val")"),\"touched\":$(_doc_jstr "$(_doc_hxstr "$_w_touched")"),\"owner\":$(_doc_jstr "$_w_owner")}"
# 写向位求值(唯一实现,检查五同源):形态 -> 覆盖位/清位/可否判定
_doc_wr_eval "$_w_fld" "$_w_form" "$_w_msk" "$_w_val" "${_w_src:-}"
_w_kind=$WR_KIND
_w_known=$WR_KNOWN
if [ "$_w_known" = "0" ]; then
# 不可判定(整值写/无法解析):按"整段都可能被写"保守处理
_w_touched=$_M
else
_w_touched=$(( WR_CLEAR | WR_BITS ))
fi
_w_prod=$(_doc_guess_product "$_w_fam" "$_w_tab" "$_w_chn")
case "$_w_owner" in
third_party | legacy)
mwan3 | isp-split)
# 排除清单(fix38):mwan3 自家表/链(iface_in_*、policy_*、mwan3_hook、
# dynamic、mwan3_or_ct_* 等,任意 family)与 isp-split 自家表里的写者,
# 都不是第三方。它们写的正是 mmx_mask 位段内的线路值——值签名不能
# 反过来当"第三方 DPI"的证据。
_c3_excl_n=$((_c3_excl_n + 1))
case "$_w_owner" in
mwan3) _c3_excl_mw=$((_c3_excl_mw + 1)) ;;
*) _c3_excl_own=$((_c3_excl_own + 1)) ;;
esac
if [ "$_c3_excl_n" -le 40 ]; then
_c3_excluded="${_c3_excluded}${_c3_excluded:+,}{\"fam\":$(_doc_jstr "$_w_fam"),\"table\":$(_doc_jstr "$_w_tab"),\"chain\":$(_doc_jstr "$_w_chn"),\"field\":$(_doc_jstr "$_w_fld"),\"form\":$(_doc_jstr "$_w_form"),\"mask\":$(_doc_jstr "$([ "$_w_msk" = "-" ] && printf '' || printf '%s' "$_w_msk")"),\"value\":$(_doc_jstr "$([ "$_w_val" = "-" ] && printf '' || printf '%s' "$_w_val")"),\"touched\":$(_doc_jstr "$(_doc_hxstr "$_w_touched")"),\"owner\":$(_doc_jstr "$_w_owner"),\"reason\":$(_doc_jstr "$([ "$_w_owner" = "mwan3" ] && printf 'mwan3_own_table' || printf 'isp_split_own')")}"
fi
;;
*)
_c3_third=$((_c3_third + 1))
# 表达式文本只给第三方写者用(自家写者上百条,别为它们多 fork)
_w_expr=$(_doc_wr_expr "$_w_fld" "$_w_form" "$_w_msk" "$_w_val" "${_w_src:-}")
_w_inmask=$(( _w_touched & _M ))
if [ "$_w_known" = "0" ]; then
_c3_unknown_n=$((_c3_unknown_n + 1))
[ "$_w_fld" = "ctmark" ] && _c3_ct_unknown=$((_c3_ct_unknown + 1))
fi
if [ "$_c3_writers_n" -lt 40 ]; then
_c3_writers_n=$((_c3_writers_n + 1))
_c3_writers="${_c3_writers}${_c3_writers:+,}{\"fam\":$(_doc_jstr "$_w_fam"),\"table\":$(_doc_jstr "$_w_tab"),\"chain\":$(_doc_jstr "$_w_chn"),\"handle\":$(_doc_jstr "$_w_hnd"),\"field\":$(_doc_jstr "$_w_fld"),\"form\":$(_doc_jstr "$_w_form"),\"mask\":$(_doc_jstr "$([ "$_w_msk" = "-" ] && printf '' || printf '%s' "$_w_msk")"),\"value\":$(_doc_jstr "$([ "$_w_val" = "-" ] && printf '' || printf '%s' "$_w_val")"),\"src\":$(_doc_jstr "${_w_src:-}"),\"expr\":$(_doc_jstr "$_w_expr"),\"eval\":$(_doc_jstr "$_w_kind"),\"bits\":$(_doc_jstr "$(_doc_hxstr "$WR_BITS")"),\"known\":$(_doc_jbool "$_w_known"),\"touched\":$(_doc_jstr "$(_doc_hxstr "$_w_touched")"),\"overlap\":$(_doc_jbool "$([ "$_w_inmask" -ne 0 ] && printf 1 || printf 0)"),\"owner\":$(_doc_jstr "$_w_owner"),\"product_guess\":$(_doc_jstr "$_w_prod")}"
fi
# 判定矩阵留痕:形态 -> 覆盖位 -> 是否与 mmx_mask 重叠
if [ "$_c3_third" -le 40 ]; then
_c3_eval="${_c3_eval}${_c3_eval:+,}{\"fam\":$(_doc_jstr "$_w_fam"),\"table\":$(_doc_jstr "$_w_tab"),\"chain\":$(_doc_jstr "$_w_chn"),\"handle\":$(_doc_jstr "$_w_hnd"),\"field\":$(_doc_jstr "$_w_fld"),\"form\":$(_doc_jstr "$_w_form"),\"src\":$(_doc_jstr "${_w_src:-}"),\"expr\":$(_doc_jstr "$_w_expr"),\"bits\":$(_doc_jstr "$(_doc_hxstr "$WR_BITS")"),\"known\":$(_doc_jbool "$_w_known"),\"kind\":$(_doc_jstr "$_w_kind"),\"overlap\":$(_doc_jbool "$([ "$_w_inmask" -ne 0 ] && printf 1 || printf 0)")}"
fi
if [ "$(( _w_touched & _M ))" -ne 0 ]; then
_c3_overlap=$((_c3_overlap + 1))
DOC_C3_OVERLAP_NAMES="${DOC_C3_OVERLAP_NAMES}${DOC_C3_OVERLAP_NAMES:+ }${_w_prod:-third_party}"
@@ -1292,11 +1512,51 @@ EOF
else
_c3_overlap_unordered=$((_c3_overlap_unordered + 1))
fi
if [ "$_w_fld" = "ctmark" ]; then
_c3_ct=$((_c3_ct + 1))
[ "$_c3_ct" -le 3 ] &&
_c3_ct_names="${_c3_ct_names}${_c3_ct_names:+, }${_w_fam} ${_w_tab} ${_w_chn}(${_w_expr})"
# 产品名只是 hint:名字级证据 > 值签名(低置信度)> 未识别。
if [ "$_c3_hint_conf" != "high" ]; then
if [ -n "$_w_prod" ]; then
_c3_hint=$_w_prod
_c3_hint_conf=high
_c3_hint_basis=name
elif _doc_val_in_mask "$_w_val" && [ "$HEX" = "256" ]; then
# 0x100 既可能是 OAF 的默认 ct mark,也是 mwan3 的 wan1
# 线路值:只作低置信度猜测,绝不参与 verdict。
_c3_hint=oaf
_c3_hint_conf=low
_c3_hint_basis=value_signature
fi
fi
fi
else
_c3_outside="${_c3_outside}${_c3_outside:+,}{\"fam\":$(_doc_jstr "$_w_fam"),\"table\":$(_doc_jstr "$_w_tab"),\"chain\":$(_doc_jstr "$_w_chn"),\"handle\":$(_doc_jstr "$_w_hnd"),\"product\":$(_doc_jstr "${_w_prod:-third_party}"),\"value\":$(_doc_jstr "$_w_val")}"
_c3_outside="${_c3_outside}${_c3_outside:+,}{\"fam\":$(_doc_jstr "$_w_fam"),\"table\":$(_doc_jstr "$_w_tab"),\"chain\":$(_doc_jstr "$_w_chn"),\"handle\":$(_doc_jstr "$_w_hnd"),\"product\":$(_doc_jstr "${_w_prod:-third_party}"),\"form\":$(_doc_jstr "$_w_form"),\"expr\":$(_doc_jstr "$_w_expr"),\"bits\":$(_doc_jstr "$(_doc_hxstr "$WR_BITS")"),\"value\":$(_doc_jstr "$_w_val")}"
_c3_outside_n=$((_c3_outside_n + 1))
# fix39:求值结论 = 无重叠。必须让用户看见"为什么没报",
# 否则一次误报修完只留下"哦现在不报了"的不信任。
if [ "$_w_kind" = "mask_expr" ]; then
_w_seg=$_w_msk
[ "$_w_val" = "-" ] || _w_seg="${_w_msk} | ${_w_val}"
_w_key="${_w_tab}/${_w_chn}/${_w_seg}"
case "$_c3_disjoint_keys" in
*" $_w_key "*)
;;
*)
_c3_disjoint_keys="${_c3_disjoint_keys}${_w_key} "
_c3_disjoint_n=$((_c3_disjoint_n + 1))
if [ "$_c3_disjoint_n" -le 4 ]; then
_c3_disjoint="${_c3_disjoint}${_w_tab} ${_w_chn} 的 fwmark 同步使用 ${_w_seg} 位段,与 mmx_mask($DOC_MMX_MASK) 无重叠(已验证)
"
fi
;;
esac
elif [ "$_w_kind" = "read_from_ct" ]; then
_c3_readn=$((_c3_readn + 1))
[ -n "$_c3_read_txt" ] || _c3_read_txt="${_w_tab} ${_w_chn} 的 ${_w_expr}"
fi
fi
[ "$_w_fld" = "ctmark" ] && [ "$(( _w_touched & _M ))" -ne 0 ] && _c3_ct=$((_c3_ct + 1))
;;
esac
done <<EOF
@@ -1307,6 +1567,10 @@ EOF
_c3_readers=""
_c3_starved=""
_c3_starved_n=0
# mwan3 线路 rule 家族(fwmark <线值>/<mmx_mask>):这是 mwan3 自己的读者,
# 0x100/0x200/... 与 OAF 默认 mark 撞值也不改变归属(只做事实登记)。
_c3_line_n=0
_c3_line_vals=""
while IFS=" " read -r _r_pref _r_val _r_mask _r_lk _r_iif _r_sup _r_not; do
[ -n "${_r_pref:-}" ] || continue
[ "$_r_val" = "-" ] && continue
@@ -1315,6 +1579,11 @@ EOF
_r_v=$HEX
_doc_hx "$_r_mask"
_r_m=$HEX
if [ "$_r_m" -eq "$_M" ] && [ "$_r_v" -ne 0 ]; then
_c3_line_n=$((_c3_line_n + 1))
[ "$_c3_line_n" -le 8 ] &&
_c3_line_vals="${_c3_line_vals}${_c3_line_vals:+,}$(_doc_jstr "$_r_val")"
fi
# 精确匹配(无掩码)且值的位段内有 mwan3 的位 -> mwan3 清位后这条规则失配
if [ "$_r_m" -ne "$_M" ] && [ "$(( _r_v & _M ))" -ne 0 ]; then
_c3_starved="${_c3_starved}${_c3_starved:+,}{\"where\":\"ip rule\",\"pref\":$(_doc_jstr "$_r_pref"),\"value\":$(_doc_jstr "$_r_val"),\"lookup\":$(_doc_jstr "$_r_lk")}"
@@ -1346,23 +1615,30 @@ EOF
_c3_route="null"
fi
DOC_C3_FACTS="\"mmx_mask\":$(_doc_jstr "$DOC_MMX_MASK"),\"mmx_mask_source\":$(_doc_jstr "$DOC_MMX_SOURCE"),\"writers\":[$_c3_writers],\"readers\":[$_c3_readers],\"mwan3_min_pref\":$DOC_MINPREF,\"third_party_outside_mask\":[$_c3_outside],\"reader_starved\":[$_c3_starved],\"route_get_mark\":$_c3_route"
_c3_find=""
_doc_find3() {
_c3_find="${_c3_find}${_c3_find:+,}{\"code\":$(_doc_jstr "$1"),\"verdict\":$(_doc_jstr "$2"),\"detail\":$(_doc_jstr "$3"),\"evidence\":$(_doc_jstr "$4")}"
}
_c3_collision="null"
if [ "$_c3_ct" -gt 0 ]; then
_c3_collision="{\"kind\":\"ctmark\",\"count\":$_c3_ct,\"writers\":[$(_doc_jstr "$_c3_ct_names")],\"hint\":$(_doc_jstr "$_c3_hint"),\"hint_confidence\":$(_doc_jstr "$_c3_hint_conf"),\"hint_basis\":$(_doc_jstr "$_c3_hint_basis")}"
fi
DOC_C3_FACTS="\"mmx_mask\":$(_doc_jstr "$DOC_MMX_MASK"),\"mmx_mask_source\":$(_doc_jstr "$DOC_MMX_SOURCE"),\"writer_total\":$_c3_wn,\"writers\":[$_c3_writers],\"excluded_count\":$_c3_excl_n,\"excluded\":{\"mwan3\":$_c3_excl_mw,\"isp_split\":$_c3_excl_own},\"excluded_writers\":[$_c3_excluded],\"write_eval\":{\"rule\":\"覆盖位 = mask | orbits(跨字段);同字段按 mask 内写 + mask 外清;无掩码整值写不可判定\",\"mmx_mask\":$(_doc_jstr "$DOC_MMX_MASK"),\"writers\":[$_c3_eval],\"disjoint_count\":$_c3_disjoint_n,\"read_direction_count\":$_c3_readn,\"unevaluable_count\":$_c3_unknown_n},\"line_mark_rules\":{\"owner\":\"mwan3\",\"mask\":$(_doc_jstr "$DOC_MMX_MASK"),\"count\":$_c3_line_n,\"values\":[$_c3_line_vals]},\"readers\":[$_c3_readers],\"mwan3_min_pref\":$DOC_MINPREF,\"third_party_outside_mask\":[$_c3_outside],\"reader_starved\":[$_c3_starved],\"collision\":$_c3_collision,\"route_get_mark\":$_c3_route"
if [ "$DOC_P4B" != "1" ]; then
DOC_C3_VERDICT=unknown
_doc_find3 "" unknown "没有 table inet mwan3(mwan3-nft 未运行),无法判定位段占用" "P4b"
_doc_find3 "" unknown "没有 mwan3 的 nft 表(inet/ip/ip6 都不存在,mwan3-nft 未运行),无法判定位段占用" "P4b"
DOC_C3_ADVICE="先确认 mwan3(nft 版)在跑;只看 /etc/config/mwan3 无法判断运行态。"
elif [ "$_c3_wn" -eq 0 ]; then
DOC_C3_VERDICT=ok
_doc_find3 "" ok "规则表里没有任何 mark 写者" "P4"
elif [ "$_c3_ct" -gt 0 ]; then
DOC_C3_VERDICT=conflict
_doc_find3 "CTMARK_COLLISION" conflict "有程序在写 ct mark 的位段(OAF 类 DPI),改道语义会串" "P4 ct mark set"
case "$_c3_hint_basis" in
name) _c3_hint_txt="疑似 $(_doc_product_label "$_c3_hint")(表名/链名匹配,置信度高)" ;;
value_signature) _c3_hint_txt="疑似 $(_doc_product_label "$_c3_hint")(ct mark 0x100 与 mwan3 的 wan1 线路值撞值;仅值签名,置信度低,不作判据)" ;;
*) _c3_hint_txt="产品未识别(不建议按名字臆断)" ;;
esac
_c3_hint_txt_more=""
[ "$_c3_ct_unknown" -gt 0 ] &&
_c3_hint_txt_more=";其中 $_c3_ct_unknown 条 ct mark 写入形态无法静态求值(整值写/未加掩码),按保守口径计入"
_doc_find3 "CTMARK_COLLISION" conflict "有第三方程序改写了 ct mark 中与 mmx_mask($DOC_MMX_MASK) 重叠的位:$_c3_ct_names;$_c3_hint_txt${_c3_hint_txt_more}" "P4 ct mark set(已排除 mwan3/isp-split 自家写者 $_c3_excl_n 条)"
elif [ "$_c3_overlap_unordered" -gt 0 ]; then
DOC_C3_VERDICT=conflict
_doc_find3 "MARK_OVERLAP_UNORDERED" conflict "有 $_c3_overlap_unordered 个第三方写者落在 mmx_mask 位段内,且没有排在 mwan3 之前的 rule" "P4 + P5"
@@ -1374,7 +1650,18 @@ EOF
_doc_find3 "MARK_OUTSIDE" ok "第三方 mark 写者共 $_c3_outside_n 个,全部落在 mmx_mask 之外" "P4"
else
DOC_C3_VERDICT=ok
_doc_find3 "" ok "标记位段只有 mwan3 自己在用" "P4"
_doc_find3 "" ok "标记位段只有 mwan3/isp-split 自己在用($_c3_excl_n 条自家写者已按归属排除,第三方 0 条)" "P4"
fi
# fix39:求值结论 = 无重叠的写者,逐条记 info。判据是"覆盖位 & mmx_mask == 0",
# 文案必须带上具体位段,否则用户只看到"现在不报了"而不知道为什么。
while IFS= read -r _c3_note; do
[ -n "${_c3_note:-}" ] || continue
_doc_find3 "MARK_FWMARK_DISJOINT" info "$_c3_note" "P4 表达式求值:覆盖位 = mask | orbits;(mask|orbits) & mmx_mask($DOC_MMX_MASK) = 0"
done <<EOF
${_c3_disjoint}
EOF
if [ "$_c3_readn" -gt 0 ]; then
_doc_find3 "MARK_READ_DIRECTION" info "有 $_c3_readn 条规则只读 ct mark、写 packet mark(读向,例如 ${_c3_read_txt}),不写 ct mark,不计入冲突" "P4 读向(ct -> packet)"
fi
if [ "$_c3_starved_n" -gt 0 ]; then
[ "$DOC_C3_VERDICT" = "ok" ] && DOC_C3_VERDICT=warn
@@ -1384,9 +1671,15 @@ EOF
_doc_find3 "MMX_MASK_CUSTOM" ok "mmx_mask=$(printf '%s' "$DOC_MMX_MASK") 不是默认 0x3F00,本报告的阈值已按它现算" "P11"
case "$DOC_C3_VERDICT" in
ok) DOC_C3_ADVICE="路由标记位段只有 mwan3 在用(或第三方全部在位段之外),分流选路正常。" ;;
ok) DOC_C3_ADVICE="标记位段只有 mwan3/isp-split 自己(或位段之外的第三方)在用,分流选路正常。" ;;
warn) DOC_C3_ADVICE="无需改动即可用;若要彻底消除歧义,把代理的 fwmark 移到 mmx_mask 之外(如 0x80000 段)。" ;;
conflict) DOC_C3_ADVICE="检测到有程序直接改写与 mwan3 相同的标记位,分流可能选错线;建议把它的标记值改到位段之外,或把它排到 mwan3 之后。" ;;
conflict)
if [ "$_c3_ct" -gt 0 ]; then
DOC_C3_ADVICE="检测到第三方程序改写了 mwan3 用的 ct mark 位段(mmx_mask);分流可能选错线。先把它的 mark 值移到位段之外,或让它排在 mwan3 之后;产品名只是提示,别按名字下结论。"
else
DOC_C3_ADVICE="检测到第三方程序改写了与 mwan3 相同的标记位,分流可能选错线;建议把它的标记值改到位段之外,或把它排到 mwan3 之后。"
fi
;;
unknown) DOC_C3_ADVICE="没能读取当前 nft 规则,无法判断标记位是否被占用。" ;;
esac
DOC_C3_FIND="$_c3_find"
@@ -1471,31 +1764,26 @@ doc_check_writers() {
_c5_inmask=0
_c5_out=0
_c5_legacy=0
_c5_legacy_inmask=0
_c5_third_inmask=0
# 清单分两段:第三方/legacy 最多 40 条(决策相关,优先展示),
# mwan3/isp-split 自家写者最多 10 条(生产上 mwan3_or_* 有上百条,
# 全列会把真正的第三方挤出清单)。
_c5_list=""
while IFS=" " read -r _w_fam _w_tab _w_chn _w_hnd _w_fld _w_form _w_msk _w_val _w_cm; do
_c5_list_n=0
_c5_list_mw=""
_c5_list_mw_n=0
while IFS=" " read -r _w_fam _w_tab _w_chn _w_hnd _w_fld _w_form _w_msk _w_val _w_cm _w_src; do
[ -n "${_w_fam:-}" ] || continue
_c5_total=$((_c5_total + 1))
_w_owner=$(_doc_owner_of "$_w_fam" "$_w_tab" "$_w_chn" "$_w_cm")
_w_owned=0
_w_cleared=0
case "$_w_form" in
set)
_doc_hx "$_w_val"
_w_owned=$(( HEX & DOC_MMX_MASK_DEC ))
;;
mask_set | mask_or)
_doc_hx "$_w_val"
_w_owned=$(( HEX & DOC_MMX_MASK_DEC ))
_doc_hx "$_w_msk"
_w_cleared=$(( (~HEX) & DOC_MMX_MASK_DEC ))
;;
mask_clear)
_doc_hx "$_w_msk"
_w_cleared=$(( (~HEX) & DOC_MMX_MASK_DEC ))
;;
esac
_w_touched=$(( _w_cleared | _w_owned ))
# 与检查三同源求值(fix39):不允许两套"写了哪些位"的算法漂移
_doc_wr_eval "$_w_fld" "$_w_form" "$_w_msk" "$_w_val" "${_w_src:-}"
if [ "$WR_KNOWN" = "0" ]; then
_w_touched=$DOC_MMX_MASK_DEC
else
_w_touched=$(( WR_CLEAR | WR_BITS ))
fi
_w_prod=$(_doc_guess_product "$_w_fam" "$_w_tab" "$_w_chn")
case "$_w_owner" in
mwan3) _c5_mw=$((_c5_mw + 1)) ;;
@@ -1506,20 +1794,34 @@ doc_check_writers() {
if [ "$(( _w_touched & DOC_MMX_MASK_DEC ))" -ne 0 ]; then
_c5_inmask=$((_c5_inmask + 1))
case "$_w_owner" in
third_party | legacy) _c5_third_inmask=$((_c5_third_inmask + 1)) ;;
legacy) _c5_legacy_inmask=$((_c5_legacy_inmask + 1)) ;;
third_party) _c5_third_inmask=$((_c5_third_inmask + 1)) ;;
esac
else
_c5_out=$((_c5_out + 1))
fi
# JSON 体积上限:清单只列前 40 条(真实机器上写者通常 < 10)
if [ "$_c5_total" -le 40 ]; then
_c5_list="${_c5_list}${_c5_list:+,}{\"owner\":$(_doc_jstr "$_w_owner"),\"product_guess\":$(_doc_jstr "$_w_prod"),\"fam\":$(_doc_jstr "$_w_fam"),\"table\":$(_doc_jstr "$_w_tab"),\"chain\":$(_doc_jstr "$_w_chn"),\"handle\":$(_doc_jstr "$_w_hnd"),\"field\":$(_doc_jstr "$_w_fld"),\"form\":$(_doc_jstr "$_w_form"),\"mask\":$(_doc_jstr "$([ "$_w_msk" = "-" ] && printf '' || printf '%s' "$_w_msk")"),\"value\":$(_doc_jstr "$([ "$_w_val" = "-" ] && printf '' || printf '%s' "$_w_val")"),\"touched\":$(_doc_jstr "$(_doc_hxstr "$_w_touched")")}"
fi
# JSON 体积上限 + 优先级:第三方最多 40 条,自家写者最多 10 条
case "$_w_owner" in
mwan3 | isp-split)
if [ "$_c5_list_mw_n" -lt 10 ]; then
_c5_list_mw_n=$((_c5_list_mw_n + 1))
_c5_list_mw="${_c5_list_mw}${_c5_list_mw:+,}{\"owner\":$(_doc_jstr "$_w_owner"),\"product_guess\":$(_doc_jstr "$_w_prod"),\"fam\":$(_doc_jstr "$_w_fam"),\"table\":$(_doc_jstr "$_w_tab"),\"chain\":$(_doc_jstr "$_w_chn"),\"handle\":$(_doc_jstr "$_w_hnd"),\"field\":$(_doc_jstr "$_w_fld"),\"form\":$(_doc_jstr "$_w_form"),\"mask\":$(_doc_jstr "$([ "$_w_msk" = "-" ] && printf '' || printf '%s' "$_w_msk")"),\"value\":$(_doc_jstr "$([ "$_w_val" = "-" ] && printf '' || printf '%s' "$_w_val")"),\"touched\":$(_doc_jstr "$(_doc_hxstr "$_w_touched")")}"
fi
;;
*)
if [ "$_c5_list_n" -lt 40 ]; then
_c5_list_n=$((_c5_list_n + 1))
_c5_list="${_c5_list}${_c5_list:+,}{\"owner\":$(_doc_jstr "$_w_owner"),\"product_guess\":$(_doc_jstr "$_w_prod"),\"fam\":$(_doc_jstr "$_w_fam"),\"table\":$(_doc_jstr "$_w_tab"),\"chain\":$(_doc_jstr "$_w_chn"),\"handle\":$(_doc_jstr "$_w_hnd"),\"field\":$(_doc_jstr "$_w_fld"),\"form\":$(_doc_jstr "$_w_form"),\"mask\":$(_doc_jstr "$([ "$_w_msk" = "-" ] && printf '' || printf '%s' "$_w_msk")"),\"value\":$(_doc_jstr "$([ "$_w_val" = "-" ] && printf '' || printf '%s' "$_w_val")"),\"touched\":$(_doc_jstr "$(_doc_hxstr "$_w_touched")")}"
fi
;;
esac
done <<EOF
${DOC_W:-}
EOF
# inet mwan3 里的外来链(有人往 mwan3 表里塞规则)
# mwan3 表里的外来链(有人往 mwan3 表里塞规则)。
# 3.6 的自有链名并不都是 mwan3* 前缀(iface_in_*、policy_*、dynamic、mwan3_hook),
# 只认 mwan3* 会把 mwan3 自己的子链当成"外来"(fix38 同一类误判)。
_c5_foreign=""
_c5_foreign_n=0
if [ -n "${DOC_P4:-}" ]; then
@@ -1529,7 +1831,11 @@ EOF
_c5_foreign_n=$((_c5_foreign_n + 1))
done <<EOF
$(printf '%s\n' "$DOC_P4" | awk '
/^[ \t]*table / { intab = ($2 == "inet" && $3 == "mwan3"); next }
/^[ \t]*table / {
intab = (($2 == "inet" || $2 == "ip" || $2 == "ip6") &&
($3 == "mwan3" || $3 ~ /^mwan3[_-]/))
next
}
{
if (!intab) next
tl = $0
@@ -1537,13 +1843,16 @@ $(printf '%s\n' "$DOC_P4" | awk '
if (index(tl, "chain ") != 1) next
n = split(tl, A, " ")
c = A[2]
if (c ~ /^mwan3/) next
if (c ~ /^mwan3/ || c ~ /^iface_(in|out)_/ || c ~ /^policy_/ || c ~ /^dynamic/ || c == "mwan3_hook") next
print c
}')
EOF
fi
DOC_C5_FACTS="\"counts\":{\"total\":$_c5_total,\"mwan3\":$_c5_mw,\"isp_split\":$_c5_own,\"third_party\":$_c5_third,\"legacy\":$_c5_legacy,\"in_mask\":$_c5_inmask,\"third_party_in_mask\":$_c5_third_inmask,\"outside_mask\":$_c5_out},\"writers\":[$_c5_list],\"foreign_chains_in_mwan3\":[$_c5_foreign],\"legacy_writers\":$(_doc_jbool "$([ "$_c5_legacy" -gt 0 ] && printf 1 || printf 0)")"
_c5_writers_all="$_c5_list"
[ -n "$_c5_list_mw" ] &&
_c5_writers_all="${_c5_writers_all}${_c5_writers_all:+,}${_c5_list_mw}"
DOC_C5_FACTS="\"counts\":{\"total\":$_c5_total,\"mwan3\":$_c5_mw,\"isp_split\":$_c5_own,\"third_party\":$_c5_third,\"legacy\":$_c5_legacy,\"legacy_in_mask\":$_c5_legacy_inmask,\"in_mask\":$_c5_inmask,\"third_party_in_mask\":$_c5_third_inmask,\"outside_mask\":$_c5_out},\"writers\":[$_c5_writers_all],\"foreign_chains_in_mwan3\":[$_c5_foreign],\"legacy_writers\":$(_doc_jbool "$([ "$_c5_legacy" -gt 0 ] && printf 1 || printf 0)")"
_c5_find=""
_doc_find5() {
@@ -1554,11 +1863,11 @@ EOF
_c5_verdict=conflict
elif [ "$_c5_foreign_n" -gt 0 ]; then
_c5_verdict=warn
_doc_find5 "MWAN3_TABLE_FOREIGN_CHAIN" warn "inet mwan3 表里有非 mwan3* 的链:$_c5_foreign" "P4"
elif [ "$_c5_legacy" -gt 0 ]; then
_doc_find5 "MWAN3_TABLE_FOREIGN_CHAIN" warn "mwan3 表里有不属于 mwan3 的链(已排除 mwan3*/iface_in_*/iface_out_*/policy_*/dynamic 等自有链名):$_c5_foreign" "P4"
elif [ "$_c5_legacy_inmask" -gt 0 ]; then
_c5_verdict=warn
_doc_find5 "LEGACY_MARK_WRITER" warn "非 inet 表里有 mark 写者(iptables-nft 残留),清单里已列出" "P4 fam=ip/ip6"
elif [ "$_c5_third" -eq 0 ]; then
_doc_find5 "LEGACY_MARK_WRITER" warn "非 mwan3 的 ip/ip6 表里有 mark 写者落在 mmx_mask 位段内(iptables-nft 残留),清单里已列出" "P4 fam=ip/ip6"
elif [ "$_c5_third" -eq 0 ] && [ "$_c5_legacy" -eq 0 ]; then
_c5_verdict=ok
_doc_find5 "" ok "规则表里没有第三方 mark 写者" "P4"
elif [ "$_c5_third_inmask" -gt 0 ] && [ "${DOC_C3_VERDICT:-ok}" != "warn" ]; then
@@ -1566,7 +1875,10 @@ EOF
_doc_find5 "MARK_WRITERS_IN_MASK" warn "有写者的位段与 mmx_mask 有交集(明细见清单)" "P4 + P5"
else
_c5_verdict=ok
_doc_find5 "MARK_WRITERS_OUTSIDE" ok "第三方写者共 $_c5_third 个,均不在 mmx_mask 位段内" "P4"
_c5_out_txt="第三方写者共 $_c5_third 个,均不在 mmx_mask 位段内"
[ "$_c5_legacy" -gt 0 ] &&
_c5_out_txt="第三方/legacy 写者共 $((_c5_third + _c5_legacy)) 个,写入位均不在 mmx_mask 位段内(ip/ip6 表里的 $_c5_legacy 条经掩码表达式求值也不重叠)"
_doc_find5 "MARK_WRITERS_OUTSIDE" ok "$_c5_out_txt" "P4"
fi
DOC_C5_VERDICT=$_c5_verdict
DOC_C5_ADVICE="清单仅供排查;位段是否真的冲突以 mark_segment 的结论为准。"
+1 -1
View File
@@ -15,7 +15,7 @@
# install 注册开机自启与每日更新
# uninstall 卸载所有改动
set -u
ISP_VERSION="0.4.0"
ISP_VERSION="0.4.1"
# 配置表里的注释行(# 开头,允许前导空白)必须在按空白展开之前整行删掉
# (B-1)。否则 `for row in $ISP_TABLE` 会把 "#" 与后面的文字拆成两个
+4
View File
@@ -3,3 +3,7 @@
SHA256SUMS
.DS_Store
Thumbs.db
dist/
*.apk
.ipkbuild/
+1 -1
View File
@@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-cf-dns-speedtest
PKG_VERSION:=1.1.0
PKG_RELEASE:=1
PKG_RELEASE:=2
PKG_LICENSE:=GPL-3.0-only
PKG_MAINTAINER:=xiaokeikei
+4
View File
@@ -49,3 +49,7 @@ opkg install ./luci-app-cf-dns-speedtest_1.1.0-1_x86_64.ipk
[XIU2/CloudflareSpeedTest v2.3.5](https://github.com/XIU2/CloudflareSpeedTest/releases/tag/v2.3.5),
该项目同样采用 GPL-3.0。Cloudflare、OpenWrt、iStoreOS、mosdns 和
CloudflareSpeedTest 均为其各自权利人的项目或商标,本项目与其不存在官方隶属关系。
## OpenWrt 25.12 APK
MU300 / U30 Air 的 APK v3 包及构建、安装说明见 [APK 安装指南](docs/APK.md)。
+18
View File
@@ -0,0 +1,18 @@
# OpenWrt APK 安装包
面向 OpenWrt 25.12.5 / APK v3;MU300 / U30 Air 目标架构为 `aarch64_generic`。这不是安卓应用。保留原有 IPK 供 opkg 系统使用。
```sh
apk update
apk add --allow-untrusted ./mosdns-5.3.4-r14.apk ./luci-app-cf-dns-speedtest-*.apk
```
本包未签名,仅在核对本发布页 SHA256SUMS 后安装。保留 `/etc/config` 中已有配置。
构建:Linux 上设置 `APK` 为 OpenWrt 25.12.5 SDK 中的 `staging_dir/host/bin/apk`,运行 `sudo --preserve-env=APK python3 tools/build_apk.py`。也可手动运行 GitHub Actions 的 Build OpenWrt APK。源文件只从 Git 跟踪清单读取,排除截图、缓存和历史安装包;源码包不含 Git 历史或本机路径。
验证覆盖原生 APK v3 打包与解包、逐文件内容校验及已有自动测试;未声称已在实体 U30 Air 上完成安装或联网验证。
APK 使用上游 XIU2/CloudflareSpeedTest v2.3.5 的 ARM64 程序,下载校验值固定在构建脚本中,并验证 ELF 架构。Git 中的 amd64 程序继续用于原有 x86_64 IPK,APK 不会使用它。程序遵循 GPL-3.0-only,源码:https://github.com/XIU2/CloudflareSpeedTest/tree/v2.3.5 。
官方 OpenWrt 软件源不含 mosdns,因此发布同时附带 sbwml/luci-app-mosdns v5.3.4-r14 的 aarch64_generic / OpenWrt 25.12 运行程序包。上游压缩包 SHA-256 在构建脚本中固定;只提取 mosdns APK,不安装上游 LuCI 插件、DNS 配置或地理数据库。依赖包保留上游原始文件及签名,遵循 GPL-3.0;对应构建配方与补丁:https://github.com/sbwml/luci-app-mosdns/tree/v5.3.4-r14/mosdns ,上游源码:https://github.com/IrineSistiana/mosdns/tree/v5.3.4 。
@@ -0,0 +1,119 @@
"""APK v3 via OpenWrt 25.12 SDK's apk mkpkg; only tracked files are input.
Reference: openwrt/openwrt openwrt-25.12 include/package-pack.mk.
"""
import hashlib, io, os, pathlib, re, shutil, struct, subprocess, tarfile, tempfile, urllib.request, zipfile
ROOT = pathlib.Path(__file__).resolve().parents[1]
make = (ROOT / 'Makefile').read_text()
NAME = re.search(r'PKG_NAME:=(\S+)', make)[1]
VERSION = re.search(r'PKG_VERSION:=(\S+)', make)[1]
RELEASE = re.search(r'PKG_RELEASE:=(\S+)', make)[1]
APK = os.environ.get('APK', 'apk')
OUT = ROOT / 'dist'
FILES = subprocess.check_output(['git', '-c', f'safe.directory={ROOT}', 'ls-files', '-z'], cwd=ROOT).decode().split('\0')
EPOCH = int(subprocess.check_output(['git', '-c', f'safe.directory={ROOT}', 'log', '-1', '--format=%ct'], cwd=ROOT))
CF_URL = 'https://github.com/XIU2/CloudflareSpeedTest/releases/download/v2.3.5/cfst_linux_arm64.tar.gz'
CF_SHA256 = '0ac992fcf24d4684caed33620deb9b83ce82f32d2418dc1f90be490ce5900300'
def allowed(f):
return f and not f.startswith(('.git/', 'docs/images/', 'docs/screenshots/')) and pathlib.Path(f).suffix.lower() not in {'.apk', '.ipk', '.zip', '.gz', '.run', '.png', '.jpg', '.jpeg', '.webp', '.pyc'}
def checked(f, data):
if b'\0' in data[:2000]:
if f != 'root/usr/share/cf-dns-speedtest/cfst':
raise ValueError('Unexpected binary: ' + f)
return data
text = data.decode('utf-8')
if re.search(r'[A-Za-z]:[\\/]Users[\\/]|/home/[A-Za-z0-9_.-]+/|\b192\.168\.\d+\.\d+\b', text):
raise ValueError('Private path or LAN address: ' + f)
for mail in re.findall(r'[\w.+-]+@[\w.-]+\.[A-Za-z]{2,}', text):
if not mail.endswith('@users.noreply.github.com'):
raise ValueError('Personal email: ' + f)
return data.replace(b'\r\n', b'\n')
def main():
if os.geteuid() != 0:
raise ValueError('Run with sudo to preserve root ownership in APK metadata')
OUT.mkdir(exist_ok=True)
if any(OUT.iterdir()):
raise ValueError('Use an empty dist directory to avoid stale release files')
with tempfile.TemporaryDirectory(prefix='apk-build-') as work:
work = pathlib.Path(work)
stage = work / 'root'
stage.mkdir()
prefix = 'files/' if NAME == 'luci-app-wan-latency' else 'root/'
for f in filter(allowed, FILES):
if not f.startswith(prefix):
continue
rel = f[len(prefix):]
target = stage / rel
target.parent.mkdir(parents=True, exist_ok=True)
target.write_bytes(checked(f, (ROOT / f).read_bytes()))
target.chmod(0o755 if rel.startswith(('etc/init.d/', 'usr/sbin/', 'usr/bin/', 'usr/libexec/')) else 0o644)
os.utime(target, (EPOCH, EPOCH))
if NAME == 'luci-app-cf-dns-speedtest':
blob = urllib.request.urlopen(CF_URL, timeout=120).read()
if hashlib.sha256(blob).hexdigest() != CF_SHA256:
raise ValueError('Upstream checksum mismatch')
with tarfile.open(fileobj=io.BytesIO(blob), mode='r:gz') as tf:
member = next(m for m in tf if pathlib.PurePosixPath(m.name).name == 'cfst' and m.isfile())
data = tf.extractfile(member).read()
if data[:4] != b'\x7fELF' or struct.unpack('<H', data[18:20])[0] != 183:
raise ValueError('Expected ARM64 ELF')
target = stage / 'usr/share/cf-dns-speedtest/cfst'
target.write_bytes(data)
target.chmod(0o755)
os.utime(target, (EPOCH, EPOCH))
# Official OpenWrt feeds do not carry mosdns. Ship only its runtime
# package, not luci-app-mosdns or unrelated geo databases.
url = 'https://github.com/sbwml/luci-app-mosdns/releases/download/v5.3.4-r14/aarch64_generic-openwrt-25.12.tar.gz'
bundle = urllib.request.urlopen(url, timeout=120).read()
if hashlib.sha256(bundle).hexdigest() != '1c889fe6b0845c073653e9e7973ea926e90efb0669f641d2a31fcc8e107286c5':
raise ValueError('mosdns dependency checksum mismatch')
with tarfile.open(fileobj=io.BytesIO(bundle), mode='r:gz') as tf:
member = next(m for m in tf if pathlib.PurePosixPath(m.name).name == 'mosdns-5.3.4-r14.apk' and m.isfile())
(OUT / 'mosdns-5.3.4-r14.apk').write_bytes(tf.extractfile(member).read())
arch = 'aarch64_generic' if NAME == 'luci-app-cf-dns-speedtest' else 'noarch'
deps = re.search(r' DEPENDS:=(.*)', make)[1].replace('+', '').split()
if NAME == 'luci-app-cpemonitor':
deps = [d for d in deps if not d.startswith('kmod-')] + ['ip-full', 'flock']
scripts = []
for section, event in [('postinst', 'post-install'), ('postinst', 'post-upgrade'), ('prerm', 'pre-deinstall')]:
match = re.search(r'define Package/' + re.escape(NAME) + '/' + section + r'\n(.*?)\nendef', make, re.S)
if match:
dest = work / event
dest.write_text(match[1].replace('$$', '$') + '\n')
scripts += ['--script', event + ':' + str(dest)]
package = OUT / f'{NAME}-{VERSION}-r{RELEASE}-{arch}.apk'
info = {'name': NAME, 'version': f'{VERSION}-r{RELEASE}', 'arch': arch,
'license': 'GPL-3.0-only' if NAME.endswith('cf-dns-speedtest') else 'GPL-2.0-only',
'description': 'LuCI ' + NAME.removeprefix('luci-app-') + ' for OpenWrt 25.12',
'maintainer': 'Project contributors', 'depends': ' '.join(deps)}
command = [APK, 'mkpkg']
for key, value in info.items():
command += ['--info', key + ':' + value]
subprocess.run(command + scripts + ['--files', str(stage), '--output', str(package)], check=True)
if package.read_bytes()[:3] != b'ADB':
raise ValueError('Expected native APK v3 ADB container')
unpack = work / 'unpacked'
unpack.mkdir()
subprocess.run([APK, 'extract', '--allow-untrusted', '--destination', str(unpack), str(package)], check=True)
for source in stage.rglob('*'):
if source.is_file():
actual = unpack / source.relative_to(stage)
if not actual.exists() or actual.read_bytes() != source.read_bytes() or actual.stat().st_uid != 0 or (actual.stat().st_mode & 0o777) != (source.stat().st_mode & 0o777):
raise ValueError('Extracted payload mismatch: ' + str(source.relative_to(stage)))
with zipfile.ZipFile(OUT / f'{NAME}-{VERSION}-{RELEASE}-source.zip', 'w', zipfile.ZIP_DEFLATED) as archive:
for f in filter(allowed, FILES):
if f == 'root/usr/share/cf-dns-speedtest/cfst':
continue
entry = zipfile.ZipInfo(NAME + '/' + f, (2026, 1, 1, 0, 0, 0))
entry.compress_type = zipfile.ZIP_DEFLATED
entry.external_attr = 0o100644 << 16
archive.writestr(entry, checked(f, (ROOT / f).read_bytes()))
shutil.copyfile(ROOT / 'docs/APK.md', OUT / 'INSTALL-APK.md')
sums = ''.join(hashlib.sha256(p.read_bytes()).hexdigest() + ' ' + p.name + '\n' for p in sorted(OUT.iterdir()) if p.name != 'SHA256SUMS')
(OUT / 'SHA256SUMS').write_text(sums)
print('PASS native APK v3, exact payload round-trip, source privacy checks and SHA256SUMS')
if __name__ == '__main__':
main()
+2 -2
View File
@@ -1,8 +1,8 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-isp-split
PKG_VERSION:=0.4.0
PKG_RELEASE:=6
PKG_VERSION:=0.4.1
PKG_RELEASE:=7
PKG_LICENSE:=MIT
PKG_LICENSE_FILES:=LICENSE
@@ -169,7 +169,6 @@ return view.extend({
o.rmempty = false;
o = s.option(form.Value, 'def_fwmark_value', _('Default ') + _('Fwmark value'));
o.rmempty = true;
o = s.option(form.Value, 'def_tcp_interval', _('Default ') + _('TCP ') + _('keep-alive interval (seconds)'));
o.datatype = "and(uinteger, min(1))";
@@ -280,7 +279,10 @@ return view.extend({
let udp_stun_host = uci.get_first(conf, 'global', 'def_udp_stun');
let tcp_stun_host = uci.get_first(conf, 'global', 'def_tcp_stun');
return fs.exec('/usr/libexec/natmap/natcheck.sh', [udp_stun_host + ':3478', tcp_stun_host + ':3478', test_port, nattest_result_path])
return fs.exec('/usr/libexec/natmap/natcheck.sh', [
udp_stun_host + (udp_stun_host.match(/:\d+$/) ? '' : ':3478'),
tcp_stun_host + (tcp_stun_host.match(/:\d+$/) ? '' : ':3478'),
test_port, nattest_result_path])
.then((res) => { return window.location = window.location.href.split('#')[0] })
.catch((e) => { ui.addNotification(null, E('p', e.message), 'error') });
};
@@ -288,7 +290,7 @@ return view.extend({
if (nattest_result.length) {
o = s.option(form.DummyValue, '_nattest_result', ' ');
o.rawhtml = true;
o.cfgvalue = function(s) {
o.cfgvalue = function(section_id) {
return '<details><summary>' + _('Expand/Collapse result') + '</summary>' + nattest_result + '</details>';
}
};
@@ -308,35 +310,28 @@ return view.extend({
o = s.option(form.Flag, 'enable', _('Enable'));
o.default = o.disabled;
o.editable = true;
o.rmempty = true;
o.modalonly = false;
o = s.taboption('general', form.Value, 'interval', _('Keep-alive interval'));
o.datatype = "and(uinteger, min(1))";
o.rmempty = true;
o.modalonly = true;
o = s.taboption('general', form.Value, 'stun_cycle', _('STUN check cycle (once every n intervals)'), _('For UDP mode'));
o.datatype = 'uinteger';
o.rmempty = true;
o.modalonly = true;
o = s.taboption('general', form.Value, 'stun_server', _('STUN server'));
o.datatype = 'or(hostname, hostport)';
o.rmempty = true;
o.modalonly = true;
o = s.taboption('general', form.Value, 'http_server', _('HTTP server'), _('For TCP mode'));
o.datatype = 'or(hostname, hostport)';
o.rmempty = true;
o.modalonly = true;
o = s.taboption('general', form.Value, 'fwmark_value', _('Fwmark value'), _('Mark fwmark for STUN/HTTP outbound traffic'));
o.rmempty = true;
o.modalonly = true;
o = s.taboption('general', form.Value, 'comment', _('Comment'));
o.rmempty = true;
o = s.taboption('general', form.ListValue, 'udp_mode', _('Protocol'));
o.default = '0';
@@ -380,7 +375,6 @@ return view.extend({
o.noaliases = true;
o.nobridges = true;
o.nocreate = false;
o.rmempty = true;
o = s.taboption('general', form.Value, 'port', _('Bind port'),
_('Note: After using <code>portrange</code>, the public ports will be opened in rotation</br>') +
@@ -411,11 +405,11 @@ return view.extend({
let cval = this.cfgvalue(section_id) || this.default;
let i = this.keylist.indexOf(cval);
return [this.vallist[i], cval];
}.call(s.getOption('forward_mode'));
}.call(this.section.getOption('forward_mode'));
let loopback = function() {
let cval = this.cfgvalue(section_id) || this.default;
return (cval == this.enabled) ? ' L' : '';
}.call(s.getOption('natloopback'));
}.call(this.section.getOption('natloopback'));
return (cval == this.enabled) ? mode[0] + (mode[1] === 'dnat' ? loopback : '') : _('No');
};
@@ -432,7 +426,7 @@ return view.extend({
let E = document.getElementById('widget.' + this.cbid(section_id).match(/.+\./) + 'family');
let i = E ? E.selectedIndex : null;
return E ? E.options[i].value : null;
}.call(s.getOption('family'));
}.call(this.section.getOption('family'));
if (value == 'dnat' && family == 'ipv6')
return _('The DNAT method not support under IPv6');
@@ -442,7 +436,6 @@ return view.extend({
o = s.taboption('forward', form.Flag, 'natloopback', _('NAT loopback'));
o.default = o.enabled;
o.rmempty = true;
o.retain = true;
o.depends('forward_mode', 'dnat');
o.modalonly = true;
@@ -452,14 +445,12 @@ return view.extend({
congestions.forEach((res) => {
o.value.apply(o, [res]);
})
o.rmempty = true;
o.depends('forward_mode', 'via');
o.modalonly = true;
o = s.taboption('forward', form.Value, 'forward_timeout', _('Forward timeout'), _('Port forwarding session idle timeout in seconds'));
o.datatype = "and(uinteger, min(1))";
o.placeholder = '300000';
o.rmempty = true;
o.depends('forward_mode', 'via');
o.modalonly = true;
@@ -491,7 +482,7 @@ return view.extend({
let enforward = function() {
let cval = this.cfgvalue(section_id) || this.default;
return (cval == this.enabled) ? true : false;
}.call(s.getOption('forward'));
}.call(this.section.getOption('forward'));
return enforward ? this.vallist[i] ?? cval : _('No');
};
@@ -505,16 +496,16 @@ return view.extend({
let enforward = function() {
let cval = this.cfgvalue(section_id) || this.default;
return (cval == this.enabled) ? true : false;
}.call(s.getOption('forward'));
}.call(this.section.getOption('forward'));
let refresh = function() {
let cval = this.cfgvalue(section_id) || this.default;
return (cval == this.enabled) ? true : false;
}.call(s.getOption('refresh'));
}.call(this.section.getOption('refresh'));
let cltname = function() {
let cval = this.cfgvalue(section_id) || this.default;
let i = this.keylist.indexOf(cval);
return this.vallist[i];
}.call(s.getOption('clt_script'));
}.call(this.section.getOption('clt_script'));
return enforward ? ((cval == '0' && refresh) ? cltname : cval) : _('No');
};
@@ -555,14 +546,12 @@ return view.extend({
o.modalonly = true;
o = s.taboption('forward', form.Value, 'clt_username', _('Username'));
o.rmempty = true;
o.retain = true;
o.depends('refresh', '1');
o.modalonly = true;
o = s.taboption('forward', form.Value, 'clt_password', _('Password'));
o.password = true;
o.rmempty = true;
o.depends('refresh', '1');
o.modalonly = true;
@@ -583,7 +572,6 @@ return view.extend({
o = s.taboption('notify', form.Flag, 'notify_enable', _('EnNotify'));
o.default = o.disabled;
o.editable = true;
o.rmempty = true;
o = s.taboption('notify', form.ListValue, 'notify_script', _('Notify Scripts'));
o.datatype = 'file';
@@ -602,24 +590,20 @@ return view.extend({
.format('https://github.com/muink/openwrt-natmapt/tree/master/files/notify/', _('<code># All external tokens required</code> Field')));
o.datatype = 'list(string)';
o.placeholder = 'KEY=VAL';
o.rmempty = true;
o.modalonly = true;
o = s.taboption('notify', form.Value, 'notify_custom_domain', _('Private API Domain'));
o.datatype = 'hostname';
o.placeholder = 'api.example.com';
o.rmempty = true;
o.modalonly = true;
o = s.taboption('notify', form.Value, 'notify_text', _('Text content'));
o.placeholder = 'NATMap: <comment>: [<protocol>] <inner_ip>:<inner_port> -> <ip>:<port>';
o.rmempty = true;
o.modalonly = true;
o = s.taboption('ddns', form.Flag, 'ddns_enable', _('EnDDNS'));
o.default = o.disabled;
o.editable = true;
o.rmempty = true;
o = s.taboption('ddns', form.ListValue, 'ddns_script', _('DDNS Scripts'));
o.datatype = 'file';
@@ -638,23 +622,43 @@ return view.extend({
.format('https://github.com/muink/openwrt-natmapt/tree/master/files/ddns/', _('<code># All external tokens required</code> Field')));
o.datatype = 'list(string)';
o.placeholder = 'KEY=VAL';
o.rmempty = true;
o.modalonly = true;
o = s.taboption('ddns', form.Value, 'ddns_a', _('A Record ') + _('FQDN'));
o.datatype = 'hostname';
o.placeholder = 'www.example.com';
o.rmempty = true;
o.depends('family', 'ipv4');
o.modalonly = true;
o = s.taboption('ddns', form.Value, 'ddns_aaaa', _('AAAA Record ') + _('FQDN'));
o.datatype = 'hostname';
o.placeholder = 'ipv6.example.com';
o.rmempty = true;
o.depends('family', 'ipv6');
o.modalonly = true;
o = s.taboption('ddns', form.DummyValue, '_ddns_txt_dump', _('TXT Record'));
o.rawhtml = false;
o.cfgvalue = function(section_id) {
let fqdn = uci.get(conf, section_id, 'ddns_txt');
let cont = uci.get(conf, section_id, 'ddns_txt_content');
if ( fqdn && cont )
return fqdn + '. <TTL> IN TXT ' + cont;
};
o.modalonly = true;
o = s.taboption('ddns', form.Value, 'ddns_txt', _('FQDN'));
o.datatype = 'hostname';
o.placeholder = 'txt.example.com';
o.modalonly = true;
o = s.taboption('ddns', form.Value, 'ddns_txt_content', _('Content'));
o.placeholder = '"port={{port}}; target={{ip}}"';
o.default = '"port={{port}}; target={{ip}}"';
o.rmempty = false;
o.depends({ ddns_txt: "", "!reverse": true })
o.modalonly = true;
o = s.taboption('ddns', form.DummyValue, '_ddns_srv_dump', _('SRV Record'));
o.rawhtml = false;
o.cfgvalue = function(section_id) {
@@ -673,13 +677,13 @@ return view.extend({
o = s.taboption('ddns', form.Value, 'ddns_srv', _('FQDN'));
o.datatype = 'hostname';
o.placeholder = 'mc.example.com';
o.rmempty = true;
o.modalonly = true;
o = s.taboption('ddns', form.Value, 'ddns_srv_serv', _('Service'), _('Can refer to RFC8552'));
o.value('http');
o.value('minecraft');
o.value('factorio');
o.value('easytier');
o.default = 'minecraft';
o.rmempty = false;
o.depends({ ddns_srv: "", "!reverse": true })
@@ -689,6 +693,7 @@ return view.extend({
o.value('tcp', _('TCP'));
o.value('udp', _('UDP'));
o.value('tls', _('TLS'));
o.value('wg', _('WireGuard'));
o.default = 'tcp';
o.rmempty = false;
o.depends({ ddns_srv: "", "!reverse": true })
@@ -697,21 +702,20 @@ return view.extend({
o = s.taboption('ddns', form.Value, 'ddns_srv_target', _('Target'));
o.datatype = 'hostname';
o.placeholder = 'cdn.example.com';
o.rmempty = true;
o.depends({ ddns_srv: "", "!reverse": true })
o.modalonly = true;
o = s.taboption('ddns', form.Value, 'ddns_srv_priority', _('Priority'));
o.datatype = 'range(0, 65535)';
o.default = 0;
o.rmempty = true;
o.rmempty = false;
o.depends({ ddns_srv: "", "!reverse": true })
o.modalonly = true;
o = s.taboption('ddns', form.Value, 'ddns_srv_weight', _('Weight'));
o.datatype = 'range(0, 65535)';
o.default = 65535;
o.rmempty = true;
o.rmempty = false;
o.depends({ ddns_srv: "", "!reverse": true })
o.modalonly = true;
@@ -731,28 +735,27 @@ return view.extend({
o = s.taboption('ddns', form.Value, 'ddns_https', _('FQDN'));
o.datatype = 'hostname';
o.placeholder = 'www.example.com';
o.rmempty = true;
o.modalonly = true;
o = s.taboption('ddns', form.Value, 'ddns_https_target', _('Target'));
o.datatype = "or('.', hostname)";
o.placeholder = '. or web.example.com';
o.default = '.';
o.rmempty = true;
o.rmempty = false;
o.depends({ ddns_https: "", "!reverse": true })
o.modalonly = true;
o = s.taboption('ddns', form.Value, 'ddns_https_svcparams', _('SvcParams'));
o.placeholder = 'alpn="h3,h3-29,h2,http/1.1" ipv4hint= ipv6hint= port=';
o.default = 'alpn="h2,http/1.1"';
o.rmempty = true;
o.rmempty = false;
o.depends({ ddns_https: "", "!reverse": true })
o.modalonly = true;
o = s.taboption('ddns', form.Value, 'ddns_https_priority', _('Priority'));
o.datatype = 'range(1, 65535)';
o.default = 1;
o.rmempty = true;
o.rmempty = false;
o.depends({ ddns_https: "", "!reverse": true })
o.modalonly = true;
+32 -19
View File
@@ -42,8 +42,8 @@ msgstr ""
msgid "Boot delay"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:679
#: htdocs/luci-static/resources/view/natmap.js:688
#: htdocs/luci-static/resources/view/natmap.js:702
#: htdocs/luci-static/resources/view/natmap.js:712
msgid "Can refer to RFC8552"
msgstr ""
@@ -59,8 +59,12 @@ msgstr ""
msgid "Comment"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:675
msgid "Content"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:306
#: htdocs/luci-static/resources/view/natmap.js:759
#: htdocs/luci-static/resources/view/natmap.js:784
msgid "Custom Script"
msgstr ""
@@ -116,8 +120,9 @@ msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:644
#: htdocs/luci-static/resources/view/natmap.js:651
#: htdocs/luci-static/resources/view/natmap.js:673
#: htdocs/luci-static/resources/view/natmap.js:731
#: htdocs/luci-static/resources/view/natmap.js:669
#: htdocs/luci-static/resources/view/natmap.js:696
#: htdocs/luci-static/resources/view/natmap.js:756
msgid "FQDN"
msgstr ""
@@ -180,7 +185,7 @@ msgstr ""
msgid "HTTP server"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:718
#: htdocs/luci-static/resources/view/natmap.js:743
msgid "HTTPS Record"
msgstr ""
@@ -277,8 +282,8 @@ msgstr ""
msgid "Port forwarding session idle timeout in seconds"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:704
#: htdocs/luci-static/resources/view/natmap.js:752
#: htdocs/luci-static/resources/view/natmap.js:729
#: htdocs/luci-static/resources/view/natmap.js:777
msgid "Priority"
msgstr ""
@@ -287,7 +292,7 @@ msgid "Private API Domain"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:341
#: htdocs/luci-static/resources/view/natmap.js:688
#: htdocs/luci-static/resources/view/natmap.js:712
#: htdocs/luci-static/resources/view/status/include/72_natmap.js:60
msgid "Protocol"
msgstr ""
@@ -313,7 +318,7 @@ msgstr ""
msgid "Restrict to address family"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:658
#: htdocs/luci-static/resources/view/natmap.js:681
msgid "SRV Record"
msgstr ""
@@ -332,7 +337,7 @@ msgstr ""
msgid "Server"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:679
#: htdocs/luci-static/resources/view/natmap.js:702
msgid "Service"
msgstr ""
@@ -340,12 +345,12 @@ msgstr ""
msgid "Set 0 will follow Public port"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:745
#: htdocs/luci-static/resources/view/natmap.js:770
msgid "SvcParams"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:174
#: htdocs/luci-static/resources/view/natmap.js:689
#: htdocs/luci-static/resources/view/natmap.js:713
msgid "TCP"
msgstr ""
@@ -357,12 +362,16 @@ msgstr ""
msgid "TCP STUN"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:691
#: htdocs/luci-static/resources/view/natmap.js:715
msgid "TLS"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:697
#: htdocs/luci-static/resources/view/natmap.js:737
#: htdocs/luci-static/resources/view/natmap.js:658
msgid "TXT Record"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:722
#: htdocs/luci-static/resources/view/natmap.js:762
msgid "Target"
msgstr ""
@@ -420,7 +429,7 @@ msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:179
#: htdocs/luci-static/resources/view/natmap.js:184
#: htdocs/luci-static/resources/view/natmap.js:690
#: htdocs/luci-static/resources/view/natmap.js:714
msgid "UDP"
msgstr ""
@@ -444,10 +453,14 @@ msgstr ""
msgid "Web UI Port"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:711
#: htdocs/luci-static/resources/view/natmap.js:736
msgid "Weight"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:716
msgid "WireGuard"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:163
msgid ""
"https://github.com/muink/rfc5780-stun-server/blob/master/valid_hosts_rfc5780."
@@ -469,6 +482,6 @@ msgstr ""
msgid "luci check may not detect all conflicts."
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:764
#: htdocs/luci-static/resources/view/natmap.js:789
msgid "⭮"
msgstr ""
+32 -19
View File
@@ -48,8 +48,8 @@ msgstr "Bind port"
msgid "Boot delay"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:679
#: htdocs/luci-static/resources/view/natmap.js:688
#: htdocs/luci-static/resources/view/natmap.js:702
#: htdocs/luci-static/resources/view/natmap.js:712
msgid "Can refer to RFC8552"
msgstr ""
@@ -65,8 +65,12 @@ msgstr ""
msgid "Comment"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:675
msgid "Content"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:306
#: htdocs/luci-static/resources/view/natmap.js:759
#: htdocs/luci-static/resources/view/natmap.js:784
msgid "Custom Script"
msgstr ""
@@ -122,8 +126,9 @@ msgstr "Ekstern port"
#: htdocs/luci-static/resources/view/natmap.js:644
#: htdocs/luci-static/resources/view/natmap.js:651
#: htdocs/luci-static/resources/view/natmap.js:673
#: htdocs/luci-static/resources/view/natmap.js:731
#: htdocs/luci-static/resources/view/natmap.js:669
#: htdocs/luci-static/resources/view/natmap.js:696
#: htdocs/luci-static/resources/view/natmap.js:756
msgid "FQDN"
msgstr ""
@@ -186,7 +191,7 @@ msgstr ""
msgid "HTTP server"
msgstr "HTTP server"
#: htdocs/luci-static/resources/view/natmap.js:718
#: htdocs/luci-static/resources/view/natmap.js:743
msgid "HTTPS Record"
msgstr ""
@@ -283,8 +288,8 @@ msgstr ""
msgid "Port forwarding session idle timeout in seconds"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:704
#: htdocs/luci-static/resources/view/natmap.js:752
#: htdocs/luci-static/resources/view/natmap.js:729
#: htdocs/luci-static/resources/view/natmap.js:777
msgid "Priority"
msgstr ""
@@ -293,7 +298,7 @@ msgid "Private API Domain"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:341
#: htdocs/luci-static/resources/view/natmap.js:688
#: htdocs/luci-static/resources/view/natmap.js:712
#: htdocs/luci-static/resources/view/status/include/72_natmap.js:60
msgid "Protocol"
msgstr "Protokol"
@@ -319,7 +324,7 @@ msgstr ""
msgid "Restrict to address family"
msgstr "Begræns til adresse familie"
#: htdocs/luci-static/resources/view/natmap.js:658
#: htdocs/luci-static/resources/view/natmap.js:681
msgid "SRV Record"
msgstr ""
@@ -338,7 +343,7 @@ msgstr "STUN server"
msgid "Server"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:679
#: htdocs/luci-static/resources/view/natmap.js:702
msgid "Service"
msgstr ""
@@ -346,12 +351,12 @@ msgstr ""
msgid "Set 0 will follow Public port"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:745
#: htdocs/luci-static/resources/view/natmap.js:770
msgid "SvcParams"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:174
#: htdocs/luci-static/resources/view/natmap.js:689
#: htdocs/luci-static/resources/view/natmap.js:713
msgid "TCP"
msgstr ""
@@ -363,12 +368,16 @@ msgstr ""
msgid "TCP STUN"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:691
#: htdocs/luci-static/resources/view/natmap.js:715
msgid "TLS"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:697
#: htdocs/luci-static/resources/view/natmap.js:737
#: htdocs/luci-static/resources/view/natmap.js:658
msgid "TXT Record"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:722
#: htdocs/luci-static/resources/view/natmap.js:762
msgid "Target"
msgstr ""
@@ -426,7 +435,7 @@ msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:179
#: htdocs/luci-static/resources/view/natmap.js:184
#: htdocs/luci-static/resources/view/natmap.js:690
#: htdocs/luci-static/resources/view/natmap.js:714
msgid "UDP"
msgstr ""
@@ -450,10 +459,14 @@ msgstr ""
msgid "Web UI Port"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:711
#: htdocs/luci-static/resources/view/natmap.js:736
msgid "Weight"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:716
msgid "WireGuard"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:163
msgid ""
"https://github.com/muink/rfc5780-stun-server/blob/master/valid_hosts_rfc5780."
@@ -475,7 +488,7 @@ msgstr ""
msgid "luci check may not detect all conflicts."
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:764
#: htdocs/luci-static/resources/view/natmap.js:789
msgid "⭮"
msgstr ""
+32 -19
View File
@@ -48,8 +48,8 @@ msgstr "Bindungsport"
msgid "Boot delay"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:679
#: htdocs/luci-static/resources/view/natmap.js:688
#: htdocs/luci-static/resources/view/natmap.js:702
#: htdocs/luci-static/resources/view/natmap.js:712
msgid "Can refer to RFC8552"
msgstr ""
@@ -65,8 +65,12 @@ msgstr ""
msgid "Comment"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:675
msgid "Content"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:306
#: htdocs/luci-static/resources/view/natmap.js:759
#: htdocs/luci-static/resources/view/natmap.js:784
msgid "Custom Script"
msgstr ""
@@ -122,8 +126,9 @@ msgstr "Externer Port"
#: htdocs/luci-static/resources/view/natmap.js:644
#: htdocs/luci-static/resources/view/natmap.js:651
#: htdocs/luci-static/resources/view/natmap.js:673
#: htdocs/luci-static/resources/view/natmap.js:731
#: htdocs/luci-static/resources/view/natmap.js:669
#: htdocs/luci-static/resources/view/natmap.js:696
#: htdocs/luci-static/resources/view/natmap.js:756
msgid "FQDN"
msgstr ""
@@ -186,7 +191,7 @@ msgstr ""
msgid "HTTP server"
msgstr "HTTP Server"
#: htdocs/luci-static/resources/view/natmap.js:718
#: htdocs/luci-static/resources/view/natmap.js:743
msgid "HTTPS Record"
msgstr ""
@@ -283,8 +288,8 @@ msgstr ""
msgid "Port forwarding session idle timeout in seconds"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:704
#: htdocs/luci-static/resources/view/natmap.js:752
#: htdocs/luci-static/resources/view/natmap.js:729
#: htdocs/luci-static/resources/view/natmap.js:777
msgid "Priority"
msgstr ""
@@ -293,7 +298,7 @@ msgid "Private API Domain"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:341
#: htdocs/luci-static/resources/view/natmap.js:688
#: htdocs/luci-static/resources/view/natmap.js:712
#: htdocs/luci-static/resources/view/status/include/72_natmap.js:60
msgid "Protocol"
msgstr "Protokoll"
@@ -319,7 +324,7 @@ msgstr ""
msgid "Restrict to address family"
msgstr "Auf Adressfamilie beschränken"
#: htdocs/luci-static/resources/view/natmap.js:658
#: htdocs/luci-static/resources/view/natmap.js:681
msgid "SRV Record"
msgstr ""
@@ -338,7 +343,7 @@ msgstr "STUN-Server"
msgid "Server"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:679
#: htdocs/luci-static/resources/view/natmap.js:702
msgid "Service"
msgstr ""
@@ -346,12 +351,12 @@ msgstr ""
msgid "Set 0 will follow Public port"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:745
#: htdocs/luci-static/resources/view/natmap.js:770
msgid "SvcParams"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:174
#: htdocs/luci-static/resources/view/natmap.js:689
#: htdocs/luci-static/resources/view/natmap.js:713
msgid "TCP"
msgstr ""
@@ -363,12 +368,16 @@ msgstr ""
msgid "TCP STUN"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:691
#: htdocs/luci-static/resources/view/natmap.js:715
msgid "TLS"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:697
#: htdocs/luci-static/resources/view/natmap.js:737
#: htdocs/luci-static/resources/view/natmap.js:658
msgid "TXT Record"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:722
#: htdocs/luci-static/resources/view/natmap.js:762
msgid "Target"
msgstr ""
@@ -426,7 +435,7 @@ msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:179
#: htdocs/luci-static/resources/view/natmap.js:184
#: htdocs/luci-static/resources/view/natmap.js:690
#: htdocs/luci-static/resources/view/natmap.js:714
msgid "UDP"
msgstr ""
@@ -450,10 +459,14 @@ msgstr ""
msgid "Web UI Port"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:711
#: htdocs/luci-static/resources/view/natmap.js:736
msgid "Weight"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:716
msgid "WireGuard"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:163
msgid ""
"https://github.com/muink/rfc5780-stun-server/blob/master/valid_hosts_rfc5780."
@@ -475,7 +488,7 @@ msgstr ""
msgid "luci check may not detect all conflicts."
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:764
#: htdocs/luci-static/resources/view/natmap.js:789
msgid "⭮"
msgstr ""
+32 -19
View File
@@ -48,8 +48,8 @@ msgstr "Vincular puerto"
msgid "Boot delay"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:679
#: htdocs/luci-static/resources/view/natmap.js:688
#: htdocs/luci-static/resources/view/natmap.js:702
#: htdocs/luci-static/resources/view/natmap.js:712
msgid "Can refer to RFC8552"
msgstr ""
@@ -65,8 +65,12 @@ msgstr ""
msgid "Comment"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:675
msgid "Content"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:306
#: htdocs/luci-static/resources/view/natmap.js:759
#: htdocs/luci-static/resources/view/natmap.js:784
msgid "Custom Script"
msgstr ""
@@ -122,8 +126,9 @@ msgstr "Puerto externo"
#: htdocs/luci-static/resources/view/natmap.js:644
#: htdocs/luci-static/resources/view/natmap.js:651
#: htdocs/luci-static/resources/view/natmap.js:673
#: htdocs/luci-static/resources/view/natmap.js:731
#: htdocs/luci-static/resources/view/natmap.js:669
#: htdocs/luci-static/resources/view/natmap.js:696
#: htdocs/luci-static/resources/view/natmap.js:756
msgid "FQDN"
msgstr ""
@@ -186,7 +191,7 @@ msgstr ""
msgid "HTTP server"
msgstr "Servidor HTTP"
#: htdocs/luci-static/resources/view/natmap.js:718
#: htdocs/luci-static/resources/view/natmap.js:743
msgid "HTTPS Record"
msgstr ""
@@ -283,8 +288,8 @@ msgstr ""
msgid "Port forwarding session idle timeout in seconds"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:704
#: htdocs/luci-static/resources/view/natmap.js:752
#: htdocs/luci-static/resources/view/natmap.js:729
#: htdocs/luci-static/resources/view/natmap.js:777
msgid "Priority"
msgstr ""
@@ -293,7 +298,7 @@ msgid "Private API Domain"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:341
#: htdocs/luci-static/resources/view/natmap.js:688
#: htdocs/luci-static/resources/view/natmap.js:712
#: htdocs/luci-static/resources/view/status/include/72_natmap.js:60
msgid "Protocol"
msgstr "Protocolo"
@@ -319,7 +324,7 @@ msgstr ""
msgid "Restrict to address family"
msgstr "Restringir para dirigirse a la familia"
#: htdocs/luci-static/resources/view/natmap.js:658
#: htdocs/luci-static/resources/view/natmap.js:681
msgid "SRV Record"
msgstr ""
@@ -338,7 +343,7 @@ msgstr "Servidor STUN"
msgid "Server"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:679
#: htdocs/luci-static/resources/view/natmap.js:702
msgid "Service"
msgstr ""
@@ -346,12 +351,12 @@ msgstr ""
msgid "Set 0 will follow Public port"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:745
#: htdocs/luci-static/resources/view/natmap.js:770
msgid "SvcParams"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:174
#: htdocs/luci-static/resources/view/natmap.js:689
#: htdocs/luci-static/resources/view/natmap.js:713
msgid "TCP"
msgstr ""
@@ -363,12 +368,16 @@ msgstr ""
msgid "TCP STUN"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:691
#: htdocs/luci-static/resources/view/natmap.js:715
msgid "TLS"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:697
#: htdocs/luci-static/resources/view/natmap.js:737
#: htdocs/luci-static/resources/view/natmap.js:658
msgid "TXT Record"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:722
#: htdocs/luci-static/resources/view/natmap.js:762
msgid "Target"
msgstr ""
@@ -426,7 +435,7 @@ msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:179
#: htdocs/luci-static/resources/view/natmap.js:184
#: htdocs/luci-static/resources/view/natmap.js:690
#: htdocs/luci-static/resources/view/natmap.js:714
msgid "UDP"
msgstr ""
@@ -450,10 +459,14 @@ msgstr ""
msgid "Web UI Port"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:711
#: htdocs/luci-static/resources/view/natmap.js:736
msgid "Weight"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:716
msgid "WireGuard"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:163
msgid ""
"https://github.com/muink/rfc5780-stun-server/blob/master/valid_hosts_rfc5780."
@@ -475,7 +488,7 @@ msgstr ""
msgid "luci check may not detect all conflicts."
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:764
#: htdocs/luci-static/resources/view/natmap.js:789
msgid "⭮"
msgstr ""
+32 -19
View File
@@ -48,8 +48,8 @@ msgstr "Port de liaison"
msgid "Boot delay"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:679
#: htdocs/luci-static/resources/view/natmap.js:688
#: htdocs/luci-static/resources/view/natmap.js:702
#: htdocs/luci-static/resources/view/natmap.js:712
msgid "Can refer to RFC8552"
msgstr ""
@@ -65,8 +65,12 @@ msgstr ""
msgid "Comment"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:675
msgid "Content"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:306
#: htdocs/luci-static/resources/view/natmap.js:759
#: htdocs/luci-static/resources/view/natmap.js:784
msgid "Custom Script"
msgstr ""
@@ -122,8 +126,9 @@ msgstr "Port externe"
#: htdocs/luci-static/resources/view/natmap.js:644
#: htdocs/luci-static/resources/view/natmap.js:651
#: htdocs/luci-static/resources/view/natmap.js:673
#: htdocs/luci-static/resources/view/natmap.js:731
#: htdocs/luci-static/resources/view/natmap.js:669
#: htdocs/luci-static/resources/view/natmap.js:696
#: htdocs/luci-static/resources/view/natmap.js:756
msgid "FQDN"
msgstr ""
@@ -186,7 +191,7 @@ msgstr ""
msgid "HTTP server"
msgstr "Serveur HTTP"
#: htdocs/luci-static/resources/view/natmap.js:718
#: htdocs/luci-static/resources/view/natmap.js:743
msgid "HTTPS Record"
msgstr ""
@@ -283,8 +288,8 @@ msgstr ""
msgid "Port forwarding session idle timeout in seconds"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:704
#: htdocs/luci-static/resources/view/natmap.js:752
#: htdocs/luci-static/resources/view/natmap.js:729
#: htdocs/luci-static/resources/view/natmap.js:777
msgid "Priority"
msgstr ""
@@ -293,7 +298,7 @@ msgid "Private API Domain"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:341
#: htdocs/luci-static/resources/view/natmap.js:688
#: htdocs/luci-static/resources/view/natmap.js:712
#: htdocs/luci-static/resources/view/status/include/72_natmap.js:60
msgid "Protocol"
msgstr "Protocole"
@@ -319,7 +324,7 @@ msgstr ""
msgid "Restrict to address family"
msgstr "Restreindre à la famille d’adresses"
#: htdocs/luci-static/resources/view/natmap.js:658
#: htdocs/luci-static/resources/view/natmap.js:681
msgid "SRV Record"
msgstr ""
@@ -338,7 +343,7 @@ msgstr "Serveur STUN"
msgid "Server"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:679
#: htdocs/luci-static/resources/view/natmap.js:702
msgid "Service"
msgstr ""
@@ -346,12 +351,12 @@ msgstr ""
msgid "Set 0 will follow Public port"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:745
#: htdocs/luci-static/resources/view/natmap.js:770
msgid "SvcParams"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:174
#: htdocs/luci-static/resources/view/natmap.js:689
#: htdocs/luci-static/resources/view/natmap.js:713
msgid "TCP"
msgstr ""
@@ -363,12 +368,16 @@ msgstr ""
msgid "TCP STUN"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:691
#: htdocs/luci-static/resources/view/natmap.js:715
msgid "TLS"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:697
#: htdocs/luci-static/resources/view/natmap.js:737
#: htdocs/luci-static/resources/view/natmap.js:658
msgid "TXT Record"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:722
#: htdocs/luci-static/resources/view/natmap.js:762
msgid "Target"
msgstr ""
@@ -426,7 +435,7 @@ msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:179
#: htdocs/luci-static/resources/view/natmap.js:184
#: htdocs/luci-static/resources/view/natmap.js:690
#: htdocs/luci-static/resources/view/natmap.js:714
msgid "UDP"
msgstr ""
@@ -450,10 +459,14 @@ msgstr ""
msgid "Web UI Port"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:711
#: htdocs/luci-static/resources/view/natmap.js:736
msgid "Weight"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:716
msgid "WireGuard"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:163
msgid ""
"https://github.com/muink/rfc5780-stun-server/blob/master/valid_hosts_rfc5780."
@@ -475,7 +488,7 @@ msgstr ""
msgid "luci check may not detect all conflicts."
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:764
#: htdocs/luci-static/resources/view/natmap.js:789
msgid "⭮"
msgstr ""
+32 -19
View File
@@ -48,8 +48,8 @@ msgstr "collega porta"
msgid "Boot delay"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:679
#: htdocs/luci-static/resources/view/natmap.js:688
#: htdocs/luci-static/resources/view/natmap.js:702
#: htdocs/luci-static/resources/view/natmap.js:712
msgid "Can refer to RFC8552"
msgstr ""
@@ -65,8 +65,12 @@ msgstr ""
msgid "Comment"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:675
msgid "Content"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:306
#: htdocs/luci-static/resources/view/natmap.js:759
#: htdocs/luci-static/resources/view/natmap.js:784
msgid "Custom Script"
msgstr ""
@@ -122,8 +126,9 @@ msgstr "Porta Esterna"
#: htdocs/luci-static/resources/view/natmap.js:644
#: htdocs/luci-static/resources/view/natmap.js:651
#: htdocs/luci-static/resources/view/natmap.js:673
#: htdocs/luci-static/resources/view/natmap.js:731
#: htdocs/luci-static/resources/view/natmap.js:669
#: htdocs/luci-static/resources/view/natmap.js:696
#: htdocs/luci-static/resources/view/natmap.js:756
msgid "FQDN"
msgstr ""
@@ -186,7 +191,7 @@ msgstr ""
msgid "HTTP server"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:718
#: htdocs/luci-static/resources/view/natmap.js:743
msgid "HTTPS Record"
msgstr ""
@@ -283,8 +288,8 @@ msgstr ""
msgid "Port forwarding session idle timeout in seconds"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:704
#: htdocs/luci-static/resources/view/natmap.js:752
#: htdocs/luci-static/resources/view/natmap.js:729
#: htdocs/luci-static/resources/view/natmap.js:777
msgid "Priority"
msgstr ""
@@ -293,7 +298,7 @@ msgid "Private API Domain"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:341
#: htdocs/luci-static/resources/view/natmap.js:688
#: htdocs/luci-static/resources/view/natmap.js:712
#: htdocs/luci-static/resources/view/status/include/72_natmap.js:60
msgid "Protocol"
msgstr ""
@@ -319,7 +324,7 @@ msgstr ""
msgid "Restrict to address family"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:658
#: htdocs/luci-static/resources/view/natmap.js:681
msgid "SRV Record"
msgstr ""
@@ -338,7 +343,7 @@ msgstr ""
msgid "Server"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:679
#: htdocs/luci-static/resources/view/natmap.js:702
msgid "Service"
msgstr ""
@@ -346,12 +351,12 @@ msgstr ""
msgid "Set 0 will follow Public port"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:745
#: htdocs/luci-static/resources/view/natmap.js:770
msgid "SvcParams"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:174
#: htdocs/luci-static/resources/view/natmap.js:689
#: htdocs/luci-static/resources/view/natmap.js:713
msgid "TCP"
msgstr ""
@@ -363,12 +368,16 @@ msgstr ""
msgid "TCP STUN"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:691
#: htdocs/luci-static/resources/view/natmap.js:715
msgid "TLS"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:697
#: htdocs/luci-static/resources/view/natmap.js:737
#: htdocs/luci-static/resources/view/natmap.js:658
msgid "TXT Record"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:722
#: htdocs/luci-static/resources/view/natmap.js:762
msgid "Target"
msgstr ""
@@ -426,7 +435,7 @@ msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:179
#: htdocs/luci-static/resources/view/natmap.js:184
#: htdocs/luci-static/resources/view/natmap.js:690
#: htdocs/luci-static/resources/view/natmap.js:714
msgid "UDP"
msgstr ""
@@ -450,10 +459,14 @@ msgstr ""
msgid "Web UI Port"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:711
#: htdocs/luci-static/resources/view/natmap.js:736
msgid "Weight"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:716
msgid "WireGuard"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:163
msgid ""
"https://github.com/muink/rfc5780-stun-server/blob/master/valid_hosts_rfc5780."
@@ -475,6 +488,6 @@ msgstr ""
msgid "luci check may not detect all conflicts."
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:764
#: htdocs/luci-static/resources/view/natmap.js:789
msgid "⭮"
msgstr ""
+32 -19
View File
@@ -50,8 +50,8 @@ msgstr "Priskirti prievadą"
msgid "Boot delay"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:679
#: htdocs/luci-static/resources/view/natmap.js:688
#: htdocs/luci-static/resources/view/natmap.js:702
#: htdocs/luci-static/resources/view/natmap.js:712
msgid "Can refer to RFC8552"
msgstr ""
@@ -67,8 +67,12 @@ msgstr ""
msgid "Comment"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:675
msgid "Content"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:306
#: htdocs/luci-static/resources/view/natmap.js:759
#: htdocs/luci-static/resources/view/natmap.js:784
msgid "Custom Script"
msgstr ""
@@ -124,8 +128,9 @@ msgstr "Išorinis prievadas"
#: htdocs/luci-static/resources/view/natmap.js:644
#: htdocs/luci-static/resources/view/natmap.js:651
#: htdocs/luci-static/resources/view/natmap.js:673
#: htdocs/luci-static/resources/view/natmap.js:731
#: htdocs/luci-static/resources/view/natmap.js:669
#: htdocs/luci-static/resources/view/natmap.js:696
#: htdocs/luci-static/resources/view/natmap.js:756
msgid "FQDN"
msgstr ""
@@ -188,7 +193,7 @@ msgstr ""
msgid "HTTP server"
msgstr "„HTTP“ serveris"
#: htdocs/luci-static/resources/view/natmap.js:718
#: htdocs/luci-static/resources/view/natmap.js:743
msgid "HTTPS Record"
msgstr ""
@@ -285,8 +290,8 @@ msgstr ""
msgid "Port forwarding session idle timeout in seconds"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:704
#: htdocs/luci-static/resources/view/natmap.js:752
#: htdocs/luci-static/resources/view/natmap.js:729
#: htdocs/luci-static/resources/view/natmap.js:777
msgid "Priority"
msgstr ""
@@ -295,7 +300,7 @@ msgid "Private API Domain"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:341
#: htdocs/luci-static/resources/view/natmap.js:688
#: htdocs/luci-static/resources/view/natmap.js:712
#: htdocs/luci-static/resources/view/status/include/72_natmap.js:60
msgid "Protocol"
msgstr "Protokolas"
@@ -321,7 +326,7 @@ msgstr ""
msgid "Restrict to address family"
msgstr "Riboti tik adresų šeimai"
#: htdocs/luci-static/resources/view/natmap.js:658
#: htdocs/luci-static/resources/view/natmap.js:681
msgid "SRV Record"
msgstr ""
@@ -340,7 +345,7 @@ msgstr "„STUN“ serveris"
msgid "Server"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:679
#: htdocs/luci-static/resources/view/natmap.js:702
msgid "Service"
msgstr ""
@@ -348,12 +353,12 @@ msgstr ""
msgid "Set 0 will follow Public port"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:745
#: htdocs/luci-static/resources/view/natmap.js:770
msgid "SvcParams"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:174
#: htdocs/luci-static/resources/view/natmap.js:689
#: htdocs/luci-static/resources/view/natmap.js:713
msgid "TCP"
msgstr ""
@@ -365,12 +370,16 @@ msgstr ""
msgid "TCP STUN"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:691
#: htdocs/luci-static/resources/view/natmap.js:715
msgid "TLS"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:697
#: htdocs/luci-static/resources/view/natmap.js:737
#: htdocs/luci-static/resources/view/natmap.js:658
msgid "TXT Record"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:722
#: htdocs/luci-static/resources/view/natmap.js:762
msgid "Target"
msgstr ""
@@ -428,7 +437,7 @@ msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:179
#: htdocs/luci-static/resources/view/natmap.js:184
#: htdocs/luci-static/resources/view/natmap.js:690
#: htdocs/luci-static/resources/view/natmap.js:714
msgid "UDP"
msgstr ""
@@ -452,10 +461,14 @@ msgstr ""
msgid "Web UI Port"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:711
#: htdocs/luci-static/resources/view/natmap.js:736
msgid "Weight"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:716
msgid "WireGuard"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:163
msgid ""
"https://github.com/muink/rfc5780-stun-server/blob/master/valid_hosts_rfc5780."
@@ -477,7 +490,7 @@ msgstr ""
msgid "luci check may not detect all conflicts."
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:764
#: htdocs/luci-static/resources/view/natmap.js:789
msgid "⭮"
msgstr ""
+32 -19
View File
@@ -48,8 +48,8 @@ msgstr "Bind poort"
msgid "Boot delay"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:679
#: htdocs/luci-static/resources/view/natmap.js:688
#: htdocs/luci-static/resources/view/natmap.js:702
#: htdocs/luci-static/resources/view/natmap.js:712
msgid "Can refer to RFC8552"
msgstr ""
@@ -65,8 +65,12 @@ msgstr ""
msgid "Comment"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:675
msgid "Content"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:306
#: htdocs/luci-static/resources/view/natmap.js:759
#: htdocs/luci-static/resources/view/natmap.js:784
msgid "Custom Script"
msgstr ""
@@ -122,8 +126,9 @@ msgstr "Externe poort"
#: htdocs/luci-static/resources/view/natmap.js:644
#: htdocs/luci-static/resources/view/natmap.js:651
#: htdocs/luci-static/resources/view/natmap.js:673
#: htdocs/luci-static/resources/view/natmap.js:731
#: htdocs/luci-static/resources/view/natmap.js:669
#: htdocs/luci-static/resources/view/natmap.js:696
#: htdocs/luci-static/resources/view/natmap.js:756
msgid "FQDN"
msgstr ""
@@ -186,7 +191,7 @@ msgstr ""
msgid "HTTP server"
msgstr "HTTP-server"
#: htdocs/luci-static/resources/view/natmap.js:718
#: htdocs/luci-static/resources/view/natmap.js:743
msgid "HTTPS Record"
msgstr ""
@@ -283,8 +288,8 @@ msgstr ""
msgid "Port forwarding session idle timeout in seconds"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:704
#: htdocs/luci-static/resources/view/natmap.js:752
#: htdocs/luci-static/resources/view/natmap.js:729
#: htdocs/luci-static/resources/view/natmap.js:777
msgid "Priority"
msgstr ""
@@ -293,7 +298,7 @@ msgid "Private API Domain"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:341
#: htdocs/luci-static/resources/view/natmap.js:688
#: htdocs/luci-static/resources/view/natmap.js:712
#: htdocs/luci-static/resources/view/status/include/72_natmap.js:60
msgid "Protocol"
msgstr "Protocol"
@@ -319,7 +324,7 @@ msgstr ""
msgid "Restrict to address family"
msgstr "Beperken tot adres familie"
#: htdocs/luci-static/resources/view/natmap.js:658
#: htdocs/luci-static/resources/view/natmap.js:681
msgid "SRV Record"
msgstr ""
@@ -338,7 +343,7 @@ msgstr "STUN-server"
msgid "Server"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:679
#: htdocs/luci-static/resources/view/natmap.js:702
msgid "Service"
msgstr ""
@@ -346,12 +351,12 @@ msgstr ""
msgid "Set 0 will follow Public port"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:745
#: htdocs/luci-static/resources/view/natmap.js:770
msgid "SvcParams"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:174
#: htdocs/luci-static/resources/view/natmap.js:689
#: htdocs/luci-static/resources/view/natmap.js:713
msgid "TCP"
msgstr ""
@@ -363,12 +368,16 @@ msgstr ""
msgid "TCP STUN"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:691
#: htdocs/luci-static/resources/view/natmap.js:715
msgid "TLS"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:697
#: htdocs/luci-static/resources/view/natmap.js:737
#: htdocs/luci-static/resources/view/natmap.js:658
msgid "TXT Record"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:722
#: htdocs/luci-static/resources/view/natmap.js:762
msgid "Target"
msgstr ""
@@ -426,7 +435,7 @@ msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:179
#: htdocs/luci-static/resources/view/natmap.js:184
#: htdocs/luci-static/resources/view/natmap.js:690
#: htdocs/luci-static/resources/view/natmap.js:714
msgid "UDP"
msgstr ""
@@ -450,10 +459,14 @@ msgstr ""
msgid "Web UI Port"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:711
#: htdocs/luci-static/resources/view/natmap.js:736
msgid "Weight"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:716
msgid "WireGuard"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:163
msgid ""
"https://github.com/muink/rfc5780-stun-server/blob/master/valid_hosts_rfc5780."
@@ -475,7 +488,7 @@ msgstr ""
msgid "luci check may not detect all conflicts."
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:764
#: htdocs/luci-static/resources/view/natmap.js:789
msgid "⭮"
msgstr ""
+32 -19
View File
@@ -49,8 +49,8 @@ msgstr "Zwiąż port"
msgid "Boot delay"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:679
#: htdocs/luci-static/resources/view/natmap.js:688
#: htdocs/luci-static/resources/view/natmap.js:702
#: htdocs/luci-static/resources/view/natmap.js:712
msgid "Can refer to RFC8552"
msgstr ""
@@ -66,8 +66,12 @@ msgstr ""
msgid "Comment"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:675
msgid "Content"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:306
#: htdocs/luci-static/resources/view/natmap.js:759
#: htdocs/luci-static/resources/view/natmap.js:784
msgid "Custom Script"
msgstr ""
@@ -123,8 +127,9 @@ msgstr "Port zewnętrzny"
#: htdocs/luci-static/resources/view/natmap.js:644
#: htdocs/luci-static/resources/view/natmap.js:651
#: htdocs/luci-static/resources/view/natmap.js:673
#: htdocs/luci-static/resources/view/natmap.js:731
#: htdocs/luci-static/resources/view/natmap.js:669
#: htdocs/luci-static/resources/view/natmap.js:696
#: htdocs/luci-static/resources/view/natmap.js:756
msgid "FQDN"
msgstr ""
@@ -187,7 +192,7 @@ msgstr ""
msgid "HTTP server"
msgstr "Serwer HTTP"
#: htdocs/luci-static/resources/view/natmap.js:718
#: htdocs/luci-static/resources/view/natmap.js:743
msgid "HTTPS Record"
msgstr ""
@@ -284,8 +289,8 @@ msgstr ""
msgid "Port forwarding session idle timeout in seconds"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:704
#: htdocs/luci-static/resources/view/natmap.js:752
#: htdocs/luci-static/resources/view/natmap.js:729
#: htdocs/luci-static/resources/view/natmap.js:777
msgid "Priority"
msgstr ""
@@ -294,7 +299,7 @@ msgid "Private API Domain"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:341
#: htdocs/luci-static/resources/view/natmap.js:688
#: htdocs/luci-static/resources/view/natmap.js:712
#: htdocs/luci-static/resources/view/status/include/72_natmap.js:60
msgid "Protocol"
msgstr "Protokół"
@@ -320,7 +325,7 @@ msgstr ""
msgid "Restrict to address family"
msgstr "Ogranicz do rodziny adresów"
#: htdocs/luci-static/resources/view/natmap.js:658
#: htdocs/luci-static/resources/view/natmap.js:681
msgid "SRV Record"
msgstr ""
@@ -339,7 +344,7 @@ msgstr "Serwer STUN"
msgid "Server"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:679
#: htdocs/luci-static/resources/view/natmap.js:702
msgid "Service"
msgstr ""
@@ -347,12 +352,12 @@ msgstr ""
msgid "Set 0 will follow Public port"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:745
#: htdocs/luci-static/resources/view/natmap.js:770
msgid "SvcParams"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:174
#: htdocs/luci-static/resources/view/natmap.js:689
#: htdocs/luci-static/resources/view/natmap.js:713
msgid "TCP"
msgstr ""
@@ -364,12 +369,16 @@ msgstr ""
msgid "TCP STUN"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:691
#: htdocs/luci-static/resources/view/natmap.js:715
msgid "TLS"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:697
#: htdocs/luci-static/resources/view/natmap.js:737
#: htdocs/luci-static/resources/view/natmap.js:658
msgid "TXT Record"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:722
#: htdocs/luci-static/resources/view/natmap.js:762
msgid "Target"
msgstr ""
@@ -427,7 +436,7 @@ msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:179
#: htdocs/luci-static/resources/view/natmap.js:184
#: htdocs/luci-static/resources/view/natmap.js:690
#: htdocs/luci-static/resources/view/natmap.js:714
msgid "UDP"
msgstr ""
@@ -451,10 +460,14 @@ msgstr ""
msgid "Web UI Port"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:711
#: htdocs/luci-static/resources/view/natmap.js:736
msgid "Weight"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:716
msgid "WireGuard"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:163
msgid ""
"https://github.com/muink/rfc5780-stun-server/blob/master/valid_hosts_rfc5780."
@@ -476,7 +489,7 @@ msgstr ""
msgid "luci check may not detect all conflicts."
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:764
#: htdocs/luci-static/resources/view/natmap.js:789
msgid "⭮"
msgstr ""
+32 -19
View File
@@ -48,8 +48,8 @@ msgstr "Porta do bind"
msgid "Boot delay"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:679
#: htdocs/luci-static/resources/view/natmap.js:688
#: htdocs/luci-static/resources/view/natmap.js:702
#: htdocs/luci-static/resources/view/natmap.js:712
msgid "Can refer to RFC8552"
msgstr ""
@@ -65,8 +65,12 @@ msgstr ""
msgid "Comment"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:675
msgid "Content"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:306
#: htdocs/luci-static/resources/view/natmap.js:759
#: htdocs/luci-static/resources/view/natmap.js:784
msgid "Custom Script"
msgstr ""
@@ -122,8 +126,9 @@ msgstr "Porta externa"
#: htdocs/luci-static/resources/view/natmap.js:644
#: htdocs/luci-static/resources/view/natmap.js:651
#: htdocs/luci-static/resources/view/natmap.js:673
#: htdocs/luci-static/resources/view/natmap.js:731
#: htdocs/luci-static/resources/view/natmap.js:669
#: htdocs/luci-static/resources/view/natmap.js:696
#: htdocs/luci-static/resources/view/natmap.js:756
msgid "FQDN"
msgstr ""
@@ -186,7 +191,7 @@ msgstr ""
msgid "HTTP server"
msgstr "Servidor HTTP"
#: htdocs/luci-static/resources/view/natmap.js:718
#: htdocs/luci-static/resources/view/natmap.js:743
msgid "HTTPS Record"
msgstr ""
@@ -283,8 +288,8 @@ msgstr ""
msgid "Port forwarding session idle timeout in seconds"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:704
#: htdocs/luci-static/resources/view/natmap.js:752
#: htdocs/luci-static/resources/view/natmap.js:729
#: htdocs/luci-static/resources/view/natmap.js:777
msgid "Priority"
msgstr ""
@@ -293,7 +298,7 @@ msgid "Private API Domain"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:341
#: htdocs/luci-static/resources/view/natmap.js:688
#: htdocs/luci-static/resources/view/natmap.js:712
#: htdocs/luci-static/resources/view/status/include/72_natmap.js:60
msgid "Protocol"
msgstr "Protocolo"
@@ -319,7 +324,7 @@ msgstr ""
msgid "Restrict to address family"
msgstr "Restringe à uma família de endereços"
#: htdocs/luci-static/resources/view/natmap.js:658
#: htdocs/luci-static/resources/view/natmap.js:681
msgid "SRV Record"
msgstr ""
@@ -338,7 +343,7 @@ msgstr "Servidor STUN"
msgid "Server"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:679
#: htdocs/luci-static/resources/view/natmap.js:702
msgid "Service"
msgstr ""
@@ -346,12 +351,12 @@ msgstr ""
msgid "Set 0 will follow Public port"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:745
#: htdocs/luci-static/resources/view/natmap.js:770
msgid "SvcParams"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:174
#: htdocs/luci-static/resources/view/natmap.js:689
#: htdocs/luci-static/resources/view/natmap.js:713
msgid "TCP"
msgstr ""
@@ -363,12 +368,16 @@ msgstr ""
msgid "TCP STUN"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:691
#: htdocs/luci-static/resources/view/natmap.js:715
msgid "TLS"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:697
#: htdocs/luci-static/resources/view/natmap.js:737
#: htdocs/luci-static/resources/view/natmap.js:658
msgid "TXT Record"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:722
#: htdocs/luci-static/resources/view/natmap.js:762
msgid "Target"
msgstr ""
@@ -426,7 +435,7 @@ msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:179
#: htdocs/luci-static/resources/view/natmap.js:184
#: htdocs/luci-static/resources/view/natmap.js:690
#: htdocs/luci-static/resources/view/natmap.js:714
msgid "UDP"
msgstr ""
@@ -450,10 +459,14 @@ msgstr ""
msgid "Web UI Port"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:711
#: htdocs/luci-static/resources/view/natmap.js:736
msgid "Weight"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:716
msgid "WireGuard"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:163
msgid ""
"https://github.com/muink/rfc5780-stun-server/blob/master/valid_hosts_rfc5780."
@@ -475,7 +488,7 @@ msgstr ""
msgid "luci check may not detect all conflicts."
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:764
#: htdocs/luci-static/resources/view/natmap.js:789
msgid "⭮"
msgstr ""
+32 -19
View File
@@ -48,8 +48,8 @@ msgstr "Porta do bind"
msgid "Boot delay"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:679
#: htdocs/luci-static/resources/view/natmap.js:688
#: htdocs/luci-static/resources/view/natmap.js:702
#: htdocs/luci-static/resources/view/natmap.js:712
msgid "Can refer to RFC8552"
msgstr ""
@@ -65,8 +65,12 @@ msgstr ""
msgid "Comment"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:675
msgid "Content"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:306
#: htdocs/luci-static/resources/view/natmap.js:759
#: htdocs/luci-static/resources/view/natmap.js:784
msgid "Custom Script"
msgstr ""
@@ -122,8 +126,9 @@ msgstr "Porta externa"
#: htdocs/luci-static/resources/view/natmap.js:644
#: htdocs/luci-static/resources/view/natmap.js:651
#: htdocs/luci-static/resources/view/natmap.js:673
#: htdocs/luci-static/resources/view/natmap.js:731
#: htdocs/luci-static/resources/view/natmap.js:669
#: htdocs/luci-static/resources/view/natmap.js:696
#: htdocs/luci-static/resources/view/natmap.js:756
msgid "FQDN"
msgstr ""
@@ -186,7 +191,7 @@ msgstr ""
msgid "HTTP server"
msgstr "Servidor HTTP"
#: htdocs/luci-static/resources/view/natmap.js:718
#: htdocs/luci-static/resources/view/natmap.js:743
msgid "HTTPS Record"
msgstr ""
@@ -283,8 +288,8 @@ msgstr ""
msgid "Port forwarding session idle timeout in seconds"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:704
#: htdocs/luci-static/resources/view/natmap.js:752
#: htdocs/luci-static/resources/view/natmap.js:729
#: htdocs/luci-static/resources/view/natmap.js:777
msgid "Priority"
msgstr ""
@@ -293,7 +298,7 @@ msgid "Private API Domain"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:341
#: htdocs/luci-static/resources/view/natmap.js:688
#: htdocs/luci-static/resources/view/natmap.js:712
#: htdocs/luci-static/resources/view/status/include/72_natmap.js:60
msgid "Protocol"
msgstr "Protocolo"
@@ -319,7 +324,7 @@ msgstr ""
msgid "Restrict to address family"
msgstr "Restringe à uma família de endereços"
#: htdocs/luci-static/resources/view/natmap.js:658
#: htdocs/luci-static/resources/view/natmap.js:681
msgid "SRV Record"
msgstr ""
@@ -338,7 +343,7 @@ msgstr "Servidor STUN"
msgid "Server"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:679
#: htdocs/luci-static/resources/view/natmap.js:702
msgid "Service"
msgstr ""
@@ -346,12 +351,12 @@ msgstr ""
msgid "Set 0 will follow Public port"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:745
#: htdocs/luci-static/resources/view/natmap.js:770
msgid "SvcParams"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:174
#: htdocs/luci-static/resources/view/natmap.js:689
#: htdocs/luci-static/resources/view/natmap.js:713
msgid "TCP"
msgstr ""
@@ -363,12 +368,16 @@ msgstr ""
msgid "TCP STUN"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:691
#: htdocs/luci-static/resources/view/natmap.js:715
msgid "TLS"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:697
#: htdocs/luci-static/resources/view/natmap.js:737
#: htdocs/luci-static/resources/view/natmap.js:658
msgid "TXT Record"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:722
#: htdocs/luci-static/resources/view/natmap.js:762
msgid "Target"
msgstr ""
@@ -426,7 +435,7 @@ msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:179
#: htdocs/luci-static/resources/view/natmap.js:184
#: htdocs/luci-static/resources/view/natmap.js:690
#: htdocs/luci-static/resources/view/natmap.js:714
msgid "UDP"
msgstr ""
@@ -450,10 +459,14 @@ msgstr ""
msgid "Web UI Port"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:711
#: htdocs/luci-static/resources/view/natmap.js:736
msgid "Weight"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:716
msgid "WireGuard"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:163
msgid ""
"https://github.com/muink/rfc5780-stun-server/blob/master/valid_hosts_rfc5780."
@@ -475,7 +488,7 @@ msgstr ""
msgid "luci check may not detect all conflicts."
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:764
#: htdocs/luci-static/resources/view/natmap.js:789
msgid "⭮"
msgstr ""
+32 -19
View File
@@ -49,8 +49,8 @@ msgstr "Legați portul"
msgid "Boot delay"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:679
#: htdocs/luci-static/resources/view/natmap.js:688
#: htdocs/luci-static/resources/view/natmap.js:702
#: htdocs/luci-static/resources/view/natmap.js:712
msgid "Can refer to RFC8552"
msgstr ""
@@ -66,8 +66,12 @@ msgstr ""
msgid "Comment"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:675
msgid "Content"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:306
#: htdocs/luci-static/resources/view/natmap.js:759
#: htdocs/luci-static/resources/view/natmap.js:784
msgid "Custom Script"
msgstr ""
@@ -123,8 +127,9 @@ msgstr "Port extern"
#: htdocs/luci-static/resources/view/natmap.js:644
#: htdocs/luci-static/resources/view/natmap.js:651
#: htdocs/luci-static/resources/view/natmap.js:673
#: htdocs/luci-static/resources/view/natmap.js:731
#: htdocs/luci-static/resources/view/natmap.js:669
#: htdocs/luci-static/resources/view/natmap.js:696
#: htdocs/luci-static/resources/view/natmap.js:756
msgid "FQDN"
msgstr ""
@@ -187,7 +192,7 @@ msgstr ""
msgid "HTTP server"
msgstr "Server HTTP"
#: htdocs/luci-static/resources/view/natmap.js:718
#: htdocs/luci-static/resources/view/natmap.js:743
msgid "HTTPS Record"
msgstr ""
@@ -284,8 +289,8 @@ msgstr ""
msgid "Port forwarding session idle timeout in seconds"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:704
#: htdocs/luci-static/resources/view/natmap.js:752
#: htdocs/luci-static/resources/view/natmap.js:729
#: htdocs/luci-static/resources/view/natmap.js:777
msgid "Priority"
msgstr ""
@@ -294,7 +299,7 @@ msgid "Private API Domain"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:341
#: htdocs/luci-static/resources/view/natmap.js:688
#: htdocs/luci-static/resources/view/natmap.js:712
#: htdocs/luci-static/resources/view/status/include/72_natmap.js:60
msgid "Protocol"
msgstr "Protocol"
@@ -320,7 +325,7 @@ msgstr ""
msgid "Restrict to address family"
msgstr "Restricționarea la adresa familiei"
#: htdocs/luci-static/resources/view/natmap.js:658
#: htdocs/luci-static/resources/view/natmap.js:681
msgid "SRV Record"
msgstr ""
@@ -339,7 +344,7 @@ msgstr "Server STUN"
msgid "Server"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:679
#: htdocs/luci-static/resources/view/natmap.js:702
msgid "Service"
msgstr ""
@@ -347,12 +352,12 @@ msgstr ""
msgid "Set 0 will follow Public port"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:745
#: htdocs/luci-static/resources/view/natmap.js:770
msgid "SvcParams"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:174
#: htdocs/luci-static/resources/view/natmap.js:689
#: htdocs/luci-static/resources/view/natmap.js:713
msgid "TCP"
msgstr ""
@@ -364,12 +369,16 @@ msgstr ""
msgid "TCP STUN"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:691
#: htdocs/luci-static/resources/view/natmap.js:715
msgid "TLS"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:697
#: htdocs/luci-static/resources/view/natmap.js:737
#: htdocs/luci-static/resources/view/natmap.js:658
msgid "TXT Record"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:722
#: htdocs/luci-static/resources/view/natmap.js:762
msgid "Target"
msgstr ""
@@ -427,7 +436,7 @@ msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:179
#: htdocs/luci-static/resources/view/natmap.js:184
#: htdocs/luci-static/resources/view/natmap.js:690
#: htdocs/luci-static/resources/view/natmap.js:714
msgid "UDP"
msgstr ""
@@ -451,10 +460,14 @@ msgstr ""
msgid "Web UI Port"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:711
#: htdocs/luci-static/resources/view/natmap.js:736
msgid "Weight"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:716
msgid "WireGuard"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:163
msgid ""
"https://github.com/muink/rfc5780-stun-server/blob/master/valid_hosts_rfc5780."
@@ -476,7 +489,7 @@ msgstr ""
msgid "luci check may not detect all conflicts."
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:764
#: htdocs/luci-static/resources/view/natmap.js:789
msgid "⭮"
msgstr ""
+32 -19
View File
@@ -49,8 +49,8 @@ msgstr "Порт сервера"
msgid "Boot delay"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:679
#: htdocs/luci-static/resources/view/natmap.js:688
#: htdocs/luci-static/resources/view/natmap.js:702
#: htdocs/luci-static/resources/view/natmap.js:712
msgid "Can refer to RFC8552"
msgstr ""
@@ -66,8 +66,12 @@ msgstr ""
msgid "Comment"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:675
msgid "Content"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:306
#: htdocs/luci-static/resources/view/natmap.js:759
#: htdocs/luci-static/resources/view/natmap.js:784
msgid "Custom Script"
msgstr ""
@@ -123,8 +127,9 @@ msgstr "Внешний порт"
#: htdocs/luci-static/resources/view/natmap.js:644
#: htdocs/luci-static/resources/view/natmap.js:651
#: htdocs/luci-static/resources/view/natmap.js:673
#: htdocs/luci-static/resources/view/natmap.js:731
#: htdocs/luci-static/resources/view/natmap.js:669
#: htdocs/luci-static/resources/view/natmap.js:696
#: htdocs/luci-static/resources/view/natmap.js:756
msgid "FQDN"
msgstr ""
@@ -187,7 +192,7 @@ msgstr ""
msgid "HTTP server"
msgstr "HTTP-сервер"
#: htdocs/luci-static/resources/view/natmap.js:718
#: htdocs/luci-static/resources/view/natmap.js:743
msgid "HTTPS Record"
msgstr ""
@@ -284,8 +289,8 @@ msgstr ""
msgid "Port forwarding session idle timeout in seconds"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:704
#: htdocs/luci-static/resources/view/natmap.js:752
#: htdocs/luci-static/resources/view/natmap.js:729
#: htdocs/luci-static/resources/view/natmap.js:777
msgid "Priority"
msgstr ""
@@ -294,7 +299,7 @@ msgid "Private API Domain"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:341
#: htdocs/luci-static/resources/view/natmap.js:688
#: htdocs/luci-static/resources/view/natmap.js:712
#: htdocs/luci-static/resources/view/status/include/72_natmap.js:60
msgid "Protocol"
msgstr "Протокол"
@@ -320,7 +325,7 @@ msgstr ""
msgid "Restrict to address family"
msgstr "Использовать протокол"
#: htdocs/luci-static/resources/view/natmap.js:658
#: htdocs/luci-static/resources/view/natmap.js:681
msgid "SRV Record"
msgstr ""
@@ -339,7 +344,7 @@ msgstr "Сервер STUN"
msgid "Server"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:679
#: htdocs/luci-static/resources/view/natmap.js:702
msgid "Service"
msgstr ""
@@ -347,12 +352,12 @@ msgstr ""
msgid "Set 0 will follow Public port"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:745
#: htdocs/luci-static/resources/view/natmap.js:770
msgid "SvcParams"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:174
#: htdocs/luci-static/resources/view/natmap.js:689
#: htdocs/luci-static/resources/view/natmap.js:713
msgid "TCP"
msgstr ""
@@ -364,12 +369,16 @@ msgstr ""
msgid "TCP STUN"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:691
#: htdocs/luci-static/resources/view/natmap.js:715
msgid "TLS"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:697
#: htdocs/luci-static/resources/view/natmap.js:737
#: htdocs/luci-static/resources/view/natmap.js:658
msgid "TXT Record"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:722
#: htdocs/luci-static/resources/view/natmap.js:762
msgid "Target"
msgstr ""
@@ -427,7 +436,7 @@ msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:179
#: htdocs/luci-static/resources/view/natmap.js:184
#: htdocs/luci-static/resources/view/natmap.js:690
#: htdocs/luci-static/resources/view/natmap.js:714
msgid "UDP"
msgstr ""
@@ -451,10 +460,14 @@ msgstr ""
msgid "Web UI Port"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:711
#: htdocs/luci-static/resources/view/natmap.js:736
msgid "Weight"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:716
msgid "WireGuard"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:163
msgid ""
"https://github.com/muink/rfc5780-stun-server/blob/master/valid_hosts_rfc5780."
@@ -476,7 +489,7 @@ msgstr ""
msgid "luci check may not detect all conflicts."
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:764
#: htdocs/luci-static/resources/view/natmap.js:789
msgid "⭮"
msgstr ""
+32 -19
View File
@@ -42,8 +42,8 @@ msgstr ""
msgid "Boot delay"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:679
#: htdocs/luci-static/resources/view/natmap.js:688
#: htdocs/luci-static/resources/view/natmap.js:702
#: htdocs/luci-static/resources/view/natmap.js:712
msgid "Can refer to RFC8552"
msgstr ""
@@ -59,8 +59,12 @@ msgstr ""
msgid "Comment"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:675
msgid "Content"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:306
#: htdocs/luci-static/resources/view/natmap.js:759
#: htdocs/luci-static/resources/view/natmap.js:784
msgid "Custom Script"
msgstr ""
@@ -116,8 +120,9 @@ msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:644
#: htdocs/luci-static/resources/view/natmap.js:651
#: htdocs/luci-static/resources/view/natmap.js:673
#: htdocs/luci-static/resources/view/natmap.js:731
#: htdocs/luci-static/resources/view/natmap.js:669
#: htdocs/luci-static/resources/view/natmap.js:696
#: htdocs/luci-static/resources/view/natmap.js:756
msgid "FQDN"
msgstr ""
@@ -180,7 +185,7 @@ msgstr ""
msgid "HTTP server"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:718
#: htdocs/luci-static/resources/view/natmap.js:743
msgid "HTTPS Record"
msgstr ""
@@ -277,8 +282,8 @@ msgstr ""
msgid "Port forwarding session idle timeout in seconds"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:704
#: htdocs/luci-static/resources/view/natmap.js:752
#: htdocs/luci-static/resources/view/natmap.js:729
#: htdocs/luci-static/resources/view/natmap.js:777
msgid "Priority"
msgstr ""
@@ -287,7 +292,7 @@ msgid "Private API Domain"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:341
#: htdocs/luci-static/resources/view/natmap.js:688
#: htdocs/luci-static/resources/view/natmap.js:712
#: htdocs/luci-static/resources/view/status/include/72_natmap.js:60
msgid "Protocol"
msgstr ""
@@ -313,7 +318,7 @@ msgstr ""
msgid "Restrict to address family"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:658
#: htdocs/luci-static/resources/view/natmap.js:681
msgid "SRV Record"
msgstr ""
@@ -332,7 +337,7 @@ msgstr ""
msgid "Server"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:679
#: htdocs/luci-static/resources/view/natmap.js:702
msgid "Service"
msgstr ""
@@ -340,12 +345,12 @@ msgstr ""
msgid "Set 0 will follow Public port"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:745
#: htdocs/luci-static/resources/view/natmap.js:770
msgid "SvcParams"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:174
#: htdocs/luci-static/resources/view/natmap.js:689
#: htdocs/luci-static/resources/view/natmap.js:713
msgid "TCP"
msgstr ""
@@ -357,12 +362,16 @@ msgstr ""
msgid "TCP STUN"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:691
#: htdocs/luci-static/resources/view/natmap.js:715
msgid "TLS"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:697
#: htdocs/luci-static/resources/view/natmap.js:737
#: htdocs/luci-static/resources/view/natmap.js:658
msgid "TXT Record"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:722
#: htdocs/luci-static/resources/view/natmap.js:762
msgid "Target"
msgstr ""
@@ -420,7 +429,7 @@ msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:179
#: htdocs/luci-static/resources/view/natmap.js:184
#: htdocs/luci-static/resources/view/natmap.js:690
#: htdocs/luci-static/resources/view/natmap.js:714
msgid "UDP"
msgstr ""
@@ -444,10 +453,14 @@ msgstr ""
msgid "Web UI Port"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:711
#: htdocs/luci-static/resources/view/natmap.js:736
msgid "Weight"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:716
msgid "WireGuard"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:163
msgid ""
"https://github.com/muink/rfc5780-stun-server/blob/master/valid_hosts_rfc5780."
@@ -469,6 +482,6 @@ msgstr ""
msgid "luci check may not detect all conflicts."
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:764
#: htdocs/luci-static/resources/view/natmap.js:789
msgid "⭮"
msgstr ""
+32 -19
View File
@@ -39,8 +39,8 @@ msgstr ""
msgid "Boot delay"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:679
#: htdocs/luci-static/resources/view/natmap.js:688
#: htdocs/luci-static/resources/view/natmap.js:702
#: htdocs/luci-static/resources/view/natmap.js:712
msgid "Can refer to RFC8552"
msgstr ""
@@ -56,8 +56,12 @@ msgstr ""
msgid "Comment"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:675
msgid "Content"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:306
#: htdocs/luci-static/resources/view/natmap.js:759
#: htdocs/luci-static/resources/view/natmap.js:784
msgid "Custom Script"
msgstr ""
@@ -113,8 +117,9 @@ msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:644
#: htdocs/luci-static/resources/view/natmap.js:651
#: htdocs/luci-static/resources/view/natmap.js:673
#: htdocs/luci-static/resources/view/natmap.js:731
#: htdocs/luci-static/resources/view/natmap.js:669
#: htdocs/luci-static/resources/view/natmap.js:696
#: htdocs/luci-static/resources/view/natmap.js:756
msgid "FQDN"
msgstr ""
@@ -177,7 +182,7 @@ msgstr ""
msgid "HTTP server"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:718
#: htdocs/luci-static/resources/view/natmap.js:743
msgid "HTTPS Record"
msgstr ""
@@ -274,8 +279,8 @@ msgstr ""
msgid "Port forwarding session idle timeout in seconds"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:704
#: htdocs/luci-static/resources/view/natmap.js:752
#: htdocs/luci-static/resources/view/natmap.js:729
#: htdocs/luci-static/resources/view/natmap.js:777
msgid "Priority"
msgstr ""
@@ -284,7 +289,7 @@ msgid "Private API Domain"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:341
#: htdocs/luci-static/resources/view/natmap.js:688
#: htdocs/luci-static/resources/view/natmap.js:712
#: htdocs/luci-static/resources/view/status/include/72_natmap.js:60
msgid "Protocol"
msgstr ""
@@ -310,7 +315,7 @@ msgstr ""
msgid "Restrict to address family"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:658
#: htdocs/luci-static/resources/view/natmap.js:681
msgid "SRV Record"
msgstr ""
@@ -329,7 +334,7 @@ msgstr ""
msgid "Server"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:679
#: htdocs/luci-static/resources/view/natmap.js:702
msgid "Service"
msgstr ""
@@ -337,12 +342,12 @@ msgstr ""
msgid "Set 0 will follow Public port"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:745
#: htdocs/luci-static/resources/view/natmap.js:770
msgid "SvcParams"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:174
#: htdocs/luci-static/resources/view/natmap.js:689
#: htdocs/luci-static/resources/view/natmap.js:713
msgid "TCP"
msgstr ""
@@ -354,12 +359,16 @@ msgstr ""
msgid "TCP STUN"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:691
#: htdocs/luci-static/resources/view/natmap.js:715
msgid "TLS"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:697
#: htdocs/luci-static/resources/view/natmap.js:737
#: htdocs/luci-static/resources/view/natmap.js:658
msgid "TXT Record"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:722
#: htdocs/luci-static/resources/view/natmap.js:762
msgid "Target"
msgstr ""
@@ -417,7 +426,7 @@ msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:179
#: htdocs/luci-static/resources/view/natmap.js:184
#: htdocs/luci-static/resources/view/natmap.js:690
#: htdocs/luci-static/resources/view/natmap.js:714
msgid "UDP"
msgstr ""
@@ -441,10 +450,14 @@ msgstr ""
msgid "Web UI Port"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:711
#: htdocs/luci-static/resources/view/natmap.js:736
msgid "Weight"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:716
msgid "WireGuard"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:163
msgid ""
"https://github.com/muink/rfc5780-stun-server/blob/master/valid_hosts_rfc5780."
@@ -466,6 +479,6 @@ msgstr ""
msgid "luci check may not detect all conflicts."
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:764
#: htdocs/luci-static/resources/view/natmap.js:789
msgid "⭮"
msgstr ""
+32 -19
View File
@@ -42,8 +42,8 @@ msgstr ""
msgid "Boot delay"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:679
#: htdocs/luci-static/resources/view/natmap.js:688
#: htdocs/luci-static/resources/view/natmap.js:702
#: htdocs/luci-static/resources/view/natmap.js:712
msgid "Can refer to RFC8552"
msgstr ""
@@ -59,8 +59,12 @@ msgstr ""
msgid "Comment"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:675
msgid "Content"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:306
#: htdocs/luci-static/resources/view/natmap.js:759
#: htdocs/luci-static/resources/view/natmap.js:784
msgid "Custom Script"
msgstr ""
@@ -116,8 +120,9 @@ msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:644
#: htdocs/luci-static/resources/view/natmap.js:651
#: htdocs/luci-static/resources/view/natmap.js:673
#: htdocs/luci-static/resources/view/natmap.js:731
#: htdocs/luci-static/resources/view/natmap.js:669
#: htdocs/luci-static/resources/view/natmap.js:696
#: htdocs/luci-static/resources/view/natmap.js:756
msgid "FQDN"
msgstr ""
@@ -180,7 +185,7 @@ msgstr ""
msgid "HTTP server"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:718
#: htdocs/luci-static/resources/view/natmap.js:743
msgid "HTTPS Record"
msgstr ""
@@ -277,8 +282,8 @@ msgstr ""
msgid "Port forwarding session idle timeout in seconds"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:704
#: htdocs/luci-static/resources/view/natmap.js:752
#: htdocs/luci-static/resources/view/natmap.js:729
#: htdocs/luci-static/resources/view/natmap.js:777
msgid "Priority"
msgstr ""
@@ -287,7 +292,7 @@ msgid "Private API Domain"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:341
#: htdocs/luci-static/resources/view/natmap.js:688
#: htdocs/luci-static/resources/view/natmap.js:712
#: htdocs/luci-static/resources/view/status/include/72_natmap.js:60
msgid "Protocol"
msgstr ""
@@ -313,7 +318,7 @@ msgstr ""
msgid "Restrict to address family"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:658
#: htdocs/luci-static/resources/view/natmap.js:681
msgid "SRV Record"
msgstr ""
@@ -332,7 +337,7 @@ msgstr ""
msgid "Server"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:679
#: htdocs/luci-static/resources/view/natmap.js:702
msgid "Service"
msgstr ""
@@ -340,12 +345,12 @@ msgstr ""
msgid "Set 0 will follow Public port"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:745
#: htdocs/luci-static/resources/view/natmap.js:770
msgid "SvcParams"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:174
#: htdocs/luci-static/resources/view/natmap.js:689
#: htdocs/luci-static/resources/view/natmap.js:713
msgid "TCP"
msgstr ""
@@ -357,12 +362,16 @@ msgstr ""
msgid "TCP STUN"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:691
#: htdocs/luci-static/resources/view/natmap.js:715
msgid "TLS"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:697
#: htdocs/luci-static/resources/view/natmap.js:737
#: htdocs/luci-static/resources/view/natmap.js:658
msgid "TXT Record"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:722
#: htdocs/luci-static/resources/view/natmap.js:762
msgid "Target"
msgstr ""
@@ -420,7 +429,7 @@ msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:179
#: htdocs/luci-static/resources/view/natmap.js:184
#: htdocs/luci-static/resources/view/natmap.js:690
#: htdocs/luci-static/resources/view/natmap.js:714
msgid "UDP"
msgstr ""
@@ -444,10 +453,14 @@ msgstr ""
msgid "Web UI Port"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:711
#: htdocs/luci-static/resources/view/natmap.js:736
msgid "Weight"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:716
msgid "WireGuard"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:163
msgid ""
"https://github.com/muink/rfc5780-stun-server/blob/master/valid_hosts_rfc5780."
@@ -469,6 +482,6 @@ msgstr ""
msgid "luci check may not detect all conflicts."
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:764
#: htdocs/luci-static/resources/view/natmap.js:789
msgid "⭮"
msgstr ""
+32 -19
View File
@@ -48,8 +48,8 @@ msgstr "Cổng Bind"
msgid "Boot delay"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:679
#: htdocs/luci-static/resources/view/natmap.js:688
#: htdocs/luci-static/resources/view/natmap.js:702
#: htdocs/luci-static/resources/view/natmap.js:712
msgid "Can refer to RFC8552"
msgstr ""
@@ -65,8 +65,12 @@ msgstr ""
msgid "Comment"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:675
msgid "Content"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:306
#: htdocs/luci-static/resources/view/natmap.js:759
#: htdocs/luci-static/resources/view/natmap.js:784
msgid "Custom Script"
msgstr ""
@@ -122,8 +126,9 @@ msgstr "Cổng bên ngoài"
#: htdocs/luci-static/resources/view/natmap.js:644
#: htdocs/luci-static/resources/view/natmap.js:651
#: htdocs/luci-static/resources/view/natmap.js:673
#: htdocs/luci-static/resources/view/natmap.js:731
#: htdocs/luci-static/resources/view/natmap.js:669
#: htdocs/luci-static/resources/view/natmap.js:696
#: htdocs/luci-static/resources/view/natmap.js:756
msgid "FQDN"
msgstr ""
@@ -186,7 +191,7 @@ msgstr ""
msgid "HTTP server"
msgstr "HTTP server"
#: htdocs/luci-static/resources/view/natmap.js:718
#: htdocs/luci-static/resources/view/natmap.js:743
msgid "HTTPS Record"
msgstr ""
@@ -283,8 +288,8 @@ msgstr ""
msgid "Port forwarding session idle timeout in seconds"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:704
#: htdocs/luci-static/resources/view/natmap.js:752
#: htdocs/luci-static/resources/view/natmap.js:729
#: htdocs/luci-static/resources/view/natmap.js:777
msgid "Priority"
msgstr ""
@@ -293,7 +298,7 @@ msgid "Private API Domain"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:341
#: htdocs/luci-static/resources/view/natmap.js:688
#: htdocs/luci-static/resources/view/natmap.js:712
#: htdocs/luci-static/resources/view/status/include/72_natmap.js:60
msgid "Protocol"
msgstr "Giao thức"
@@ -319,7 +324,7 @@ msgstr ""
msgid "Restrict to address family"
msgstr "Giới hạn địa chỉ gia đình"
#: htdocs/luci-static/resources/view/natmap.js:658
#: htdocs/luci-static/resources/view/natmap.js:681
msgid "SRV Record"
msgstr ""
@@ -338,7 +343,7 @@ msgstr "Máy chủ STUN"
msgid "Server"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:679
#: htdocs/luci-static/resources/view/natmap.js:702
msgid "Service"
msgstr ""
@@ -346,12 +351,12 @@ msgstr ""
msgid "Set 0 will follow Public port"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:745
#: htdocs/luci-static/resources/view/natmap.js:770
msgid "SvcParams"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:174
#: htdocs/luci-static/resources/view/natmap.js:689
#: htdocs/luci-static/resources/view/natmap.js:713
msgid "TCP"
msgstr ""
@@ -363,12 +368,16 @@ msgstr ""
msgid "TCP STUN"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:691
#: htdocs/luci-static/resources/view/natmap.js:715
msgid "TLS"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:697
#: htdocs/luci-static/resources/view/natmap.js:737
#: htdocs/luci-static/resources/view/natmap.js:658
msgid "TXT Record"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:722
#: htdocs/luci-static/resources/view/natmap.js:762
msgid "Target"
msgstr ""
@@ -426,7 +435,7 @@ msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:179
#: htdocs/luci-static/resources/view/natmap.js:184
#: htdocs/luci-static/resources/view/natmap.js:690
#: htdocs/luci-static/resources/view/natmap.js:714
msgid "UDP"
msgstr ""
@@ -450,10 +459,14 @@ msgstr ""
msgid "Web UI Port"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:711
#: htdocs/luci-static/resources/view/natmap.js:736
msgid "Weight"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:716
msgid "WireGuard"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:163
msgid ""
"https://github.com/muink/rfc5780-stun-server/blob/master/valid_hosts_rfc5780."
@@ -475,7 +488,7 @@ msgstr ""
msgid "luci check may not detect all conflicts."
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:764
#: htdocs/luci-static/resources/view/natmap.js:789
msgid "⭮"
msgstr ""
+32 -19
View File
@@ -48,8 +48,8 @@ msgstr "绑定端口"
msgid "Boot delay"
msgstr "启动延迟"
#: htdocs/luci-static/resources/view/natmap.js:679
#: htdocs/luci-static/resources/view/natmap.js:688
#: htdocs/luci-static/resources/view/natmap.js:702
#: htdocs/luci-static/resources/view/natmap.js:712
msgid "Can refer to RFC8552"
msgstr "可以参考 RFC8552"
@@ -65,8 +65,12 @@ msgstr "检测 NAT 行为"
msgid "Comment"
msgstr "备注"
#: htdocs/luci-static/resources/view/natmap.js:675
msgid "Content"
msgstr "内容"
#: htdocs/luci-static/resources/view/natmap.js:306
#: htdocs/luci-static/resources/view/natmap.js:759
#: htdocs/luci-static/resources/view/natmap.js:784
msgid "Custom Script"
msgstr "自定义脚本"
@@ -122,8 +126,9 @@ msgstr "外部端口"
#: htdocs/luci-static/resources/view/natmap.js:644
#: htdocs/luci-static/resources/view/natmap.js:651
#: htdocs/luci-static/resources/view/natmap.js:673
#: htdocs/luci-static/resources/view/natmap.js:731
#: htdocs/luci-static/resources/view/natmap.js:669
#: htdocs/luci-static/resources/view/natmap.js:696
#: htdocs/luci-static/resources/view/natmap.js:756
msgid "FQDN"
msgstr ""
@@ -186,7 +191,7 @@ msgstr ""
msgid "HTTP server"
msgstr "HTTP 服务器"
#: htdocs/luci-static/resources/view/natmap.js:718
#: htdocs/luci-static/resources/view/natmap.js:743
msgid "HTTPS Record"
msgstr "HTTPS 记录"
@@ -283,8 +288,8 @@ msgstr "请检查<a href=\"%s\"><b>防火墙规则</b></a>以避免端口冲突<
msgid "Port forwarding session idle timeout in seconds"
msgstr "端口转发会话空闲超时 (以秒为单位)"
#: htdocs/luci-static/resources/view/natmap.js:704
#: htdocs/luci-static/resources/view/natmap.js:752
#: htdocs/luci-static/resources/view/natmap.js:729
#: htdocs/luci-static/resources/view/natmap.js:777
msgid "Priority"
msgstr "优先权"
@@ -293,7 +298,7 @@ msgid "Private API Domain"
msgstr "私有 API 域名"
#: htdocs/luci-static/resources/view/natmap.js:341
#: htdocs/luci-static/resources/view/natmap.js:688
#: htdocs/luci-static/resources/view/natmap.js:712
#: htdocs/luci-static/resources/view/status/include/72_natmap.js:60
msgid "Protocol"
msgstr "协议"
@@ -319,7 +324,7 @@ msgstr "重新载入"
msgid "Restrict to address family"
msgstr "地址族限制"
#: htdocs/luci-static/resources/view/natmap.js:658
#: htdocs/luci-static/resources/view/natmap.js:681
msgid "SRV Record"
msgstr "SRV 记录"
@@ -338,7 +343,7 @@ msgstr "STUN 服务器"
msgid "Server"
msgstr "服务器"
#: htdocs/luci-static/resources/view/natmap.js:679
#: htdocs/luci-static/resources/view/natmap.js:702
msgid "Service"
msgstr "服务"
@@ -346,12 +351,12 @@ msgstr "服务"
msgid "Set 0 will follow Public port"
msgstr "设为 0 将跟随公共端口"
#: htdocs/luci-static/resources/view/natmap.js:745
#: htdocs/luci-static/resources/view/natmap.js:770
msgid "SvcParams"
msgstr "服务参数"
#: htdocs/luci-static/resources/view/natmap.js:174
#: htdocs/luci-static/resources/view/natmap.js:689
#: htdocs/luci-static/resources/view/natmap.js:713
msgid "TCP"
msgstr ""
@@ -363,12 +368,16 @@ msgstr "TCP 拥塞控制"
msgid "TCP STUN"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:691
#: htdocs/luci-static/resources/view/natmap.js:715
msgid "TLS"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:697
#: htdocs/luci-static/resources/view/natmap.js:737
#: htdocs/luci-static/resources/view/natmap.js:658
msgid "TXT Record"
msgstr "TXT 记录"
#: htdocs/luci-static/resources/view/natmap.js:722
#: htdocs/luci-static/resources/view/natmap.js:762
msgid "Target"
msgstr "目标"
@@ -428,7 +437,7 @@ msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:179
#: htdocs/luci-static/resources/view/natmap.js:184
#: htdocs/luci-static/resources/view/natmap.js:690
#: htdocs/luci-static/resources/view/natmap.js:714
msgid "UDP"
msgstr ""
@@ -452,10 +461,14 @@ msgstr "经由 natmap"
msgid "Web UI Port"
msgstr "Web UI 端口"
#: htdocs/luci-static/resources/view/natmap.js:711
#: htdocs/luci-static/resources/view/natmap.js:736
msgid "Weight"
msgstr "权重"
#: htdocs/luci-static/resources/view/natmap.js:716
msgid "WireGuard"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:163
msgid ""
"https://github.com/muink/rfc5780-stun-server/blob/master/valid_hosts_rfc5780."
@@ -477,7 +490,7 @@ msgstr "keep-alive 间隔 (秒)"
msgid "luci check may not detect all conflicts."
msgstr "luci 检查可能无法检测到所有冲突"
#: htdocs/luci-static/resources/view/natmap.js:764
#: htdocs/luci-static/resources/view/natmap.js:789
msgid "⭮"
msgstr ""
+32 -19
View File
@@ -48,8 +48,8 @@ msgstr "綁定埠"
msgid "Boot delay"
msgstr "啟動延遲"
#: htdocs/luci-static/resources/view/natmap.js:679
#: htdocs/luci-static/resources/view/natmap.js:688
#: htdocs/luci-static/resources/view/natmap.js:702
#: htdocs/luci-static/resources/view/natmap.js:712
msgid "Can refer to RFC8552"
msgstr "可以參考 RFC8552"
@@ -65,8 +65,12 @@ msgstr "檢查 NAT 行為"
msgid "Comment"
msgstr "備註"
#: htdocs/luci-static/resources/view/natmap.js:675
msgid "Content"
msgstr "內容"
#: htdocs/luci-static/resources/view/natmap.js:306
#: htdocs/luci-static/resources/view/natmap.js:759
#: htdocs/luci-static/resources/view/natmap.js:784
msgid "Custom Script"
msgstr "自定義腳本"
@@ -122,8 +126,9 @@ msgstr "外部通訊埠"
#: htdocs/luci-static/resources/view/natmap.js:644
#: htdocs/luci-static/resources/view/natmap.js:651
#: htdocs/luci-static/resources/view/natmap.js:673
#: htdocs/luci-static/resources/view/natmap.js:731
#: htdocs/luci-static/resources/view/natmap.js:669
#: htdocs/luci-static/resources/view/natmap.js:696
#: htdocs/luci-static/resources/view/natmap.js:756
msgid "FQDN"
msgstr ""
@@ -186,7 +191,7 @@ msgstr ""
msgid "HTTP server"
msgstr "HTTP 伺服器"
#: htdocs/luci-static/resources/view/natmap.js:718
#: htdocs/luci-static/resources/view/natmap.js:743
msgid "HTTPS Record"
msgstr "HTTPS 記錄"
@@ -283,8 +288,8 @@ msgstr "請檢查<a href=\"%s\"><b>防火牆規則</b></a>以避免埠衝突</br
msgid "Port forwarding session idle timeout in seconds"
msgstr "連接埠轉發會話空閒逾時 (以秒為單位)"
#: htdocs/luci-static/resources/view/natmap.js:704
#: htdocs/luci-static/resources/view/natmap.js:752
#: htdocs/luci-static/resources/view/natmap.js:729
#: htdocs/luci-static/resources/view/natmap.js:777
msgid "Priority"
msgstr "優先權"
@@ -293,7 +298,7 @@ msgid "Private API Domain"
msgstr "私有 API 域名"
#: htdocs/luci-static/resources/view/natmap.js:341
#: htdocs/luci-static/resources/view/natmap.js:688
#: htdocs/luci-static/resources/view/natmap.js:712
#: htdocs/luci-static/resources/view/status/include/72_natmap.js:60
msgid "Protocol"
msgstr "協議"
@@ -319,7 +324,7 @@ msgstr "重新載入"
msgid "Restrict to address family"
msgstr "限制到位址群"
#: htdocs/luci-static/resources/view/natmap.js:658
#: htdocs/luci-static/resources/view/natmap.js:681
msgid "SRV Record"
msgstr "SRV 記錄"
@@ -338,7 +343,7 @@ msgstr "STUN 伺服器"
msgid "Server"
msgstr "伺服器"
#: htdocs/luci-static/resources/view/natmap.js:679
#: htdocs/luci-static/resources/view/natmap.js:702
msgid "Service"
msgstr "服務"
@@ -346,12 +351,12 @@ msgstr "服務"
msgid "Set 0 will follow Public port"
msgstr "設為 0 將遵循公共埠"
#: htdocs/luci-static/resources/view/natmap.js:745
#: htdocs/luci-static/resources/view/natmap.js:770
msgid "SvcParams"
msgstr "服務參數"
#: htdocs/luci-static/resources/view/natmap.js:174
#: htdocs/luci-static/resources/view/natmap.js:689
#: htdocs/luci-static/resources/view/natmap.js:713
msgid "TCP"
msgstr ""
@@ -363,12 +368,16 @@ msgstr "TCP 擁塞控制"
msgid "TCP STUN"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:691
#: htdocs/luci-static/resources/view/natmap.js:715
msgid "TLS"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:697
#: htdocs/luci-static/resources/view/natmap.js:737
#: htdocs/luci-static/resources/view/natmap.js:658
msgid "TXT Record"
msgstr "TXT 記錄"
#: htdocs/luci-static/resources/view/natmap.js:722
#: htdocs/luci-static/resources/view/natmap.js:762
msgid "Target"
msgstr "目標"
@@ -428,7 +437,7 @@ msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:179
#: htdocs/luci-static/resources/view/natmap.js:184
#: htdocs/luci-static/resources/view/natmap.js:690
#: htdocs/luci-static/resources/view/natmap.js:714
msgid "UDP"
msgstr ""
@@ -452,10 +461,14 @@ msgstr "經由 natmap"
msgid "Web UI Port"
msgstr "Web UI 通訊埠"
#: htdocs/luci-static/resources/view/natmap.js:711
#: htdocs/luci-static/resources/view/natmap.js:736
msgid "Weight"
msgstr "權重"
#: htdocs/luci-static/resources/view/natmap.js:716
msgid "WireGuard"
msgstr ""
#: htdocs/luci-static/resources/view/natmap.js:163
msgid ""
"https://github.com/muink/rfc5780-stun-server/blob/master/valid_hosts_rfc5780."
@@ -477,7 +490,7 @@ msgstr "keep-alive 間隔 (秒)"
msgid "luci check may not detect all conflicts."
msgstr "luci 檢查可能無法檢測到所有衝突"
#: htdocs/luci-static/resources/view/natmap.js:764
#: htdocs/luci-static/resources/view/natmap.js:789
msgid "⭮"
msgstr ""
+2 -2
View File
@@ -1,8 +1,8 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-open-box
PKG_VERSION:=0.1.400
PKG_RELEASE:=63
PKG_VERSION:=0.1.405
PKG_RELEASE:=64
PKG_MAINTAINER:=liandu2024
PKG_LICENSE:=GPL-3.0-or-later
+2 -2
View File
@@ -10,8 +10,8 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-vantage
PKG_VERSION:=1.0.14
PKG_RELEASE:=24
PKG_VERSION:=1.0.15
PKG_RELEASE:=25
PKG_LICENSE:=GPL-3.0-or-later
LUCI_TITLE:=Vantage dashboard
File diff suppressed because it is too large. Load diff
@@ -23,6 +23,16 @@ var UNSAFE_TEXT = /[\u0000-\u001f\u007f-\u009f\u00ad\u034f\u061c\u115f\u1160\u17
return baseclass.extend({
DASH: DASH,
/* N_() with English's rule where no catalogue has the string: LuCI
picks the form by the language's rule even then, and a one-form
language (Japanese, Korean...) would show "2 radio up" */
N_: function(n, singular, plural, ctx) {
var t = N_(n, singular, plural, ctx);
if (n !== 1 && t === singular) return plural;
if (n === 1 && t === plural) return singular;
return t;
},
/* bits per second -> { v: '372', u: 'Mbit/s' } */
bitsParts: function(bps) {
bps = num(bps);
@@ -37,13 +47,31 @@ return baseclass.extend({
return p.u ? p.v + ' ' + p.u : p.v;
},
/* compact axis label: 400M, 1.5G, 20k */
bitsShort: function(bps) {
bps = num(bps);
if (bps == null || bps <= 0) return '0';
var u = [ '', 'k', 'M', 'G', 'T' ], i = 0;
while (bps >= 1000 && i < u.length - 1) { bps /= 1000; i++; }
return (bps >= 10 ? bps.toFixed(0) : String(+bps.toFixed(1))) + u[i];
/* a download and upload rate for a row, as { dn, up, unit }: one
unit for both when they read in the same one ('376', '3.4',
'Mbit/s'), else each with its own and unit '' */
rateParts: function(down, up) {
var a = this.bitsParts(down), b = this.bitsParts(up);
if (a.u && a.u === b.u) return { dn: a.v, up: b.v, unit: a.u };
return { dn: a.u ? a.v + ' ' + a.u : a.v, up: b.u ? b.v + ' ' + b.u : b.v, unit: '' };
},
/* '↓ 376 ↑ 3.4 Mbit/s', '↓ 12.3 Mbit/s ↑ 800 bit/s'; 'idle' when
nothing moves */
rates: function(down, up) {
if (num(down) === 0 && num(up) === 0) return _('idle');
var p = this.rateParts(down, up);
return '↓ ' + p.dn + ' ↑ ' + p.up + (p.unit ? ' ' + p.unit : '');
},
/* a count, its thousands set apart by a narrow no-break space (as SI
writes them) from five digits on: 20224239 -> '20 224 239' */
count: function(n) {
n = num(n);
if (n == null) return DASH;
var s = String(Math.round(Math.abs(n)));
if (s.length > 4) s = s.replace(/\B(?=(\d{3})+$)/g, '\u202f');
return (n < 0 ? '−' : '') + s;
},
/* binary units for memory/storage */
@@ -115,10 +143,11 @@ return baseclass.extend({
return /^([0-9a-f]{2}[:-]){5}[0-9a-f]{2}$/i.test(s) ? s.replace(/-/g, ':').toUpperCase() : s;
},
/* last three octets: '…00:53:C1' (the distinguishing part of a MAC) */
/* last two octets: '53:C1', what tells devices apart in a one-line
name; '' for anything but a MAC */
macTail: function(mac) {
var m = this.mac(mac);
return /^([0-9A-F]{2}:){5}[0-9A-F]{2}$/.test(m) ? '…' + m.slice(9) : m;
return /^([0-9A-F]{2}:){5}[0-9A-F]{2}$/.test(m) ? m.slice(12) : '';
},
/* Client- or radio-supplied text for display: invisible/reordering
@@ -245,11 +245,6 @@ function ipBytes(a) {
return out.concat(tail);
}
function ipFamily(a) {
var b = ipBytes(a);
return b ? (b.length === 4 ? 4 : 6) : 0;
}
/* the leading one bits of a mask, -1 when they are not contiguous */
function maskBits(m) {
var bits = 0, zero = false;
@@ -782,9 +777,10 @@ return baseclass.extend({
families: fams
};
};
/* the admin ports (ADMIN_PORTS) it opens to any address, [] for none */
var admin = function(item, ports) {
return item.from.anywhere && (item.proto[0] === 'all' || item.proto.indexOf('tcp') >= 0) &&
ADMIN_PORTS.some(function(p) { return hasPort(ports, p); });
if (!item.from.anywhere || !(item.proto[0] === 'all' || item.proto.indexOf('tcp') >= 0)) return [];
return ADMIN_PORTS.filter(function(p) { return hasPort(ports, p); });
};
/* standard housekeeping: ping and other control messages, multicast
membership, DHCP client replies, IPsec passed on to a host */
@@ -848,8 +844,9 @@ return baseclass.extend({
return;
}
open.push(item);
if (admin(item, item.ports))
warnFor(s, { level: 'warn', code: 'admin-open', kind: 'rule', name: item.name, ports: item.portText });
var a = admin(item, item.ports);
if (a.length)
warnFor(s, { level: 'warn', code: 'admin-open', kind: 'rule', name: item.name, ports: item.portText, admin: a });
});
of('redirect').forEach(function(s) {
@@ -881,16 +878,17 @@ return baseclass.extend({
var first = function(f) { return r.dest_ip.addrs.filter(function(a) { return a.family === f; })[0]; };
item.local = !r.dest_ip ? true : (!own || !r.dest_ip.addrs) ? null
: item.families.some(function(f) { var a = first(f); return !a || isOwn(a); });
var a = admin(item, inner);
if (item.local === null) {
fwd.push(item);
if (admin(item, inner))
warnFor(s, { level: 'warn', code: 'admin-unsure', kind: 'redirect', name: item.name, host: item.host, ports: item.portText });
if (a.length)
warnFor(s, { level: 'warn', code: 'admin-unsure', kind: 'redirect', name: item.name, host: item.host, ports: item.portText, admin: a });
return;
}
if (!item.local) { fwd.push(item); return; }
open.push(item);
if (admin(item, inner))
warnFor(s, { level: 'warn', code: 'admin-open', kind: 'redirect', name: item.name, ports: item.portText });
if (a.length)
warnFor(s, { level: 'warn', code: 'admin-open', kind: 'redirect', name: item.name, ports: item.portText, admin: a });
});
/* custom rules: files the firewall config names, whose rules this
@@ -21,7 +21,6 @@ var WEAK_DBM = -75;
function num(v) { v = +v; return isFinite(v) ? v : 0; }
function finite(v) { return typeof v === 'number' && isFinite(v); }
function own(o, k) { return o != null && Object.prototype.hasOwnProperty.call(o, k); }
function speedShort(mbps) { return mbps >= 1000 ? (mbps / 1000) + 'G' : mbps + 'M'; }
function speedLong(mbps) { return mbps >= 1000 ? _('%s Gbit/s').format(mbps / 1000) : _('%s Mbit/s').format(mbps); }
function rank(level) { return { err: 3, warn: 2, info: 1, ok: 0 }[level] || 0; }
@@ -53,10 +52,11 @@ return baseclass.extend({
detail: _('%s negotiated only %d Mbit/s; a cable pair or port may be faulty').format(up.dev, up.speed.mbps), page: PAGES.network });
else {
var rate = live.uplinkRate && finite(live.uplinkRate.rx) && finite(live.uplinkRate.tx) ? live.uplinkRate : null;
/* no-break spaces keep '↑ 2.9 Mbit/s' and '2.5G link' whole when the chip wraps */
var link = up.speed ? _('%s link').format(speedShort(up.speed.mbps)).replace(/ /g, '\u00a0') : null;
/* no-break spaces keep '↑ 2.9 Mbit/s' and '2.5 Gbit/s link' whole when the chip wraps */
var link = up.speed ? _('%s link').format(speedLong(up.speed.mbps)).replace(/ /g, '\u00a0') : null;
out.push({ id: 'uplink', level: 'ok', title: _('Uplink', 'vantage'), value: rate ? '↓ ' + fmt.bits(rate.rx) : _('Up', 'vantage'),
reason: [ rate ? ('↑ ' + fmt.bits(rate.tx)).replace(/ /g, '\u00a0') : null, up.dev, link ].filter(Boolean).join(' · '),
/* the device name only until there is a rate (the path names it) */
reason: [ rate ? ('↑ ' + fmt.bits(rate.tx)).replace(/ /g, '\u00a0') : up.dev, link ].filter(Boolean).join(' · '),
detail: (up.speed ? _('%s up at %s').format(up.dev, speedLong(up.speed.mbps)) : _('%s up').format(up.dev)) +
(up.gateway ? ', ' + _('gateway %s').format(up.gateway) : '') +
(rate ? '; ' + _('now %s down, %s up').format(fmt.bits(rate.rx), fmt.bits(rate.tx)) : ''),
@@ -70,8 +70,9 @@ return baseclass.extend({
var hot = live.temps && live.temps[0] ? fmt.celsius(live.temps[0].celsius) : null;
if (cpu == null) out.push({ id: 'cpu', level: 'info', title: _('CPU', 'vantage'), value: fmt.DASH, reason: _('Measuring…'), detail: _('Measuring over the next poll'), page: PAGES.processes });
else out.push({ id: 'cpu', level: cpu >= 90 ? 'err' : cpu >= 70 ? 'warn' : 'ok', title: _('CPU', 'vantage'), value: fmt.pct(cpu),
reason: (cpu >= 70 ? _('Busy · load %s').format(fmt.load(load)) : _('Load %s · %d cores').format(fmt.load(load), cores)) + (hot ? ' · ' + hot : ''),
detail: (cpu >= 70 ? _('Busy; load %s on %d cores; see the busiest processes').format(fmt.load(load), cores) : _('Load %s on %d cores').format(fmt.load(load), cores)) +
reason: (cpu >= 70 ? _('Busy · load %s').format(fmt.load(load)) : fmt.N_(cores, 'Load %s · %d core', 'Load %s · %d cores').format(fmt.load(load), cores)) + (hot ? ' · ' + hot : ''),
detail: (cpu >= 70 ? fmt.N_(cores, 'Busy; load %s on %d core; see the busiest processes', 'Busy; load %s on %d cores; see the busiest processes').format(fmt.load(load), cores)
: fmt.N_(cores, 'Load %s on %d core', 'Load %s on %d cores').format(fmt.load(load), cores)) +
(hot ? ' · ' + _('Temperature %s').format(hot) : ''),
page: PAGES.processes });
@@ -96,33 +97,40 @@ return baseclass.extend({
},
/* the Wi-Fi and Signal checks, appended to out; an SSID without
clients is normal, not a fault */
clients is idle, not a fault */
wifiHealth: function(model, out) {
var radios = model.radios || [], nSsid = (model.ssids || []).length;
var off = radios.filter(function(r) { return r.disabled; }), down = radios.filter(function(r) { return !r.disabled && !r.up; });
var idle = (model.ssids || []).filter(function(s) { return s.up && !s.clients; });
if (!radios.length) out.push({ id: 'radios', level: 'info', title: _('Wi-Fi'), value: _('None'), reason: _('No radios reported'), detail: _('This device reports no radios'), page: PAGES.wireless });
else if (down.length) out.push({ id: 'radios', level: 'err', title: _('Wi-Fi'), value: _('%d down').format(down.length), reason: _('%s is not up').format(down[0].bandLabel),
else if (down.length) out.push({ id: 'radios', level: 'err', title: _('Wi-Fi'), value: fmt.N_(down.length, '%d radio down', '%d radios down').format(down.length), reason: _('%s is not up').format(down[0].bandLabel),
detail: _('%s (%s) is enabled but not up').format(down[0].id, down[0].bandLabel), page: PAGES.wireless });
else if (off.length) out.push({ id: 'radios', level: 'warn', title: _('Wi-Fi'), value: _('%d off').format(off.length), reason: _('%s is disabled').format(off[0].bandLabel),
else if (off.length) out.push({ id: 'radios', level: 'warn', title: _('Wi-Fi'), value: fmt.N_(off.length, '%d radio off', '%d radios off').format(off.length), reason: _('%s is disabled').format(off[0].bandLabel),
detail: _('%s (%s) is disabled').format(off[0].id, off[0].bandLabel), page: PAGES.wireless });
else out.push({ id: 'radios', level: 'ok', title: _('Wi-Fi'), value: _('%d up').format(radios.length),
reason: idle.length ? _('%d SSIDs · %d idle').format(nSsid, idle.length) : _('%d SSIDs on air').format(nSsid),
detail: idle.length ? _('%d SSIDs broadcasting; %d without clients, which is fine').format(nSsid, idle.length) : _('%d SSIDs broadcasting').format(nSsid),
else out.push({ id: 'radios', level: 'ok', title: _('Wi-Fi'), value: fmt.N_(radios.length, '%d radio up', '%d radios up').format(radios.length),
reason: idle.length ? fmt.N_(nSsid, '%d SSID · %d idle', '%d SSIDs · %d idle').format(nSsid, idle.length) : fmt.N_(nSsid, '%d SSID on air', '%d SSIDs on air').format(nSsid),
detail: idle.length ? fmt.N_(nSsid, '%d SSID broadcasting, %d idle', '%d SSIDs broadcasting, %d idle').format(nSsid, idle.length) : fmt.N_(nSsid, '%d SSID broadcasting', '%d SSIDs broadcasting').format(nSsid),
page: PAGES.wireless });
/* signal */
var clients = model.clients || [];
var weak = clients.filter(function(c) { return c.signal != null && c.signal < WEAK_DBM; }).sort(function(a, b) { return a.signal - b.signal; });
if (!clients.length) out.push({ id: 'signal', level: 'info', title: _('Signal'), value: _('No clients'), reason: _('Nobody associated'), detail: _('No stations are associated right now'), page: PAGES.wireless });
var heard = clients.filter(function(c) { return c.signal != null; }).sort(function(a, b) { return a.signal - b.signal; });
if (!clients.length) out.push({ id: 'signal', level: 'info', title: _('Signal'), value: _('No clients'), reason: '', detail: _('No stations are associated right now'), page: PAGES.wireless });
else if (weak.length) out.push({ id: 'signal', level: 'warn', title: _('Signal'), value: _('%d weak').format(weak.length),
reason: _('%s · %s').format(weak[0].name, fmt.dbm(weak[0].signal)),
/* the level stays in one piece when the line wraps */
reason: _('%s · %s').format(weak[0].name, fmt.dbm(weak[0].signal).replace(/ /g, '\u00a0')),
detail: _('%s at %s; below %s roaming and throughput suffer').format(weak[0].name, fmt.dbm(weak[0].signal), fmt.dbm(WEAK_DBM)), page: PAGES.wireless,
/* one weak client opens its details, several the list of them
(the weakest's details where the layout hides that list) */
client: weak[0].mac, list: weak.length > 1 ? 'weak' : null });
else out.push({ id: 'signal', level: 'ok', title: _('Signal'), value: _('All good'), reason: _('All above %s').format(fmt.dbm(WEAK_DBM)),
detail: _('All %d clients above %s').format(clients.length, fmt.dbm(WEAK_DBM)), page: PAGES.wireless });
/* all fine: the weakest, which is how close the network is to a problem */
else out.push({ id: 'signal', level: 'ok', title: _('Signal'), value: heard.length ? fmt.dbm(heard[0].signal) : fmt.DASH,
reason: !heard.length ? _('Not reported by the driver') : heard.length === 1 ? _('The only measured client')
: fmt.N_(heard.length, 'Weakest of %d client', 'Weakest of %d clients').format(heard.length),
detail: !heard.length ? _('The driver reports no signal levels')
: heard.length === 1 ? _('The one measured client is above %s').format(fmt.dbm(WEAK_DBM))
: fmt.N_(heard.length, '%d measured client is above %s', '%d measured clients are above %s').format(heard.length, fmt.dbm(WEAK_DBM)), page: PAGES.wireless });
},
/* -> { level, count (warn+err), headline } */
@@ -248,8 +256,6 @@ return baseclass.extend({
else if (!r.up) notes.push({ level: 'err', text: _('Radio is enabled but not up') });
if (top === r.id && sum > 0 && (radios.length > 1)) notes.push({ level: 'info', text: _('Busiest radio: %s of Wi-Fi traffic').format(fmt.pct(v / sum * 100)) });
if (r.legacy) notes.push({ level: 'info', text: r.legacy === 1 ? _('1 older device (Wi-Fi 4 or earlier) slows airtime') : _('%d older devices (Wi-Fi 4 or earlier) slow airtime').format(r.legacy) });
if (r.noise == null && r.noiseRaw != null) notes.push({ level: 'muted', text: _('Noise floor not reported by the driver') });
if (r.up && !r.clients) notes.push({ level: 'muted', text: _('No clients; idle radios are fine') });
out[r.id] = { busiest: top === r.id && sum > 0, share: sum > 0 ? v / sum : 0, notes: notes };
});
return out;
@@ -25,7 +25,7 @@ var MIN = {
};
/* tiles whose content grows to fill a taller slot */
var FLEX = { chart: true };
var FLEX = { chart: true, radios: true };
/* how far a tile stretches to line up with the tile below it */
var SNAP = 48;
@@ -564,6 +564,7 @@ return baseclass.extend({
mac: mac,
macLabel: fmt.mac(mac),
name: nm.name,
plainName: nm.plain,
nameSource: nm.source,
nameSourceLabel: nm.sourceLabel,
icon: nm.icon,
@@ -625,6 +626,7 @@ return baseclass.extend({
mac: mac,
macLabel: fmt.mac(mac),
name: nm.name,
plainName: nm.plain,
nameSource: nm.source,
nameSourceLabel: nm.sourceLabel,
icon: nm.icon,
@@ -20,7 +20,7 @@
wps the device name the station sends in its WPS element
vendor manufacturer from the OUI of a globally unique MAC
private "Private device" for a locally administered MAC
mac "Device" as the last resort
mac "Unknown device" as the last resort
Pure functions; names are untrusted and only ever rendered as text. */
@@ -238,7 +238,11 @@ return baseclass.extend({
/* Resolve the display name of one station.
s: { mac, alias: { name, icon }, ips: [..], rdns: { ip: name },
mdns: { ip: name }, hint: { name }, wps, vendor }
-> { name, source, sourceLabel, icon, secondary, isPrivate } */
-> { name, plain, source, sourceLabel, icon, secondary, isPrivate }
Without a name (vendor, private, mac) name is the one-line label,
which carries the MAC's last two octets ("Private device 53:0F"),
and plain the same without them, for where the MAC sits beside
it; otherwise both are the name. */
resolve: function(s) {
s = s || {};
var self = this, mac = this.normMac(s.mac) || str(s.mac);
@@ -261,16 +265,18 @@ return baseclass.extend({
if (!name && (name = fromIps(s.mdns))) source = 'mdns';
if (!name && s.wps && (name = fmt.safeText(str(s.wps).trim(), NAME_MAX) || null)) source = 'wps';
var tail = fmt.macTail(mac);
var plain = name, tail = fmt.macTail(mac);
if (!name) {
if (priv) { name = _('Private device') + ' · ' + tail; source = 'private'; }
else if (s.vendor) { name = _('%s device').format(fmt.safeText(s.vendor, 32)) + ' · ' + tail; source = 'vendor'; }
else { name = _('Device') + ' · ' + tail; source = 'mac'; }
if (priv) { plain = _('Private device'); source = 'private'; }
else if (s.vendor) { plain = _('%s device').format(fmt.safeText(s.vendor, 32)); source = 'vendor'; }
else { plain = _('Unknown device', 'vantage'); source = 'mac'; }
name = tail ? plain + ' ' + tail : plain;
}
var icon = (s.alias && this.validIcon(s.alias.icon)) || this.guessIcon(source === 'vendor' ? s.vendor : name) || 'device';
return {
name: name,
plain: plain,
source: source,
sourceLabel: SOURCES[source],
icon: icon,
@@ -37,14 +37,6 @@ return baseclass.extend({
return out.length > maxLen ? out.slice(out.length - maxLen) : out;
},
/* a round axis maximum >= v: 1, 2, 2.5, 5 x 10^n */
niceMax: function(v) {
if (!finite(v) || v <= 0) return 1;
var e = Math.pow(10, Math.floor(Math.log10(v))), f = v / e;
var n = f <= 1 ? 1 : f <= 2 ? 2 : f <= 2.5 ? 2.5 : f <= 5 ? 5 : 10;
return n * e;
},
/* a round axis maximum >= v with finer steps (1, 1.5, 2, 2.5, 3, 4,
5, 6, 8, 10 x 10^n), so a 374 Mbit/s peak gets a 400 axis, not 500 */
niceCeil: function(v) {
@@ -45,7 +45,6 @@ var ICONS = {
globe: [ 'M12 3a9 9 0 1 0 0 18a9 9 0 1 0 0-18Z', 'M3.4 9.2h17.2M3.4 14.8h17.2', 'M12 3c2.4 2.5 3.6 5.5 3.6 9s-1.2 6.5-3.6 9c-2.4-2.5-3.6-5.5-3.6-9s1.2-6.5 3.6-9Z' ],
zones: [ 'M4 5.5h6.5v6.5H4zM13.5 5.5H20v6.5h-6.5zM8.75 15.5h6.5V22h-6.5z', 'M7.25 12v1.75h9.5V12M12 13.75v1.75' ],
off: [ 'M12 3.5a8.5 8.5 0 1 0 0 17a8.5 8.5 0 1 0 0-17Z', 'M6 6l12 12' ],
check: [ 'M5 12.5l4.5 4.5L19 7.5' ],
warn: [ 'M10.3 4.6 3 17.5A2 2 0 0 0 4.7 20.5h14.6A2 2 0 0 0 21 17.5L13.7 4.6a2 2 0 0 0-3.4 0Z', 'M12 9.5v4.5', 'M12 17h.01' ],
err: [ 'M12 3.5a8.5 8.5 0 1 0 0 17a8.5 8.5 0 1 0 0-17Z', 'M9 9l6 6M15 9l-6 6' ],
chevron: [ 'M9.5 6l6 6-6 6' ],
@@ -116,9 +115,16 @@ function named(item) {
return item.kind === 'redirect' ? _('Unnamed port forward') : _('Unnamed rule', 'vantage');
}
/* a zone's input policy: neutral, whatever it is, except an internet
zone that accepts */
function polBadge(p, risky) {
var cls = risky ? 'is-risk' : p === 'ACCEPT' ? 'is-accept' : 'is-stop';
return E('span', { 'class': 'vt-fg-pol ' + cls }, [ p === 'ACCEPT' ? _('accept', 'vantage') : p === 'DROP' ? _('drop', 'vantage') : _('reject', 'vantage') ]);
return E('span', { 'class': 'vt-fg-pol' + (risky ? ' is-risk' : '') }, [ p === 'ACCEPT' ? _('accept', 'vantage') : p === 'DROP' ? _('drop', 'vantage') : _('reject', 'vantage') ]);
}
/* the admin ports a warning names (fwglance ADMIN_PORTS), by service */
function adminServices(ports) {
var names = { 22: _('SSH (port 22)'), 23: _('Telnet (port 23)'), 80: _('HTTP (port 80)'), 443: _('HTTPS (port 443)') };
return (ports || []).map(function(p) { return names[p] || String(p); }).join(', ');
}
/* a setup fwglance does not follow (its complex) */
@@ -139,6 +145,17 @@ function ignoredWhy(i) {
return _('ignored: option %s has a value firewall4 refuses').format(i.bad);
}
/* N_() with English's rule where no catalogue has the string (as
vantage.fmt's, which this view doesn't load) */
var tr = {
N_: function(n, singular, plural, ctx) {
var t = N_(n, singular, plural, ctx);
if (n !== 1 && t === singular) return plural;
if (n === 1 && t === plural) return singular;
return t;
}
};
return view.extend({
handleSave: null,
handleSaveApply: null,
@@ -219,7 +236,7 @@ return view.extend({
}
else {
head = _('Nothing is open in your firewall rules');
sub = fw.standard.length ? N_(fw.standard.length, 'Only standard housekeeping (ping, DHCP renewals and the like): %d rule', 'Only standard housekeeping (ping, DHCP renewals and the like): %d rules').format(fw.standard.length) : _('Internet-facing: %s').format(facing);
sub = fw.standard.length ? tr.N_(fw.standard.length, 'Only standard housekeeping (ping, DHCP renewals and the like): %d rule', 'Only standard housekeeping (ping, DHCP renewals and the like): %d rules').format(fw.standard.length) : _('Internet-facing: %s').format(facing);
}
return this.head(fw.level, head, sub);
},
@@ -257,12 +274,12 @@ return view.extend({
}
else if (w.code === 'admin-unsure') {
title = _('%s may open the router\'s admin').format(w.name || w.ports);
reason = _('It forwards SSH or web ports from any address on the internet to %s. The interface list could not be read, so this page cannot tell whether that is the router itself.').format(w.host);
reason = _('It forwards %s from any address on the internet to %s. The interface list could not be read, so this page cannot tell whether that is the router itself.').format(adminServices(w.admin), w.host);
tab = 'forwards';
}
else {
title = _('Router admin open: %s').format(w.name || w.ports);
reason = _('SSH or the web interface answers any address on the internet. Limit the rule to the addresses you use.');
reason = _('The firewall lets any address on the internet reach %s. Limit the rule to the addresses you use.').format(adminServices(w.admin));
tab = w.kind === 'redirect' ? 'forwards' : 'rules';
}
return E('a', { 'class': 'vt-chip vt-chip-' + w.level, 'role': 'listitem', 'href': page(tab) }, [
@@ -316,7 +333,7 @@ return view.extend({
: _('Nothing is open in your firewall rules.') ]));
if (fw.standard.length)
body.push(E('details', { 'class': 'vt-fg-std' }, [
E('summary', {}, [ N_(fw.standard.length, 'Standard housekeeping: %d rule', 'Standard housekeeping: %d rules').format(fw.standard.length) ]),
E('summary', {}, [ tr.N_(fw.standard.length, 'Standard housekeeping: %d rule', 'Standard housekeeping: %d rules').format(fw.standard.length) ]),
E('p', { 'class': 'vt-hint' }, [ _('Ping and other control messages, multicast, DHCP renewals and IPsec passed on to your network: what every router lets in.') ]),
E('div', { 'class': 'vt-fg-rows' }, fw.standard.map(function(i) {
return self.row(page('rules'), named(i), [ what(i) ]);
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+2 -2
View File
@@ -10,8 +10,8 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-theme-vantage
PKG_VERSION:=1.0.14
PKG_RELEASE:=30
PKG_VERSION:=1.0.15
PKG_RELEASE:=31
PKG_LICENSE:=GPL-3.0-or-later
LUCI_TITLE:=Vantage theme
@@ -26,6 +26,10 @@ const callWirelessDevices = rpc.declare({ object: 'luci-rpc', method: 'getWirele
const THEME_KEY = 'vantage.theme';
const RAIL_KEY = 'vantage.rail';
const LOGIN_KEY = 'vantage.login'; /* read by the login page's login.js */
/* the dashboard's network path backdrop (luci-app-vantage view/vantage/overview.js),
picked here when the dashboard is installed; the dashboard listens for BACKDROP_EVENT */
const BACKDROP_KEY = 'vantage.backdrop.v1';
const BACKDROP_EVENT = 'vantage:backdrop';
/* 24px stroke icons, drawn for this theme: constant [tag, attributes] lists,
built with createElementNS (no markup parsing, no user data) */
@@ -41,13 +45,12 @@ const ICONS = {
generic: [ [ 'circle', { cx: '12', cy: '12', r: '8' } ], [ 'circle', { cx: '12', cy: '12', r: '2.2' } ] ],
page: [ [ 'path', { d: 'M7 3.5h6.5l4.5 4.5v12.5H7z' } ], [ 'path', { d: 'M13.5 3.5V8H18' } ] ],
caret: [ [ 'path', { d: 'm9 6 6 6-6 6' } ] ],
enter: [ [ 'path', { d: 'M19 5v6a3 3 0 0 1-3 3H6M10 10l-4 4 4 4' } ] ],
down: [ [ 'path', { d: 'm6 9 6 6 6-6' } ] ],
search: [ [ 'circle', { cx: '11', cy: '11', r: '6.5' } ], [ 'path', { d: 'm16 16 4.5 4.5' } ] ],
sun: [ [ 'circle', { cx: '12', cy: '12', r: '4' } ], [ 'path', { d: 'M12 2.5v2M12 19.5v2M4.6 4.6 6 6M18 18l1.4 1.4M2.5 12h2M19.5 12h2M4.6 19.4 6 18M18 6l1.4-1.4' } ] ],
moon: [ [ 'path', { d: 'M19.5 14.5A7.5 7.5 0 0 1 9.5 4.5a7.5 7.5 0 1 0 10 10z' } ] ],
auto: [ [ 'circle', { cx: '12', cy: '12', r: '8' } ], [ 'path', { d: 'M12 4a8 8 0 0 1 0 16z', 'class': 'vt-fill' } ] ],
logout: [ [ 'path', { d: 'M14 4h4.5A1.5 1.5 0 0 1 20 5.5v13a1.5 1.5 0 0 1-1.5 1.5H14' } ], [ 'path', { d: 'M10 8l-4 4 4 4M6 12h10' } ] ],
pin: [ [ 'path', { d: 'M4 5v14M9 12h11M15 7l5 5-5 5' } ] ],
info: [ [ 'circle', { cx: '12', cy: '12', r: '8.5' } ], [ 'path', { d: 'M12 11v5.5' } ], [ 'circle', { cx: '12', cy: '7.75', r: '.6' } ] ],
more: [ [ 'circle', { cx: '5.5', cy: '12', r: '.9' } ], [ 'circle', { cx: '12', cy: '12', r: '.9' } ], [ 'circle', { cx: '18.5', cy: '12', r: '.9' } ] ]
};
@@ -174,6 +177,54 @@ function highlight(text, idx) {
return out;
}
/* A path ("Network › Firewall") matches only on what it says: the query
as a substring, or each of its words starting a word of the path. Not
as letters scattered across it ("wire" in "Network › Firewall").
null = no match. */
function pathScore(query, path) {
const q = query.toLowerCase().trim().replace(/\s+/g, ' ');
const t = String(path || '').toLowerCase();
if (!q || !t) return null;
const at = t.indexOf(q);
if (at >= 0) return (at === 0 || /[\s\/›\-_(.]/.test(t[at - 1])) ? 10 : 6;
const words = t.split(/[\s\/›\-_(.]+/).filter(Boolean);
return q.split(' ').every(w => words.some(x => x.startsWith(w))) ? 4 : null;
}
/* Palette results for a query, best first: entries whose title matches,
or only when no title does, those whose path does. Equal scores go to
the shallower entry (Network › Wireless before a Wireless tab). */
function rankPalette(query, pool) {
const onTitle = [], onPath = [];
for (const e of pool) {
const t = fuzzy(query, e.title);
if (t) { onTitle.push({ e, score: t.score, idx: t.idx }); continue; }
const p = pathScore(query, e.path);
if (p != null) onPath.push({ e, score: p, idx: [] });
}
const depth = r => (r.e.segs || []).length;
return (onTitle.length ? onTitle : onPath).sort((a, b) => (b.score - a.score) || (depth(a) - depth(b)));
}
/* The palette without a query: rows under their category's header, which
a category of one row goes without. Under a header a row's path leaves
the category out (a tab shows its page only); a row without one keeps
its whole path. results: [ { e } ] in index order -> [ { r, head, path } ] */
function paletteLayout(results) {
const size = new Map();
for (const r of results) size.set(r.e.group, (size.get(r.e.group) || 0) + 1);
let prev = null;
return results.map(r => {
const g = r.e.group, headed = size.get(g) > 1;
const head = (headed && g !== prev) ? g : null;
prev = g;
let path = r.e.path || '';
if (headed && path === g) path = '';
else if (headed && path.startsWith(g + ' › ')) path = path.slice(g.length + 3);
return { r, head, path };
});
}
/* cells holding only addresses, counters, rates or durations get the mono stack */
const UNITS = /(?<![A-Za-z])(?:[kKMGTP]?i?B(?:\/s)?|[kMG]?bit\/s|[kMG]?bps|dBm|dB|[kMG]?Hz|ms|us|MCS|NSS|EHT|HE|VHT|HT|pkts?|packets|bytes|min|d|h|m|s)(?![A-Za-z])/g;
function technical(text) {
@@ -196,6 +247,26 @@ function repeatsTrail(key, tag, trail) {
return (tag === 'H2' ? trail.all : trail.page).has(key);
}
/* The crumbs as shown: one that only repeats the crumb before it
("System › System", a page and its tab of the same name) takes that
crumb's place, with its own (deeper) link and level. */
function mergeCrumbs(parts) {
const out = [];
for (const p of parts) {
if (out.length && headingKey(out[out.length - 1].title) === headingKey(p.title)) out[out.length - 1] = p;
else out.push(p);
}
return out;
}
/* LuCI renders its 404 with the deepest node it resolved (L.env.nodespec):
a category (firstchild) or a node without an action */
function notFound(spec) {
if (!spec || typeof spec !== 'object') return false;
const type = spec.action && typeof spec.action === 'object' ? spec.action.type : null;
return !type || type === 'firstchild';
}
/* Does help text carry a warning? LuCI writes them as a paragraph that
opens with a short label and a colon ("Note:", "Warning:", "注意:",
"Uwaga:"), whatever the language, and sometimes in bold. Line breaks
@@ -380,7 +451,7 @@ function enhanceMemory() {
});
}
/* Storage: one thin bar per mount, "used · free of total" beside it */
/* Storage: one thin bar per mount, "used · free · total" beside it */
function enhanceStorage() {
statusSections(_('Storage')).forEach(section => {
const table = pendingTable(section);
@@ -397,10 +468,11 @@ function enhanceStorage() {
return E('div', { 'class': 'v-meter-row' }, [
label,
E('div', { 'class': 'v-meter-bar is-thin', 'role': 'img',
'aria-label': '%s: %s %s, %s %s'.format(r.label, fmtBytes(used), _('used'), fmtBytes(size - used), _('free')) },
'aria-label': '%s: %s %s, %s %s, %s %s'.format(r.label, fmtBytes(used), _('used'), fmtBytes(size - used), _('free'), fmtBytes(size), _('total')) },
segment('v-seg-used' + (p >= 95 ? ' is-crit' : p >= 80 ? ' is-warn' : ''), used, size)),
E('span', { 'class': 'v-meter-val' }, [
E('b', {}, [ fmtBytes(used) ]), ' ' + _('used') + ' · ' + fmtBytes(size - used) + ' ' + _('free') + ' · ' + fmtBytes(size)
/* a figure and its word stay on one line */
E('b', {}, [ fmtBytes(used) ]), '\u00a0' + _('used') + ' · ' + fmtBytes(size - used) + '\u00a0' + _('free') + ' · ' + fmtBytes(size) + '\u00a0' + _('total')
])
]);
})));
@@ -700,7 +772,14 @@ return baseclass.extend({
if (!admin) return;
this.admin = admin;
/* Appearance opened before the menu came in lacks the network
path's group (hasDashboard): add it now */
if (this.appear && this.hasDashboard() && !this.appear.querySelector('input[name="vt-backdrop"]')) {
this.appear.appendChild(this.backdropGroup());
this.syncAppearance();
}
this.dp = (L.env.dispatchpath || []).slice();
this.missing = notFound(L.env.nodespec);
this.cats = ui.menu.getChildren(admin)
.filter(c => c.name !== 'logout')
@@ -958,18 +1037,14 @@ return baseclass.extend({
this.endSwitch();
if (fly.getAttribute('data-cat') !== cat.name || fly.hidden) {
fly.textContent = '';
const total = cat.pages.length;
fly.appendChild(E('div', { 'class': 'vt-fly-head' }, [
svg(cat.icon, 18),
E('span', { 'class': 'vt-fly-title', 'id': 'vt-fly-title' }, [ cat.title ]),
E('span', { 'class': 'vt-fly-count' }, [ '%d %s'.format(total, total === 1 ? _('page') : _('pages')) ])
E('span', { 'class': 'vt-fly-title', 'id': 'vt-fly-title' }, [ cat.title ])
]));
const ul = E('ul', { 'class': 'vt-fly-list', 'role': 'list' });
for (const p of cat.pages) {
const cur = this.dp[1] === cat.name && this.dp[2] === p.name;
const item = E('li', {}, E('a', { 'class': 'vt-fly-link', 'href': L.url('admin', cat.name, p.name), 'aria-current': cur ? 'page' : null }, [
E('span', {}, [ p.title ]),
p.subs.length ? E('span', { 'class': 'vt-fly-meta' }, [ String(p.subs.length) ]) : ''
E('span', {}, [ p.title ])
]));
if (p.subs.length > 1) {
const sub = E('ul', { 'class': 'vt-fly-sub' });
@@ -982,7 +1057,6 @@ return baseclass.extend({
ul.appendChild(item);
}
fly.appendChild(ul);
fly.appendChild(E('div', { 'class': 'vt-fly-foot' }, [ _('Press', 'vantage-theme'), ' ', E('kbd', {}, [ 'Ctrl K' ]), ' ', _('to search all pages') ]));
fly.setAttribute('data-cat', cat.name);
fly.setAttribute('role', 'region');
fly.setAttribute('aria-labelledby', 'vt-fly-title');
@@ -1032,11 +1106,8 @@ return baseclass.extend({
this.openTimer = null;
this.closeFlyout(false);
fly.textContent = '';
const total = page.subs.length;
fly.appendChild(E('div', { 'class': 'vt-fly-head' }, [
svg(cat.icon, 18),
E('span', { 'class': 'vt-fly-title', 'id': 'vt-fly-title' }, [ page.title ]),
E('span', { 'class': 'vt-fly-count' }, [ '%d %s'.format(total, total === 1 ? _('page') : _('pages')) ])
E('span', { 'class': 'vt-fly-title', 'id': 'vt-fly-title' }, [ page.title ])
]));
const ul = E('ul', { 'class': 'vt-fly-list', 'role': 'list' });
for (const s of page.subs)
@@ -1088,28 +1159,31 @@ return baseclass.extend({
for (let i = 1; i < this.dp.length && i <= 3; i++) {
node = node && node.children ? node.children[this.dp[i]] : null;
if (!node || !node.title) break;
parts.push({ title: _(node.title), segs: [ 'admin' ].concat(this.dp.slice(1, i + 1)) });
parts.push({ title: _(node.title), segs: [ 'admin' ].concat(this.dp.slice(1, i + 1)), level: i });
}
/* a 404: the crumbs lead to the category LuCI resolved, then say so */
if (this.missing) parts.push({ title: _('Not found'), segs: null, level: 0 });
if (!parts.length) return;
/* a page with page tabs: its current crumb switches between them,
from the top bar, which stays in view while the tabs scroll away */
const cat = this.cats.find(c => c.name === this.dp[1]);
const page = cat && this.dp.length > 3 ? cat.pages.find(p => p.name === this.dp[2]) : null;
const page = cat && !this.missing && this.dp.length > 3 ? cat.pages.find(p => p.name === this.dp[2]) : null;
const siblings = page && page.subs.length > 1 ? page : null;
const crumbs = mergeCrumbs(parts);
nav.textContent = '';
parts.forEach((p, i) => {
const last = i === parts.length - 1;
crumbs.forEach((p, i) => {
const last = i === crumbs.length - 1;
if (i) nav.appendChild(E('span', { 'class': 'vt-crumb-sep', 'aria-hidden': 'true' }, [ '›' ]));
if (last && siblings && parts.length === 3) {
if (last && siblings && p.level === 3) {
/* a disclosure (aria-expanded + aria-controls): the panel is a
region of links, not a menu, so no aria-haspopup */
const btn = E('button', {
'type': 'button', 'class': 'vt-crumb vt-crumb-current vt-crumb-switch', 'aria-current': 'page',
'aria-expanded': 'false', 'aria-controls': 'vt-flyout',
'title': _('Other pages in %s').format(siblings.title)
}, [ E('span', {}, [ p.title ]), svg('caret', 14) ]);
}, [ E('span', {}, [ p.title ]), svg('down', 14) ]);
btn.addEventListener('click', ev => {
if (this.switchOpen()) this.closeFlyout(true);
else this.openSwitch(cat, siblings, btn, ev.detail === 0);
@@ -1134,7 +1208,7 @@ return baseclass.extend({
renderTabs() {
const bar = document.getElementById('vt-tabs');
if (!bar || this.dp.length < 3) return;
if (!bar || this.missing || this.dp.length < 3) return;
const cat = this.admin.children[this.dp[1]];
const page = cat && cat.children ? cat.children[this.dp[2]] : null;
if (!page) return;
@@ -1161,16 +1235,19 @@ return baseclass.extend({
const key = e.segs.join('/') + '|' + e.title;
if (seen.has(key)) return;
seen.add(key);
e.hay = (e.path ? e.path + ' › ' : '') + e.title;
this.entries.push(e);
};
/* pages carry no icon: their category heads them, and only actions
have one. A tab named like its page (System Log's "System Log")
is that page's entry already. */
for (const c of this.cats) {
if (c.leaf) add({ title: c.title, path: '', group: c.title, segs: [ 'admin', c.name ], icon: c.icon });
if (c.leaf) add({ title: c.title, path: '', group: c.title, segs: [ 'admin', c.name ] });
for (const p of c.pages) {
add({ title: p.title, path: c.title, group: c.title, segs: [ 'admin', c.name, p.name ], icon: c.icon });
add({ title: p.title, path: c.title, group: c.title, segs: [ 'admin', c.name, p.name ] });
if (p.subs.length > 1)
for (const s of p.subs)
add({ title: s.title, path: c.title + ' › ' + p.title, group: c.title, segs: [ 'admin', c.name, p.name, s.name ], icon: 'page' });
if (headingKey(s.title) !== headingKey(p.title))
add({ title: s.title, path: c.title + ' › ' + p.title, group: c.title, segs: [ 'admin', c.name, p.name, s.name ] });
}
}
},
@@ -1184,8 +1261,13 @@ return baseclass.extend({
const login = this.loginPref(), names = this.loginNames();
for (const k of [ 'grid', 'stars', 'none' ])
if (k !== login) list.push({ title: '%s: %s'.format(_('Login background'), names[k]), path: _('Appearance', 'vantage-theme'), icon: 'page', run: () => this.setLoginPref(k) });
if (this.hasDashboard()) {
const bd = this.backdropPref();
for (const k of [ 'grid', 'stars', 'none' ])
if (k !== bd) list.push({ title: '%s: %s'.format(_('Network path background'), names[k]), path: _('Appearance', 'vantage-theme'), icon: 'page', run: () => this.setBackdropPref(k) });
}
list.push({ title: _('Log out'), path: _('Session'), icon: 'logout', segs: [ 'admin', 'logout' ] });
list.forEach(a => { a.hay = a.path + ' › ' + a.title; a.group = _('Actions'); });
list.forEach(a => { a.group = _('Actions'); });
return list;
},
@@ -1210,7 +1292,7 @@ return baseclass.extend({
const list = E('ul', { 'class': 'vt-pal-list', 'id': 'vt-pal-list', 'role': 'listbox', 'aria-label': _('Results', 'vantage-theme') });
const count = E('span', { 'class': 'vt-pal-count' });
const box = E('div', { 'class': 'vt-pal', 'role': 'dialog', 'aria-modal': 'true', 'aria-label': _('Command palette') }, [
E('div', { 'class': 'vt-pal-field' }, [ svg('search', 18), input, E('kbd', {}, [ 'Esc' ]) ]),
E('div', { 'class': 'vt-pal-field' }, [ svg('search', 18), input ]),
list,
E('div', { 'class': 'vt-pal-foot' }, [
E('span', {}, [ E('kbd', {}, [ '↑' ]), E('kbd', {}, [ '↓' ]), _('navigate') ]),
@@ -1242,35 +1324,18 @@ return baseclass.extend({
const pal = this.pal;
const q = pal.input.value.trim();
const pool = this.entries.concat(this.actions());
let results;
if (!q) {
results = pool.map(e => ({ e, idx: [] }));
}
else {
results = [];
for (const e of pool) {
const onTitle = fuzzy(q, e.title);
const onPath = onTitle ? null : fuzzy(q, e.hay);
if (onTitle) results.push({ e, score: onTitle.score + 6, idx: onTitle.idx });
else if (onPath) results.push({ e, score: onPath.score * .6, idx: [] });
}
results.sort((a, b) => b.score - a.score);
results = results.slice(0, 40);
}
const found = q ? rankPalette(q, pool) : null;
const results = q ? found.slice(0, 40) : pool.map(e => ({ e, idx: [] }));
const rows = q ? results.map(r => ({ r, head: null, path: r.e.path })) : paletteLayout(results);
pal.list.textContent = '';
pal.items = [];
let group = null;
results.forEach((r, n) => {
if (!q && r.e.group !== group) {
group = r.e.group;
pal.list.appendChild(E('li', { 'class': 'vt-pal-group', 'role': 'presentation' }, [ group ]));
}
rows.forEach(({ r, head, path }, n) => {
if (head) pal.list.appendChild(E('li', { 'class': 'vt-pal-group', 'role': 'presentation' }, [ head ]));
const li = E('li', { 'class': 'vt-pal-item', 'role': 'option', 'id': 'vt-pal-opt-' + n, 'aria-selected': 'false' }, [
svg(r.e.icon, 18),
r.e.icon ? svg(r.e.icon, 18) : '',
highlight(r.e.title, r.idx),
r.e.path ? E('span', { 'class': 'vt-pal-path' }, [ r.e.path ]) : '',
path ? E('span', { 'class': 'vt-pal-path' }, [ path ]) : '',
E('kbd', { 'class': 'vt-pal-enter' }, [ '↵' ])
]);
li.addEventListener('mousemove', () => this.setPaletteActive(n, false));
@@ -1282,7 +1347,7 @@ return baseclass.extend({
if (!results.length)
pal.list.appendChild(E('li', { 'class': 'vt-pal-empty', 'role': 'presentation' }, [ _('No matching pages') ]));
pal.count.textContent = q ? '%d / %d'.format(results.length, pool.length) : '%d %s'.format(pool.length, _('items', 'vantage-theme'));
pal.count.textContent = q ? N_(found.length, '%d result', '%d results', 'vantage-theme').format(found.length) : '';
this.setPaletteActive(0, true);
},
@@ -1398,6 +1463,23 @@ return baseclass.extend({
this.syncAppearance();
},
/* Vantage's dashboard, where this user may open it: its network path
takes the same backdrops as the login page */
hasDashboard() {
return !!(this.admin && ui.menu.getChildren(this.admin).some(c => c.name === 'dashboard' && c.action && c.action.path === 'vantage/overview'));
},
backdropPref() {
const p = storageGet(BACKDROP_KEY);
return (p === 'stars' || p === 'none') ? p : 'grid';
},
setBackdropPref(pref) {
storageSet(BACKDROP_KEY, pref === 'grid' ? null : pref);
window.dispatchEvent(new CustomEvent(BACKDROP_EVENT));
this.syncAppearance();
},
setupTheme() {
const btn = document.getElementById('vt-theme');
if (btn) {
@@ -1415,19 +1497,20 @@ return baseclass.extend({
if (mq && mq.addEventListener) mq.addEventListener('change', () => { if (this.themePref() === 'auto') this.applyTheme(); });
window.addEventListener('storage', ev => {
if (ev.key === THEME_KEY) this.applyTheme();
else if (ev.key === LOGIN_KEY) this.syncAppearance();
else if (ev.key === LOGIN_KEY || ev.key === BACKDROP_KEY) this.syncAppearance();
});
this.applyTheme();
},
/* Appearance, under the colour-theme button: the colour theme and the
login background, each a native radio group (the arrow keys move
/* Appearance, under the colour-theme button: the colour theme, the
login background and, with the dashboard, the network path's
background, each a native radio group (the arrow keys move
within it) drawn as segments. Choices apply at once and stay open;
Escape, Tab out of either end, a click outside, resizing or
scrolling the page close it. */
appearanceEl() {
if (this.appear) return this.appear;
const group = (name, legend, opts, set) => E('fieldset', { 'class': 'vt-appear-set' }, [
/* one of Appearance's radio groups */
appearGroup(name, legend, opts, set) {
return E('fieldset', { 'class': 'vt-appear-set' }, [
E('legend', {}, [ legend ]),
E('div', { 'class': 'vt-appear-track' }, opts.map(o => {
const input = E('input', { 'type': 'radio', 'name': name, 'value': o[0] });
@@ -1435,13 +1518,24 @@ return baseclass.extend({
return E('label', { 'class': 'vt-appear-opt' }, [ input ].concat(o[2] ? [ svg(o[2], 16) ] : [], [ E('span', {}, [ o[1] ]) ]));
}))
]);
},
/* the network path's group, once the menu says the dashboard is here */
backdropGroup() {
const n = this.loginNames();
return this.appearGroup('vt-backdrop', _('Network path background'), [ [ 'grid', n.grid ], [ 'stars', n.stars ], [ 'none', n.none ] ], v => this.setBackdropPref(v));
},
appearanceEl() {
if (this.appear) return this.appear;
const group = (name, legend, opts, set) => this.appearGroup(name, legend, opts, set);
const login = this.loginNames();
const box = E('div', { 'id': 'vt-appearance', 'class': 'vt-appearance', 'role': 'dialog', 'aria-label': _('Appearance', 'vantage-theme'), 'hidden': 'hidden' }, [
group('vt-colour', _('Colour theme'), [
[ 'auto', _('Auto', 'vantage-theme'), 'auto' ], [ 'light', _('Light', 'vantage-theme'), 'sun' ], [ 'dark', _('Dark', 'vantage-theme'), 'moon' ]
], v => this.setTheme(v)),
group('vt-login', _('Login background'), [ [ 'grid', login.grid ], [ 'stars', login.stars ], [ 'none', login.none ] ], v => this.setLoginPref(v))
]);
].concat(this.hasDashboard() ? [ this.backdropGroup() ] : []));
box.addEventListener('keydown', ev => {
if (ev.key === 'Escape') { ev.preventDefault(); ev.stopPropagation(); this.closeAppearance(true); }
else if (ev.key === 'Tab') {
@@ -1465,7 +1559,7 @@ return baseclass.extend({
syncAppearance() {
if (!this.appear) return;
const now = { 'vt-colour': this.themePref(), 'vt-login': this.loginPref() };
const now = { 'vt-colour': this.themePref(), 'vt-login': this.loginPref(), 'vt-backdrop': this.backdropPref() };
Array.prototype.forEach.call(this.appear.querySelectorAll('input[type="radio"]'), r => { r.checked = (now[r.name] === r.value); });
},
@@ -1513,11 +1607,10 @@ return baseclass.extend({
chip.textContent = '';
/* device strings go in as text nodes (E() would parse a string as HTML) */
const text = v => [ document.createTextNode(String(v)) ];
chip.appendChild(E('b', {}, text(board.hostname)));
if (product) {
chip.appendChild(E('span', { 'class': 'vt-host-sep', 'aria-hidden': 'true' }, text('·')));
chip.appendChild(E('span', {}, text(product)));
}
host.chipNames(board.hostname, product).forEach((name, i) => {
if (i) chip.appendChild(E('span', { 'class': 'vt-host-sep', 'aria-hidden': 'true' }, text('·')));
chip.appendChild(i ? E('span', {}, text(name)) : E('b', {}, text(name)));
});
const rel = board.release && board.release.description;
chip.setAttribute('title', [ board.hostname, product, rel ].filter(Boolean).join('\n'));
chip.hidden = false;
@@ -1909,12 +2002,8 @@ return baseclass.extend({
/* (i) shows, and the text folds to a line, while the text needs more
than one line. Text that is not displayed (another tab, an unmet
dependency) measures 0 and waits for the next resize notification. */
fitHelp(desc) {
this.fitHelps([ desc ]);
},
/* all sizes are read before any button changes: a write between two
dependency) measures 0 and waits for the next resize notification.
All sizes are read before any button changes: a write between two
reads forced a layout per field (66 when the Wireless modal opened).
Folded, the text still measures its full height. The line's end is
set as data-v-end ("cut" at --vt-cut, or "fade"), which the CSS reads. */
@@ -8,8 +8,9 @@
* name ("Qualcomm Technologies, Inc. IPQ5332/RDP442/AP-MI01.3") rather than
* the product. In that case (or when the model is missing) the product name
* is derived from board_name ("zyxel,nwa50be" -> "Zyxel NWA50BE"): vendor
* capitalised (or its usual spelling), model upper-cased. Pure functions,
* no DOM, unit tested in tests/host.test.js.
* capitalised (or its usual spelling), model upper-cased. The chip leaves
* out a name the other one contains. Pure functions, no DOM, unit tested
* in tests/host.test.js.
*/
/* SoC vendors, reference boards and placeholders: not a product name.
@@ -52,6 +53,22 @@ function fromBoardName(boardName) {
return vendorName(vendor) + ' ' + model.replace(/,/g, ' ').toUpperCase();
}
/* names compared for repeats: case, spaces and punctuation don't count
("cudy-wr-3000" and "Cudy WR3000" are one name) */
function nameKey(s) {
return clean(s).toLowerCase().replace(/[^\p{L}\p{N}]+/gu, '');
}
/* the host chip's names, hostname first: a name the other one contains
only repeats it and is left out (the product, when they are the same) */
function chipNames(hostname, product) {
const h = clean(hostname), p = clean(product);
const hk = nameKey(h), pk = nameKey(p);
if (!p || (h && pk && hk.indexOf(pk) >= 0)) return h ? [ h ] : [];
if (!h || (hk && pk.indexOf(hk) >= 0)) return [ p ];
return [ h, p ];
}
/* product name for a `system board` reply, '' when nothing usable */
function productName(board) {
if (!board || typeof board !== 'object') return '';
@@ -62,6 +79,7 @@ function productName(board) {
return baseclass.extend({
productName,
chipNames,
fromBoardName,
looksLikeReference
});
@@ -24,7 +24,10 @@
--fs-xl: 20px;
--fs-2xl: 24px;
/* spacing: steps of 4px, with 2px and 6px for tight insides */
--sp-0-5: 2px;
--sp-1: 4px;
--sp-1-5: 6px;
--sp-2: 8px;
--sp-3: 12px;
--sp-4: 16px;
@@ -32,9 +35,19 @@
--sp-6: 24px;
--sp-8: 32px;
/* font weights: four steps only (static fonts draw 620-680 as bold) */
--fw-regular: 400;
--fw-medium: 500;
--fw-semibold: 600;
--fw-bold: 700;
/* radii: bars, small controls, controls, cards, large icon tiles
(pills are 999px, dots 50%) */
--r-xs: 2px;
--r-sm: 4px;
--r: 6px;
--r-lg: 8px;
--r-xl: 12px;
--rail-w: 56px;
--rail-w-pinned: 204px;
@@ -70,6 +83,7 @@
--err: #c42b2b;
--err-soft: rgba(196, 43, 43, .1);
--err-ink: #ffffff; /* text on an --err / --info fill, >= 4.5:1 */
--warn-ink: #ffffff; /* text on a --warn fill (counts, warning tooltips); #1a1200 was 3.4:1 */
--info: #1c64c4;
--info-ink: #ffffff;
--ok-text: #0a7262; /* --ok as text on control fills (s3, s4) */
@@ -82,9 +96,9 @@
--scrim: rgba(20, 23, 26, .38);
--shadow-pop: 0 12px 32px rgba(20, 23, 26, .16), 0 2px 6px rgba(20, 23, 26, .08);
--zone-mix: .22;
--seg-bg: var(--s3); /* segmented control: track, selected segment */
--seg-bg: var(--s3); /* segmented control: track, selected segment and its ring */
--seg-on: var(--s2);
--seg-on-ring: inset 0 0 0 1px var(--line); /* inset: drawn at the segment's padding edge */
--seg-on-ring: inset 0 0 0 1px var(--accent-line); /* inset: drawn at the segment's padding edge */
}
html[data-darkmode="true"] {
@@ -116,6 +130,7 @@ html[data-darkmode="true"] {
--err: #ff6b6b;
--err-soft: rgba(255, 107, 107, .12);
--err-ink: #1a0505; /* white on the light dark-mode red was 2.8:1 */
--warn-ink: #1a1200;
--info: #6cb4ff;
--info-ink: #0d0e10;
--ok-text: var(--ok);
@@ -128,7 +143,6 @@ html[data-darkmode="true"] {
--zone-mix: .2;
--seg-bg: var(--bg);
--seg-on: var(--s3);
--seg-on-ring: inset 0 0 0 1px var(--line-2);
}
/* touch: controls are sized by the pointer, not the screen width */
@@ -153,6 +167,14 @@ html[data-darkmode="true"] {
--v-accent: var(--accent-text); /* olive: lime is unreadable on light */
--v-accent-ink: #ffffff;
--v-accent-soft: var(--accent-soft);
/* the accent by role, as the theme's own controls use it: text and
icons; borders, rings and selection bars; the lime fill of primary
buttons and pressed picks with its ink */
--v-accent-text: var(--accent-text);
--v-accent-line: var(--accent-line);
--v-accent-fill: var(--accent);
--v-accent-fill-hover: var(--accent-hover);
--v-accent-fill-ink: var(--accent-ink);
--v-ok: var(--ok);
--v-ok-soft: var(--ok-soft);
--v-warn: var(--warn);
@@ -161,6 +183,34 @@ html[data-darkmode="true"] {
--v-err-soft: var(--err-soft);
--v-info: var(--info);
--v-info-soft: var(--info-soft);
/* status colours as text on their soft fill, >= 4.5:1 on a hovered row too */
--v-ok-text: var(--ok-text);
--v-warn-text: var(--warn-text);
--v-err-text: var(--err-text);
--v-info-text: var(--info-text);
--v-focus: var(--focus); /* a focused field: border plus a 1px ring of it */
--v-scrim: var(--scrim); /* under dialogs, drawers and sheets */
--v-seg-bg: var(--seg-bg); /* segmented control: track, selected segment, its ring */
--v-seg-on: var(--seg-on);
--v-seg-on-ring: var(--seg-on-ring);
--v-hover: color-mix(in srgb, var(--s3) 55%, transparent); /* a hovered row on a card */
--v-surface-4: var(--s4); /* a hovered item in a menu (menus are --v-surface-2) */
--v-meter: var(--info); /* memory and storage bars: blue (lime is download) */
--v-card-px: var(--card-px); /* card insides: 20 / 16px, 14px on phones */
--v-card-py: var(--card-py);
--v-sp-0-5: var(--sp-0-5); /* spacing: 2 4 6 8 12 16 20 24 32 px */
--v-sp-1: var(--sp-1);
--v-sp-1-5: var(--sp-1-5);
--v-sp-2: var(--sp-2);
--v-sp-3: var(--sp-3);
--v-sp-4: var(--sp-4);
--v-sp-5: var(--sp-5);
--v-sp-6: var(--sp-6);
--v-sp-8: var(--sp-8);
--v-fw-regular: var(--fw-regular); /* weights: 400 500 600 700 */
--v-fw-medium: var(--fw-medium);
--v-fw-semibold: var(--fw-semibold);
--v-fw-bold: var(--fw-bold);
--v-font: var(--font);
--v-mono: var(--mono);
--v-fs-micro: var(--fs-micro); /* the type scale: 10.5 12 13 14 16 20 24 px */
@@ -170,12 +220,18 @@ html[data-darkmode="true"] {
--v-fs-lg: var(--fs-lg);
--v-fs-xl: var(--fs-xl);
--v-fs-2xl: var(--fs-2xl);
--v-r-xs: var(--r-xs); /* radii: 2 4 6 8 12 px */
--v-r-sm: var(--r-sm);
--v-r: var(--r);
--v-r-lg: var(--r-lg);
--v-r-xl: var(--r-xl);
--v-ctl-h: var(--ctl-h); /* every control: 36px, 44px on touch */
--v-shadow: 0 1px 2px rgba(20, 23, 26, .06), 0 1px 0 rgba(20, 23, 26, .03);
--v-shadow-raise: 0 6px 16px rgba(20, 23, 26, .08); /* a card lifted under the pointer */
--v-shadow-pop: var(--shadow-pop); /* lists and menus that open over the page */
/* the dashboard's drawer and its scrim: over the rail and the top bar
(300), under modals (1000) and menus (1100) */
--v-z-drawer: 890;
/* where sticky content may start: below the top bar (page tabs scroll) */
--v-stick-top: var(--stick-top);
@@ -199,6 +255,7 @@ html[data-darkmode="true"] {
--v-accent: var(--accent); /* lime signal */
--v-accent-ink: var(--accent-ink);
--v-shadow: 0 1px 0 rgba(0, 0, 0, .35);
--v-shadow-raise: 0 6px 16px rgba(0, 0, 0, .4);
--v-chart-1: #c8f031;
--v-chart-2: #3f8fe8;
@@ -336,11 +393,6 @@ html[data-darkmode="true"] .vt-brand-tile { fill: #16181b; }
text-decoration: none !important;
}
.vt-brand svg { flex: none; }
.vt-brand-word {
font-weight: 650; font-size: var(--fs-md); letter-spacing: .02em;
white-space: nowrap; overflow: hidden;
display: none;
}
.vt-rail-list {
list-style: none; margin: 0; padding: var(--sp-2) 0;
@@ -395,7 +447,6 @@ html[data-darkmode="true"] .vt-brand-tile { fill: #16181b; }
/* pinned rail: labels + inline accordion instead of flyouts */
html[data-rail="pinned"] .vt-rail { width: var(--rail-w-pinned); }
html[data-rail="pinned"] .vt-brand-word,
html[data-rail="pinned"] .vt-rail-label { display: block; }
html[data-rail="pinned"] .vt-rail-caret { display: block; }
html[data-rail="pinned"] .vt-rail-item.is-expanded > .vt-rail-link .vt-rail-caret { transform: rotate(90deg); }
@@ -436,9 +487,7 @@ html[data-rail="pinned"] .vt-frame { margin-left: var(--rail-w-pinned); }
border-bottom: 1px solid var(--line);
flex: none;
}
.vt-fly-head svg { width: 18px; height: 18px; color: var(--accent-text); }
.vt-fly-title { font-size: var(--fs-md); font-weight: 600; }
.vt-fly-count { margin-left: auto; font: var(--fs-micro)/1 var(--mono); color: var(--text-3); letter-spacing: .04em; }
.vt-fly-list { list-style: none; margin: 0; padding: var(--sp-2); overflow-y: auto; flex: 1 1 auto; }
.vt-fly-list > li + li { margin-top: 1px; }
@@ -451,7 +500,6 @@ html[data-rail="pinned"] .vt-frame { margin-left: var(--rail-w-pinned); }
}
.vt-fly-link:hover { background: var(--s3); }
.vt-fly-link[aria-current] { background: var(--accent-soft); color: var(--accent-text); font-weight: 600; }
.vt-fly-link .vt-fly-meta { margin-left: auto; font: var(--fs-micro)/1 var(--mono); color: var(--text-3); }
.vt-fly-sub { list-style: none; margin: 0 0 var(--sp-1) 18px; padding: 0; border-left: 1px solid var(--line-2); }
.vt-fly-sub a {
display: block; padding: 4px 10px;
@@ -461,11 +509,6 @@ html[data-rail="pinned"] .vt-frame { margin-left: var(--rail-w-pinned); }
}
.vt-fly-sub a:hover { color: var(--text); background: var(--s3); }
.vt-fly-sub a[aria-current] { color: var(--accent-text); font-weight: 600; box-shadow: inset 2px 0 0 var(--accent-text); }
.vt-fly-foot {
flex: none; padding: var(--sp-2) var(--sp-4);
border-top: 1px solid var(--line);
color: var(--text-3); font-size: var(--fs-micro); letter-spacing: .06em; text-transform: uppercase;
}
.vt-scrim { position: fixed; inset: 0; z-index: 280; background: var(--scrim); }
@@ -516,7 +559,7 @@ a.vt-crumb:hover { color: var(--text); }
.vt-crumb-switch > svg { flex: none; width: 14px; height: 14px; color: var(--text-3); transition: transform .15s; }
.vt-crumb-switch:hover, .vt-crumb-switch[aria-expanded="true"] { background: var(--s3); }
.vt-crumb-switch:focus-visible { outline-offset: 0; }
.vt-crumb-switch[aria-expanded="true"] > svg { transform: rotate(90deg); }
.vt-crumb-switch[aria-expanded="true"] > svg { transform: rotate(180deg); }
@media (width > 720px) {
/* the flyout as a dropdown under the crumb (left: set from the crumb) */
.vt-flyout.is-switch {
@@ -593,7 +636,7 @@ a.vt-crumb:hover { color: var(--text); }
}
.vt-appear-opt:hover { color: var(--text); }
.vt-appear-opt > input { position: absolute; width: 1px; height: 1px; margin: 0; opacity: 0; pointer-events: none; }
.vt-appear-opt:has(> input:checked) { background-color: var(--seg-on); color: var(--accent-text); font-weight: 600; box-shadow: inset 0 0 0 1px var(--accent-line); }
.vt-appear-opt:has(> input:checked) { background-color: var(--seg-on); color: var(--accent-text); font-weight: 600; box-shadow: var(--seg-on-ring); }
.vt-appear-opt:has(> input:focus-visible) { outline: 2px solid var(--focus); outline-offset: -1px; }
.vt-appear-opt svg { flex: none; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
@@ -608,7 +651,7 @@ a.vt-crumb:hover { color: var(--text); }
font-size: var(--fs-sm);
white-space: nowrap;
}
.vt-host b { color: var(--text); font-weight: 600; font-family: var(--mono); font-size: var(--fs-micro); }
.vt-host b { color: var(--text); font-weight: 600; }
.vt-host span { overflow: hidden; text-overflow: ellipsis; }
.vt-host-sep { color: var(--text-3); }
@@ -626,31 +669,15 @@ a.vt-crumb:hover { color: var(--text); }
}
#indicators > span[data-clickable] { cursor: pointer; }
#indicators > span[data-clickable]:hover { border-color: var(--text-3); color: var(--text); }
#indicators > span[data-indicator="poll-status"][data-style="active"] { position: relative; isolation: isolate; }
/* auto-refresh on: a still dot (the label says it, a pulse only
distracted); paused: a square */
#indicators > span[data-indicator="poll-status"][data-style="active"]::before {
content: ""; width: 7px; height: 7px; border-radius: 50%;
background: var(--accent-text);
}
/* the pulse is a ring of its own that scales and fades: the compositor
runs it, where an animated box-shadow repainted the page every frame.
It sits behind the dot (z-index -1 in the pill's isolated stack), so
the dot never lightens, and is invisible unless animated: with reduced
motion the one near-instant run ends on opacity 0. */
#indicators > span[data-indicator="poll-status"][data-style="active"]::after {
content: ""; position: absolute; z-index: -1; left: 10px; top: calc(50% - 3.5px);
width: 7px; height: 7px; border-radius: 50%;
background: var(--accent-line);
opacity: 0;
pointer-events: none;
animation: vt-live 2s ease-out infinite;
background: var(--ok);
}
#indicators > span[data-indicator="poll-status"][data-style="inactive"]::before {
content: ""; width: 7px; height: 7px; border-radius: 1px; background: var(--text-3);
}
@keyframes vt-live {
0% { transform: scale(1); opacity: 1; }
70%, 100% { transform: scale(2.7); opacity: 0; }
}
/* "Unsaved Changes: N" -> label + prominent count (text node stays for LuCI) */
#indicators > span[data-indicator][data-count] {
font-size: 0; gap: 8px; padding-right: 4px;
@@ -666,7 +693,7 @@ a.vt-crumb:hover { color: var(--text); }
display: inline-flex; align-items: center; justify-content: center;
min-width: 20px; height: 20px; padding: 0 6px;
border-radius: 999px;
background: var(--warn); color: #1a1200;
background: var(--warn); color: var(--warn-ink);
font: 700 var(--fs-micro)/1 var(--mono);
}
#indicators > span[data-indicator="media_error"] { color: var(--err); border-color: var(--err); background: var(--err-soft); }
@@ -770,6 +797,11 @@ a.vt-crumb:hover { color: var(--text); }
}
.vt-pal-foot span { display: inline-flex; align-items: center; gap: 6px; }
.vt-pal-foot .vt-pal-count { margin-left: auto; font-family: var(--mono); font-size: var(--fs-micro); }
/* no keyboard, no key hints */
/* no keyboard hints on touch; the result count stays */
@media (hover: none) {
.vt-pal kbd, .vt-pal-foot > span:not(.vt-pal-count) { display: none; }
}
/* ------------------------------------ 5. content: map, section, options */
@@ -786,7 +818,7 @@ a.vt-crumb:hover { color: var(--text); }
.cbi-map { margin-bottom: var(--sp-6); }
.cbi-map > h2, .vt-main > h2, #view > h2, #view > div > h2:first-child, .cbi-map > h2:first-child {
margin: 0 0 6px;
font-size: var(--fs-2xl); font-weight: 650; line-height: 1.2;
font-size: var(--fs-2xl); font-weight: 600; line-height: 1.2;
letter-spacing: -.02em;
}
.vt-main > h2[name="content"] { margin-bottom: var(--sp-5); }
@@ -1012,7 +1044,8 @@ legend { font-size: var(--fs-md); font-weight: 600; padding: 0; margin-bottom: v
/* ------------------------------------------------------------ 6. widgets */
input, select, textarea, button { font: inherit; color: inherit; }
/* zero specificity, like the button rule below, which must win over it */
:where(input, select, textarea, button) { font: inherit; color: inherit; }
.cbi-input-text, .cbi-input-password, .cbi-input-select, .cbi-input-textarea,
.cbi-section-create-name, .vt-main select, .vt-main textarea,
@@ -1709,22 +1742,24 @@ table { border-collapse: separate; border-spacing: 0; width: 100%; }
.td.cbi-section-actions .vt-acts-more.spinning::before { margin: 0; }
.vt-actmenu {
position: fixed; z-index: 1100;
display: flex; flex-direction: column; gap: 2px;
min-width: 168px; max-width: calc(100vw - 16px); padding: 6px;
display: flex; flex-direction: column;
min-width: 168px; max-width: calc(100vw - 16px); padding: var(--sp-1);
overflow-y: auto; overscroll-behavior: contain;
border: 1px solid var(--line-2); border-radius: var(--r);
background: var(--s2); box-shadow: var(--shadow-pop);
}
.vt-actmenu[hidden] { display: none; }
/* items as .cbi-dropdown's: the pointer's on --s4, the keyboard's with the
accent bar too */
.vt-actmenu-item {
display: flex; align-items: center;
min-height: var(--ctl-h); padding: 0 12px;
min-height: calc(var(--ctl-h) - 4px); padding: 4px 8px;
border: 0; border-radius: var(--r-sm);
background: transparent; color: var(--text);
font: inherit; font-size: var(--fs-base); text-align: left; white-space: nowrap; cursor: pointer;
font: inherit; font-size: var(--fs-base); font-weight: 400; text-align: left; white-space: nowrap; cursor: pointer;
}
.vt-actmenu-item:hover { background: var(--s3); }
.vt-actmenu-item:focus-visible { background: var(--s3); outline: 2px solid var(--focus); outline-offset: -2px; }
.vt-actmenu-item:hover { background: var(--s4); }
.vt-actmenu-item:focus-visible { background: var(--s4); outline: none; box-shadow: inset 2px 0 0 var(--accent-text); }
.vt-actmenu-item.is-danger { color: var(--err); }
.vt-actmenu-item[disabled] { opacity: .5; cursor: default; }
.vt-actmenu-item[disabled]:hover { background: none; }
@@ -1785,7 +1820,7 @@ div.right > .btn, div.right > .cbi-button, div.right > button { margin-left: 0;
:is(.cbi-tabmenu > li > a, .vt-pagetabs > .vt-tab):focus-visible { outline-offset: -1px; border-radius: calc(var(--r) + 3px); }
/* the selected segment carries the signal (text and ring): white on the
grey track alone was hard to pick out; hover keeps it */
.cbi-tabmenu > li.cbi-tab > a, .cbi-tabmenu > li.cbi-tab > a:hover, .vt-pagetabs > .vt-tab[aria-current] { background-color: var(--seg-on); color: var(--accent-text); font-weight: 600; box-shadow: inset 0 0 0 1px var(--accent-line); }
.cbi-tabmenu > li.cbi-tab > a, .cbi-tabmenu > li.cbi-tab > a:hover, .vt-pagetabs > .vt-tab[aria-current] { background-color: var(--seg-on); color: var(--accent-text); font-weight: 600; box-shadow: var(--seg-on-ring); }
.cbi-tabmenu > li[data-errors] > a::after {
content: attr(data-errors);
display: inline-flex; align-items: center; justify-content: center;
@@ -1867,7 +1902,7 @@ div.right > .btn, div.right > .cbi-button, div.right > button { margin-left: 0;
:is(.ifacebox-body, .ifacebadge) > span:has(> .nowrap) > .nowrap > strong {
color: var(--text-3);
font: 600 var(--fs-micro)/1.55 var(--font);
letter-spacing: .06em; text-transform: uppercase;
letter-spacing: .08em; text-transform: uppercase;
white-space: nowrap;
}
@@ -1920,16 +1955,17 @@ div:has(> .ifacebox > .ifacebox-head.cbi-tooltip-container) > .ifacebox { width:
.ifacebox-head.active { background: var(--accent-soft); color: var(--accent-text); }
.ifacebox-body { padding: 8px 10px; font-size: var(--fs-sm); color: var(--text-2); }
.ifacebox-body img { width: 18px; height: 18px; }
/* Status -> Port status, each box: a link dot by the name (as on the
upstream and radio cards), the speed as its figure, the zone as a bare
bar and the counters' arrows in the dashboard's upload and download
colours (menu-vantage.js puts them in spans). LuCI's port picture only
repeats the link state: hidden. A port without link is muted throughout */
/* Status -> Port status, each box: a ring by the name of a port without
link (as on the upstream and radio cards; a port with link has none),
the speed as its figure, the zone as a bare bar and the counters'
arrows in the dashboard's upload and download colours (menu-vantage.js
puts them in spans). LuCI's port picture only repeats the link state:
hidden. A port without link is muted throughout */
.ifacebox:has(> .ifacebox-head.cbi-tooltip-container) > .ifacebox-head:first-child {
display: flex; align-items: center; gap: 8px;
padding: 10px 10px 2px;
background: none; border-bottom: 0;
font-family: var(--mono); font-weight: 650 !important; text-align: left;
font-family: var(--mono); font-weight: 600 !important; text-align: left;
}
.ifacebox:has(> .ifacebox-head.cbi-tooltip-container) > .ifacebox-head:first-child::before {
content: ""; flex: none;
@@ -1937,7 +1973,7 @@ div:has(> .ifacebox > .ifacebox-head.cbi-tooltip-container) > .ifacebox { width:
box-shadow: inset 0 0 0 1.5px var(--text-3);
}
.ifacebox:has(> .ifacebox-body > img[src$="_down.svg"]) > .ifacebox-head:first-child { color: var(--text-2); }
.ifacebox:has(> .ifacebox-body > img[src$="_up.svg"]) > .ifacebox-head:first-child::before { background: var(--v-chart-1); box-shadow: 0 0 0 3px var(--accent-soft); }
.ifacebox:has(> .ifacebox-body > img[src$="_up.svg"]) > .ifacebox-head:first-child::before { content: none; }
.ifacebox:has(> .ifacebox-head.cbi-tooltip-container) > .ifacebox-head:first-child + .ifacebox-body { padding: 2px 10px 6px; color: var(--text-3); }
.ifacebox:has(> .ifacebox-body > img[src$="_up.svg"]) > .ifacebox-head:first-child + .ifacebox-body { color: var(--text-2); }
.ifacebox:has(> .ifacebox-head.cbi-tooltip-container) > .ifacebox-head:first-child + .ifacebox-body > :is(img, br) { display: none; }
@@ -1948,7 +1984,7 @@ div:has(> .ifacebox > .ifacebox-head.cbi-tooltip-container) > .ifacebox { width:
.ifacebox-head.cbi-tooltip-container:has(> .zonebadge) { gap: 3px; padding: 4px 10px; background: none; border-bottom: 0; }
.ifacebox-head.cbi-tooltip-container > .zonebadge[style*="--zone-color-rgb"] {
height: 3px !important; padding: 0;
border: 0; border-radius: 2px; box-shadow: none;
border: 0; border-radius: var(--r-xs); box-shadow: none;
background-color: rgb(var(--zone-color-rgb)) !important;
}
.ifacebox-head.cbi-tooltip-container > .zonebadge:is([style*="--zone-color-rgb: 238, 238, 238"], [style*="--zone-color-rgb:238, 238, 238"], [style*="--zone-color-rgb:238,238,238"]) { background-color: var(--text-3) !important; }
@@ -1968,13 +2004,15 @@ div:has(> .ifacebox > .ifacebox-head.cbi-tooltip-container) > .ifacebox { width:
color: var(--text) !important;
font-size: var(--fs-md); text-align: left;
}
.network-status-table .ifacebox-head strong { font-weight: 650; font-family: var(--mono); letter-spacing: -.01em; }
.network-status-table .ifacebox-head strong { font-weight: 600; font-family: var(--mono); letter-spacing: -.01em; }
/* up is the normal case and draws nothing; a box that is down (an
upstream not connected, a radio off) gets a hollow ring */
.network-status-table .ifacebox-head::before {
content: ""; flex: none;
width: 8px; height: 8px; border-radius: 50%;
background: var(--text-3);
box-shadow: inset 0 0 0 1.5px var(--text-3);
}
.network-status-table .ifacebox-head.active::before { background: var(--accent-text); box-shadow: 0 0 0 3px var(--accent-soft); }
.network-status-table .ifacebox-head.active::before { content: none; }
.network-status-table .ifacebox-head[data-v-band]::after {
content: attr(data-v-band);
margin-left: auto;
@@ -2038,34 +2076,6 @@ div:has(> .ifacebox > .ifacebox-head.cbi-tooltip-container) > .ifacebox { width:
.zone-forwards { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; }
.zone-forwards > span { color: var(--text-3); }
/* progress bar: text only exists in title */
.cbi-progressbar {
position: relative;
width: 100%; max-width: 520px;
height: 22px;
background: var(--s2);
border: 1px solid var(--line);
border-radius: var(--r-sm);
overflow: hidden;
}
.cbi-progressbar > div {
height: 100%;
width: 0;
background: linear-gradient(90deg, color-mix(in srgb, var(--accent-text) 22%, transparent), color-mix(in srgb, var(--accent-text) 48%, transparent));
transition: width .3s ease-out;
}
.cbi-progressbar::before {
content: attr(title);
position: absolute; inset: 0;
display: flex; align-items: center;
padding: 0 8px;
color: var(--text);
font: 500 var(--fs-micro)/1 var(--mono);
font-variant-numeric: tabular-nums;
white-space: nowrap;
z-index: 1;
}
/* notifications */
.alert-message {
position: relative;
@@ -2128,7 +2138,7 @@ div:has(> .ifacebox > .ifacebox-head.cbi-tooltip-container) > .ifacebox { width:
pointer-events: none;
}
.cbi-tooltip.error { background: var(--err); border-color: var(--err); color: var(--err-ink); }
.cbi-tooltip.warning { background: var(--warn); border-color: var(--warn); color: #1a1200; }
.cbi-tooltip.warning { background: var(--warn); border-color: var(--warn); color: var(--warn-ink); }
.cbi-tooltip.info { background: var(--info); border-color: var(--info); color: var(--info-ink); }
.cbi-tooltip-container { position: relative; cursor: help; }
/* sized to its text, not to a narrow container such as a 100 px port box */
@@ -2280,7 +2290,7 @@ body.modal-overlay-active #modal_overlay { display: block; }
.uci-change-legend-label > :is(ins, del, var) {
display: inline-flex; flex: none;
width: 14px; height: 14px; padding: 0;
border: 1px solid; border-radius: 3px;
border: 1px solid; border-radius: var(--r-xs);
font-size: 0; overflow: hidden;
text-decoration: none; font-style: normal;
}
@@ -2429,7 +2439,7 @@ body[data-page^="admin-status-realtime"] #view .table:has(strong[style*="border-
}
body[data-page^="admin-status-realtime"] #view .table:has(strong[style*="border-bottom"]) > .tr > .td:nth-child(1) strong::before {
content: ""; flex: none;
width: 14px; height: 3px; border-radius: 2px;
width: 14px; height: 3px; border-radius: var(--r-xs);
background: var(--key);
}
body[data-page^="admin-status-realtime"] #view .table:has(strong[style*="border-bottom"]) > .tr > .td:is(:nth-child(3), :nth-child(5)) strong {
@@ -2442,7 +2452,7 @@ body[data-page^="admin-status-realtime"] #view .table:has(strong[style*="border-
white-space: nowrap;
}
body[data-page^="admin-status-realtime"] #view .table:has(strong[style*="border-bottom"]) > .tr > .td:is(:nth-child(2), :nth-child(4), :nth-child(6))::first-line { color: var(--v-text-2); font-size: var(--fs-sm); }
body[data-page^="admin-status-realtime"] #view .table:has(strong[style*="border-bottom"]) > .tr > .td:nth-child(2)::first-line { color: var(--v-text); font-weight: 650; font-size: var(--fs-md); }
body[data-page^="admin-status-realtime"] #view .table:has(strong[style*="border-bottom"]) > .tr > .td:nth-child(2)::first-line { color: var(--v-text); font-weight: 600; font-size: var(--fs-md); }
body[data-page^="admin-status-realtime"] #view .tr:has(:is(#lb_load01_cur, #rx_bw_cur, #rssi_bw_cur, #lb_udp_cur)) { --key: var(--v-chart-1); }
body[data-page^="admin-status-realtime"] #view .tr:has(:is(#lb_load05_cur, #tx_bw_cur, #rate_bw_cur, #lb_tcp_cur)) { --key: var(--v-chart-2); }
body[data-page^="admin-status-realtime"] #view .tr:has(:is(#lb_load15_cur, #noise_bw_cur, #lb_otr_cur)) { --key: var(--v-chart-3); }
@@ -2519,7 +2529,7 @@ body[data-page^="admin-status-channel_analysis"] #view [data-tab-title]:not([dat
paint-order: stroke;
stroke: var(--s1); stroke-width: 3px; stroke-linejoin: round;
}
#view div[id="channel_graph"] svg text.v-ca-label[data-v-own] { fill: var(--v-accent) !important; font-weight: 650 !important; }
#view div[id="channel_graph"] svg text.v-ca-label[data-v-own] { fill: var(--v-accent) !important; font-weight: 600 !important; }
#view span[data-v-series] { font-size: .75em; vertical-align: 1px; color: var(--v-chart-2) !important; }
#view span[data-v-series="1"] { color: var(--v-chart-1) !important; }
#view span[data-v-series="3"] { color: var(--v-chart-3) !important; }
@@ -2549,16 +2559,10 @@ body[data-page^="admin-status-channel_analysis"] #view [data-tab-title]:not([dat
.vt-login-page {
display: flex; align-items: center; justify-content: center;
padding: var(--sp-4);
background:
radial-gradient(1200px 600px at 50% -10%, color-mix(in srgb, var(--accent) 6%, transparent), transparent 60%),
linear-gradient(var(--line) 1px, transparent 1px) 0 0 / 32px 32px,
linear-gradient(90deg, var(--line) 1px, transparent 1px) 0 0 / 32px 32px,
var(--bg);
background-blend-mode: normal;
}
html[data-darkmode="true"] .vt-login-page {
background:
radial-gradient(900px 520px at 50% 0%, rgba(200, 240, 49, .06), transparent 60%),
radial-gradient(900px 520px at 50% 0%, color-mix(in srgb, var(--accent) 6%, transparent), transparent 60%),
linear-gradient(rgba(255, 255, 255, .025) 1px, transparent 1px) 0 0 / 32px 32px,
linear-gradient(90deg, rgba(255, 255, 255, .025) 1px, transparent 1px) 0 0 / 32px 32px,
var(--bg);
@@ -2569,7 +2573,7 @@ html:not([data-darkmode="true"]) .vt-login-page {
linear-gradient(90deg, rgba(20, 23, 26, .04) 1px, transparent 1px) 0 0 / 32px 32px,
var(--bg);
}
/* the starfield (login.js), between the grid and the card */
/* the backdrop (login.js: packets or starfield), between the grid and the card */
.vt-login-sky { position: fixed; inset: 0; width: 100%; height: 100%; pointer-events: none; }
.vt-login {
position: relative; width: min(380px, 100%);
@@ -2599,19 +2603,7 @@ html:not([data-darkmode="true"]) .vt-login-page::before {
}
.vt-login-head { display: flex; align-items: center; gap: var(--sp-3); margin-bottom: var(--sp-6); }
.vt-login-mark { flex: none; }
.vt-login-title { margin: 0; font-size: var(--fs-lg); font-family: var(--mono); font-weight: 600; letter-spacing: -.01em; overflow-wrap: anywhere; }
.vt-login-sub {
display: flex; align-items: center; gap: 6px;
margin: 2px 0 0;
color: var(--text-3);
font-size: var(--fs-micro); font-weight: 600; letter-spacing: .08em; text-transform: uppercase;
}
.vt-login-sub { isolation: isolate; }
/* the ring as on the poll pill: behind the dot (the dot itself makes no
stacking context, so z-index -1 lands under its background), opacity 0
unless animated */
.vt-login-dot { position: relative; width: 6px; height: 6px; border-radius: 50%; background: var(--accent-text); }
.vt-login-dot::after { content: ""; position: absolute; z-index: -1; inset: 0; border-radius: 50%; background: var(--accent-line); opacity: 0; animation: vt-live 2s ease-out infinite; }
.vt-login-title { margin: 0; font-size: var(--fs-lg); font-weight: 600; letter-spacing: -.01em; overflow-wrap: anywhere; }
.vt-login-error {
margin: 0 0 var(--sp-4);
padding: var(--sp-2) var(--sp-3);
@@ -2635,7 +2627,6 @@ html:not([data-darkmode="true"]) .vt-login-page::before {
}
.vt-field input[name="luci_username"] { font-family: var(--mono); }
.vt-login-submit { height: var(--ctl-h); margin-top: var(--sp-2); font-size: var(--fs-md) !important; width: 100%; }
.vt-login-foot { margin: var(--sp-4) 0 0; text-align: center; color: var(--text-3); font: var(--fs-micro)/1.4 var(--mono); letter-spacing: .03em; }
/* ------------------------------------------------ 9. stock page layouts */
@@ -2683,7 +2674,7 @@ body[data-page="admin-statistics-graphs"] p.controls > .nowrap > .cbi-button { m
}
[id$="-ifc-description"] > .nowrap > strong, .td[data-name="_stat"] .nowrap > strong {
color: var(--text-3);
font: 600 var(--fs-micro)/1.5 var(--font); letter-spacing: .06em; text-transform: uppercase;
font: 600 var(--fs-micro)/1.5 var(--font); letter-spacing: .08em; text-transform: uppercase;
}
/* Network -> Wireless: radios as group headers, networks as rows */
@@ -2702,21 +2693,27 @@ body[data-page="admin-statistics-graphs"] p.controls > .nowrap > .cbi-button { m
.cbi-section-table-row:has(> .td[data-name="_stat"] big):first-child > .td { border-top: 0; }
.cbi-section-table-row:not(:has(big)) > .td[data-name="_badge"] { padding-left: calc(var(--card-px) + 16px); }
/* progress bars: thin track, value text above it and in flow, so larger
browser fonts cannot run into the heading */
/* progress bars (their text only exists in title): a thin track, the
value text above it and in flow, so larger browser fonts cannot run
into the heading */
.cbi-progressbar {
position: relative;
display: grid;
grid-template-columns: minmax(0, 1fr);
grid-template-rows: auto 6px;
gap: 5px;
height: auto; margin: 0 0 2px;
background: none;
border: 0; border-radius: 3px;
overflow: visible;
width: 100%; max-width: 520px;
margin: 0 0 2px;
}
.cbi-progressbar > div {
grid-area: 2 / 1; z-index: 1;
width: 0; height: 6px;
border-radius: 3px;
background: var(--accent-text);
transition: width .3s ease-out;
}
.cbi-progressbar > div { grid-area: 2 / 1; height: 6px; z-index: 1; border-radius: 3px; background: var(--accent-text); }
.cbi-progressbar::before {
position: static; inset: auto; padding: 0;
content: attr(title);
display: block;
color: var(--text-2);
font: 500 var(--fs-micro)/1.2 var(--mono);
@@ -2729,8 +2726,9 @@ body[data-page="admin-statistics-graphs"] p.controls > .nowrap > .cbi-button { m
.v-meter { display: grid; gap: 10px; padding: 4px 0 6px; }
.v-meter + .v-meter { margin-top: var(--sp-3); padding-top: var(--sp-4); border-top: 1px solid var(--line); }
.v-meter-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 12px; }
.v-meter-title { color: var(--text); font-weight: 600; }
.v-meter-big { font: 650 var(--fs-xl)/1 var(--mono); letter-spacing: -.02em; color: var(--text); }
/* the meter's figures, as the dashboard's: the share in tabular sans, the
amounts in monospace */
.v-meter-big { font: 700 var(--fs-xl)/1 var(--font); font-variant-numeric: tabular-nums; letter-spacing: -.02em; color: var(--text); }
.v-meter-big small { margin-left: 6px; font: 500 var(--fs-sm) var(--font); letter-spacing: 0; color: var(--text-2); }
.v-meter-sub { margin-left: auto; color: var(--text-2); font: var(--fs-sm)/1.4 var(--mono); font-variant-numeric: tabular-nums; }
.v-meter-bar {
@@ -2738,18 +2736,19 @@ body[data-page="admin-statistics-graphs"] p.controls > .nowrap > .cbi-button { m
display: flex;
height: 12px;
background: var(--s3);
border-radius: 3px;
border-radius: var(--r-xs);
overflow: hidden;
box-shadow: inset 0 0 0 1px var(--line);
}
.v-meter-bar.is-thin { height: 6px; }
.v-seg { height: 100%; flex: none; transition: width .4s ease-out; }
.v-seg + .v-seg { box-shadow: inset 1px 0 0 var(--s1); }
.v-seg-used { background: var(--v-accent); }
/* blue, as the dashboard's meters: lime there means download */
.v-seg-used { background: var(--v-meter); }
.v-seg-cache {
background: repeating-linear-gradient(-45deg,
color-mix(in srgb, var(--v-accent) 55%, transparent) 0 3px,
color-mix(in srgb, var(--v-accent) 25%, transparent) 3px 6px);
color-mix(in srgb, var(--v-meter) 55%, transparent) 0 3px,
color-mix(in srgb, var(--v-meter) 25%, transparent) 3px 6px);
}
.v-seg.is-warn { background: var(--warn); }
.v-seg.is-crit { background: var(--err); }
@@ -2757,7 +2756,7 @@ body[data-page="admin-statistics-graphs"] p.controls > .nowrap > .cbi-button { m
.v-meter-legend li { display: inline-flex; align-items: center; gap: 7px; }
.v-meter-legend b { color: var(--text); font: 600 var(--fs-sm) var(--mono); font-variant-numeric: tabular-nums; }
.v-meter-legend em { color: var(--text-3); font-style: normal; font-size: var(--fs-micro); }
.v-key { width: 10px; height: 10px; border-radius: 2px; flex: none; }
.v-key { width: 10px; height: 10px; border-radius: var(--r-xs); flex: none; }
.v-key.v-seg-free { background: var(--s3); box-shadow: inset 0 0 0 1px var(--line-2); }
.v-meter-rows { display: grid; grid-template-columns: minmax(120px, max-content) minmax(0, 1fr) max-content; gap: 14px 20px; padding: 4px 0 6px; }
.v-meter-row { display: grid; grid-column: 1 / -1; grid-template-columns: subgrid; grid-template-areas: "label bar val"; align-items: center; }
@@ -2766,7 +2765,6 @@ body[data-page="admin-statistics-graphs"] p.controls > .nowrap > .cbi-button { m
.v-meter-row > .v-meter-bar { grid-area: bar; }
.v-meter-row > .v-meter-val { grid-area: val; color: var(--text-2); font: var(--fs-sm) var(--mono); font-variant-numeric: tabular-nums; text-align: right; white-space: nowrap; }
.v-meter-row > .v-meter-val b { color: var(--text); font-weight: 600; }
.v-sr { position: absolute; width: 1px; height: 1px; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
/* Share the widest table's intrinsic width with its headings and dividers. */
@media (width > 720px) {
@@ -2876,16 +2874,16 @@ html[data-darkmode="true"] :is(#ban-status, #ban-report) { --ban-ok: #63c79b; --
}
.zone-forwards :is(.zone-src, .zone-dest) { display: inline-flex; flex-wrap: wrap; gap: 6px; }
.zonebadge.zonebadge-empty { background: transparent !important; border-style: dashed; color: var(--text-2) !important; box-shadow: none; }
/* zone and default policies: reject and drop in red, accept in green */
select[id^="widget.cbid.firewall."]:has(> option:checked:is([value="REJECT"], [value="DROP"])) { color: var(--err); font-weight: 600; }
select[id^="widget.cbid.firewall."]:has(> option:checked[value="ACCEPT"]) { color: var(--ok-text); font-weight: 600; }
select[id^="widget.cbid.firewall."] > option { color: var(--text); font-weight: 400; }
/* except where accept is the risk: the masquerading (internet-facing)
zone accepting input, or forwarding into another zone. Green says
"passes", not "safe"; these two get the warning colour instead. */
/* zone and default policies stay plain: reject and drop are the safe
values, accept between your own zones is normal (as on the Overview
tab). The one policy that is a risk is coloured: the masquerading
(internet-facing) zone accepting input, in the error colours the
Overview tab gives it. Forwarding from it into another zone is
outlined in the warning colour. */
#cbi-firewall-zone .tr:has(input[data-widget-id$=".masq"]:checked) > .td[data-name="input"] select:has(> option:checked[value="ACCEPT"]) {
color: var(--warn-text); border-color: var(--warn); background-color: var(--warn-soft);
color: var(--err-text); border-color: var(--err); background-color: var(--err-soft); font-weight: 600;
}
#cbi-firewall-zone .tr:has(input[data-widget-id$=".masq"]:checked) > .td[data-name="input"] select:has(> option:checked[value="ACCEPT"]) > option { color: var(--text); font-weight: 400; }
#cbi-firewall-zone .tr:has(input[data-widget-id$=".masq"]:checked) .zone-dest > .zonebadge:not(.zonebadge-empty) {
outline: 2px solid var(--warn); outline-offset: 1px;
}
@@ -3083,7 +3081,7 @@ select[id^="widget.cbid.firewall."] > option { color: var(--text); font-weight:
.v-nft-what-name[data-muted="true"] { color: var(--text-3); font-weight: 500; }
/* the chain's end: one quiet line */
.v-nft-step-end { padding: 4px 0 2px; }
.v-nft-step-end .v-nft-n { width: 10px; height: 10px; margin: 4px 5px; border-radius: 3px; background: var(--line-2); }
.v-nft-step-end .v-nft-n { width: 10px; height: 10px; margin: 4px 5px; border-radius: var(--r-xs); background: var(--line-2); }
.v-nft-step-end .v-nft-what { color: var(--text-3); font-size: var(--fs-sm); }
.v-nft-step-end .v-nft-verdict { min-height: 20px; padding: 0 8px; font-size: var(--fs-micro); }
.v-nft-step-more { grid-template-columns: 20px auto; }
@@ -3212,17 +3210,26 @@ body[data-page="admin-system-filemanager"] .sort-button { border-color: var(--li
@media (max-width: 900px) {
.vt-host { display: none !important; }
/* indicators go compact (dot, count) before the crumbs get cut; the
label stays as text for screen readers and as the title */
/* indicators go compact (a sign, a count) before the crumbs get cut;
the label stays as text for screen readers and as the title */
#indicators > span[data-indicator] { height: 28px; padding: 0 8px; }
#indicators > span[data-indicator="poll-status"] { font-size: 0; gap: 0; width: 28px; justify-content: center; padding: 0; }
#indicators > span[data-indicator="poll-status"][data-style="active"]::after { left: calc(50% - 3.5px); }
/* without its label the pill is the pause / resume button: a pause
sign while it refreshes, a play sign while paused */
#indicators > span[data-indicator="poll-status"][data-style="active"]::before {
width: 8px; height: 10px; border-radius: 0;
background: none; border-inline: 3px solid currentColor;
}
#indicators > span[data-indicator="poll-status"][data-style="inactive"]::before {
width: 0; height: 0; margin-left: 2px; border-radius: 0;
background: none; border-left: 9px solid currentColor; border-block: 5px solid transparent;
}
#indicators > span[data-indicator][data-count] { padding: 0 3px; gap: 0; }
#indicators > span[data-indicator][data-count]::before { content: none; }
html[data-rail="pinned"] .vt-rail { width: var(--rail-w); }
html[data-rail="pinned"] .vt-frame { margin-left: var(--rail-w); }
html[data-rail="pinned"] body.vt-auth .vt-shell::before { width: var(--rail-w); }
html[data-rail="pinned"] .vt-brand-word, html[data-rail="pinned"] .vt-rail-label,
html[data-rail="pinned"] .vt-rail-label,
html[data-rail="pinned"] .vt-rail-caret, html[data-rail="pinned"] .vt-rail-sub { display: none !important; }
.vt-rail-pin { display: none; }
}
@@ -3289,7 +3296,6 @@ body[data-page="admin-system-filemanager"] .sort-button { border-color: var(--li
}
.vt-fly-link { min-height: 44px; font-size: var(--fs-md); }
.vt-fly-sub a { padding: 8px 12px; font-size: var(--fs-base); }
.vt-fly-foot { display: none; }
@keyframes vt-sheet-in { from { transform: translateY(16px); opacity: 0; } to { transform: none; opacity: 1; } }
.vt-topbar { gap: var(--sp-2); padding: 0 var(--sp-3) 0 var(--sp-4); }
@@ -3475,7 +3481,7 @@ body[data-page="admin-system-filemanager"] .sort-button { border-color: var(--li
/* short row buttons (Processes' Kill) at least as wide as they are high */
.td.cbi-section-actions :is(.btn, .cbi-button):not(.drag-handle) { min-width: var(--ctl-h); }
/* dropdown choices (Save & Apply's too) are touch targets */
.cbi-dropdown[open] > ul.dropdown > li { min-height: var(--ctl-h); }
.cbi-dropdown[open] > ul.dropdown > li, .vt-actmenu-item { min-height: var(--ctl-h); }
.cbi-value { padding: 8px 0; }
}
+100 -101
View File
@@ -13,6 +13,13 @@ msgid_plural "%d more chains at this hook"
msgstr[0] ""
msgstr[1] ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1350
msgctxt "vantage-theme"
msgid "%d result"
msgid_plural "%d results"
msgstr[0] ""
msgstr[1] ""
#: luci-theme-vantage/htdocs/luci-static/resources/vantage-theme/nftflow.js:345
msgctxt "vantage-theme"
msgid "%d rule"
@@ -49,7 +56,7 @@ msgctxt "vantage-theme"
msgid "Accept"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1188
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1270
msgid "Actions"
msgstr ""
@@ -58,21 +65,22 @@ msgctxt "nft match any traffic"
msgid "Any packet"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1181
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1182
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1183
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1186
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1439
#: luci-theme-vantage/ucode/template/themes/vantage/header.ut:81
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1258
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1259
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1260
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1263
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1267
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1533
#: luci-theme-vantage/ucode/template/themes/vantage/header.ut:84
msgctxt "vantage-theme"
msgid "Appearance"
msgstr ""
#: luci-theme-vantage/ucode/template/themes/vantage/sysauth.ut:43
#: luci-theme-vantage/ucode/template/themes/vantage/sysauth.ut:41
msgid "Authorization Required"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1441
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1535
msgctxt "vantage-theme"
msgid "Auto"
msgstr ""
@@ -87,7 +95,7 @@ msgctxt "vantage-theme"
msgid "Back to the calling chain"
msgstr ""
#: luci-theme-vantage/ucode/template/themes/vantage/header.ut:69
#: luci-theme-vantage/ucode/template/themes/vantage/header.ut:72
msgctxt "vantage-theme"
msgid "Breadcrumb"
msgstr ""
@@ -105,17 +113,17 @@ msgctxt "vantage-theme"
msgid "Bridged between ports"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:338
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:409
msgid "Buffered"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:349
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:359
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:363
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:420
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:430
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:434
msgid "Cache"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:338
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:409
msgid "Cached"
msgstr ""
@@ -134,19 +142,19 @@ msgctxt "vantage-theme"
msgid "Chains no rule jumps to directly"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:419
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:491
msgid "Channel"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:818
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:897
msgid "Collapse navigation"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1440
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1534
msgid "Colour theme"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1212
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1294
msgid "Command palette"
msgstr ""
@@ -165,7 +173,7 @@ msgctxt "vantage-theme"
msgid "Continues in %s, then comes back here"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1441
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1535
msgctxt "vantage-theme"
msgid "Dark"
msgstr ""
@@ -189,8 +197,8 @@ msgstr ""
msgid "Egress"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:818
#: luci-theme-vantage/ucode/template/themes/vantage/header.ut:54
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:897
#: luci-theme-vantage/ucode/template/themes/vantage/header.ut:57
msgid "Expand navigation"
msgstr ""
@@ -207,7 +215,7 @@ msgid ""
"their counters see only its first packets."
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1183
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1260
msgid "Follow system theme"
msgstr ""
@@ -225,17 +233,17 @@ msgid ""
"if br_netfilter is enabled; the ruleset doesn't show that.)"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:350
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:364
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:421
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:435
msgctxt "vantage-theme"
msgid "Free"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1393
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1458
msgid "Grid packets"
msgstr ""
#: luci-theme-vantage/ucode/template/themes/vantage/header.ut:48
#: luci-theme-vantage/ucode/template/themes/vantage/header.ut:52
msgctxt "vantage-theme"
msgid "Home"
msgstr ""
@@ -252,9 +260,9 @@ msgstr ""
msgid "IPv6 traffic table \"%h\""
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:349
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:358
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:362
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:420
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:429
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:433
msgctxt "vantage-theme"
msgid "In use"
msgstr ""
@@ -268,11 +276,11 @@ msgctxt "vantage-theme"
msgid "Input"
msgstr ""
#: luci-theme-vantage/ucode/template/themes/vantage/sysauth.ut:48
#: luci-theme-vantage/ucode/template/themes/vantage/sysauth.ut:45
msgid "Invalid username and/or password! Please try again."
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1208
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1290
msgid "Jump to a page or action…"
msgstr ""
@@ -281,27 +289,27 @@ msgctxt "vantage-theme"
msgid "Leaves unmatched unchanged"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1441
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1535
msgctxt "vantage-theme"
msgid "Light"
msgstr ""
#: luci-theme-vantage/ucode/template/themes/vantage/sysauth.ut:23
#: luci-theme-vantage/ucode/template/themes/vantage/sysauth.ut:60
#: luci-theme-vantage/ucode/template/themes/vantage/sysauth.ut:57
msgid "Log in"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1187
#: luci-theme-vantage/ucode/template/themes/vantage/header.ut:84
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1269
#: luci-theme-vantage/ucode/template/themes/vantage/header.ut:87
msgid "Log out"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1186
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1443
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1263
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1537
msgid "Login background"
msgstr ""
#: luci-theme-vantage/ucode/template/themes/vantage/header.ut:47
#: luci-theme-vantage/ucode/template/themes/vantage/header.ut:51
msgctxt "vantage-theme"
msgid "Main navigation"
msgstr ""
@@ -311,13 +319,13 @@ msgctxt "vantage-theme"
msgid "Masquerade"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:333
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:348
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:404
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:419
msgid "Memory"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1713
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1730
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1806
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1823
msgctxt "vantage-theme"
msgid "More actions"
msgstr ""
@@ -335,12 +343,17 @@ msgstr ""
msgid "Network device table \"%h\""
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1267
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1526
msgid "Network path background"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/vantage-theme/nftflow.js:162
msgctxt "vantage-theme"
msgid "Next rule"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1283
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1348
msgid "No matching pages"
msgstr ""
@@ -354,12 +367,17 @@ msgctxt "vantage-theme"
msgid "No rules: %s"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1393
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1458
msgctxt "vantage-theme"
msgid "None"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1111
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1165
#: luci-theme-vantage/ucode/template/themes/vantage/header.ut:17
msgid "Not found"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1185
msgid "Other pages in %s"
msgstr ""
@@ -373,7 +391,7 @@ msgctxt "vantage-theme"
msgid "Packet path"
msgstr ""
#: luci-theme-vantage/ucode/template/themes/vantage/header.ut:90
#: luci-theme-vantage/ucode/template/themes/vantage/header.ut:93
msgid "Page tabs"
msgstr ""
@@ -387,7 +405,7 @@ msgctxt "vantage-theme"
msgid "Passes unmatched"
msgstr ""
#: luci-theme-vantage/ucode/template/themes/vantage/sysauth.ut:57
#: luci-theme-vantage/ucode/template/themes/vantage/sysauth.ut:54
msgid "Password"
msgstr ""
@@ -401,11 +419,6 @@ msgctxt "vantage-theme"
msgid "Prerouting"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:985
msgctxt "vantage-theme"
msgid "Press"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/vantage-theme/nftflow.js:158
msgctxt "vantage-theme"
msgid "Queue to a program"
@@ -421,7 +434,7 @@ msgctxt "vantage-theme"
msgid "Reject"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1210
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1292
msgctxt "vantage-theme"
msgid "Results"
msgstr ""
@@ -441,13 +454,13 @@ msgctxt "vantage-theme"
msgid "Rules table"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1208
#: luci-theme-vantage/ucode/template/themes/vantage/header.ut:72
#: luci-theme-vantage/ucode/template/themes/vantage/header.ut:74
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1290
#: luci-theme-vantage/ucode/template/themes/vantage/header.ut:75
#: luci-theme-vantage/ucode/template/themes/vantage/header.ut:77
msgid "Search pages"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1187
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1269
msgid "Session"
msgstr ""
@@ -458,33 +471,33 @@ msgid_plural "Show %d more rules"
msgstr[0] ""
msgstr[1] ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1870
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1963
msgid "Show full description"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1859
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1952
msgid "Show full help: %s"
msgstr ""
#: luci-theme-vantage/ucode/template/themes/vantage/header.ut:44
#: luci-theme-vantage/ucode/template/themes/vantage/header.ut:48
msgid "Skip to content"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1393
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1458
msgid "Starfield"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:385
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:456
msgid "Storage"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:371
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:373
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:442
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:444
msgctxt "vantage-theme"
msgid "Swap"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:338
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:409
msgid "Swap free"
msgstr ""
@@ -493,12 +506,12 @@ msgctxt "vantage-theme"
msgid "This device"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:350
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:421
msgctxt "vantage-theme"
msgid "Total"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:337
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:408
msgid "Total Available"
msgstr ""
@@ -512,19 +525,19 @@ msgctxt "vantage-theme"
msgid "Unchanged"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1181
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1258
msgid "Use dark theme"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1182
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1259
msgid "Use light theme"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:337
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:408
msgid "Used"
msgstr ""
#: luci-theme-vantage/ucode/template/themes/vantage/sysauth.ut:53
#: luci-theme-vantage/ucode/template/themes/vantage/sysauth.ut:50
msgid "Username"
msgstr ""
@@ -538,16 +551,16 @@ msgctxt "vantage-theme"
msgid "View"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:412
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:484
msgid "Wireless"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:354
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:425
msgctxt "vantage-theme"
msgid "available"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1218
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1300
msgid "close"
msgstr ""
@@ -561,17 +574,12 @@ msgctxt "vantage-theme"
msgid "drops unmatched %s"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:400
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:403
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:471
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:475
msgid "free"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1285
msgctxt "vantage-theme"
msgid "items"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1216
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1298
msgid "navigate"
msgstr ""
@@ -590,29 +598,19 @@ msgctxt "vantage-theme"
msgid "on %s"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1217
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1299
msgctxt "vantage-theme"
msgid "open"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:965
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1039
msgid "page"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:965
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1039
msgid "pages"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/vantage-theme/nftflow.js:340
msgctxt "vantage-theme"
msgid "priority %d"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:349
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:359
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:363
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:420
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:430
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:434
msgid "reclaimable"
msgstr ""
@@ -621,11 +619,12 @@ msgctxt "vantage-theme"
msgid "rules drop some"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:985
msgid "to search all pages"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:471
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:475
msgid "total"
msgstr ""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:400
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:403
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:471
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:475
msgid "used"
msgstr ""
+117 -102
View File
@@ -21,6 +21,12 @@ msgid "%d more chain at this hook"
msgid_plural "%d more chains at this hook"
msgstr[0] "此钩子还有 %d 条链"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1350
msgctxt "vantage-theme"
msgid "%d result"
msgid_plural "%d results"
msgstr[0] "%d 个结果"
#: luci-theme-vantage/htdocs/luci-static/resources/vantage-theme/nftflow.js:345
msgctxt "vantage-theme"
msgid "%d rule"
@@ -56,7 +62,7 @@ msgctxt "vantage-theme"
msgid "Accept"
msgstr "接受"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1188
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1270
msgid "Actions"
msgstr "操作"
@@ -65,21 +71,22 @@ msgctxt "nft match any traffic"
msgid "Any packet"
msgstr "任意数据包"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1181
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1182
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1183
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1186
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1439
#: luci-theme-vantage/ucode/template/themes/vantage/header.ut:81
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1258
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1259
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1260
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1263
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1267
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1533
#: luci-theme-vantage/ucode/template/themes/vantage/header.ut:84
msgctxt "vantage-theme"
msgid "Appearance"
msgstr "外观"
#: luci-theme-vantage/ucode/template/themes/vantage/sysauth.ut:43
#: luci-theme-vantage/ucode/template/themes/vantage/sysauth.ut:41
msgid "Authorization Required"
msgstr "需要授权"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1441
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1535
msgctxt "vantage-theme"
msgid "Auto"
msgstr "自动"
@@ -94,7 +101,7 @@ msgctxt "vantage-theme"
msgid "Back to the calling chain"
msgstr "返回调用链"
#: luci-theme-vantage/ucode/template/themes/vantage/header.ut:69
#: luci-theme-vantage/ucode/template/themes/vantage/header.ut:72
msgctxt "vantage-theme"
msgid "Breadcrumb"
msgstr "面包屑"
@@ -112,17 +119,17 @@ msgctxt "vantage-theme"
msgid "Bridged between ports"
msgstr "端口间桥接"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:338
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:409
msgid "Buffered"
msgstr "已缓冲"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:349
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:359
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:363
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:420
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:430
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:434
msgid "Cache"
msgstr "缓存"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:338
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:409
msgid "Cached"
msgstr "已缓存"
@@ -141,19 +148,19 @@ msgctxt "vantage-theme"
msgid "Chains no rule jumps to directly"
msgstr "没有规则直接跳转到的链"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:419
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:491
msgid "Channel"
msgstr "信道"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:818
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:897
msgid "Collapse navigation"
msgstr "收起导航"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1440
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1534
msgid "Colour theme"
msgstr "颜色主题"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1212
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1294
msgid "Command palette"
msgstr "命令面板"
@@ -172,7 +179,7 @@ msgctxt "vantage-theme"
msgid "Continues in %s, then comes back here"
msgstr "转入 %s,随后返回此处"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1441
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1535
msgctxt "vantage-theme"
msgid "Dark"
msgstr "深色"
@@ -196,8 +203,8 @@ msgstr "丢弃未匹配流量"
msgid "Egress"
msgstr "出口"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:818
#: luci-theme-vantage/ucode/template/themes/vantage/header.ut:54
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:897
#: luci-theme-vantage/ucode/template/themes/vantage/header.ut:57
msgid "Expand navigation"
msgstr "展开导航"
@@ -216,7 +223,7 @@ msgstr ""
"流量卸载已开启:一条转发连接建立后,它的数据包可以跳过 prerouting、forward "
"和 postrouting,因此这些链和它们的计数器只会看到该连接最初的数据包。"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1183
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1260
msgid "Follow system theme"
msgstr "跟随系统主题"
@@ -237,17 +244,17 @@ msgstr ""
"达 input、forward 或 output 链,这里也没有桥接链会过滤它们。(启用 "
"br_netfilter 时内核可以把它们交给 IP 链;规则集不体现这一点。)"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:350
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:364
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:421
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:435
msgctxt "vantage-theme"
msgid "Free"
msgstr "空闲"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1393
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1458
msgid "Grid packets"
msgstr "网格数据包"
#: luci-theme-vantage/ucode/template/themes/vantage/header.ut:48
#: luci-theme-vantage/ucode/template/themes/vantage/header.ut:52
msgctxt "vantage-theme"
msgid "Home"
msgstr "首页"
@@ -264,9 +271,9 @@ msgstr "IPv4/IPv6 流量表 \"%h\""
msgid "IPv6 traffic table \"%h\""
msgstr "IPv6 流量表 \"%h\""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:349
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:358
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:362
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:420
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:429
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:433
msgctxt "vantage-theme"
msgid "In use"
msgstr "已用"
@@ -280,11 +287,11 @@ msgctxt "vantage-theme"
msgid "Input"
msgstr "输入"
#: luci-theme-vantage/ucode/template/themes/vantage/sysauth.ut:48
#: luci-theme-vantage/ucode/template/themes/vantage/sysauth.ut:45
msgid "Invalid username and/or password! Please try again."
msgstr "用户名或密码错误,请重试。"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1208
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1290
msgid "Jump to a page or action…"
msgstr "跳转到页面或操作…"
@@ -293,27 +300,27 @@ msgctxt "vantage-theme"
msgid "Leaves unmatched unchanged"
msgstr "未匹配流量保持原样"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1441
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1535
msgctxt "vantage-theme"
msgid "Light"
msgstr "浅色"
#: luci-theme-vantage/ucode/template/themes/vantage/sysauth.ut:23
#: luci-theme-vantage/ucode/template/themes/vantage/sysauth.ut:60
#: luci-theme-vantage/ucode/template/themes/vantage/sysauth.ut:57
msgid "Log in"
msgstr "登录"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1187
#: luci-theme-vantage/ucode/template/themes/vantage/header.ut:84
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1269
#: luci-theme-vantage/ucode/template/themes/vantage/header.ut:87
msgid "Log out"
msgstr "退出登录"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1186
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1443
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1263
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1537
msgid "Login background"
msgstr "登录背景"
#: luci-theme-vantage/ucode/template/themes/vantage/header.ut:47
#: luci-theme-vantage/ucode/template/themes/vantage/header.ut:51
msgctxt "vantage-theme"
msgid "Main navigation"
msgstr "主导航"
@@ -323,13 +330,13 @@ msgctxt "vantage-theme"
msgid "Masquerade"
msgstr "伪装(NAT)"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:333
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:348
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:404
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:419
msgid "Memory"
msgstr "内存"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1713
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1730
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1806
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1823
msgctxt "vantage-theme"
msgid "More actions"
msgstr "更多操作"
@@ -347,12 +354,17 @@ msgstr "网络"
msgid "Network device table \"%h\""
msgstr "网络设备表 \"%h\""
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1267
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1526
msgid "Network path background"
msgstr "网络路径背景"
#: luci-theme-vantage/htdocs/luci-static/resources/vantage-theme/nftflow.js:162
msgctxt "vantage-theme"
msgid "Next rule"
msgstr "下一条规则"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1283
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1348
msgid "No matching pages"
msgstr "没有匹配的页面"
@@ -366,12 +378,17 @@ msgctxt "vantage-theme"
msgid "No rules: %s"
msgstr "无规则:%s"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1393
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1458
msgctxt "vantage-theme"
msgid "None"
msgstr "无"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1111
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1165
#: luci-theme-vantage/ucode/template/themes/vantage/header.ut:17
msgid "Not found"
msgstr "未找到"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1185
msgid "Other pages in %s"
msgstr "%s 中的其他页面"
@@ -385,7 +402,7 @@ msgctxt "vantage-theme"
msgid "Packet path"
msgstr "数据包路径"
#: luci-theme-vantage/ucode/template/themes/vantage/header.ut:90
#: luci-theme-vantage/ucode/template/themes/vantage/header.ut:93
msgid "Page tabs"
msgstr "页面标签页"
@@ -399,7 +416,7 @@ msgctxt "vantage-theme"
msgid "Passes unmatched"
msgstr "放行未匹配流量"
#: luci-theme-vantage/ucode/template/themes/vantage/sysauth.ut:57
#: luci-theme-vantage/ucode/template/themes/vantage/sysauth.ut:54
msgid "Password"
msgstr "密码"
@@ -413,11 +430,6 @@ msgctxt "vantage-theme"
msgid "Prerouting"
msgstr "路由前"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:985
msgctxt "vantage-theme"
msgid "Press"
msgstr "按"
#: luci-theme-vantage/htdocs/luci-static/resources/vantage-theme/nftflow.js:158
msgctxt "vantage-theme"
msgid "Queue to a program"
@@ -433,7 +445,7 @@ msgctxt "vantage-theme"
msgid "Reject"
msgstr "拒绝"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1210
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1292
msgctxt "vantage-theme"
msgid "Results"
msgstr "结果"
@@ -453,13 +465,13 @@ msgctxt "vantage-theme"
msgid "Rules table"
msgstr "规则表"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1208
#: luci-theme-vantage/ucode/template/themes/vantage/header.ut:72
#: luci-theme-vantage/ucode/template/themes/vantage/header.ut:74
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1290
#: luci-theme-vantage/ucode/template/themes/vantage/header.ut:75
#: luci-theme-vantage/ucode/template/themes/vantage/header.ut:77
msgid "Search pages"
msgstr "搜索页面"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1187
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1269
msgid "Session"
msgstr "会话"
@@ -469,33 +481,33 @@ msgid "Show %d more rule"
msgid_plural "Show %d more rules"
msgstr[0] "再显示 %d 条规则"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1870
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1963
msgid "Show full description"
msgstr "显示完整描述"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1859
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1952
msgid "Show full help: %s"
msgstr "显示完整帮助:%s"
#: luci-theme-vantage/ucode/template/themes/vantage/header.ut:44
#: luci-theme-vantage/ucode/template/themes/vantage/header.ut:48
msgid "Skip to content"
msgstr "跳至正文"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1393
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1458
msgid "Starfield"
msgstr "星空"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:385
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:456
msgid "Storage"
msgstr "存储"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:371
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:373
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:442
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:444
msgctxt "vantage-theme"
msgid "Swap"
msgstr "交换空间"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:338
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:409
msgid "Swap free"
msgstr "空闲交换空间"
@@ -504,12 +516,12 @@ msgctxt "vantage-theme"
msgid "This device"
msgstr "本设备"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:350
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:421
msgctxt "vantage-theme"
msgid "Total"
msgstr "总计"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:337
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:408
msgid "Total Available"
msgstr "总可用"
@@ -523,19 +535,19 @@ msgctxt "vantage-theme"
msgid "Unchanged"
msgstr "不变"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1181
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1258
msgid "Use dark theme"
msgstr "使用深色主题"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1182
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1259
msgid "Use light theme"
msgstr "使用浅色主题"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:337
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:408
msgid "Used"
msgstr "已用"
#: luci-theme-vantage/ucode/template/themes/vantage/sysauth.ut:53
#: luci-theme-vantage/ucode/template/themes/vantage/sysauth.ut:50
msgid "Username"
msgstr "用户名"
@@ -549,16 +561,16 @@ msgctxt "vantage-theme"
msgid "View"
msgstr "视图"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:412
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:484
msgid "Wireless"
msgstr "无线"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:354
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:425
msgctxt "vantage-theme"
msgid "available"
msgstr "可用"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1218
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1300
msgid "close"
msgstr "关闭"
@@ -572,17 +584,12 @@ msgctxt "vantage-theme"
msgid "drops unmatched %s"
msgstr "丢弃未匹配的 %s"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:400
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:403
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:471
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:475
msgid "free"
msgstr "空闲"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1285
msgctxt "vantage-theme"
msgid "items"
msgstr "条"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1216
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1298
msgid "navigate"
msgstr "移动"
@@ -601,29 +608,19 @@ msgctxt "vantage-theme"
msgid "on %s"
msgstr "接口 %s"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1217
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1299
msgctxt "vantage-theme"
msgid "open"
msgstr "打开"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:965
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1039
msgid "page"
msgstr "页"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:965
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:1039
msgid "pages"
msgstr "页"
#: luci-theme-vantage/htdocs/luci-static/resources/vantage-theme/nftflow.js:340
msgctxt "vantage-theme"
msgid "priority %d"
msgstr "优先级 %d"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:349
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:359
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:363
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:420
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:430
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:434
msgid "reclaimable"
msgstr "可回收"
@@ -632,15 +629,33 @@ msgctxt "vantage-theme"
msgid "rules drop some"
msgstr "部分规则会丢弃"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:985
msgid "to search all pages"
msgstr "搜索所有页面"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:471
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:475
msgid "total"
msgstr "总计"
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:400
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:403
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:471
#: luci-theme-vantage/htdocs/luci-static/resources/menu-vantage.js:475
msgid "used"
msgstr "已用"
#~ msgctxt "vantage-theme"
#~ msgid "Press"
#~ msgstr "按"
#~ msgctxt "vantage-theme"
#~ msgid "items"
#~ msgstr "条"
#~ msgid "page"
#~ msgstr "页"
#~ msgid "pages"
#~ msgstr "页"
#~ msgid "to search all pages"
#~ msgstr "搜索所有页面"
#~ msgid "Show full help"
#~ msgstr "显示完整帮助"
@@ -10,7 +10,11 @@
-#}
{%
const authed = !!ctx.authsession;
const page_title = (authed && dispatched?.title) ? striptags(_(dispatched.title)) : 'LuCI';
/* LuCI renders its 404 with the deepest node it resolved: a category
(firstchild) or a node without an action, whose title is not the
page asked for. Visitors who are not logged in get no title at all. */
const found = !!dispatched?.action?.type && dispatched.action.type != 'firstchild';
const page_title = !authed ? null : !found ? _('Not found') : dispatched?.title ? striptags(_(dispatched.title)) : null;
const lang = entityencode(dispatcher.lang ?? 'en', true);
const boardinfo = ubus.call("system", "board");
const hostname = striptags(boardinfo?.hostname ?? '?');
@@ -21,7 +25,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<meta name="color-scheme" content="dark light">
<title>{{ entityencode(page_title, true) }} · LuCI</title>
<title>{{ page_title ? entityencode(page_title, true) + ' · ' : '' }}LuCI</title>
<script>
(function (r) {
var p = 'auto', rail = 'pinned';
@@ -75,7 +79,7 @@
<svg viewBox="0 0 32 32" width="28" height="28"><rect x="1" y="1" width="30" height="30" rx="7" class="vt-brand-tile"/><path d="M9 10.5 16 22" class="vt-brand-l"/><path d="M16 22 23 10.5" class="vt-brand-r"/><circle cx="23" cy="10.5" r="1.9" class="vt-brand-dot"/></svg>
</span>
{% endif %}
<nav class="vt-crumbs" id="vt-crumbs" aria-label="{{ entityencode(_('Breadcrumb', 'vantage-theme'), true) }}"><span class="vt-crumb vt-crumb-current">{{ entityencode(page_title, true) }}</span></nav>
<nav class="vt-crumbs" id="vt-crumbs" aria-label="{{ entityencode(_('Breadcrumb', 'vantage-theme'), true) }}"><span class="vt-crumb vt-crumb-current">{{ entityencode(page_title ?? 'LuCI', true) }}</span></nav>
<div class="vt-top-actions">
{% if (authed): %}
<button type="button" class="vt-search" id="vt-search-btn" aria-haspopup="dialog" aria-keyshortcuts="Control+K /" aria-label="{{ entityencode(_('Search pages'), true) }}">
@@ -12,8 +12,6 @@
if (type(tls) == 'string')
tls = split(tls, /\s+/);
const tls_ports = uniq(filter(map(tls, e => +(match(e, /\d+$/)?.[0] ?? 0)), p => (p > 0 && p <= 65535)));
const boardinfo = ubus.call("system", "board");
const hostname = striptags(boardinfo?.hostname ?? '?');
-%}
<!DOCTYPE html>
<html lang="{{ entityencode(dispatcher.lang ?? 'en', true) }}" data-darkmode="true">
@@ -47,10 +45,7 @@
<circle cx="21.5" cy="10.5" r="1.9" class="vt-brand-dot"></circle>
<circle cx="21.5" cy="21.5" r="1.9" class="vt-brand-dot"></circle>
</svg>
<div>
<h1 class="vt-login-title">{{ hostname }}</h1>
<p class="vt-login-sub"><span class="vt-login-dot" aria-hidden="true"></span>{{ entityencode(_('Authorization Required'), true) }}</p>
</div>
<h1 class="vt-login-title">{{ entityencode(_('Authorization Required'), true) }}</h1>
</div>
{% if (fuser): %}
+2 -2
View File
@@ -5,8 +5,8 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=meow
PKG_VERSION:=0.28.0
PKG_RELEASE:=18
PKG_VERSION:=0.29.0
PKG_RELEASE:=19
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/meow-rs/meow-rs/tar.gz/v$(PKG_VERSION)?
+2 -2
View File
@@ -6,8 +6,8 @@ include $(TOPDIR)/rules.mk
PKG_NPM_NAME:=pnpm
PKG_NAME:=node-$(PKG_NPM_NAME)
PKG_VERSION:=11.27.1
PKG_RELEASE:=3
PKG_VERSION:=11.28.10
PKG_RELEASE:=4
PKG_SOURCE:=$(PKG_NPM_NAME)-$(PKG_VERSION).tgz
PKG_SOURCE_URL:=https://mirrors.tencent.com/npm/$(PKG_NPM_NAME)/-/ \
+1 -1
View File
@@ -9,7 +9,7 @@ PKG_NAME:=natmapt
PKG_UPSTREAM_VERSION:=20260214
PKG_UPSTREAM_GITHASH:=
PKG_VERSION:=$(PKG_UPSTREAM_VERSION)$(if $(PKG_UPSTREAM_GITHASH),~$(call version_abbrev,$(PKG_UPSTREAM_GITHASH)))
PKG_RELEASE:=16
PKG_RELEASE:=17
SCRIPTS_VERSION:=0.2026.01.24
PKG_SOURCE_SUBDIR:=$(PKG_UPSTREAM_NAME)-$(PKG_UPSTREAM_VERSION)
+12 -1
View File
@@ -51,6 +51,17 @@ start() {
http_request "$(json_dump)" "$(jsonfilter -s "$the_record" -qe '@.id')"
fi
if [ -n "${txt}" ]; then
the_record="$(jsonfilter -s "$records" -qe '@.result[*]'|grep "\"type\": \"TXT\""|grep "\"name\": \"${txt}\""|grep '"content": "\\"natmaptmgmt=.*\\""')"
json_init
json_add_string type "TXT"
json_add_string name "${txt}"
json_add_string content "$(echo "${txt_content}"|sed -E 's|^"?(natmaptmgmt=\S*\s*)?|"natmaptmgmt=; |; s|"?$|"|')"
json_add_int ttl 1
json_add_boolean proxied 0
http_request "$(json_dump)" "$(jsonfilter -s "$the_record" -qe '@.id')"
fi
if [ -n "${srv}" ]; then
the_record="$(jsonfilter -s "$records" -qe '@.result[*]'|grep "\"type\": \"SRV\""|grep "\"name\": \"_${srv_serv}._${srv_proto}.${srv}\""|grep "\"priority\": ${srv_priority}"|grep "\"weight\": ${srv_weight}")"
json_init
@@ -85,7 +96,7 @@ start() {
# All external parameters required
ALL_PARAMS="hostype host srv srv_serv srv_proto srv_target srv_priority srv_weight ip port https https_target https_svcparams https_priority tokens"
ALL_PARAMS="hostype host txt txt_content srv srv_serv srv_proto srv_target srv_priority srv_weight ip port https https_target https_svcparams https_priority tokens"
eval "$(JSON_EXPORT "$1")"; shift
# All external tokens required
INIT_GLOBAL_VAR TOKEN ZONE_ID
+5 -1
View File
@@ -65,12 +65,16 @@ if [ -n "$NOTIFY" ]; then
fi
if [ -n "$DDNS" ]; then
_hostype="$(jsonfilter -qs "$DDNS_PARAM" -e '@["hostype"]')"
_content="$(jsonfilter -qs "$DDNS_PARAM" -e '@["txt_content"]')"
_svcparams="$(jsonfilter -qs "$DDNS_PARAM" -e '@["https_svcparams"]')"
_svcparams="$(echo "$_svcparams" | sed -E "s,\s*(port=\d*|$), port=${port},")" # port
_content="$(echo "$_content" | sed "s,{{port}},${port},")" # port
_svcparams="$(echo "$_svcparams" | sed -E "s,\s*(port=\d*|$), port=${port},")" # port
_content="$(echo "$_content" | sed "s,{{ip}},${ip},")" # ip
[ "$_hostype" = A ] && _svcparams="$(echo "$_svcparams" | sed -E "s|\b(ipv4hint=)\S*|\1${ip}|")" # ipv4hint
[ "$_hostype" = AAAA ] && _svcparams="$(echo "$_svcparams" | sed -E "s|\b(ipv6hint=)\S*|\1${ip}|")" # ipv6hint
json_init
json_load "$DDNS_PARAM"
json_add_string txt_content "$_content"
json_add_string https_svcparams "$_svcparams"
json_add_string ip "$ip"
json_add_int port "$port"
+7 -1
View File
@@ -134,6 +134,8 @@ validate_section_natmap() {
'ddns_tokens:list(string)' \
'ddns_a:hostname' \
'ddns_aaaa:hostname' \
'ddns_txt:hostname' \
'ddns_txt_content:string' \
'ddns_srv:hostname' \
'ddns_srv_serv:string' \
'ddns_srv_proto:string:tcp' \
@@ -241,6 +243,10 @@ natmap_instance() {
json_add_string tokens "$ddns_tokens"
[ "$family" = ipv4 ] && json_add_string hostype "A" && json_add_string host "$ddns_a"
[ "$family" = ipv6 ] && json_add_string hostype "AAAA" && json_add_string host "$ddns_aaaa"
[ -n "$ddns_txt" ] && {
json_add_string txt "$ddns_txt"
json_add_string txt_content "$ddns_txt_content"
}
[ -n "$ddns_srv" ] && {
json_add_string srv "$ddns_srv"
json_add_string srv_serv "$ddns_srv_serv"
@@ -416,7 +422,7 @@ launcher() {
# review
[ -n "$NATMAP_DEBUG" ] && {
local v ucivv="enable interval stun_cycle stun_server http_server fwmark_value comment udp_mode proto family bind_ifname ifname bind_ip lan_addr port port_pointer forward forward_mode natloopback forward_congestion forward_timeout forward_target forward_port refresh clt_script clt_scheme clt_web_port clt_username clt_password notify_enable notify_script notify_tokens notify_custom_domain notify_text ddns_enable ddns_script ddns_tokens ddns_a ddns_aaaa ddns_srv ddns_srv_serv ddns_srv_proto ddns_srv_target ddns_srv_priority ddns_srv_weight ddns_https ddns_https_target ddns_https_svcparams ddns_https_priority custom_script"
local v ucivv="enable interval stun_cycle stun_server http_server fwmark_value comment udp_mode proto family bind_ifname ifname bind_ip lan_addr port port_pointer forward forward_mode natloopback forward_congestion forward_timeout forward_target forward_port refresh clt_script clt_scheme clt_web_port clt_username clt_password notify_enable notify_script notify_tokens notify_custom_domain notify_text ddns_enable ddns_script ddns_tokens ddns_a ddns_aaaa ddns_txt ddns_txt_content ddns_srv ddns_srv_serv ddns_srv_proto ddns_srv_target ddns_srv_priority ddns_srv_weight ddns_https ddns_https_target ddns_https_svcparams ddns_https_priority custom_script"
for v in $ucivv; do eval "echo $1 $v=\'\$$v\'"; done # ash not support ${!v}
}
[ "$error" -gt 0 ] && return 1
+2 -1
View File
@@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=vsftpd
PKG_VERSION:=3.0.5
PKG_RELEASE:=3
PKG_RELEASE:=4
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://security.appspot.com/downloads/
@@ -43,6 +43,7 @@ endef
define Package/vsftpd-tls
$(call Package/vsftpd/Default)
VARIANT:=tls
CONFLICTS:=vsftpd
TITLE+= (TLS)
DEPENDS+=+libopenssl
endef
+3 -3
View File
@@ -50,12 +50,12 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=wwand
PKG_VERSION:=1.8.0_pre48
PKG_RELEASE:=87
PKG_VERSION:=1.9.0_pre3
PKG_RELEASE:=88
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/ddimension/wwand.git
PKG_SOURCE_VERSION:=f2c804f06ff028088ad262bf86683b2519b1b127
PKG_SOURCE_VERSION:=0a1f02375df53f21a105b375b9af051c36464530
PKG_MIRROR_HASH:=skip
PKG_LICENSE:=GPL-2.0-only