diff --git a/cloudflared-next/Makefile b/cloudflared-next/Makefile index a3e1adee3..5a63759bf 100644 --- a/cloudflared-next/Makefile +++ b/cloudflared-next/Makefile @@ -5,8 +5,8 @@ include $(TOPDIR)/rules.mk PKG_NAME:=cloudflared-next -PKG_VERSION:=2026.10.5 -PKG_RELEASE:=8 +PKG_VERSION:=2026.10.6 +PKG_RELEASE:=9 # Architecture mapping for pre-built binaries ifeq ($(ARCH),x86_64) diff --git a/dae/Makefile b/dae/Makefile index 40f6e7394..05d96b5d5 100644 --- a/dae/Makefile +++ b/dae/Makefile @@ -6,9 +6,9 @@ include $(TOPDIR)/rules.mk PKG_NAME:=dae PKG_VERSION:=2026.10.18 -PKG_RELEASE:=51 +PKG_RELEASE:=52 -PKG_SOURCE:=dae-src-2026.10.18-9ec2736d61b0.tar.gz +PKG_SOURCE:=dae-src-2026.10.18-32ed09f0d784.tar.gz PKG_SOURCE_URL:=https://github.com/kenzok8/openwrt-daede/releases/download/dae-src PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION) PKG_HASH:=skip diff --git a/daed/Makefile b/daed/Makefile index c98136a04..1562341d3 100644 --- a/daed/Makefile +++ b/daed/Makefile @@ -6,9 +6,9 @@ include $(TOPDIR)/rules.mk PKG_NAME:=daed PKG_VERSION:=2026.10.18 -PKG_RELEASE:=63 +PKG_RELEASE:=64 -PKG_SOURCE:=daed-src-2026.10.18-ac22e7a32e17.tar.gz +PKG_SOURCE:=daed-src-2026.10.18-97c371b47eb5.tar.gz PKG_SOURCE_URL:=https://github.com/kenzok8/openwrt-daede/releases/download/daed-src PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION) PKG_HASH:=skip diff --git a/honk/Makefile b/honk/Makefile index e7dfb491e..781e20e2b 100644 --- a/honk/Makefile +++ b/honk/Makefile @@ -4,7 +4,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=honk PKG_VERSION:=0.1.0_beta3 -PKG_RELEASE:=23 +PKG_RELEASE:=24 # Raw upstream release tag. It keeps the dots in qualifiers (e.g. v0.0.1.beta.49) # because that is what the GitHub release asset name uses. @@ -12,8 +12,8 @@ HONK_RELEASE_TAG:=v0.1.0-beta.3 HONK_ASSET_TAG:=v0.1.0-beta.3 # SHA256 of each release tarball, kept in sync by scripts/update_honk_version.sh. -HONK_HASH_X86_64:=c839b648a443abde8b9bbc8290ebc3c6cd6a5c3e579f391a79016304820354ff -HONK_HASH_AARCH64:=216ace91ba157f909fca2ecf1f3b33015251b9481382609542fc6907dd521602 +HONK_HASH_X86_64:=259836292247434ee1ced18f8239c4486697c4855ac124195ee6f4c80e0df697 +HONK_HASH_AARCH64:=97e49b43c8efc4618f77a9ccb8a1b015e07e5e942728fd466a2d118cd82db169 PKG_LICENSE:=AGPL-3.0-only PKG_LICENSE_FILE:=LICENSE @@ -27,9 +27,9 @@ else ifeq ($(ARCH),aarch64) HONK_HASH:=$(HONK_HASH_AARCH64) endif -# Default to the stock-malloc build: lower RSS high-water on small gateways. -# Use an empty suffix to switch back to the mimalloc build (faster QUIC). -HONK_SUFFIX:=-stock +# mimalloc build: faster QUIC. honk.init sets MIMALLOC_ARENA_RESERVE=0 so the +# 1 GiB arena reservation stays out of VmSize. Use "-stock" to go back. +HONK_SUFFIX:= # Upstream feature variant: -native-api (recommended for OpenWrt gateway), -full, or empty for legacy releases. HONK_VARIANT:=-native-api HONK_ASSET:=honk-core-$(HONK_ASSET_TAG)-$(HONK_TARGET)$(HONK_SUFFIX)$(HONK_VARIANT).tar.gz diff --git a/honk/files/honk.init b/honk/files/honk.init index ceb4faa79..386a2a8bf 100644 --- a/honk/files/honk.init +++ b/honk/files/honk.init @@ -87,7 +87,9 @@ start_service() { hijack_resolv_conf procd_open_instance "$CONF" - procd_set_param env DAE_LOCATION_ASSET="/usr/share/v2ray" + # 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 command "$PROG" --config "$config_file" procd_set_param netdev "$HONK_HOST_IFACE" "$HONK_PEER_IFACE" procd_set_param limits core="unlimited" diff --git a/luci-app-diskman/Makefile b/luci-app-diskman/Makefile index 109a9d42d..fbde3cd5a 100644 --- a/luci-app-diskman/Makefile +++ b/luci-app-diskman/Makefile @@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-diskman PKG_VERSION:=0.2.13 -PKG_RELEASE:=4 +PKG_RELEASE:=5 PKG_MAINTAINER:=lisaac PKG_LICENSE:=AGPL-3.0 @@ -11,7 +11,7 @@ LUCI_TITLE:=Disk Manager interface for LuCI LUCI_DEPENDS:= \ +luci-compat +blkid +e2fsprogs +parted +smartmontools +dosfstools \ +kmod-fs-msdos +kmod-fs-ntfs3 \ - +LINUX_5_4:kmod-fs-exfat-oot +!LINUX_5_4:kmod-fs-exfat +exfat-mkfs \ + +kmod-fs-exfat +exfat-mkfs \ +exfat-fsck +kmod-nls-cp932 +kmod-nls-cp936 +kmod-nls-cp950 \ +PACKAGE_$(PKG_NAME)_INCLUDE_btrfs_progs:btrfs-progs \ +PACKAGE_$(PKG_NAME)_INCLUDE_lsblk:lsblk \ diff --git a/luci-app-mail/root/etc/uci-defaults/4101-mail b/luci-app-mail/root/etc/uci-defaults/4101-mail index 40d754bc2..31c795fe5 100644 --- a/luci-app-mail/root/etc/uci-defaults/4101-mail +++ b/luci-app-mail/root/etc/uci-defaults/4101-mail @@ -13,4 +13,7 @@ uci -q batch <<-EOF >/dev/null set ucitrack.@mail[-1].init=msmtp commit ucitrack EOF +# The SMTP password is in there: root only, as /etc/msmtprc (uci commit +# keeps the file mode) +chmod 600 /etc/config/mail 2>/dev/null exit 0 \ No newline at end of file diff --git a/luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp.js b/luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp.js index a1e3d3a5d..1e98855b8 100644 --- a/luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp.js +++ b/luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp.js @@ -146,7 +146,10 @@ return L.view.extend({ _('bpf_burst => same as the default scheduler') + '
' + _('bpf_red => sends all packets redundantly on all available subflows') + '
' + _('bpf_first => always picks the first subflow to send data') + '
' + - _('bpf_rr => always picks the next available subflow to send data (round-robin)') + _('bpf_rr => always picks the next available subflow to send data (round-robin)') + '
' + + _('bpf_ecf => sends on the lowest-latency subflow, and on a slower one only when that does not delay the data') + '
' + + _('bpf_blest => sends on the lowest-latency subflow, and on a slower one only when that does not block the fastest one') + '
' + + _('bpf_overflow => uses the subflows by weight: a lower weight only carries what the higher ones cannot') ); scheduler.value("default", _("default")); @@ -178,6 +181,9 @@ return L.view.extend({ // bpf_red => sends all packets redundantly on all available subflows // bpf_first => always picks the first subflow to send data // bpf_rr => always picks the next available subflow to send data (round-robin) + // bpf_ecf => sends on the lowest-latency subflow, and on a slower one only when that does not delay the data + // bpf_blest => sends on the lowest-latency subflow, and on a slower one only when that does not block the fastest one + // bpf_overflow => uses the subflows by weight: a lower weight only carries what the higher ones cannot } scheduler.onchange = function(ev, section_id, value) { @@ -203,6 +209,7 @@ return L.view.extend({ o.depends("mptcp_scheduler", "bpf_weight"); o.depends("mptcp_scheduler", "bpf_weight_rr"); o.depends("mptcp_scheduler", "bpf_burstweight"); + o.depends("mptcp_scheduler", "bpf_overflow"); /* Every option below that depends() on another field keeps its stored * value while hidden: form.js otherwise removes it on any save, so a * save made with another scheduler or with mptcp_pm_type=0 silently @@ -402,7 +409,7 @@ return L.view.extend({ // (discussion #4368). o.rmempty = false; - o = s.option(form.Value, "multipath_weight", _("Weight"), _("Only used by *weight schedulers/path managers. Ignored if no weight scheduler is selected.") + '
' + _("A weight >100 make it more attractive, a weight <100 make it less attractive. Max 256")); + o = s.option(form.Value, "multipath_weight", _("Weight"), _("Only used by the weight schedulers (bpf_weight, bpf_weight_rr, bpf_burstweight, bpf_overflow) and path managers. Ignored if no weight scheduler is selected.") + '
' + _("A weight >100 make it more attractive, a weight <100 make it less attractive. Max 256")); // 1 to 256, as "ip route" takes it: the schedulers read 0 as no weight // (neutral 100) and add the weight to a signed counter o.datatype = "and(uinteger,range(1,256))"; diff --git a/luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp_dscp_routing.js b/luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp_dscp_routing.js index ec0e36c19..94fd7e0b3 100644 --- a/luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp_dscp_routing.js +++ b/luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp_dscp_routing.js @@ -96,7 +96,7 @@ return L.view.extend({ var mptcpScheduler = normalizeSchedulerValue(uci.get('network', 'globals', 'mptcp_scheduler')); var mptcpHasDscpScheduler = (mptcpScheduler === 'bpf_dscp'); - var mptcpHasWeightScheduler = (mptcpScheduler === 'bpf_weight' || mptcpScheduler === 'bpf_weight_rr' || mptcpScheduler === 'bpf_burstweight'); + var mptcpHasWeightScheduler = (mptcpScheduler === 'bpf_weight' || mptcpScheduler === 'bpf_weight_rr' || mptcpScheduler === 'bpf_burstweight' || mptcpScheduler === 'bpf_overflow'); var mqvpnEnabled = (mqvpn.enabled === true); var mqvpnScheduler = mqvpn.scheduler; @@ -115,10 +115,10 @@ return L.view.extend({ var showWeight = mptcpHasWeightScheduler || mqvpnHasWeightScheduler; m = new form.Map('network', _('DSCP / Weight Routing'), - _('Choose which WAN carries each DSCP-tagged traffic class -- for the MPTCP bpf_dscp scheduler and for mqvpn -- and review the per-WAN weight used by the *weight schedulers on both. This page only edits the pins/weights; pick the scheduler that actually uses them on the MPTCP and MQVPN pages themselves.')); + _('Choose which WAN carries each DSCP-tagged traffic class -- for the MPTCP bpf_dscp scheduler and for mqvpn -- and review the per-WAN weight used by the weight schedulers on both. This page only edits the pins/weights; pick the scheduler that actually uses them on the MPTCP and MQVPN pages themselves.')); if (!showDscp && !showWeight) { - m.description += '

' + _('Nothing to show yet: no DSCP scheduler (bpf_dscp) or weight scheduler (bpf_weight/bpf_weight_rr/bpf_burstweight, or mqvpn’s Weighted RTT/Weighted Round Robin) is currently selected on the MPTCP or MQVPN pages.'); + m.description += '

' + _('Nothing to show yet: no DSCP scheduler (bpf_dscp) or weight scheduler (bpf_weight/bpf_weight_rr/bpf_burstweight/bpf_overflow, or mqvpn’s Weighted RTT/Weighted Round Robin) is currently selected on the MPTCP or MQVPN pages.'); return m.render(); } @@ -170,7 +170,7 @@ return L.view.extend({ if (showWeight) { s = m.section(form.TypedSection, 'interface', _('WAN weight'), - _('Used by the *weight MPTCP schedulers (bpf_weight/bpf_weight_rr) and by mqvpn’s "Weighted RTT"/"Weighted Round Robin" schedulers -- same value, applies to both upload and download (mirrored to the gateway automatically). Unlike the DSCP pins above, weight is a single value per WAN, not per traffic class.') + ' ' + + _('Used by the weight MPTCP schedulers (bpf_weight/bpf_weight_rr/bpf_overflow) and by mqvpn’s "Weighted RTT"/"Weighted Round Robin" schedulers -- same value, applies to both upload and download (mirrored to the gateway automatically). Unlike the DSCP pins above, weight is a single value per WAN, not per traffic class.') + ' ' + _('Only WANs with Multipath TCP enabled (the “Multipath TCP” option on the MPTCP page’s Interfaces Settings) are listed below.')); s.filter = function(section) { return isEligibleIface(section) && isMultipathEnabledIface(section); diff --git a/luci-app-mptcp/po/de/mptcp.po b/luci-app-mptcp/po/de/mptcp.po index 3ec297084..f64e9121f 100644 --- a/luci-app-mptcp/po/de/mptcp.po +++ b/luci-app-mptcp/po/de/mptcp.po @@ -173,13 +173,13 @@ msgstr "Prüfsumme" msgid "" "Choose which WAN carries each DSCP-tagged traffic class -- for the MPTCP " "bpf_dscp scheduler and for mqvpn -- and review the per-WAN weight used by " -"the *weight schedulers on both. This page only edits the pins/weights; pick " +"the weight schedulers on both. This page only edits the pins/weights; pick " "the scheduler that actually uses them on the MPTCP and MQVPN pages " "themselves." msgstr "" "Legen Sie fest, welches WAN welche DSCP-markierte Verkehrsklasse trägt – für " "den MPTCP-Scheduler bpf_dscp und für mqvpn – und prüfen Sie das WAN-Gewicht, " -"das die *weight-Scheduler auf beiden verwenden. Diese Seite bearbeitet nur " +"das die Gewichts-Scheduler auf beiden verwenden. Diese Seite bearbeitet nur " "die Zuordnungen und Gewichte; wählen Sie den Scheduler, der sie tatsächlich " "nutzt, auf den Seiten MPTCP und MQVPN selbst." @@ -715,13 +715,13 @@ msgstr "Keine WAN-Schnittstellen mit aktiviertem Multipath gefunden." #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp_dscp_routing.js:121 msgid "" "Nothing to show yet: no DSCP scheduler (bpf_dscp) or weight scheduler " -"(bpf_weight/bpf_weight_rr/bpf_burstweight, or mqvpn’s Weighted RTT/Weighted " -"Round Robin) is currently selected on the MPTCP or MQVPN pages." +"(bpf_weight/bpf_weight_rr/bpf_burstweight/bpf_overflow, or mqvpn’s Weighted " +"RTT/Weighted Round Robin) is currently selected on the MPTCP or MQVPN pages." msgstr "" "Noch nichts anzuzeigen: Auf den Seiten MPTCP und MQVPN ist derzeit weder ein " "DSCP-Scheduler (bpf_dscp) noch ein Gewichts-Scheduler (bpf_weight/" -"bpf_weight_rr/bpf_burstweight oder mqvpns „Weighted RTT“/„Weighted Round " -"Robin“) ausgewählt." +"bpf_weight_rr/bpf_burstweight/bpf_overflow oder mqvpns „Weighted RTT“/" +"„Weighted Round Robin“) ausgewählt." #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp.js:378 msgid "One interface must be set as master" @@ -737,10 +737,12 @@ msgstr "" #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp.js:394 msgid "" -"Only used by *weight schedulers/path managers. Ignored if no weight " +"Only used by the weight schedulers (bpf_weight, bpf_weight_rr, " +"bpf_burstweight, bpf_overflow) and path managers. Ignored if no weight " "scheduler is selected." msgstr "" -"Wird nur von den *weight-Schedulern/Pfadmanagern verwendet. Wird ignoriert, " +"Wird nur von den Gewichts-Schedulern (bpf_weight, bpf_weight_rr, " +"bpf_burstweight, bpf_overflow) und Pfadmanagern verwendet. Wird ignoriert, " "wenn kein Gewichts-Scheduler ausgewählt ist." #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/multipath.js:703 @@ -1034,17 +1036,17 @@ msgstr "Upload-Schnittstelle" #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp_dscp_routing.js:173 msgid "" -"Used by the *weight MPTCP schedulers (bpf_weight/bpf_weight_rr) and by " -"mqvpn’s \"Weighted RTT\"/\"Weighted Round Robin\" schedulers -- same value, " -"applies to both upload and download (mirrored to the gateway automatically). " -"Unlike the DSCP pins above, weight is a single value per WAN, not per " -"traffic class." +"Used by the weight MPTCP schedulers (bpf_weight/bpf_weight_rr/bpf_overflow) " +"and by mqvpn’s \"Weighted RTT\"/\"Weighted Round Robin\" schedulers -- same " +"value, applies to both upload and download (mirrored to the gateway " +"automatically). Unlike the DSCP pins above, weight is a single value per " +"WAN, not per traffic class." msgstr "" -"Wird von den *weight-MPTCP-Schedulern (bpf_weight/bpf_weight_rr) und von den " -"Schedulern „Weighted RTT“/„Weighted Round Robin“ von mqvpn verwendet — " -"derselbe Wert gilt für Upload und Download (automatisch zum Gateway " -"gespiegelt). Anders als die DSCP-Zuordnungen oben ist das Gewicht ein " -"einzelner Wert je WAN, nicht je Verkehrsklasse." +"Wird von den Gewichts-MPTCP-Schedulern (bpf_weight/bpf_weight_rr/" +"bpf_overflow) und von den Schedulern „Weighted RTT“/„Weighted Round Robin“ " +"von mqvpn verwendet — derselbe Wert gilt für Upload und Download " +"(automatisch zum Gateway gespiegelt). Anders als die DSCP-Zuordnungen oben " +"ist das Gewicht ein einzelner Wert je WAN, nicht je Verkehrsklasse." #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp_diagnostics.js:297 msgid "Userspace" @@ -1129,6 +1131,30 @@ msgstr "" "bpf_rr => wählt zum Senden von Daten immer den nächsten verfügbaren Subflow " "(Round Robin)" +#: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp.js:150 +msgid "" +"bpf_ecf => sends on the lowest-latency subflow, and on a slower one only " +"when that does not delay the data" +msgstr "" +"bpf_ecf => sendet über den Subflow mit der geringsten Latenz und über einen " +"langsameren nur, wenn das die Daten nicht verzögert" + +#: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp.js:151 +msgid "" +"bpf_blest => sends on the lowest-latency subflow, and on a slower one only " +"when that does not block the fastest one" +msgstr "" +"bpf_blest => sendet über den Subflow mit der geringsten Latenz und über " +"einen langsameren nur, wenn das den schnellsten nicht blockiert" + +#: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp.js:152 +msgid "" +"bpf_overflow => uses the subflows by weight: a lower weight only carries " +"what the higher ones cannot" +msgstr "" +"bpf_overflow => nutzt die Subflows nach Gewicht: ein niedrigeres Gewicht " +"trägt nur, was die höheren nicht tragen können" + #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp_diagnostics.js:251 msgid "cwnd" msgstr "cwnd" diff --git a/luci-app-mptcp/po/fr/mptcp.po b/luci-app-mptcp/po/fr/mptcp.po index 9f111d3d7..fbe1db7d4 100644 --- a/luci-app-mptcp/po/fr/mptcp.po +++ b/luci-app-mptcp/po/fr/mptcp.po @@ -172,13 +172,13 @@ msgstr "Somme de contrôle" msgid "" "Choose which WAN carries each DSCP-tagged traffic class -- for the MPTCP " "bpf_dscp scheduler and for mqvpn -- and review the per-WAN weight used by " -"the *weight schedulers on both. This page only edits the pins/weights; pick " +"the weight schedulers on both. This page only edits the pins/weights; pick " "the scheduler that actually uses them on the MPTCP and MQVPN pages " "themselves." msgstr "" "Choisissez quel WAN transporte chaque classe de trafic marquée DSCP — pour " "l'ordonnanceur MPTCP bpf_dscp et pour mqvpn — et vérifiez le poids par WAN " -"utilisé par les ordonnanceurs *weight sur les deux. Cette page ne modifie " +"utilisé par les ordonnanceurs de poids sur les deux. Cette page ne modifie " "que les affectations et les poids ; choisissez l'ordonnanceur qui les " "utilise réellement sur les pages MPTCP et MQVPN elles-mêmes." @@ -712,13 +712,13 @@ msgstr "Aucune interface WAN avec multipath activé n'a été trouvée." #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp_dscp_routing.js:121 msgid "" "Nothing to show yet: no DSCP scheduler (bpf_dscp) or weight scheduler " -"(bpf_weight/bpf_weight_rr/bpf_burstweight, or mqvpn’s Weighted RTT/Weighted " -"Round Robin) is currently selected on the MPTCP or MQVPN pages." +"(bpf_weight/bpf_weight_rr/bpf_burstweight/bpf_overflow, or mqvpn’s Weighted " +"RTT/Weighted Round Robin) is currently selected on the MPTCP or MQVPN pages." msgstr "" "Rien à afficher pour l'instant : aucun ordonnanceur DSCP (bpf_dscp) ni " -"ordonnanceur de poids (bpf_weight/bpf_weight_rr/bpf_burstweight, ou " -"« Weighted RTT »/« Weighted Round Robin » de mqvpn) n'est actuellement " -"sélectionné sur les pages MPTCP ou MQVPN." +"ordonnanceur de poids (bpf_weight/bpf_weight_rr/bpf_burstweight/" +"bpf_overflow, ou « Weighted RTT »/« Weighted Round Robin » de mqvpn) n'est " +"actuellement sélectionné sur les pages MPTCP ou MQVPN." #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp.js:378 msgid "One interface must be set as master" @@ -735,11 +735,13 @@ msgstr "" #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp.js:394 msgid "" -"Only used by *weight schedulers/path managers. Ignored if no weight " +"Only used by the weight schedulers (bpf_weight, bpf_weight_rr, " +"bpf_burstweight, bpf_overflow) and path managers. Ignored if no weight " "scheduler is selected." msgstr "" -"Utilisé uniquement par les ordonnanceurs/gestionnaires de chemins *weight. " -"Ignoré si aucun ordonnanceur de poids n'est sélectionné." +"Utilisé uniquement par les ordonnanceurs de poids (bpf_weight, " +"bpf_weight_rr, bpf_burstweight, bpf_overflow) et les gestionnaires de " +"chemins. Ignoré si aucun ordonnanceur de poids n'est sélectionné." #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/multipath.js:703 msgid "Out" @@ -1032,17 +1034,18 @@ msgstr "Interface d'envoi" #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp_dscp_routing.js:173 msgid "" -"Used by the *weight MPTCP schedulers (bpf_weight/bpf_weight_rr) and by " -"mqvpn’s \"Weighted RTT\"/\"Weighted Round Robin\" schedulers -- same value, " -"applies to both upload and download (mirrored to the gateway automatically). " -"Unlike the DSCP pins above, weight is a single value per WAN, not per " -"traffic class." +"Used by the weight MPTCP schedulers (bpf_weight/bpf_weight_rr/bpf_overflow) " +"and by mqvpn’s \"Weighted RTT\"/\"Weighted Round Robin\" schedulers -- same " +"value, applies to both upload and download (mirrored to the gateway " +"automatically). Unlike the DSCP pins above, weight is a single value per " +"WAN, not per traffic class." msgstr "" -"Utilisé par les ordonnanceurs MPTCP *weight (bpf_weight/bpf_weight_rr) et " -"par les ordonnanceurs « Weighted RTT »/« Weighted Round Robin » de mqvpn — " -"même valeur, appliquée à la fois à l'envoi et à la réception (répliquée " -"automatiquement vers la passerelle). Contrairement aux affectations DSCP ci-" -"dessus, le poids est une valeur unique par WAN, non par classe de trafic." +"Utilisé par les ordonnanceurs MPTCP de poids (bpf_weight/bpf_weight_rr/" +"bpf_overflow) et par les ordonnanceurs « Weighted RTT »/« Weighted Round " +"Robin » de mqvpn — même valeur, appliquée à la fois à l'envoi et à la " +"réception (répliquée automatiquement vers la passerelle). Contrairement aux " +"affectations DSCP ci-dessus, le poids est une valeur unique par WAN, non par " +"classe de trafic." #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp_diagnostics.js:297 msgid "Userspace" @@ -1132,6 +1135,30 @@ msgstr "" "bpf_rr => choisit toujours le sous-flux disponible suivant pour envoyer les " "données (tourniquet)" +#: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp.js:150 +msgid "" +"bpf_ecf => sends on the lowest-latency subflow, and on a slower one only " +"when that does not delay the data" +msgstr "" +"bpf_ecf => envoie sur le sous-flux de plus faible latence, et sur un plus " +"lent seulement quand cela ne retarde pas les données" + +#: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp.js:151 +msgid "" +"bpf_blest => sends on the lowest-latency subflow, and on a slower one only " +"when that does not block the fastest one" +msgstr "" +"bpf_blest => envoie sur le sous-flux de plus faible latence, et sur un plus " +"lent seulement quand cela ne bloque pas le plus rapide" + +#: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp.js:152 +msgid "" +"bpf_overflow => uses the subflows by weight: a lower weight only carries " +"what the higher ones cannot" +msgstr "" +"bpf_overflow => utilise les sous-flux selon leur poids : un poids plus " +"faible ne transporte que ce que les plus élevés ne peuvent pas" + #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp_diagnostics.js:251 msgid "cwnd" msgstr "cwnd" diff --git a/luci-app-mptcp/po/it/mptcp.po b/luci-app-mptcp/po/it/mptcp.po index 7bffd5feb..2e9f53670 100644 --- a/luci-app-mptcp/po/it/mptcp.po +++ b/luci-app-mptcp/po/it/mptcp.po @@ -171,13 +171,13 @@ msgstr "Checksum" msgid "" "Choose which WAN carries each DSCP-tagged traffic class -- for the MPTCP " "bpf_dscp scheduler and for mqvpn -- and review the per-WAN weight used by " -"the *weight schedulers on both. This page only edits the pins/weights; pick " +"the weight schedulers on both. This page only edits the pins/weights; pick " "the scheduler that actually uses them on the MPTCP and MQVPN pages " "themselves." msgstr "" "Scegli quale WAN trasporta ciascuna classe di traffico marcata DSCP — per lo " "scheduler MPTCP bpf_dscp e per mqvpn — e verifica il peso per WAN usato " -"dagli scheduler *weight su entrambi. Questa pagina modifica solo le " +"dagli scheduler di peso su entrambi. Questa pagina modifica solo le " "assegnazioni e i pesi; scegli lo scheduler che li usa davvero nelle pagine " "MPTCP e MQVPN." @@ -712,13 +712,13 @@ msgstr "Nessuna interfaccia WAN con multipath abilitato trovata." #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp_dscp_routing.js:121 msgid "" "Nothing to show yet: no DSCP scheduler (bpf_dscp) or weight scheduler " -"(bpf_weight/bpf_weight_rr/bpf_burstweight, or mqvpn’s Weighted RTT/Weighted " -"Round Robin) is currently selected on the MPTCP or MQVPN pages." +"(bpf_weight/bpf_weight_rr/bpf_burstweight/bpf_overflow, or mqvpn’s Weighted " +"RTT/Weighted Round Robin) is currently selected on the MPTCP or MQVPN pages." msgstr "" "Ancora nulla da mostrare: nelle pagine MPTCP o MQVPN non è attualmente " "selezionato né uno scheduler DSCP (bpf_dscp) né uno scheduler di peso " -"(bpf_weight/bpf_weight_rr/bpf_burstweight, o «Weighted RTT»/«Weighted Round " -"Robin» di mqvpn)." +"(bpf_weight/bpf_weight_rr/bpf_burstweight/bpf_overflow, o «Weighted " +"RTT»/«Weighted Round Robin» di mqvpn)." #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp.js:378 msgid "One interface must be set as master" @@ -734,10 +734,12 @@ msgstr "" #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp.js:394 msgid "" -"Only used by *weight schedulers/path managers. Ignored if no weight " +"Only used by the weight schedulers (bpf_weight, bpf_weight_rr, " +"bpf_burstweight, bpf_overflow) and path managers. Ignored if no weight " "scheduler is selected." msgstr "" -"Usato solo dagli scheduler/gestori di percorsi *weight. Ignorato se non è " +"Usato solo dagli scheduler di peso (bpf_weight, bpf_weight_rr, " +"bpf_burstweight, bpf_overflow) e dai gestori di percorsi. Ignorato se non è " "selezionato alcuno scheduler di peso." #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/multipath.js:703 @@ -1033,17 +1035,17 @@ msgstr "Interfaccia di upload" #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp_dscp_routing.js:173 msgid "" -"Used by the *weight MPTCP schedulers (bpf_weight/bpf_weight_rr) and by " -"mqvpn’s \"Weighted RTT\"/\"Weighted Round Robin\" schedulers -- same value, " -"applies to both upload and download (mirrored to the gateway automatically). " -"Unlike the DSCP pins above, weight is a single value per WAN, not per " -"traffic class." +"Used by the weight MPTCP schedulers (bpf_weight/bpf_weight_rr/bpf_overflow) " +"and by mqvpn’s \"Weighted RTT\"/\"Weighted Round Robin\" schedulers -- same " +"value, applies to both upload and download (mirrored to the gateway " +"automatically). Unlike the DSCP pins above, weight is a single value per " +"WAN, not per traffic class." msgstr "" -"Usato dagli scheduler MPTCP *weight (bpf_weight/bpf_weight_rr) e dagli " -"scheduler «Weighted RTT»/«Weighted Round Robin» di mqvpn — stesso valore, " -"vale sia per l'upload sia per il download (replicato automaticamente verso " -"il gateway). A differenza delle assegnazioni DSCP qui sopra, il peso è un " -"singolo valore per WAN, non per classe di traffico." +"Usato dagli scheduler MPTCP di peso (bpf_weight/bpf_weight_rr/bpf_overflow) " +"e dagli scheduler «Weighted RTT»/«Weighted Round Robin» di mqvpn — stesso " +"valore, vale sia per l'upload sia per il download (replicato automaticamente " +"verso il gateway). A differenza delle assegnazioni DSCP qui sopra, il peso è " +"un singolo valore per WAN, non per classe di traffico." #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp_diagnostics.js:297 msgid "Userspace" @@ -1130,6 +1132,30 @@ msgstr "" "bpf_rr => sceglie sempre il sottoflusso disponibile successivo per inviare i " "dati (round robin)" +#: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp.js:150 +msgid "" +"bpf_ecf => sends on the lowest-latency subflow, and on a slower one only " +"when that does not delay the data" +msgstr "" +"bpf_ecf => invia sul sottoflusso a latenza più bassa, e su uno più lento " +"solo quando ciò non ritarda i dati" + +#: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp.js:151 +msgid "" +"bpf_blest => sends on the lowest-latency subflow, and on a slower one only " +"when that does not block the fastest one" +msgstr "" +"bpf_blest => invia sul sottoflusso a latenza più bassa, e su uno più lento " +"solo quando ciò non blocca il più veloce" + +#: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp.js:152 +msgid "" +"bpf_overflow => uses the subflows by weight: a lower weight only carries " +"what the higher ones cannot" +msgstr "" +"bpf_overflow => usa i sottoflussi in base al peso: un peso inferiore " +"trasporta solo ciò che i superiori non riescono a trasportare" + #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp_diagnostics.js:251 msgid "cwnd" msgstr "cwnd" diff --git a/luci-app-mptcp/po/oc/mptcp.po b/luci-app-mptcp/po/oc/mptcp.po index 56d244899..a62b4ac8e 100644 --- a/luci-app-mptcp/po/oc/mptcp.po +++ b/luci-app-mptcp/po/oc/mptcp.po @@ -167,13 +167,13 @@ msgstr "Soma de contraròtle" msgid "" "Choose which WAN carries each DSCP-tagged traffic class -- for the MPTCP " "bpf_dscp scheduler and for mqvpn -- and review the per-WAN weight used by " -"the *weight schedulers on both. This page only edits the pins/weights; pick " +"the weight schedulers on both. This page only edits the pins/weights; pick " "the scheduler that actually uses them on the MPTCP and MQVPN pages " "themselves." msgstr "" "Causissètz quin WAN pòrta cada classa de trafic marcada DSCP — per " "l'ordonançador MPTCP bpf_dscp e per mqvpn — e verificatz lo pes per WAN " -"utilizat pels ordonançadors *weight suls dos. Aquesta pagina modifica " +"utilizat pels ordonançadors de pes suls dos. Aquesta pagina modifica " "solament las afectacions e los peses ; causissètz l'ordonançador que los " "utiliza vertadièrament sus las paginas MPTCP e MQVPN elas meteissas." @@ -708,13 +708,13 @@ msgstr "Cap d'interfàcia WAN amb multipath activat pas trobada." #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp_dscp_routing.js:121 msgid "" "Nothing to show yet: no DSCP scheduler (bpf_dscp) or weight scheduler " -"(bpf_weight/bpf_weight_rr/bpf_burstweight, or mqvpn’s Weighted RTT/Weighted " -"Round Robin) is currently selected on the MPTCP or MQVPN pages." +"(bpf_weight/bpf_weight_rr/bpf_burstweight/bpf_overflow, or mqvpn’s Weighted " +"RTT/Weighted Round Robin) is currently selected on the MPTCP or MQVPN pages." msgstr "" "Res a mostrar pel moment : cap d'ordonançador DSCP (bpf_dscp) ni " -"d'ordonançador de pes (bpf_weight/bpf_weight_rr/bpf_burstweight, o " -"« Weighted RTT »/« Weighted Round Robin » de mqvpn) es pas seleccionat " -"actualament sus las paginas MPTCP o MQVPN." +"d'ordonançador de pes (bpf_weight/bpf_weight_rr/bpf_burstweight/" +"bpf_overflow, o « Weighted RTT »/« Weighted Round Robin » de mqvpn) es pas " +"seleccionat actualament sus las paginas MPTCP o MQVPN." #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp.js:378 msgid "One interface must be set as master" @@ -731,11 +731,13 @@ msgstr "" #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp.js:394 msgid "" -"Only used by *weight schedulers/path managers. Ignored if no weight " +"Only used by the weight schedulers (bpf_weight, bpf_weight_rr, " +"bpf_burstweight, bpf_overflow) and path managers. Ignored if no weight " "scheduler is selected." msgstr "" -"Utilizat solament suls ordonançadors/gestionaris de camins *weight. Ignorat " -"se cap d'ordonançador de pes es pas seleccionat." +"Utilizat solament pels ordonançadors de pes (bpf_weight, bpf_weight_rr, " +"bpf_burstweight, bpf_overflow) e los gestionaris de camins. Ignorat se cap " +"d'ordonançador de pes es pas seleccionat." #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/multipath.js:703 msgid "Out" @@ -1029,17 +1031,17 @@ msgstr "Interfàcia de mandadís" #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp_dscp_routing.js:173 msgid "" -"Used by the *weight MPTCP schedulers (bpf_weight/bpf_weight_rr) and by " -"mqvpn’s \"Weighted RTT\"/\"Weighted Round Robin\" schedulers -- same value, " -"applies to both upload and download (mirrored to the gateway automatically). " -"Unlike the DSCP pins above, weight is a single value per WAN, not per " -"traffic class." +"Used by the weight MPTCP schedulers (bpf_weight/bpf_weight_rr/bpf_overflow) " +"and by mqvpn’s \"Weighted RTT\"/\"Weighted Round Robin\" schedulers -- same " +"value, applies to both upload and download (mirrored to the gateway " +"automatically). Unlike the DSCP pins above, weight is a single value per " +"WAN, not per traffic class." msgstr "" -"Utilizat pels ordonançadors MPTCP *weight (bpf_weight/bpf_weight_rr) e pels " -"ordonançadors « Weighted RTT »/« Weighted Round Robin » de mqvpn — meteissa " -"valor, aplicada a l'encòp al mandadís e a la recepcion (replicada " -"automaticament cap a la palanca). Al contrari de las afectacions DSCP çai-" -"sus, lo pes es una valor unica per WAN, pas per classa de trafic." +"Utilizat pels ordonançadors MPTCP de pes (bpf_weight/bpf_weight_rr/" +"bpf_overflow) e pels ordonançadors « Weighted RTT »/« Weighted Round Robin » " +"de mqvpn — meteissa valor, aplicada a l'encòp al mandadís e a la recepcion " +"(replicada automaticament cap a la palanca). Al contrari de las afectacions " +"DSCP çai-sus, lo pes es una valor unica per WAN, pas per classa de trafic." #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp_diagnostics.js:297 msgid "Userspace" @@ -1127,6 +1129,30 @@ msgstr "" "bpf_rr => causís totjorn lo sosflux disponible venent per mandar las donadas " "(torniquet)" +#: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp.js:150 +msgid "" +"bpf_ecf => sends on the lowest-latency subflow, and on a slower one only " +"when that does not delay the data" +msgstr "" +"bpf_ecf => manda sul sosflux de latença mai bassa, e sus un mai lent " +"solament quand aquò retarda pas las donadas" + +#: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp.js:151 +msgid "" +"bpf_blest => sends on the lowest-latency subflow, and on a slower one only " +"when that does not block the fastest one" +msgstr "" +"bpf_blest => manda sul sosflux de latença mai bassa, e sus un mai lent " +"solament quand aquò bloca pas lo mai rapid" + +#: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp.js:152 +msgid "" +"bpf_overflow => uses the subflows by weight: a lower weight only carries " +"what the higher ones cannot" +msgstr "" +"bpf_overflow => utiliza los sosfluxes segon lor pes : un pes mai bas pòrta " +"solament çò que los mai nauts pòdon pas portar" + #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp_diagnostics.js:251 msgid "cwnd" msgstr "cwnd" diff --git a/luci-app-mptcp/po/ru/mptcp.po b/luci-app-mptcp/po/ru/mptcp.po index c8de1a087..15f76b7cd 100644 --- a/luci-app-mptcp/po/ru/mptcp.po +++ b/luci-app-mptcp/po/ru/mptcp.po @@ -169,13 +169,13 @@ msgstr "Контрольная сумма" msgid "" "Choose which WAN carries each DSCP-tagged traffic class -- for the MPTCP " "bpf_dscp scheduler and for mqvpn -- and review the per-WAN weight used by " -"the *weight schedulers on both. This page only edits the pins/weights; pick " +"the weight schedulers on both. This page only edits the pins/weights; pick " "the scheduler that actually uses them on the MPTCP and MQVPN pages " "themselves." msgstr "" "Выберите, какой WAN несёт каждый класс трафика с меткой DSCP — для " "планировщика MPTCP bpf_dscp и для mqvpn — и проверьте вес каждого WAN, " -"используемый планировщиками *weight в обоих. Эта страница изменяет только " +"используемый весовыми планировщиками в обоих. Эта страница изменяет только " "закрепления и веса; сам планировщик, который их использует, выбирается на " "страницах MPTCP и MQVPN." @@ -707,13 +707,13 @@ msgstr "WAN-интерфейсы с включённым multipath не найд #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp_dscp_routing.js:121 msgid "" "Nothing to show yet: no DSCP scheduler (bpf_dscp) or weight scheduler " -"(bpf_weight/bpf_weight_rr/bpf_burstweight, or mqvpn’s Weighted RTT/Weighted " -"Round Robin) is currently selected on the MPTCP or MQVPN pages." +"(bpf_weight/bpf_weight_rr/bpf_burstweight/bpf_overflow, or mqvpn’s Weighted " +"RTT/Weighted Round Robin) is currently selected on the MPTCP or MQVPN pages." msgstr "" "Пока нечего показать: на страницах MPTCP и MQVPN сейчас не выбран ни " "планировщик DSCP (bpf_dscp), ни весовой планировщик (bpf_weight/" -"bpf_weight_rr/bpf_burstweight либо «Weighted RTT»/«Weighted Round Robin» в " -"mqvpn)." +"bpf_weight_rr/bpf_burstweight/bpf_overflow либо «Weighted RTT»/«Weighted " +"Round Robin» в mqvpn)." #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp.js:378 msgid "One interface must be set as master" @@ -729,11 +729,13 @@ msgstr "" #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp.js:394 msgid "" -"Only used by *weight schedulers/path managers. Ignored if no weight " +"Only used by the weight schedulers (bpf_weight, bpf_weight_rr, " +"bpf_burstweight, bpf_overflow) and path managers. Ignored if no weight " "scheduler is selected." msgstr "" -"Используется только планировщиками и менеджерами путей *weight. " -"Игнорируется, если весовой планировщик не выбран." +"Используется только весовыми планировщиками (bpf_weight, bpf_weight_rr, " +"bpf_burstweight, bpf_overflow) и менеджерами путей. Игнорируется, если " +"весовой планировщик не выбран." #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/multipath.js:703 msgid "Out" @@ -1026,17 +1028,17 @@ msgstr "Интерфейс передачи" #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp_dscp_routing.js:173 msgid "" -"Used by the *weight MPTCP schedulers (bpf_weight/bpf_weight_rr) and by " -"mqvpn’s \"Weighted RTT\"/\"Weighted Round Robin\" schedulers -- same value, " -"applies to both upload and download (mirrored to the gateway automatically). " -"Unlike the DSCP pins above, weight is a single value per WAN, not per " -"traffic class." +"Used by the weight MPTCP schedulers (bpf_weight/bpf_weight_rr/bpf_overflow) " +"and by mqvpn’s \"Weighted RTT\"/\"Weighted Round Robin\" schedulers -- same " +"value, applies to both upload and download (mirrored to the gateway " +"automatically). Unlike the DSCP pins above, weight is a single value per " +"WAN, not per traffic class." msgstr "" -"Используется планировщиками MPTCP *weight (bpf_weight/bpf_weight_rr) и " -"планировщиками «Weighted RTT»/«Weighted Round Robin» в mqvpn — одно и то же " -"значение применяется и к передаче, и к приёму (автоматически зеркалируется " -"на шлюз). В отличие от закреплений DSCP выше, вес — это одно значение на " -"WAN, а не на класс трафика." +"Используется весовыми планировщиками MPTCP (bpf_weight/bpf_weight_rr/" +"bpf_overflow) и планировщиками «Weighted RTT»/«Weighted Round Robin» в mqvpn " +"— одно и то же значение применяется и к передаче, и к приёму (автоматически " +"зеркалируется на шлюз). В отличие от закреплений DSCP выше, вес — это одно " +"значение на WAN, а не на класс трафика." #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp_diagnostics.js:297 msgid "Userspace" @@ -1122,6 +1124,30 @@ msgstr "" "bpf_rr => всегда выбирает следующий доступный подпоток для отправки данных " "(по кругу)" +#: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp.js:150 +msgid "" +"bpf_ecf => sends on the lowest-latency subflow, and on a slower one only " +"when that does not delay the data" +msgstr "" +"bpf_ecf => отправляет по подпотоку с наименьшей задержкой, а по более " +"медленному — только если это не задерживает данные" + +#: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp.js:151 +msgid "" +"bpf_blest => sends on the lowest-latency subflow, and on a slower one only " +"when that does not block the fastest one" +msgstr "" +"bpf_blest => отправляет по подпотоку с наименьшей задержкой, а по более " +"медленному — только если это не блокирует самый быстрый" + +#: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp.js:152 +msgid "" +"bpf_overflow => uses the subflows by weight: a lower weight only carries " +"what the higher ones cannot" +msgstr "" +"bpf_overflow => использует подпотоки по весу: подпоток с меньшим весом " +"передаёт только то, что не могут передать более весомые" + #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp_diagnostics.js:251 msgid "cwnd" msgstr "cwnd" diff --git a/luci-app-mptcp/po/templates/mptcp.pot b/luci-app-mptcp/po/templates/mptcp.pot index ed32cb363..4fefeceb0 100644 --- a/luci-app-mptcp/po/templates/mptcp.pot +++ b/luci-app-mptcp/po/templates/mptcp.pot @@ -165,7 +165,7 @@ msgstr "" msgid "" "Choose which WAN carries each DSCP-tagged traffic class -- for the MPTCP " "bpf_dscp scheduler and for mqvpn -- and review the per-WAN weight used by " -"the *weight schedulers on both. This page only edits the pins/weights; pick " +"the weight schedulers on both. This page only edits the pins/weights; pick " "the scheduler that actually uses them on the MPTCP and MQVPN pages " "themselves." msgstr "" @@ -677,8 +677,8 @@ msgstr "" #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp_dscp_routing.js:121 msgid "" "Nothing to show yet: no DSCP scheduler (bpf_dscp) or weight scheduler " -"(bpf_weight/bpf_weight_rr/bpf_burstweight, or mqvpn’s Weighted RTT/Weighted " -"Round Robin) is currently selected on the MPTCP or MQVPN pages." +"(bpf_weight/bpf_weight_rr/bpf_burstweight/bpf_overflow, or mqvpn’s Weighted " +"RTT/Weighted Round Robin) is currently selected on the MPTCP or MQVPN pages." msgstr "" #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp.js:378 @@ -693,7 +693,8 @@ msgstr "" #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp.js:394 msgid "" -"Only used by *weight schedulers/path managers. Ignored if no weight " +"Only used by the weight schedulers (bpf_weight, bpf_weight_rr, " +"bpf_burstweight, bpf_overflow) and path managers. Ignored if no weight " "scheduler is selected." msgstr "" @@ -971,11 +972,11 @@ msgstr "" #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp_dscp_routing.js:173 msgid "" -"Used by the *weight MPTCP schedulers (bpf_weight/bpf_weight_rr) and by " -"mqvpn’s \"Weighted RTT\"/\"Weighted Round Robin\" schedulers -- same value, " -"applies to both upload and download (mirrored to the gateway automatically). " -"Unlike the DSCP pins above, weight is a single value per WAN, not per " -"traffic class." +"Used by the weight MPTCP schedulers (bpf_weight/bpf_weight_rr/bpf_overflow) " +"and by mqvpn’s \"Weighted RTT\"/\"Weighted Round Robin\" schedulers -- same " +"value, applies to both upload and download (mirrored to the gateway " +"automatically). Unlike the DSCP pins above, weight is a single value per " +"WAN, not per traffic class." msgstr "" #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp_diagnostics.js:297 @@ -1052,6 +1053,24 @@ msgid "" "bpf_rr => always picks the next available subflow to send data (round-robin)" msgstr "" +#: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp.js:150 +msgid "" +"bpf_ecf => sends on the lowest-latency subflow, and on a slower one only " +"when that does not delay the data" +msgstr "" + +#: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp.js:151 +msgid "" +"bpf_blest => sends on the lowest-latency subflow, and on a slower one only " +"when that does not block the fastest one" +msgstr "" + +#: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp.js:152 +msgid "" +"bpf_overflow => uses the subflows by weight: a lower weight only carries " +"what the higher ones cannot" +msgstr "" + #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp_diagnostics.js:251 msgid "cwnd" msgstr "" diff --git a/luci-app-mptcp/po/zh_Hans/mptcp.po b/luci-app-mptcp/po/zh_Hans/mptcp.po index 3d7a9140e..c824a890f 100644 --- a/luci-app-mptcp/po/zh_Hans/mptcp.po +++ b/luci-app-mptcp/po/zh_Hans/mptcp.po @@ -165,13 +165,13 @@ msgstr "校验和" msgid "" "Choose which WAN carries each DSCP-tagged traffic class -- for the MPTCP " "bpf_dscp scheduler and for mqvpn -- and review the per-WAN weight used by " -"the *weight schedulers on both. This page only edits the pins/weights; pick " +"the weight schedulers on both. This page only edits the pins/weights; pick " "the scheduler that actually uses them on the MPTCP and MQVPN pages " "themselves." msgstr "" "选择由哪个 WAN 承载各个 DSCP 标记的流量类别 —— 用于 MPTCP 的 bpf_dscp 调度器" -"和 mqvpn —— 并查看两者中 *weight 调度器所使用的每个 WAN 的权重。本页面只编辑" -"固定关系和权重;真正使用它们的调度器请在 MPTCP 和 MQVPN 页面中选择。" +"和 mqvpn —— 并查看两者中 权重调度器所使用的每个 WAN 的权重。本页面只编辑固定" +"关系和权重;真正使用它们的调度器请在 MPTCP 和 MQVPN 页面中选择。" #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp.js:332 msgid "Close timeout" @@ -689,12 +689,12 @@ msgstr "未找到启用多路径的 WAN 接口。" #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp_dscp_routing.js:121 msgid "" "Nothing to show yet: no DSCP scheduler (bpf_dscp) or weight scheduler " -"(bpf_weight/bpf_weight_rr/bpf_burstweight, or mqvpn’s Weighted RTT/Weighted " -"Round Robin) is currently selected on the MPTCP or MQVPN pages." +"(bpf_weight/bpf_weight_rr/bpf_burstweight/bpf_overflow, or mqvpn’s Weighted " +"RTT/Weighted Round Robin) is currently selected on the MPTCP or MQVPN pages." msgstr "" "暂无可显示内容:MPTCP 或 MQVPN 页面上当前既未选择 DSCP 调度器 (bpf_dscp),也" -"未选择权重调度器 (bpf_weight/bpf_weight_rr/bpf_burstweight,或 mqvpn " -"的“Weighted RTT”/“Weighted Round Robin”)。" +"未选择权重调度器 (bpf_weight/bpf_weight_rr/bpf_burstweight/bpf_overflow,或 " +"mqvpn 的“Weighted RTT”/“Weighted Round Robin”)。" #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp.js:378 msgid "One interface must be set as master" @@ -710,9 +710,12 @@ msgstr "" #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp.js:394 msgid "" -"Only used by *weight schedulers/path managers. Ignored if no weight " +"Only used by the weight schedulers (bpf_weight, bpf_weight_rr, " +"bpf_burstweight, bpf_overflow) and path managers. Ignored if no weight " "scheduler is selected." -msgstr "仅由 *weight 调度器/路径管理器使用。未选择权重调度器时将被忽略。" +msgstr "" +"仅由权重调度器 (bpf_weight、bpf_weight_rr、bpf_burstweight、bpf_overflow) 和" +"路径管理器使用。未选择权重调度器时将被忽略。" #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/multipath.js:703 msgid "Out" @@ -999,13 +1002,13 @@ msgstr "上传接口" #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp_dscp_routing.js:173 msgid "" -"Used by the *weight MPTCP schedulers (bpf_weight/bpf_weight_rr) and by " -"mqvpn’s \"Weighted RTT\"/\"Weighted Round Robin\" schedulers -- same value, " -"applies to both upload and download (mirrored to the gateway automatically). " -"Unlike the DSCP pins above, weight is a single value per WAN, not per " -"traffic class." +"Used by the weight MPTCP schedulers (bpf_weight/bpf_weight_rr/bpf_overflow) " +"and by mqvpn’s \"Weighted RTT\"/\"Weighted Round Robin\" schedulers -- same " +"value, applies to both upload and download (mirrored to the gateway " +"automatically). Unlike the DSCP pins above, weight is a single value per " +"WAN, not per traffic class." msgstr "" -"由 *weight 系列 MPTCP 调度器 (bpf_weight/bpf_weight_rr) 以及 mqvpn " +"由权重 MPTCP 调度器 (bpf_weight/bpf_weight_rr/bpf_overflow) 以及 mqvpn " "的“Weighted RTT”/“Weighted Round Robin”调度器使用 —— 同一个值同时作用于上传和" "下载(会自动镜像到网关)。与上面的 DSCP 固定不同,权重是每个 WAN 一个值,而不" "是每个流量类别一个值。" @@ -1088,6 +1091,27 @@ msgid "" "bpf_rr => always picks the next available subflow to send data (round-robin)" msgstr "bpf_rr => 始终选择下一个可用子流发送数据(轮询)" +#: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp.js:150 +msgid "" +"bpf_ecf => sends on the lowest-latency subflow, and on a slower one only " +"when that does not delay the data" +msgstr "bpf_ecf => 通过延迟最低的子流发送,仅在不会延迟数据时才使用较慢的子流" + +#: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp.js:151 +msgid "" +"bpf_blest => sends on the lowest-latency subflow, and on a slower one only " +"when that does not block the fastest one" +msgstr "" +"bpf_blest => 通过延迟最低的子流发送,仅在不会阻塞最快子流时才使用较慢的子流" + +#: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp.js:152 +msgid "" +"bpf_overflow => uses the subflows by weight: a lower weight only carries " +"what the higher ones cannot" +msgstr "" +"bpf_overflow => 按权重使用子流:权重较低的子流只承载权重较高的子流无法承载的" +"部分" + #: luci-app-mptcp/htdocs/luci-static/resources/view/mptcp/mptcp_diagnostics.js:251 msgid "cwnd" msgstr "cwnd" diff --git a/luci-app-netstat/Makefile b/luci-app-netstat/Makefile index f97ce8e65..ee0fb6413 100644 --- a/luci-app-netstat/Makefile +++ b/luci-app-netstat/Makefile @@ -1,23 +1,28 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-netstat -PKG_VERSION:=1.1.5 -PKG_RELEASE:=5 +PKG_VERSION:=1.2.0 +PKG_RELEASE:=6 PKG_MAINTAINER:=NoobLK PKG_LICENSE:=GPL-3.0 LUCI_TITLE:=NET Stats LUCI_DESCRIPTION:=This LuCI app provides net statistic functionality in a web interface. -LUCI_DEPENDS:=+vnstat +luci-compat +LUCI_DEPENDS:=+vnstat2 +luci-compat +luci-lib-jsonc include $(TOPDIR)/feeds/luci/luci.mk define Package/luci-app-netstat/install $(CP) ./files/* $(1)/ + chmod 0755 $(1)/usr/libexec/netstat-vnstat-track # vnstat package owns /etc/config/vnstat on newer OpenWrt versions. # Remove it here to avoid file ownership conflicts if present in any tree. $(RM) $(1)/etc/config/vnstat endef +define Package/luci-app-netstat/conffiles +/etc/config/netstats +endef + $(eval $(call BuildPackage,luci-app-netstat)) diff --git a/luci-app-netstat/README.md b/luci-app-netstat/README.md index c367c588b..59b6c4dbe 100644 --- a/luci-app-netstat/README.md +++ b/luci-app-netstat/README.md @@ -1,159 +1,149 @@ -# luci-app-netstat -
-**A professional network statistics application for OpenWrt LuCI** +# luci-app-netstat -[![License: GPL-3.0](https://img.shields.io/badge/License-GPL%203.0-blue.svg)](LICENSE) -![OpenWrt Compatible](https://img.shields.io/badge/OpenWrt-Compatible-brightgreen) +**Live traffic rates and vnStat usage history for any OpenWrt router, in LuCI.** -[Features](#features) • [Installation](#installation) • [Usage](#usage) • [Development](#development) • [License](#license) +[![Release](https://img.shields.io/github/v/release/nooblk-98/luci-app-netstat?style=flat-square)](https://github.com/nooblk-98/luci-app-netstat/releases/latest) +[![Build](https://img.shields.io/github/actions/workflow/status/nooblk-98/luci-app-netstat/build.yml?branch=main&style=flat-square&label=build)](https://github.com/nooblk-98/luci-app-netstat/actions/workflows/build.yml) +[![OpenWrt](https://img.shields.io/badge/OpenWrt-24.10%20%7C%2025.x-00b5e2?style=flat-square&logo=openwrt&logoColor=white)](https://openwrt.org) +[![vnStat](https://img.shields.io/badge/requires-vnstat2-orange?style=flat-square)](https://humdi.net/vnstat/) +[![License](https://img.shields.io/badge/license-GPL--3.0-blue?style=flat-square)](Makefile) + +[Features](#features) • [Installation](#installation) • [Usage](#usage) • [Configuration](#configuration) • [How it works](#how-it-works) • [Troubleshooting](#troubleshooting) + +![Network Usage tab in dark mode](images/usage-dark.png)
---- +luci-app-netstat adds a live traffic dashboard to the LuCI status page and a **Netstat** page with daily and monthly usage charts. Live rates come straight from the kernel, and history comes from [vnStat 2](https://humdi.net/vnstat/). -## Overview -![Dashboard Preview](images/luci-app-netstat.png) - - - -**luci-app-netstat** is a modern, feature-rich LuCI application that provides comprehensive network traffic statistics and monitoring for OpenWrt routers. It leverages `vnstat` for accurate traffic monitoring and presents the data through an intuitive web interface. - -The application offers real-time network statistics, historical data visualization, and detailed traffic analysis to help you understand your network usage patterns. +It works on any OpenWrt router: there are no hard-coded interface names, device paths or board-specific files. The uplink is found from the routing table, and vnStat is set up to track your interfaces automatically. ## Features -✨ **Core Features:** -- 📊 **Real-time Network Monitoring** - Track network traffic with live statistics -- 📈 **Traffic Visualization** - Interactive charts powered by Chart.js -- 📅 **Historical Data** - Monitor trends over days, weeks, months, and years -- 🎨 **Dark Mode Support** - Professional dark theme for comfortable usage -- 📱 **Responsive Design** - Works seamlessly on desktop and mobile devices -- ⚙️ **Easy Configuration** - Simple UCI-based configuration system -- 🔄 **Automatic Updates** - Uses vnstat for continuous data collection - -## Requirements - -- OpenWrt with LuCI installed -- `vnstat` package (required dependency) -- Modern web browser (Chrome, Firefox, Safari, Edge) +- **Live dashboard on Status → Overview:** + - download and upload rates with sparklines, peaks and trend arrows; + - total downloaded and uploaded; + - internet status and public IP, as seen through Passwall or any other proxy; + - CPU temperature, uptime, RAM and storage. +- **Usage history:** daily or monthly charts per interface, read from `vnstat --json`. +- **Automatic interface detection:** follows the device that holds the default route (PPPoE, DHCP, LTE/5G modems, VPNs, DSA ports), or the interface you choose. +- **Zero-config vnStat:** interfaces are added to vnStat as they come up, so there is nothing to set up by hand. +- **Follows your theme:** uses LuCI's standard sections and tabs, so it matches any theme in light or dark mode, including live switching. +- **Lightweight:** shell, Lua and plain JavaScript only; packages are architecture-independent. ## Installation -### Automatic Installation +### Requirements -Clone the repository and install using the OpenWrt build system: +- OpenWrt **24.10** or **25.x** with LuCI +- **`vnstat2`** (vnStat 2.x): installed automatically as a dependency. The old vnStat 1.x `vnstat` package is not supported. + +### From a release + +Download the package for your OpenWrt version from the [latest release](https://github.com/nooblk-98/luci-app-netstat/releases/latest), copy it to the router and install it: ```bash -git clone https://github.com/dotycat/luci-app-netstat.git -cd luci-app-netstat -make +# OpenWrt 25.x (apk) +apk update +apk add --allow-untrusted luci-app-netstat-*.apk + +# OpenWrt 24.10 (opkg) +opkg update +opkg install luci-app-netstat_*_all.ipk ``` -### Manual Installation +### Build from source -1. Download the latest `.ipk` package from [Releases](../../releases) -2. Upload to your router via SCP: - ```bash - scp luci-app-netstat_*.ipk root@:/tmp/ - ``` -3. Install via SSH: - ```bash - ssh root@ - opkg install /tmp/luci-app-netstat_*.ipk - ``` -4. Navigate to **LuCI Web Interface** → **Status** → **Network Statistics** - -## Configuration - -### UCI Configuration - -The application configuration is managed via UCI. Edit `/etc/config/netstats` to customize: - -``` -config netstat - option enabled '1' - option interface 'wan' - option update_interval '60' -``` - -### vnstat Integration - -Ensure vnstat is properly configured and running: +Add the package to an OpenWrt buildroot or SDK that has the `luci` and `packages` feeds: ```bash -opkg install vnstat -service vnstat start -service vnstat enable +git clone https://github.com/nooblk-98/luci-app-netstat.git package/luci-app-netstat +make menuconfig # LuCI → Applications → luci-app-netstat +make package/luci-app-netstat/compile ``` +> [!IMPORTANT] +> **Upgrading from 1.1.x?** That version used vnStat 1.x. Remove both packages, install `vnstat2`, then install the new package: +> +> ```bash +> /etc/init.d/vnstat stop +> apk del luci-app-netstat vnstat # or: opkg remove luci-app-netstat vnstat +> ``` +> +> To keep your history, set `DatabaseDir` in `/etc/vnstat.conf` to the folder that holds your old 1.x files (often `/etc/vnstat`) **before** vnStat 2 starts. It imports them on first start. + ## Usage -### Dashboard +### Live dashboard -1. Log into your OpenWrt LuCI web interface -2. Navigate to **Status** → **Network Statistics** -3. View traffic statistics for your network interfaces +Open **Status → Overview**. The Netstat widget refreshes every two seconds. -### Available Data +![Live dashboard widget](images/status-widget.png) -- **Current Usage** - Real-time traffic rates -- **Daily Statistics** - Traffic breakdown by day -- **Monthly Analysis** - Monthly usage overview +### Usage history -## Dark / Light Theme Switching +Open **Tools → Netstat**: -The NoobWRT theme stores the user's theme preference in **`localStorage`** under the key `luci-theme` (`'dark'` or `'light'`). When dark mode is active the theme also adds the class `dark-theme` to `document.body`. +| Tab | What it shows | +|---|---| +| **Network Usage** | Daily or monthly download/upload for the tracked interface, with totals | +| **Settings** | Traffic backend, default view, interface, vnStat database info and reset | +| **About** | Project links, licence and support | -`tabs.htm` (rendered on every Netstat tab) reads this value at page load: + + + + + +
Network Usage tab in light modeSettings tab
-```js -var isDark = localStorage.getItem('luci-theme') === 'dark' - || document.body.classList.contains('dark-theme'); +## Configuration + +Settings live in `/etc/config/netstats` and can be changed from the **Settings** tab: + +| Option | Default | Description | +|---|---|---| +| `prefer` | `''` | Interface to track. Empty means auto (the default-route device). Accepts a device (`eth1`) or a logical interface (`wan`). | +| `backend` | `normal` | Downloaded/uploaded totals on the dashboard: `normal` counts since boot; `vnstat` shows this month from vnStat. Live rates always come from `/proc/net/dev`. | +| `mode` | `daily` | Default view on the Network Usage tab: `daily` or `monthly`. | +| `vnstat_autoadd` | `1` | Set to `0` to stop the app from adding interfaces to vnStat. | + +```bash +uci set netstats.@config[0].backend='vnstat' +uci commit netstats ``` -If dark mode is detected, a ` - - - - -
- -
-
+<%# Standard LuCI tab markup, so every theme styles it like its own tabs. %> +
    + <% for _, tab in ipairs(tabs_def) do %> +
  • "> + <%= tab.label %> +
  • + <% end %> +
diff --git a/luci-app-netstat/files/usr/lib/lua/luci/view/netstat/usage.htm b/luci-app-netstat/files/usr/lib/lua/luci/view/netstat/usage.htm index 36c09b41d..94652bb03 100644 --- a/luci-app-netstat/files/usr/lib/lua/luci/view/netstat/usage.htm +++ b/luci-app-netstat/files/usr/lib/lua/luci/view/netstat/usage.htm @@ -1,214 +1,116 @@ <% -local sys = require "luci.sys" -local http = require "luci.http" -local uci = require "luci.model.uci".cursor() +local http = require "luci.http" +local uci = require "luci.model.uci".cursor() +local jsonc = require "luci.jsonc" +local nsu = require "luci.netstat" -local function to_bytes(value, unit) - local m = { KiB=1024, MiB=1048576, GiB=1073741824, TiB=1099511627776 } - return tonumber(value) * (m[unit] or 1) -end +local have_vnstat = nsu.vnstat_installed() +local ifaces, db = nsu.db_interfaces() +local saved_mode = uci:get("netstats", "@config[0]", "mode") or "daily" -local function format_date(date_str) - local months = {"Jan","Feb","Mar","Apr","May","Jun", - "Jul","Aug","Sep","Oct","Nov","Dec"} - local mo, d, y = date_str:match("(%d%d)/(%d%d)/(%d%d)") - if mo and d and y then - return string.format("%02d %s %02d", tonumber(d), months[tonumber(mo)], tonumber(y)) - end - return date_str -end +-- Requested interface, then the Settings choice / default-route device, as +-- long as vnStat has a database entry for it; else the first one it has. +local cur_iface = nsu.resolve_device(http.formvalue("iface"), db) + or nsu.wan_device(db) + or ifaces[1] or "" -local function today_label() return os.date("%d %b %y") end -local function curmonth_label() return os.date("%b '%y") end - -local function ensure_today(stats) - local lbl = today_label() - for _, s in ipairs(stats) do if s.date == lbl then return stats end end - table.insert(stats, { date=lbl, rx=0, tx=0 }) - return stats -end - -local function ensure_curmonth(stats) - local lbl = curmonth_label() - for _, s in ipairs(stats) do if s.month == lbl then return stats end end - table.insert(stats, { month=lbl, rx=0, tx=0 }) - return stats -end - -local function get_interfaces() - local ifaces = {} - local p = io.popen("ls /etc/vnstat/ 2>/dev/null | grep -v '.conf$' | grep -v '.old$'") - if p then - for iface in p:lines() do - iface = iface:match("^%s*(.-)%s*$") - if iface ~= "" then table.insert(ifaces, iface) end - end - p:close() - end - table.sort(ifaces) - return ifaces -end - -local function get_daily_stats(iface) - local stats = {} - local out = sys.exec("vnstat -i " .. iface .. " --days 2>/dev/null") or "" - for date, rx_v, rx_u, tx_v, tx_u in - out:gmatch("(%d%d/%d%d/%d%d)%s+([%d%.]+)%s+([KMGT]iB)%s+|%s+([%d%.]+)%s+([KMGT]iB)") do - table.insert(stats, { - date = format_date(date), - rx = to_bytes(rx_v, rx_u), - tx = to_bytes(tx_v, tx_u) - }) - end - return ensure_today(stats) -end - -local function get_monthly_stats(iface) - local stats = {} - local out = sys.exec("vnstat -i " .. iface .. " --months 2>/dev/null") or "" - for month, rx_v, rx_u, tx_v, tx_u in - out:gmatch("(%a+%s+'%d%d)%s+([%d%.]+)%s+([KMGT]iB)%s+|%s+([%d%.]+)%s+([KMGT]iB)") do - table.insert(stats, { - month = month, - rx = to_bytes(rx_v, rx_u), - tx = to_bytes(tx_v, tx_u) - }) - end - return ensure_curmonth(stats) -end - -local function fmt_bytes(v) - if v >= 1099511627776 then return string.format("%.2f TB", v/1099511627776) - elseif v >= 1073741824 then return string.format("%.2f GB", v/1073741824) - elseif v >= 1048576 then return string.format("%.2f MB", v/1048576) - elseif v >= 1024 then return string.format("%.2f KB", v/1024) - end - return tostring(v) .. " B" -end - -local ifaces = get_interfaces() -local saved_iface = uci:get("netstats", "@config[0]", "prefer") or "" -local saved_mode = uci:get("netstats", "@config[0]", "mode") or "daily" - -local function has_db(name) - for _, i in ipairs(ifaces) do if i == name then return true end end - return false -end - --- Accepts a vnStat netdev name (e.g. "wwan0") or a logical interface name --- (e.g. "wan") resolved to its l3_device. Returns nil if it has no database. -local function resolve_iface(name) - if not name or name == "" or not name:match("^[%w%-%._]+$") then return nil end - if has_db(name) then return name end - local l3 = sys.exec("ubus call network.interface." .. name .. - " status 2>/dev/null | jsonfilter -e '@.l3_device' 2>/dev/null") or "" - l3 = l3:match("^%s*(.-)%s*$") - if l3 ~= "" and has_db(l3) then return l3 end - return nil -end - --- Auto detect: the default-route device is the real uplink. -local function default_route_iface() - local out = sys.exec("ip route show default 2>/dev/null") or "" - for dev in out:gmatch("dev%s+(%S+)") do - if has_db(dev) then return dev end - end - return nil -end - -local cur_iface = resolve_iface(http.formvalue("iface")) - or resolve_iface(saved_iface) - or default_route_iface() - or ifaces[1] or "" -local view_mode = http.formvalue("mode") or saved_mode +local view_mode = http.formvalue("mode") or saved_mode if view_mode ~= "daily" and view_mode ~= "monthly" then view_mode = "daily" end -local stats, title = {}, "" -if cur_iface ~= "" then - if view_mode == "monthly" then - stats = get_monthly_stats(cur_iface) - title = translate("Monthly") - else - stats = get_daily_stats(cur_iface) - title = translate("Daily") - end -end +local title = view_mode == "monthly" and translate("Monthly") or translate("Daily") -local total_rx, total_tx = 0, 0 -for _, s in ipairs(stats) do - total_rx = total_rx + (s.rx or 0) - total_tx = total_tx + (s.tx or 0) +-- Raw vnStat JSON, parsed in the browser by netstat/vnstat-parse.js. +local raw = "" +if cur_iface ~= "" then + raw = nsu.vnstat_json_raw(view_mode == "monthly" and "m" or "d", + view_mode == "monthly" and 12 or 30, cur_iface) or "" end +local raw_js = (jsonc.stringify(raw) or '""'):gsub("
-

+

<%:Usage is displayed using the Interface and View Mode selected in the Settings tab.%>

-<% if cur_iface ~= "" and #stats > 0 then %> +<% if have_vnstat and cur_iface ~= "" then %> + + + + -<% elseif #ifaces == 0 then %> +<% elseif not have_vnstat then %>
📡 - <%:No vnStat interfaces were found. Make sure vnStat has been initialized.%> + <%:vnStat is not installed. Install the vnstat2 package to record traffic history.%>
<% else %>
- 📊 - <%:No data available for%> <%=cur_iface%> + 📡 + <%:vnStat is not tracking any interface yet.%>
- <%:vnStat may still be collecting its first data. Try again in a few minutes.%> + <%:Interfaces are added automatically when they come up. You can also run "/usr/libexec/netstat-vnstat-track" on the router.%>
<% end %> diff --git a/luci-app-netstat/files/usr/lib/lua/luci/view/vnstat.htm b/luci-app-netstat/files/usr/lib/lua/luci/view/vnstat.htm deleted file mode 100644 index 77b126c18..000000000 --- a/luci-app-netstat/files/usr/lib/lua/luci/view/vnstat.htm +++ /dev/null @@ -1,250 +0,0 @@ -<%+header%> - -<% -local sys = require "luci.sys" -local http = require "luci.http" -local uci = require "luci.model.uci".cursor() - -local function to_bytes(value, unit) - local multipliers = { - ["KiB"] = 1024, - ["MiB"] = 1024^2, - ["GiB"] = 1024^3, - ["TiB"] = 1024^4 - } - return tonumber(value) * (multipliers[unit] or 1) -end - -local function format_date(date_str) - local month_names = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"} - local m, d, y = date_str:match("(%d%d)/(%d%d)/(%d%d)") - if m and d and y then - return string.format("%02d %s %02d", tonumber(d), month_names[tonumber(m)], tonumber(y)) - end - return date_str -end - -local function get_interfaces() - local ifaces = {} - local p = io.popen("ls /etc/vnstat/ 2>/dev/null | grep -v '.conf$' | grep -v '.old$'") - if p then - for iface in p:lines() do - iface = iface:match("^%s*(.-)%s*$") - if iface ~= "" then table.insert(ifaces, iface) end - end - p:close() - end - table.sort(ifaces) - return ifaces -end - -local function get_daily_stats(iface) - local stats = {} - local output = sys.exec("vnstat -i " .. iface .. " --days 2>/dev/null") or "" - for date, rx_val, rx_unit, tx_val, tx_unit in - output:gmatch("(%d%d/%d%d/%d%d)%s+([%d%.]+)%s+([KMGT]iB)%s+|%s+([%d%.]+)%s+([KMGT]iB)") do - table.insert(stats, { - date = format_date(date), - rx = to_bytes(rx_val, rx_unit), - tx = to_bytes(tx_val, tx_unit) - }) - end - return stats -end - -local function get_monthly_stats(iface) - local stats = {} - local output = sys.exec("vnstat -i " .. iface .. " --months 2>/dev/null") or "" - for month, rx_val, rx_unit, tx_val, tx_unit in - output:gmatch("(%a+%s+'%d%d)%s+([%d%.]+)%s+([KMGT]iB)%s+|%s+([%d%.]+)%s+([KMGT]iB)") do - table.insert(stats, { - month = month, - rx = to_bytes(rx_val, rx_unit), - tx = to_bytes(tx_val, tx_unit) - }) - end - return stats -end - -local ifaces = get_interfaces() -local current_iface = http.formvalue("iface") or "wwan0_1" -local view_mode = http.formvalue("mode") or "daily" -local stats, title = {}, "" - -if current_iface ~= "" then - if view_mode == "daily" then - stats = get_daily_stats(current_iface) - title = "<%:Daily%>" - else - stats = get_monthly_stats(current_iface) - title = "<%:Monthly%>" - end -end -%> - -
-

<%:Network Usage%>

- -
- -
-
- -
- -
-
- -
- -
- -
-
-
-
- - <% if current_iface ~= "" and #stats > 0 then %> -
-

<%=title%> (<%=current_iface%>)

-
- -
-
- - - - - <% else %> -
- <%:No data available for %> <%=current_iface%> -
- <% end %> -
- -<%+footer%> diff --git a/luci-app-netstat/files/usr/libexec/netstat-vnstat-track b/luci-app-netstat/files/usr/libexec/netstat-vnstat-track new file mode 100755 index 000000000..8df6043fc --- /dev/null +++ b/luci-app-netstat/files/usr/libexec/netstat-vnstat-track @@ -0,0 +1,58 @@ +#!/bin/sh +# Make vnStat track this router's network devices without any hard-coded +# names: every netifd interface that is up (WAN, LAN, modem, VPN, ...) gets +# its L3 device added to the "interface" list in /etc/config/vnstat, which +# the vnstat2 init script feeds to "vnstat --add". +# +# netstat-vnstat-track add the devices of all interfaces that are up +# netstat-vnstat-track ... add the given devices +# +# Set "option vnstat_autoadd '0'" in /etc/config/netstats to turn this off. + +[ -x /usr/bin/vnstat ] || exit 0 +[ "$(uci -q get netstats.@config[0].vnstat_autoadd)" = "0" ] && exit 0 + +valid_name() { + case "$1" in + ""|lo|*[!A-Za-z0-9._-]*) return 1 ;; + esac + return 0 +} + +discover() { + ubus call network.interface dump 2>/dev/null | \ + jsonfilter -e '@.interface[@.up=true].l3_device' 2>/dev/null +} + +[ $# -gt 0 ] && devices="$*" || devices="$(discover)" +[ -n "$devices" ] || exit 0 + +lock /var/lock/netstat-vnstat-track + +# vnstat2 ships "config vnstat" in /etc/config/vnstat; recreate it if missing. +touch /etc/config/vnstat +uci -q get vnstat.@vnstat[0] >/dev/null || uci -q add vnstat vnstat >/dev/null + +tracked=" $(uci -q get vnstat.@vnstat[0].interface) " +changed=0 +for dev in $devices; do + valid_name "$dev" || continue + case "$tracked" in *" $dev "*) continue ;; esac + uci -q add_list vnstat.@vnstat[0].interface="$dev" + tracked="$tracked$dev " + changed=1 +done + +if [ "$changed" = 1 ]; then + uci -q commit vnstat + # reload runs "vnstat --add" for the list and sends SIGHUP so vnstatd + # picks the new entries up straight away. + if /etc/init.d/vnstat running 2>/dev/null; then + /etc/init.d/vnstat reload >/dev/null 2>&1 + elif /etc/init.d/vnstat enabled 2>/dev/null; then + /etc/init.d/vnstat start >/dev/null 2>&1 + fi +fi + +lock -u /var/lock/netstat-vnstat-track +exit 0 diff --git a/luci-app-netstat/files/www/luci-static/resources/netstat/netstat-usage.css b/luci-app-netstat/files/www/luci-static/resources/netstat/netstat-usage.css index 5f98fd94e..35f27f07e 100644 --- a/luci-app-netstat/files/www/luci-static/resources/netstat/netstat-usage.css +++ b/luci-app-netstat/files/www/luci-static/resources/netstat/netstat-usage.css @@ -1,20 +1,24 @@ -/* netstat-usage.css */ +/* netstat-usage.css + * + * Layout only. Backgrounds, borders and text colours all come from the + * active LuCI theme (the pages sit inside its standard cbi-section and use + * its cbi-tabmenu tabs), so they follow the theme switcher, light or dark. + */ .netstat-dashboard { - border: 1px solid #dfe3eb; - border-radius: 10px; - background: #fff; - padding: 16px 18px; + --ns-muted: 0.7; +} + +.netstat-intro { + margin: 0 0 12px; + font-size: 13px; + opacity: var(--ns-muted); } .netstat-summary { display: flex; - gap: 12px; + gap: 16px; flex-wrap: wrap; margin-bottom: 16px; - padding: 10px 14px; - background: #f7f9fc; - border: 1px solid #dfe3eb; - border-radius: 8px; font-size: 13px; } @@ -23,7 +27,6 @@ align-items: center; gap: 6px; font-weight: 600; - color: #374151; } .netstat-summary-dot { @@ -37,7 +40,7 @@ .netstat-summary-dot.tx { background: #d97706; } .netstat-summary-label { - color: #6b7280; + opacity: var(--ns-muted); font-weight: 400; margin-right: 2px; } @@ -46,48 +49,36 @@ margin-left: auto; } -.netstat-chart-wrap { - border: 1px solid #dfe4ea; - border-radius: 8px; - padding: 14px; - background: #fff; - overflow: hidden; -} - .netstat-chart-header { display: flex; - justify-content: space-between; align-items: center; + gap: 8px; margin-bottom: 10px; flex-wrap: wrap; - gap: 6px; } .netstat-chart-title { - margin: 0; font-size: 15px; font-weight: 600; - color: #1f2937; } .netstat-chart-badge { - font-size: 11px; - font-weight: 600; - padding: 2px 8px; - border-radius: 999px; - background: #e0f2fe; - color: #0369a1; + font-size: 12px; + opacity: var(--ns-muted); } -.netstat-chart-canvas { +.netstat-chart-badge::before { content: "("; } +.netstat-chart-badge::after { content: ")"; } + +.netstat-chart-box { + position: relative; width: 100%; - display: block; + min-width: 0; } .netstat-empty { padding: 32px; text-align: center; - color: #6b7280; font-size: 14px; } @@ -100,11 +91,5 @@ .netstat-hint { margin-top: 6px; font-size: 12px; - color: #9ca3af; -} - -@media (max-width: 640px) { - .netstat-dashboard { padding: 12px; } - .netstat-summary { gap: 8px; } - .netstat-chart-wrap { padding: 10px; } + opacity: var(--ns-muted); } diff --git a/luci-app-netstat/files/www/luci-static/resources/netstat/vnstat-parse.js b/luci-app-netstat/files/www/luci-static/resources/netstat/vnstat-parse.js new file mode 100644 index 000000000..761435d5e --- /dev/null +++ b/luci-app-netstat/files/www/luci-static/resources/netstat/vnstat-parse.js @@ -0,0 +1,96 @@ +/* + * Turns `vnstat --json d|m` output into chart series for the Network Usage + * tab. Handles vnStat 2 (jsonversion "2": bytes, keys "day"/"month") and the + * older jsonversion "1" layout (KiB, keys "days"/"months"). + */ +(function (root) { + 'use strict'; + + var MONTHS = ['Jan','Feb','Mar','Apr','May','Jun', + 'Jul','Aug','Sep','Oct','Nov','Dec']; + + function pad2(n) { return (n < 10 ? '0' : '') + n; } + + function dayLabel(y, m, d) { + return pad2(d) + ' ' + MONTHS[m - 1] + ' ' + pad2(y % 100); + } + + function monthLabel(y, m) { + return MONTHS[m - 1] + " '" + pad2(y % 100); + } + + function findInterface(data, name) { + var list = (data && Array.isArray(data.interfaces)) ? data.interfaces : []; + for (var i = 0; i < list.length; i++) + if (list[i] && list[i].name === name) return list[i]; + return list.length === 1 ? list[0] : null; + } + + /* + * data: parsed vnStat JSON (or the raw string) + * iface: interface name to pick from data.interfaces + * mode: 'daily' | 'monthly' + * now: Date used to add an empty entry for today / this month + * + * Returns { labels, rx, tx, totalRx, totalTx, entries } where entries is + * the number of rows vnStat actually had (0 while it is still collecting). + */ + function series(data, iface, mode, now) { + var out = { labels: [], rx: [], tx: [], totalRx: 0, totalTx: 0, entries: 0 }; + + if (typeof data === 'string') { + try { data = JSON.parse(data); } catch (e) { return out; } + } + + var ifc = findInterface(data, iface); + if (!ifc || !ifc.traffic) return out; + + var legacy = String(data.jsonversion) === '1'; + var scale = legacy ? 1024 : 1; + var monthly = mode === 'monthly'; + var key = monthly ? (legacy ? 'months' : 'month') : (legacy ? 'days' : 'day'); + var rows = Array.isArray(ifc.traffic[key]) ? ifc.traffic[key].slice() : []; + + rows = rows.filter(function (r) { + return r && r.date && r.date.year && r.date.month && (monthly || r.date.day); + }); + + function sortKey(r) { + return r.date.year * 10000 + r.date.month * 100 + (monthly ? 0 : r.date.day); + } + rows.sort(function (a, b) { return sortKey(a) - sortKey(b); }); + + out.entries = rows.length; + if (!rows.length) return out; + + rows.forEach(function (r) { + var rx = (Number(r.rx) || 0) * scale; + var tx = (Number(r.tx) || 0) * scale; + out.labels.push(monthly ? monthLabel(r.date.year, r.date.month) + : dayLabel(r.date.year, r.date.month, r.date.day)); + out.rx.push(rx); + out.tx.push(tx); + out.totalRx += rx; + out.totalTx += tx; + }); + + // Always show the current period, even before any traffic was saved. + now = now || new Date(); + var cur = monthly ? monthLabel(now.getFullYear(), now.getMonth() + 1) + : dayLabel(now.getFullYear(), now.getMonth() + 1, now.getDate()); + if (out.labels.indexOf(cur) < 0) { + out.labels.push(cur); + out.rx.push(0); + out.tx.push(0); + } + + return out; + } + + var api = { series: series, findInterface: findInterface }; + + if (typeof module === 'object' && module.exports) + module.exports = api; + else + root.netstatVnstat = api; +})(typeof window !== 'undefined' ? window : this); diff --git a/luci-app-netstat/files/www/luci-static/resources/view/status/include/08_stats.js b/luci-app-netstat/files/www/luci-static/resources/view/status/include/08_stats.js index 43ee59f81..92eeab170 100644 --- a/luci-app-netstat/files/www/luci-static/resources/view/status/include/08_stats.js +++ b/luci-app-netstat/files/www/luci-static/resources/view/status/include/08_stats.js @@ -77,17 +77,50 @@ function parseNetdev(raw) { return stats; } +// The backend resolves the tracked device (Settings choice or default-route +// device). Without one, fall back to the busiest non-loopback interface +// rather than guessing names. function getBestWAN(stats, preferred) { for (const i of preferred) if (stats[i]) return i; - const dynamic = Object.keys(stats).find(i => - /^(wwan|usb|ppp|lte|qmi|modem)/.test(i) && i.includes('_')); - if (dynamic) return dynamic; + let best = '', bestBytes = -1; + for (const k of Object.keys(stats)) { + if (k === 'lo') continue; + const bytes = (parseInt(stats[k].rx) || 0) + (parseInt(stats[k].tx) || 0); + if (bytes > bestBytes) { best = k; bestBytes = bytes; } + } + return best; +} - for (const i of ['pppoe-wan','lte0','usb0','wan','eth1','tun0','wg0']) - if (stats[i]) return i; +// Totals for the downloaded/uploaded boxes: this month from vnStat when the +// vnStat backend is selected, otherwise the since-boot kernel counters. +function getTotals(data, curr) { + const v = data.vnstat_total; + if (v && typeof v === 'object') + return { rx: parseInt(v.rx) || 0, tx: parseInt(v.tx) || 0 }; + return curr; +} - return Object.keys(stats).find(k => k !== 'lo') || ''; +const STATS_URL = L.url('admin/tools/get_netdev_stats'); + +// Normalise one backend response into the shape render()/updateContainer() use. +function toData(r, ip) { + return { + stats: (r && r.stats) || {}, + ip: ip, + status: (r && r.status) || 'Disconnected', + uptime: (r && r.uptime) || 0, + cpu_pct: (r && r.cpu_pct) || 0, + cpu_temp: (r && r.cpu_temp) != null ? r.cpu_temp : null, + mem_pct: (r && r.mem_pct) || 0, + mem_used: (r && r.mem_used) || 0, + mem_total: (r && r.mem_total) || 0, + disk_pct: (r && r.disk_pct) || 0, + disk_used: (r && r.disk_used) || 0, + disk_total: (r && r.disk_total) || 0, + preferred: (r && r.prefer_iface) ? [r.prefer_iface] : [], + vnstat_total: (r && r.vnstat_total) || null + }; } function formatRate(bps) { @@ -381,8 +414,9 @@ function updateContainer(container, data, dt) { const rxRate = formatRate(rxSpeed * 8); const txRate = formatRate(txSpeed * 8); - const totalRx = formatBytes(curr.rx).split(' '); - const totalTx = formatBytes(curr.tx).split(' '); + const sums = getTotals(data, curr); + const totalRx = formatBytes(sums.rx).split(' '); + const totalTx = formatBytes(sums.tx).split(' '); const peakRxFmt = formatRate(peakRx * 8); const peakTxFmt = formatRate(peakTx * 8); @@ -457,18 +491,34 @@ function updateContainer(container, data, dt) { return true; } -// ─── Cached public IP (refreshed every 60 s, not on every 2 s poll) ────────── -let _cachedIp = 'N/A'; +// ─── Cached public IP (refreshed every 5 min, not on every 2 s poll) ───────── +// The public IP (as seen through any proxy such as Passwall) is shown once +// known; the tracked device's local address is only a fallback until then, +// so the 2 s fast polls must never overwrite it. +let _publicIp = null; +let _localIp = 'N/A'; let _lastIpFetch = 0; const IP_REFRESH_MS = 300000; +const IP_RETRY_MS = 30000; + +function shownIp() { + return _publicIp || _localIp; +} + +function noteLocalIp(r) { + if (r && r.ip && r.ip !== 'N/A') _localIp = r.ip; +} function maybeRefreshIp() { const now = Date.now(); - if (now - _lastIpFetch < IP_REFRESH_MS) return; + if (now - _lastIpFetch < (_publicIp ? IP_REFRESH_MS : IP_RETRY_MS)) return; _lastIpFetch = now; // Full fetch (no ?fast=1) to get public IP; update cache on success - fetchWithTimeout('/cgi-bin/luci/admin/tools/get_netdev_stats', 8000) - .then(r => { if (r && r.ip && r.ip !== 'N/A') _cachedIp = r.ip; }) + fetchWithTimeout(STATS_URL, 8000) + .then(r => { + if (r && r.ip_source === 'public' && r.ip && r.ip !== 'N/A') + _publicIp = r.ip; + }) .catch(() => {}); } @@ -478,29 +528,13 @@ return baseclass.extend({ load() { // Use ?fast=1 to skip slow public-IP APIs — response is near-instant - return fetchWithTimeout('/cgi-bin/luci/admin/tools/get_netdev_stats?fast=1', 5000) + return fetchWithTimeout(STATS_URL + '?fast=1', 5000) .then(r => { - // Seed the cached IP from the local WAN address on first load - if (r && r.ip && r.ip !== 'N/A') _cachedIp = r.ip; - return { - stats: (r && r.stats) || {}, - ip: _cachedIp, - status: (r && r.status) || 'Disconnected', - uptime: (r && r.uptime) || 0, - cpu_pct: (r && r.cpu_pct) || 0, - cpu_temp: (r && r.cpu_temp) != null ? r.cpu_temp : null, - mem_pct: (r && r.mem_pct) || 0, - mem_used: (r && r.mem_used) || 0, - mem_total: (r && r.mem_total) || 0, - disk_pct: (r && r.disk_pct) || 0, - disk_used: (r && r.disk_used) || 0, - disk_total: (r && r.disk_total) || 0, - preferred: (r && r.prefer_iface) ? [r.prefer_iface] : [] - }; + // Show the local WAN address until the public IP arrives + noteLocalIp(r); + return toData(r, shownIp()); }) - .catch(() => ({ stats: {}, ip: 'N/A', status: 'Disconnected', - uptime: 0, cpu_pct: 0, cpu_temp: null, mem_pct: 0, mem_used: 0, mem_total: 0, - disk_pct: 0, disk_used: 0, disk_total: 0, preferred: [] })); + .catch(() => toData(null, 'N/A')); }, render(data) { @@ -538,8 +572,9 @@ return baseclass.extend({ const rxRate = formatRate(rxSpeed * 8); const txRate = formatRate(txSpeed * 8); - const totalRx = formatBytes(curr.rx).split(' '); - const totalTx = formatBytes(curr.tx).split(' '); + const sums = getTotals(data, curr); + const totalRx = formatBytes(sums.rx).split(' '); + const totalTx = formatBytes(sums.tx).split(' '); // Build download box manually to inject trend badge const dlBox = createRateBox(_('download'), rxRate.number, rxRate.unit, @@ -573,31 +608,17 @@ return baseclass.extend({ L.Poll.add(() => { // Periodically refresh the public IP in the background (non-blocking) maybeRefreshIp(); - return fetchWithTimeout('/cgi-bin/luci/admin/tools/get_netdev_stats?fast=1', 5000) + return fetchWithTimeout(STATS_URL + '?fast=1', 5000) .then(r => { const now2 = Date.now(); const dt2 = Math.max(0.1, (now2 - last_time) / 1000); last_time = now2; - // Update cached IP from local WAN if we got a valid one - if (r && r.ip && r.ip !== 'N/A') _cachedIp = r.ip; + // Keep the local address current as a fallback only + noteLocalIp(r); if (_container && _container.isConnected) { - updateContainer(_container, { - stats: (r && r.stats) || {}, - ip: _cachedIp, - status: (r && r.status) || 'Disconnected', - uptime: (r && r.uptime) || 0, - cpu_pct: (r && r.cpu_pct) || 0, - cpu_temp: (r && r.cpu_temp) != null ? r.cpu_temp : null, - mem_pct: (r && r.mem_pct) || 0, - mem_used: (r && r.mem_used) || 0, - mem_total: (r && r.mem_total) || 0, - disk_pct: (r && r.disk_pct) || 0, - disk_used: (r && r.disk_used) || 0, - disk_total: (r && r.disk_total) || 0, - preferred: (r && r.prefer_iface) ? [r.prefer_iface] : [] - }, dt2); + updateContainer(_container, toData(r, shownIp()), dt2); } }) .catch(() => {}); diff --git a/luci-app-netstat/images/settings.png b/luci-app-netstat/images/settings.png new file mode 100644 index 000000000..395f71dcf Binary files /dev/null and b/luci-app-netstat/images/settings.png differ diff --git a/luci-app-netstat/images/status-widget.png b/luci-app-netstat/images/status-widget.png new file mode 100644 index 000000000..92ae8ff6c Binary files /dev/null and b/luci-app-netstat/images/status-widget.png differ diff --git a/luci-app-netstat/images/usage-dark.png b/luci-app-netstat/images/usage-dark.png new file mode 100644 index 000000000..c04e46b39 Binary files /dev/null and b/luci-app-netstat/images/usage-dark.png differ diff --git a/luci-app-netstat/images/usage-light.png b/luci-app-netstat/images/usage-light.png new file mode 100644 index 000000000..a68a30a43 Binary files /dev/null and b/luci-app-netstat/images/usage-light.png differ diff --git a/luci-app-passwall/Makefile b/luci-app-passwall/Makefile index 7d4914c03..2be356d96 100644 --- a/luci-app-passwall/Makefile +++ b/luci-app-passwall/Makefile @@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-passwall PKG_VERSION:=26.10.29 -PKG_RELEASE:=356 +PKG_RELEASE:=357 PKG_PO_VERSION:=$(PKG_VERSION) PKG_CONFIG_DEPENDS:= \ diff --git a/luci-app-passwall/luasrc/view/passwall/app_update/app_version.htm b/luci-app-passwall/luasrc/view/passwall/app_update/app_version.htm index 812fe3aaa..92e471d9f 100644 --- a/luci-app-passwall/luasrc/view/passwall/app_update/app_version.htm +++ b/luci-app-passwall/luasrc/view/passwall/app_update/app_version.htm @@ -309,11 +309,31 @@ local com = self.com //]]> + +
- +
- 【 <%=api.get_version()%> 】
- 【 <%=translate("Null")%> 】