mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-02 21:43:56 +08:00
🗽 Sync 2026-10-26 14:45:36
This commit is contained in:
1 parent
d9474ffda8
commit
ac10e08480
60 files changed
+4186
-1605
No files matched your search
+49
-11
@@ -1,19 +1,20 @@
|
|||||||
include $(TOPDIR)/rules.mk
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
PKG_NAME:=clashoo
|
PKG_NAME:=clashoo
|
||||||
PKG_SHORT_SHA:=6780eee
|
PKG_UPSTREAM_VERSION:=1.19.33
|
||||||
PKG_COMMIT_DATE:=2026.10.25
|
PKG_UPSTREAM_TAG:=v$(PKG_UPSTREAM_VERSION)
|
||||||
|
PKG_SHORT_SHA:=3f27ef3
|
||||||
|
|
||||||
PKG_VERSION:=$(PKG_COMMIT_DATE)~$(PKG_SHORT_SHA)
|
PKG_VERSION:=2026.10.25.$(PKG_UPSTREAM_VERSION)
|
||||||
PKG_RELEASE:=18
|
PKG_RELEASE:=19
|
||||||
|
|
||||||
PKG_SOURCE:=$(PKG_NAME)-alpha-$(PKG_SHORT_SHA).tar.gz
|
PKG_SOURCE:=$(PKG_NAME)-stable-$(PKG_UPSTREAM_TAG)-$(PKG_SHORT_SHA).tar.gz
|
||||||
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-alpha-$(PKG_SHORT_SHA)
|
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-stable-$(PKG_UPSTREAM_TAG)-$(PKG_SHORT_SHA)
|
||||||
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-alpha-$(PKG_SHORT_SHA)
|
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_SOURCE_SUBDIR)
|
||||||
PKG_SOURCE_URL:=https://github.com/kenzok8/openwrt-clashoo/releases/download/mihomo-src
|
PKG_SOURCE_URL:=https://github.com/kenzok8/openwrt-clashoo/releases/download/mihomo-src
|
||||||
PKG_SOURCE_VERSION:=d831028ec8a3d336ea83295bea36c32fdc973ede
|
PKG_SOURCE_VERSION:=57468807be288af4250181a024e29c2971eb228c
|
||||||
PKG_HASH:=skip
|
PKG_HASH:=skip
|
||||||
PKG_BUILD_VERSION:=alpha-$(PKG_SHORT_SHA)
|
PKG_BUILD_VERSION:=$(PKG_UPSTREAM_TAG)
|
||||||
|
|
||||||
PKG_LICENSE:=GPL3.0+
|
PKG_LICENSE:=GPL3.0+
|
||||||
PKG_MAINTAINER:=kenzok8
|
PKG_MAINTAINER:=kenzok8
|
||||||
@@ -50,8 +51,11 @@ endef
|
|||||||
define Package/clashoo/install
|
define Package/clashoo/install
|
||||||
$(call GoPackage/Package/Install/Bin,$(1))
|
$(call GoPackage/Package/Install/Bin,$(1))
|
||||||
|
|
||||||
|
$(INSTALL_DIR) $(1)/usr/libexec/clashoo
|
||||||
$(INSTALL_DIR) $(1)/usr/bin
|
$(INSTALL_DIR) $(1)/usr/bin
|
||||||
ln -sf /usr/bin/mihomo $(1)/usr/bin/clash-meta
|
mv -f $(1)/usr/bin/mihomo $(1)/usr/libexec/clashoo/mihomo-stable
|
||||||
|
ln -sf /usr/libexec/clashoo/mihomo-stable $(1)/usr/bin/mihomo
|
||||||
|
ln -sf /usr/libexec/clashoo/mihomo-stable $(1)/usr/bin/clash-meta
|
||||||
|
|
||||||
$(INSTALL_DIR) $(1)/etc/init.d
|
$(INSTALL_DIR) $(1)/etc/init.d
|
||||||
$(INSTALL_DIR) $(1)/etc/config
|
$(INSTALL_DIR) $(1)/etc/config
|
||||||
@@ -82,10 +86,44 @@ define Package/clashoo/install
|
|||||||
$(RM) $(1)/usr/share/clashoo/core_down_complete
|
$(RM) $(1)/usr/share/clashoo/core_down_complete
|
||||||
endef
|
endef
|
||||||
|
|
||||||
|
define Package/clashoo/preinst
|
||||||
|
#!/bin/sh
|
||||||
|
if [ -z "$${IPKG_INSTROOT}" ]; then
|
||||||
|
save_core() {
|
||||||
|
src="$$1"
|
||||||
|
dst="$$2"
|
||||||
|
[ -f "$$src" ] && [ ! -L "$$src" ] || return 0
|
||||||
|
if [ -e "$$dst" ] || [ -L "$$dst" ]; then return 0; fi
|
||||||
|
mkdir -p "$$(dirname "$$dst")" || return 1
|
||||||
|
tmp="$$dst.tmp.$$$$"
|
||||||
|
cp "$$src" "$$tmp" && chmod 0755 "$$tmp" && cmp -s "$$src" "$$tmp" || {
|
||||||
|
rm -f "$$tmp"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if command -v sha256sum >/dev/null 2>&1; then
|
||||||
|
old_hash="$$(sha256sum "$$src" | awk '{print $$1}')"
|
||||||
|
new_hash="$$(sha256sum "$$tmp" | awk '{print $$1}')"
|
||||||
|
[ "$$old_hash" = "$$new_hash" ] || {
|
||||||
|
rm -f "$$tmp"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
mv -f "$$tmp" "$$dst"
|
||||||
|
}
|
||||||
|
save_core /usr/bin/mihomo /usr/share/clashoo/bin/mihomo-alpha || exit 1
|
||||||
|
save_core /usr/bin/mihomo-stable /usr/share/clashoo/bin/mihomo-stable || exit 1
|
||||||
|
if [ -x /usr/bin/clash-meta ] && [ ! -L /usr/bin/clash-meta ] && [ ! -e /usr/share/clashoo/bin/mihomo-stable ]; then
|
||||||
|
if ! /usr/bin/clash-meta -v 2>/dev/null | grep -Eiq '(alpha|beta|rc|pre|smart)'; then
|
||||||
|
save_core /usr/bin/clash-meta /usr/share/clashoo/bin/mihomo-stable || exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
exit 0
|
||||||
|
endef
|
||||||
|
|
||||||
define Package/clashoo/postinst
|
define Package/clashoo/postinst
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
if [ -z "$${IPKG_INSTROOT}" ]; then
|
if [ -z "$${IPKG_INSTROOT}" ]; then
|
||||||
[ -x /usr/bin/mihomo ] && [ ! -x /usr/bin/clash-meta ] && ln -sf /usr/bin/mihomo /usr/bin/clash-meta
|
|
||||||
rm -f /usr/share/clashoo/check_luci_version.sh /usr/share/clashoo/check_clash_meta_version.sh /usr/share/clashoo/check_mihomo_core_version.sh
|
rm -f /usr/share/clashoo/check_luci_version.sh /usr/share/clashoo/check_clash_meta_version.sh /usr/share/clashoo/check_mihomo_core_version.sh
|
||||||
rm -f /usr/share/clashoo/cuslist.sh /usr/share/clashoo/groups.sh /usr/share/clashoo/load_groups.sh /usr/share/clashoo/panel_diag.sh
|
rm -f /usr/share/clashoo/cuslist.sh /usr/share/clashoo/groups.sh /usr/share/clashoo/load_groups.sh /usr/share/clashoo/panel_diag.sh
|
||||||
rm -f /usr/share/clashoo/rmlist.sh /usr/share/clashoo/uplist.sh /usr/share/clashoo/rpc_restart_async.sh /usr/share/clashoo/rpc_update_china_ip_async.sh
|
rm -f /usr/share/clashoo/rmlist.sh /usr/share/clashoo/uplist.sh /usr/share/clashoo/rpc_restart_async.sh /usr/share/clashoo/rpc_update_china_ip_async.sh
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ config clashoo 'config'
|
|||||||
option dash_port '9090'
|
option dash_port '9090'
|
||||||
option dashboard_panel 'zashboard'
|
option dashboard_panel 'zashboard'
|
||||||
option level 'info'
|
option level 'info'
|
||||||
option core '3'
|
option core '2'
|
||||||
option allow_lan '1'
|
option allow_lan '1'
|
||||||
option dnsforwader '1'
|
option dnsforwader '1'
|
||||||
option subcri 'meta'
|
option subcri 'meta'
|
||||||
@@ -28,7 +28,7 @@ config clashoo 'config'
|
|||||||
option listen_port '1053'
|
option listen_port '1053'
|
||||||
option interf '0'
|
option interf '0'
|
||||||
option download_core 'amd64-compatible'
|
option download_core 'amd64-compatible'
|
||||||
option dcore '3'
|
option dcore '2'
|
||||||
option same_tag '1'
|
option same_tag '1'
|
||||||
option geoip_source '3'
|
option geoip_source '3'
|
||||||
option geoip_format 'mmdb'
|
option geoip_format 'mmdb'
|
||||||
|
|||||||
@@ -169,43 +169,54 @@ cleanup_stale_core_nft() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_core_candidates() {
|
_core_candidates() {
|
||||||
echo "/usr/bin/mihomo-stable /usr/bin/mihomo /usr/bin/clash-meta /etc/clashoo/clash"
|
echo "/usr/share/clashoo/bin/mihomo-stable /usr/libexec/clashoo/mihomo-stable /usr/share/clashoo/bin/mihomo-alpha /usr/bin/mihomo /usr/bin/clash-meta /etc/clashoo/clash"
|
||||||
}
|
}
|
||||||
|
|
||||||
_migrate_legacy_mihomo_stable() {
|
_migrate_legacy_mihomo_stable() {
|
||||||
local legacy=/usr/bin/clash-meta version
|
local legacy=/usr/bin/clash-meta target=/usr/share/clashoo/bin/mihomo-stable version staged
|
||||||
[ -x /usr/bin/mihomo-stable ] && return 0
|
[ -x "$target" ] && return 0
|
||||||
[ -x "$legacy" ] && [ ! -L "$legacy" ] || return 0
|
[ -x "$legacy" ] && [ ! -L "$legacy" ] || return 0
|
||||||
version=$("$legacy" -v 2>/dev/null | head -n 1)
|
version=$("$legacy" -v 2>/dev/null | head -n 1)
|
||||||
[ -n "$version" ] || return 0
|
[ -n "$version" ] || return 0
|
||||||
echo "$version" | grep -Eiq '(alpha|beta|rc|pre|smart)' && return 0
|
echo "$version" | grep -Eiq '(alpha|beta|rc|pre|smart)' && return 0
|
||||||
cp -f "$legacy" /usr/bin/mihomo-stable 2>/dev/null && chmod 0755 /usr/bin/mihomo-stable
|
mkdir -p "$(dirname "$target")" || return 1
|
||||||
|
staged="${target}.$$"
|
||||||
|
cp -f "$legacy" "$staged" 2>/dev/null && chmod 0755 "$staged" && mv -f "$staged" "$target"
|
||||||
}
|
}
|
||||||
|
|
||||||
_fallback_missing_mihomo_core() {
|
_mihomo_stable_binary() {
|
||||||
local selected fallback
|
local bundled=/usr/libexec/clashoo/mihomo-stable manual=/usr/share/clashoo/bin/mihomo-stable bundled_ver manual_ver
|
||||||
is_singbox_selected && return 0
|
# A successful custom download is an explicit override, even if its version is older.
|
||||||
selected="$(uci -q get clashoo.config.core 2>/dev/null)"
|
if [ -x "$manual" ] &&
|
||||||
case "$selected" in
|
[ "$(sed -n '1{s/^[[:space:]]*//;s/[[:space:]]*$//;p;}' /usr/share/clashoo/clash_meta_version 2>/dev/null)" = "custom-url" ]; then
|
||||||
2)
|
echo "$manual"
|
||||||
[ -x /usr/bin/mihomo-stable ] && return 0
|
return 0
|
||||||
[ -x /usr/bin/mihomo ] || return 0
|
fi
|
||||||
fallback=3
|
[ -x "$bundled" ] || { [ -x "$manual" ] && { echo "$manual"; return 0; }; return 1; }
|
||||||
;;
|
[ -x "$manual" ] || { echo "$bundled"; return; }
|
||||||
3)
|
bundled_ver="$("$bundled" -v 2>/dev/null | awk '{for (i = 1; i < NF; i++) if ($i == "Meta") {print $(i + 1); exit}}')"
|
||||||
[ -x /usr/bin/mihomo ] && return 0
|
manual_ver="$("$manual" -v 2>/dev/null | awk '{for (i = 1; i < NF; i++) if ($i == "Meta") {print $(i + 1); exit}}')"
|
||||||
[ -x /usr/bin/mihomo-stable ] || return 0
|
bundled_ver="${bundled_ver#v}"
|
||||||
fallback=2
|
manual_ver="${manual_ver#v}"
|
||||||
;;
|
printf '%s\n' "$bundled_ver" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$' || bundled_ver=""
|
||||||
*) return 0 ;;
|
printf '%s\n' "$manual_ver" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$' || manual_ver=""
|
||||||
esac
|
if [ -n "$bundled_ver" ] && [ -n "$manual_ver" ]; then
|
||||||
uci -q set clashoo.config.core="$fallback"
|
if awk -v manual="$manual_ver" -v bundled="$bundled_ver" 'BEGIN {
|
||||||
uci -q set clashoo.config.dcore="$fallback"
|
split(manual, m, "."); split(bundled, b, ".");
|
||||||
uci -q commit clashoo >/dev/null 2>&1
|
for (i = 1; i <= 3; i++) {
|
||||||
if [ "$fallback" = "3" ]; then
|
if (m[i] + 0 > b[i] + 0) exit 0;
|
||||||
log_msg "Stable core missing, falling back to Alpha" "稳定版内核缺失,自动回退到 Alpha"
|
if (m[i] + 0 < b[i] + 0) exit 1;
|
||||||
|
}
|
||||||
|
exit 0;
|
||||||
|
}'; then
|
||||||
|
echo "$manual"
|
||||||
|
else
|
||||||
|
echo "$bundled"
|
||||||
|
fi
|
||||||
|
elif [ -n "$manual_ver" ]; then
|
||||||
|
echo "$manual"
|
||||||
else
|
else
|
||||||
log_msg "Alpha core missing, falling back to stable" "Alpha 内核缺失,自动回退到稳定版"
|
echo "$bundled"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,11 +231,11 @@ _detect_selected_core() {
|
|||||||
return 1
|
return 1
|
||||||
;;
|
;;
|
||||||
2|mihomo)
|
2|mihomo)
|
||||||
[ -x /usr/bin/mihomo-stable ] && echo /usr/bin/mihomo-stable && return
|
_mihomo_stable_binary
|
||||||
return 1
|
return $?
|
||||||
;;
|
;;
|
||||||
3|mihomo-alpha)
|
3|mihomo-alpha)
|
||||||
[ -x /usr/bin/mihomo ] && echo /usr/bin/mihomo && return
|
[ -x /usr/share/clashoo/bin/mihomo-alpha ] && echo /usr/share/clashoo/bin/mihomo-alpha && return
|
||||||
return 1
|
return 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
@@ -233,7 +244,7 @@ _detect_selected_core() {
|
|||||||
|
|
||||||
core_supports_advanced_features() {
|
core_supports_advanced_features() {
|
||||||
case "$CLASH" in
|
case "$CLASH" in
|
||||||
*/mihomo|*/mihomo-stable|*/clash-meta|*/smart)
|
*/mihomo|*/mihomo-stable|*/mihomo-alpha|*/clash-meta|*/smart)
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
@@ -2126,12 +2137,12 @@ _start_validate_core() {
|
|||||||
echo "Clashoo" > "$REAL_LOG"
|
echo "Clashoo" > "$REAL_LOG"
|
||||||
return 1
|
return 1
|
||||||
elif [ "${core:-0}" -eq 2 ] && [ ! -x "$CLASH" ]; then
|
elif [ "${core:-0}" -eq 2 ] && [ ! -x "$CLASH" ]; then
|
||||||
log_msg "mihomo core not found, detected binary is unavailable: $CLASH" "找不到可用的 mihomo 内核: $CLASH"
|
log_msg "mihomo stable core not found, detected binary is unavailable: $CLASH" "找不到可用的 mihomo 稳定版内核: $CLASH"
|
||||||
sleep 1
|
sleep 1
|
||||||
echo "Clashoo" > "$REAL_LOG"
|
echo "Clashoo" > "$REAL_LOG"
|
||||||
return 1
|
return 1
|
||||||
elif [ "${core:-0}" -eq 3 ] && [ ! -x "$CLASH" ]; then
|
elif [ "${core:-0}" -eq 3 ] && [ ! -x "$CLASH" ]; then
|
||||||
log_msg "mihomo core not found, detected binary is unavailable: $CLASH" "找不到可用的 mihomo 内核: $CLASH"
|
log_msg "mihomo Alpha core not found, detected binary is unavailable: $CLASH" "找不到可用的 mihomo Alpha 内核,请在组件更新中安装: $CLASH"
|
||||||
sleep 1
|
sleep 1
|
||||||
echo "Clashoo" > "$REAL_LOG"
|
echo "Clashoo" > "$REAL_LOG"
|
||||||
return 1
|
return 1
|
||||||
@@ -2167,10 +2178,10 @@ _start_prepare_config() {
|
|||||||
|
|
||||||
if { egrep -q '^ {0,}rule-providers:' "$CONFIG_YAML" || egrep -q '^ {0,}script:' "$CONFIG_YAML"; } && ! core_supports_advanced_features; then
|
if { egrep -q '^ {0,}rule-providers:' "$CONFIG_YAML" || egrep -q '^ {0,}script:' "$CONFIG_YAML"; } && ! core_supports_advanced_features; then
|
||||||
[ "$_reload_candidate" = "1" ] && return 1
|
[ "$_reload_candidate" = "1" ] && return 1
|
||||||
if [ -x /usr/bin/mihomo ]; then
|
if [ -n "$(_mihomo_stable_binary)" ]; then
|
||||||
uci set clashoo.config.core="3" && uci commit clashoo
|
|
||||||
elif [ -x /usr/bin/mihomo-stable ]; then
|
|
||||||
uci set clashoo.config.core="2" && uci commit clashoo
|
uci set clashoo.config.core="2" && uci commit clashoo
|
||||||
|
elif [ -x /usr/share/clashoo/bin/mihomo-alpha ]; then
|
||||||
|
uci set clashoo.config.core="3" && uci commit clashoo
|
||||||
else
|
else
|
||||||
log_msg "Current config requires mihomo/clash-meta core" "当前配置需要 mihomo 或 clash-meta 内核"
|
log_msg "Current config requires mihomo/clash-meta core" "当前配置需要 mihomo 或 clash-meta 内核"
|
||||||
backend_remove_rules >/dev/null 2>&1
|
backend_remove_rules >/dev/null 2>&1
|
||||||
@@ -2407,7 +2418,6 @@ start_service(){
|
|||||||
|
|
||||||
sync_legacy_core_field
|
sync_legacy_core_field
|
||||||
_migrate_legacy_mihomo_stable
|
_migrate_legacy_mihomo_stable
|
||||||
_fallback_missing_mihomo_core
|
|
||||||
CLASH="$(_detect_selected_core)"
|
CLASH="$(_detect_selected_core)"
|
||||||
|
|
||||||
cleanup_legacy_startup_backups
|
cleanup_legacy_startup_backups
|
||||||
|
|||||||
@@ -1062,10 +1062,10 @@ if [ -n "$CUSTOM_CORE_URL" ]; then
|
|||||||
write_log "使用自定义内核下载链接"
|
write_log "使用自定义内核下载链接"
|
||||||
URL="$CUSTOM_CORE_URL"
|
URL="$CUSTOM_CORE_URL"
|
||||||
if [ "$CORETYPE" = "3" ]; then
|
if [ "$CORETYPE" = "3" ]; then
|
||||||
TARGET="/usr/bin/mihomo"
|
TARGET="/usr/share/clashoo/bin/mihomo-alpha"
|
||||||
VERSION_FILE="/usr/share/clashoo/mihomo_version"
|
VERSION_FILE="/usr/share/clashoo/mihomo_version"
|
||||||
else
|
else
|
||||||
TARGET="/usr/bin/mihomo-stable"
|
TARGET="/usr/share/clashoo/bin/mihomo-stable"
|
||||||
VERSION_FILE="/usr/share/clashoo/clash_meta_version"
|
VERSION_FILE="/usr/share/clashoo/clash_meta_version"
|
||||||
fi
|
fi
|
||||||
TAG="custom"
|
TAG="custom"
|
||||||
@@ -1175,7 +1175,7 @@ elif [ "$CORETYPE" = "2" ]; then
|
|||||||
[ -z "$TAG" ] && write_log "获取稳定版版本号失败" && exit 1
|
[ -z "$TAG" ] && write_log "获取稳定版版本号失败" && exit 1
|
||||||
ASSET=$(pick_mihomo_asset "MetaCubeX/mihomo" "$TAG" "$(map_mihomo_arch "$MODELTYPE")" "stable")
|
ASSET=$(pick_mihomo_asset "MetaCubeX/mihomo" "$TAG" "$(map_mihomo_arch "$MODELTYPE")" "stable")
|
||||||
URL="https://github.com/MetaCubeX/mihomo/releases/download/${TAG}/${ASSET}"
|
URL="https://github.com/MetaCubeX/mihomo/releases/download/${TAG}/${ASSET}"
|
||||||
TARGET="/usr/bin/mihomo-stable"
|
TARGET="/usr/share/clashoo/bin/mihomo-stable"
|
||||||
VERSION_FILE="/usr/share/clashoo/clash_meta_version"
|
VERSION_FILE="/usr/share/clashoo/clash_meta_version"
|
||||||
VERSION_VALUE="$TAG"
|
VERSION_VALUE="$TAG"
|
||||||
else
|
else
|
||||||
@@ -1189,7 +1189,7 @@ else
|
|||||||
[ -z "$ASSET" ] && ASSET=$(pick_mihomo_asset "MetaCubeX/mihomo" "$TAG" "$(map_mihomo_arch "$MODELTYPE")" "alpha")
|
[ -z "$ASSET" ] && ASSET=$(pick_mihomo_asset "MetaCubeX/mihomo" "$TAG" "$(map_mihomo_arch "$MODELTYPE")" "alpha")
|
||||||
[ -z "$ASSET" ] && ASSET=$(pick_mihomo_asset "MetaCubeX/mihomo" "$TAG" "$(map_mihomo_arch "$MODELTYPE")" "stable")
|
[ -z "$ASSET" ] && ASSET=$(pick_mihomo_asset "MetaCubeX/mihomo" "$TAG" "$(map_mihomo_arch "$MODELTYPE")" "stable")
|
||||||
URL="https://github.com/MetaCubeX/mihomo/releases/download/${TAG}/${ASSET}"
|
URL="https://github.com/MetaCubeX/mihomo/releases/download/${TAG}/${ASSET}"
|
||||||
TARGET="/usr/bin/mihomo"
|
TARGET="/usr/share/clashoo/bin/mihomo-alpha"
|
||||||
VERSION_FILE="/usr/share/clashoo/mihomo_version"
|
VERSION_FILE="/usr/share/clashoo/mihomo_version"
|
||||||
VERSION_VALUE="$(printf '%s\n' "$ASSET" | sed -n 's#^mihomo-.*-alpha-\([0-9a-fA-F]\{7,\}\)\.gz$#\1#p' | head -n 1)"
|
VERSION_VALUE="$(printf '%s\n' "$ASSET" | sed -n 's#^mihomo-.*-alpha-\([0-9a-fA-F]\{7,\}\)\.gz$#\1#p' | head -n 1)"
|
||||||
[ -z "$VERSION_VALUE" ] && VERSION_VALUE="$TAG"
|
[ -z "$VERSION_VALUE" ] && VERSION_VALUE="$TAG"
|
||||||
|
|||||||
+2
-2
@@ -6,9 +6,9 @@ include $(TOPDIR)/rules.mk
|
|||||||
|
|
||||||
PKG_NAME:=daed
|
PKG_NAME:=daed
|
||||||
PKG_VERSION:=2026.10.18
|
PKG_VERSION:=2026.10.18
|
||||||
PKG_RELEASE:=62
|
PKG_RELEASE:=63
|
||||||
|
|
||||||
PKG_SOURCE:=daed-src-2026.10.18-b7827fb4b582.tar.gz
|
PKG_SOURCE:=daed-src-2026.10.18-ac22e7a32e17.tar.gz
|
||||||
PKG_SOURCE_URL:=https://github.com/kenzok8/openwrt-daede/releases/download/daed-src
|
PKG_SOURCE_URL:=https://github.com/kenzok8/openwrt-daede/releases/download/daed-src
|
||||||
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
|
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
|
||||||
PKG_HASH:=skip
|
PKG_HASH:=skip
|
||||||
|
|||||||
+2
-2
@@ -9,8 +9,8 @@ include $(TOPDIR)/rules.mk
|
|||||||
PKG_ARCH_FASTNET:=$(ARCH)
|
PKG_ARCH_FASTNET:=$(ARCH)
|
||||||
|
|
||||||
PKG_NAME:=fastnet
|
PKG_NAME:=fastnet
|
||||||
PKG_VERSION:=0.7.7
|
PKG_VERSION:=0.7.8
|
||||||
PKG_RELEASE:=6
|
PKG_RELEASE:=7
|
||||||
|
|
||||||
PKG_SOURCE:=$(PKG_NAME)-binary-$(PKG_VERSION).tar.gz
|
PKG_SOURCE:=$(PKG_NAME)-binary-$(PKG_VERSION).tar.gz
|
||||||
PKG_SOURCE_URL:=https://github.com/istoreos/istoreos-app-hub/releases/download/fastnet-runtime-v$(PKG_VERSION)/
|
PKG_SOURCE_URL:=https://github.com/istoreos/istoreos-app-hub/releases/download/fastnet-runtime-v$(PKG_VERSION)/
|
||||||
|
|||||||
+2
-2
@@ -5,8 +5,8 @@
|
|||||||
include $(TOPDIR)/rules.mk
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
PKG_NAME:=gately
|
PKG_NAME:=gately
|
||||||
PKG_VERSION:=0.5.8
|
PKG_VERSION:=0.5.9
|
||||||
PKG_RELEASE:=11
|
PKG_RELEASE:=12
|
||||||
|
|
||||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||||
PKG_SOURCE_URL:=https://codeload.github.com/wangwanjie/gately/tar.gz/v$(PKG_VERSION)?
|
PKG_SOURCE_URL:=https://codeload.github.com/wangwanjie/gately/tar.gz/v$(PKG_VERSION)?
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ PKG_ARCH_ISTOREENHANCE:=$(ARCH)
|
|||||||
|
|
||||||
PKG_NAME:=istoreenhance
|
PKG_NAME:=istoreenhance
|
||||||
# use PKG_SOURCE_DATE instead of PKG_VERSION for compitable
|
# use PKG_SOURCE_DATE instead of PKG_VERSION for compitable
|
||||||
PKG_SOURCE_DATE:=0.8.4
|
PKG_SOURCE_DATE:=0.8.5
|
||||||
PKG_RELEASE:=13
|
PKG_RELEASE:=14
|
||||||
ARCH_HEXCODE:=
|
ARCH_HEXCODE:=
|
||||||
ifeq ($(ARCH),x86_64)
|
ifeq ($(ARCH),x86_64)
|
||||||
ARCH_HEXCODE=8664
|
ARCH_HEXCODE=8664
|
||||||
|
|||||||
+3
-3
@@ -1,8 +1,8 @@
|
|||||||
include $(TOPDIR)/rules.mk
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
PKG_NAME:=kai-agent
|
PKG_NAME:=kai-agent
|
||||||
PKG_VERSION:=0.0.26
|
PKG_VERSION:=0.0.27
|
||||||
PKG_RELEASE:=4
|
PKG_RELEASE:=5
|
||||||
PKG_SOURCE:=kai-agent-bundle-$(PKG_VERSION).tar.gz
|
PKG_SOURCE:=kai-agent-bundle-$(PKG_VERSION).tar.gz
|
||||||
PKG_SOURCE_URL:=https://github.com/istoreos/istoreos-app-hub/releases/download/kai-runtime-v$(PKG_VERSION)/
|
PKG_SOURCE_URL:=https://github.com/istoreos/istoreos-app-hub/releases/download/kai-runtime-v$(PKG_VERSION)/
|
||||||
PKG_HASH:=skip
|
PKG_HASH:=skip
|
||||||
@@ -17,7 +17,7 @@ define Package/$(PKG_NAME)
|
|||||||
SECTION:=net
|
SECTION:=net
|
||||||
CATEGORY:=System
|
CATEGORY:=System
|
||||||
TITLE:=KAI OpenCode Agent bundle
|
TITLE:=KAI OpenCode Agent bundle
|
||||||
URL:=https://github.com/linkease/linkease-skills
|
URL:=https://github.com/linkease/kai-skills
|
||||||
endef
|
endef
|
||||||
|
|
||||||
define Package/$(PKG_NAME)/description
|
define Package/$(PKG_NAME)/description
|
||||||
|
|||||||
+5
-4
@@ -10,8 +10,8 @@ include $(TOPDIR)/rules.mk
|
|||||||
PKG_ARCH_kai:=$(ARCH)
|
PKG_ARCH_kai:=$(ARCH)
|
||||||
|
|
||||||
PKG_NAME:=kai
|
PKG_NAME:=kai
|
||||||
PKG_VERSION:=0.0.25
|
PKG_VERSION:=0.0.27
|
||||||
PKG_RELEASE:=9
|
PKG_RELEASE:=10
|
||||||
PKG_SOURCE:=$(PKG_NAME)-binary-$(PKG_VERSION).tar.gz
|
PKG_SOURCE:=$(PKG_NAME)-binary-$(PKG_VERSION).tar.gz
|
||||||
PKG_SOURCE_URL:=https://github.com/istoreos/istoreos-app-hub/releases/download/kai-runtime-v$(PKG_VERSION)/
|
PKG_SOURCE_URL:=https://github.com/istoreos/istoreos-app-hub/releases/download/kai-runtime-v$(PKG_VERSION)/
|
||||||
PKG_HASH:=skip
|
PKG_HASH:=skip
|
||||||
@@ -28,11 +28,12 @@ define Package/$(PKG_NAME)
|
|||||||
SUBMENU:=
|
SUBMENU:=
|
||||||
TITLE:=KAI
|
TITLE:=KAI
|
||||||
DEPENDS:=+kai_session +kai-agent +luci-lib-linkeaseauth +linkease-app-entry
|
DEPENDS:=+kai_session +kai-agent +luci-lib-linkeaseauth +linkease-app-entry
|
||||||
URL:=https://github.com/Carseason/openwrt-packages
|
EXTRA_DEPENDS:=kai-agent (>=0.0.27~0)
|
||||||
|
URL:=https://github.com/linkease/istore-ai-helper
|
||||||
endef
|
endef
|
||||||
|
|
||||||
define Package/$(PKG_NAME)/description
|
define Package/$(PKG_NAME)/description
|
||||||
KAI is a dashboard & user guide
|
KAI provides the OpenCode gateway and embedded Web UI with packaged local skills.
|
||||||
endef
|
endef
|
||||||
|
|
||||||
define Package/$(PKG_NAME)/conffiles
|
define Package/$(PKG_NAME)/conffiles
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ include $(TOPDIR)/rules.mk
|
|||||||
PKG_ARCH_kai_session:=$(ARCH)
|
PKG_ARCH_kai_session:=$(ARCH)
|
||||||
|
|
||||||
PKG_NAME:=kai_session
|
PKG_NAME:=kai_session
|
||||||
PKG_VERSION:=0.0.25
|
PKG_VERSION:=0.0.27
|
||||||
PKG_RELEASE:=8
|
PKG_RELEASE:=9
|
||||||
PKG_SOURCE:=$(PKG_NAME)-binary-$(PKG_VERSION).tar.gz
|
PKG_SOURCE:=$(PKG_NAME)-binary-$(PKG_VERSION).tar.gz
|
||||||
PKG_SOURCE_URL:=https://github.com/istoreos/istoreos-app-hub/releases/download/kai-runtime-v$(PKG_VERSION)/
|
PKG_SOURCE_URL:=https://github.com/istoreos/istoreos-app-hub/releases/download/kai-runtime-v$(PKG_VERSION)/
|
||||||
PKG_HASH:=skip
|
PKG_HASH:=skip
|
||||||
@@ -31,11 +31,11 @@ define Package/$(PKG_NAME)
|
|||||||
SUBMENU:=
|
SUBMENU:=
|
||||||
TITLE:=KAI_SESSION
|
TITLE:=KAI_SESSION
|
||||||
DEPENDS:=+libstdcpp
|
DEPENDS:=+libstdcpp
|
||||||
URL:=https://github.com/Carseason/openwrt-packages
|
URL:=https://github.com/linkease/istore-ai-helper
|
||||||
endef
|
endef
|
||||||
|
|
||||||
define Package/$(PKG_NAME)/description
|
define Package/$(PKG_NAME)/description
|
||||||
KAI
|
The OpenCode engine and offline ripgrep runtime for KAI.
|
||||||
endef
|
endef
|
||||||
|
|
||||||
define Package/$(PKG_NAME)/conffiles
|
define Package/$(PKG_NAME)/conffiles
|
||||||
|
|||||||
+2
-2
@@ -5,8 +5,8 @@ include $(TOPDIR)/rules.mk
|
|||||||
PKG_ARCH_KS_CLI:=$(ARCH)
|
PKG_ARCH_KS_CLI:=$(ARCH)
|
||||||
|
|
||||||
PKG_NAME:=ks-cli
|
PKG_NAME:=ks-cli
|
||||||
PKG_VERSION:=0.8.4
|
PKG_VERSION:=0.8.5
|
||||||
PKG_RELEASE:=2
|
PKG_RELEASE:=3
|
||||||
|
|
||||||
PKG_SOURCE:=ks-cli-binary-$(PKG_VERSION).tar.gz
|
PKG_SOURCE:=ks-cli-binary-$(PKG_VERSION).tar.gz
|
||||||
PKG_SOURCE_URL:=https://github.com/kspeeder/docker_kspeeder/releases/download/v$(PKG_VERSION)/
|
PKG_SOURCE_URL:=https://github.com/kspeeder/docker_kspeeder/releases/download/v$(PKG_VERSION)/
|
||||||
|
|||||||
@@ -11,8 +11,10 @@ LUCI_DEPENDS:=+luci-base +luci-lib-jsonc +curl +bandix
|
|||||||
PKG_MAINTAINER:=timsaya
|
PKG_MAINTAINER:=timsaya
|
||||||
|
|
||||||
PKG_VERSION:=0.12.12
|
PKG_VERSION:=0.12.12
|
||||||
PKG_RELEASE:=12
|
PKG_RELEASE:=13
|
||||||
|
# Rebuild language packages when a package revision changes without a PO commit.
|
||||||
|
PKG_PO_VERSION=$(if $(DUMP),x,$(strip $(call findrev)))-r$(PKG_RELEASE)
|
||||||
|
|
||||||
include $(TOPDIR)/feeds/luci/luci.mk
|
include $(TOPDIR)/feeds/luci/luci.mk
|
||||||
|
|
||||||
# call BuildPackage - OpenWrt buildroot signature
|
# call BuildPackage - OpenWrt buildroot signature
|
||||||
@@ -0,0 +1,227 @@
|
|||||||
|
'use strict';
|
||||||
|
'require baseclass';
|
||||||
|
'require rpc';
|
||||||
|
'require ui';
|
||||||
|
'require poll';
|
||||||
|
'require request';
|
||||||
|
|
||||||
|
var callStatus = rpc.declare({ object: 'luci.bandix', method: 'getGeoipStatus', expect: {} });
|
||||||
|
var callManage = rpc.declare({ object: 'luci.bandix', method: 'manageGeoip', params: ['action', 'profile', 'enabled'], expect: {} });
|
||||||
|
|
||||||
|
function size(bytes) {
|
||||||
|
if (bytes == null) return '—';
|
||||||
|
if (bytes < 1024) return bytes + ' B';
|
||||||
|
if (bytes < 1048576) return (bytes / 1024).toFixed(1) + ' KiB';
|
||||||
|
return (bytes / 1048576).toFixed(1) + ' MiB';
|
||||||
|
}
|
||||||
|
function unwrap(reply) {
|
||||||
|
if (!reply || reply.status !== 'success' || !reply.data) throw new Error(reply && (reply.message || reply.error) || _('GeoIP service is unavailable. Enable connection monitoring and apply settings, or update Bandix.'));
|
||||||
|
return reply.data;
|
||||||
|
}
|
||||||
|
function localized(names) {
|
||||||
|
var language = (L.env.lang || document.documentElement.lang || 'en').replace(/_/g, '-');
|
||||||
|
var key = /^zh/i.test(language) ? 'zh-CN' : language.split('-')[0];
|
||||||
|
return names && (names[key] || names.en) || '';
|
||||||
|
}
|
||||||
|
function countryName(info) {
|
||||||
|
if (info.country_code && typeof Intl !== 'undefined' && Intl.DisplayNames) {
|
||||||
|
try { return new Intl.DisplayNames([(L.env.lang || 'en').replace(/_/g, '-')], { type: 'region' }).of(info.country_code); } catch (e) { /* use database names */ }
|
||||||
|
}
|
||||||
|
return localized(info.country_names) || info.country_code || '';
|
||||||
|
}
|
||||||
|
var scopes = {
|
||||||
|
local: _('Local network'), private: _('Private address'), loopback: _('Loopback'),
|
||||||
|
link_local: _('Link-local address'), shared: _('Shared address (CGNAT)'),
|
||||||
|
multicast: _('Multicast'), unspecified: _('Unspecified address'),
|
||||||
|
documentation: _('Documentation address'), reserved: _('Reserved address'), unknown: _('Unknown location')
|
||||||
|
};
|
||||||
|
var phases = {
|
||||||
|
preparing: _('Checking database version…'), checking_space: _('Checking storage…'),
|
||||||
|
downloading: _('Downloading database…'), verifying: _('Verifying database…'),
|
||||||
|
activating: _('Loading database…'), importing: _('Importing database…'),
|
||||||
|
completed: _('Database ready'), up_to_date: _('Database is up to date'),
|
||||||
|
cancelled: _('Download cancelled'), failed: _('Database operation failed')
|
||||||
|
};
|
||||||
|
|
||||||
|
return baseclass.extend({
|
||||||
|
describe: function (info) {
|
||||||
|
if (!info) return null;
|
||||||
|
if (info.scope !== 'public') return { summary: scopes[info.scope] || scopes.unknown, details: scopes[info.scope] || scopes.unknown };
|
||||||
|
var country = countryName(info), region = localized(info.region_names), city = localized(info.city_names);
|
||||||
|
var location = [country, region, city].filter(function (value, index, all) { return value && all.indexOf(value) === index; }).join(' · ');
|
||||||
|
return {
|
||||||
|
summary: [location, info.organization].filter(Boolean).join(' · ') || scopes.unknown,
|
||||||
|
details: [location, info.asn ? 'AS' + info.asn : '', info.organization].filter(Boolean).join('\n')
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function () {
|
||||||
|
if (this._root) return this._root;
|
||||||
|
var root, current = null, pending = false, refreshing = false, dirtyProfile = false;
|
||||||
|
var badge = E('strong', {}, _('Loading…'));
|
||||||
|
var enabled = E('input', { type: 'checkbox', disabled: true });
|
||||||
|
var profile = E('select', { 'class': 'cbi-input-select', 'aria-label': _('Database contents') }, [
|
||||||
|
E('option', { value: 'country' }, _('Country/region — approximately 3.3 MiB')),
|
||||||
|
E('option', { value: 'country_asn' }, _('Country/region + network organization — approximately 12 MiB')),
|
||||||
|
E('option', { value: 'city_asn' }, _('Province/city + network organization — approximately 57 MiB'))
|
||||||
|
]);
|
||||||
|
var importedCity = null;
|
||||||
|
var details = E('div', { style: 'color:var(--text-color-secondary,#888);font-size:0.9em;line-height:1.6;overflow-wrap:anywhere' });
|
||||||
|
var message = E('div', { role: 'status', 'aria-live': 'polite', style: 'font-size:0.9em;overflow-wrap:anywhere' });
|
||||||
|
var progressFill = E('div', { style: 'width:0%' });
|
||||||
|
var progress = E('div', { 'class': 'cbi-progressbar', style: 'display:none;margin:8px 0', role: 'progressbar', 'aria-valuemin': 0, 'aria-valuemax': 100 }, progressFill);
|
||||||
|
var download = E('button', { type: 'button', 'class': 'btn cbi-button cbi-button-apply' }, _('Download and enable'));
|
||||||
|
var upload = E('button', { type: 'button', 'class': 'btn cbi-button' }, _('Upload database'));
|
||||||
|
var cancel = E('button', { type: 'button', 'class': 'btn cbi-button', style: 'display:none' }, _('Cancel'));
|
||||||
|
var remove = E('button', { type: 'button', 'class': 'btn cbi-button cbi-button-negative', style: 'display:none' }, _('Delete'));
|
||||||
|
|
||||||
|
function display(status) {
|
||||||
|
var wasBusy = current && current.job && current.job.busy;
|
||||||
|
if (wasBusy && status.job && !status.job.busy && status.job.phase === 'completed') dirtyProfile = false;
|
||||||
|
current = status;
|
||||||
|
var job = status.job || {}, databases = status.databases || [], busy = pending || job.busy;
|
||||||
|
badge.style.color = status.enabled ? 'var(--success-color,#2f9e44)' : '';
|
||||||
|
badge.textContent = status.enabled ? _('Enabled') : databases.length ? _('Disabled') : _('Not installed');
|
||||||
|
enabled.checked = !!status.enabled; enabled.disabled = busy || !databases.length;
|
||||||
|
if (!dirtyProfile && status.profile) {
|
||||||
|
if (status.profile === 'city' && !importedCity) {
|
||||||
|
importedCity = E('option', { value: 'city' }, _('Province/city (uploaded database)')); profile.appendChild(importedCity);
|
||||||
|
}
|
||||||
|
profile.value = status.profile;
|
||||||
|
}
|
||||||
|
if (job.busy && job.profile) profile.value = job.profile;
|
||||||
|
profile.disabled = busy;
|
||||||
|
download.disabled = busy; upload.disabled = busy;
|
||||||
|
download.textContent = databases.length ? _('Update database') : _('Download and enable');
|
||||||
|
remove.style.display = databases.length || status.load_error ? '' : 'none'; remove.disabled = busy;
|
||||||
|
cancel.style.display = job.busy && job.phase !== 'activating' ? '' : 'none'; cancel.disabled = pending;
|
||||||
|
var builds = databases.map(function (db) {
|
||||||
|
var name = { country: _('Country/region'), city: _('Province/city'), asn: _('Network organization') }[db.kind] || db.kind;
|
||||||
|
var date = '—';
|
||||||
|
if (db.build_epoch) {
|
||||||
|
try { date = new Date(db.build_epoch * 1000).toLocaleDateString((L.env.lang || 'en').replace(/_/g, '-')); }
|
||||||
|
catch (e) { date = new Date(db.build_epoch * 1000).toLocaleDateString(); }
|
||||||
|
}
|
||||||
|
return name + ' ' + date;
|
||||||
|
});
|
||||||
|
details.textContent = [
|
||||||
|
builds.length ? _('Version') + ': ' + builds.join(' · ') : '',
|
||||||
|
_('Storage used') + ': ' + size(status.installed_bytes) + ' · ' + _('Available space') + ': ' + size(status.free_bytes),
|
||||||
|
_('Database directory') + ': ' + status.directory
|
||||||
|
].filter(Boolean).join('\n'); details.style.whiteSpace = 'pre-line';
|
||||||
|
var total = job.total_bytes;
|
||||||
|
var percent = total > 0 ? Math.min(100, Math.floor((job.downloaded_bytes || 0) * 100 / total)) : null;
|
||||||
|
progress.style.display = job.busy && total > 0 ? '' : 'none';
|
||||||
|
progressFill.style.width = (percent || 0) + '%';
|
||||||
|
progress.title = percent == null ? '' : percent + '%';
|
||||||
|
if (percent != null) progress.setAttribute('aria-valuenow', percent);
|
||||||
|
message.style.color = job.phase === 'failed' || status.load_error ? 'var(--error-color,#c0392b)' : '';
|
||||||
|
if (status.load_error && !job.busy) {
|
||||||
|
message.textContent = _('The installed database could not be loaded. Download or upload it again.');
|
||||||
|
message.title = status.load_error;
|
||||||
|
} else if (job.phase === 'failed') {
|
||||||
|
var errorMessage = job.error_phase === 'checking_space'
|
||||||
|
? _('Cannot prepare storage. Check free space and directory permissions.')
|
||||||
|
: job.error_phase === 'verifying'
|
||||||
|
? _('Database validation failed. The previous database is unchanged.')
|
||||||
|
: _('Database operation failed. Retry or upload a local database.');
|
||||||
|
if (job.error_phase === 'checking_space' && status.free_bytes != null && job.required_bytes > status.free_bytes) {
|
||||||
|
errorMessage = _('Not enough free space for this database.') + ' ' + _('Required space') + ': ' + size(job.required_bytes) + ' · ' + _('Available space') + ': ' + size(status.free_bytes);
|
||||||
|
}
|
||||||
|
message.textContent = errorMessage; message.title = job.error || '';
|
||||||
|
} else {
|
||||||
|
message.textContent = (phases[job.phase] || '') + (job.busy && total > 0 ? ' ' + size(job.downloaded_bytes || 0) + ' / ' + size(total) + ' (' + percent + '%)' : '');
|
||||||
|
message.title = job.current_file || '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function refresh() {
|
||||||
|
if (refreshing || (root && !root.isConnected)) return Promise.resolve();
|
||||||
|
refreshing = true;
|
||||||
|
return callStatus().then(unwrap).then(display).catch(function (error) {
|
||||||
|
message.textContent = _('GeoIP service is unavailable. Enable connection monitoring and apply settings, or update Bandix.');
|
||||||
|
message.title = error.message || String(error);
|
||||||
|
download.disabled = true; upload.disabled = true; enabled.disabled = true;
|
||||||
|
}).finally(function () { refreshing = false; });
|
||||||
|
}
|
||||||
|
function manage(action, selected, checked) {
|
||||||
|
pending = true; if (current) display(current);
|
||||||
|
return callManage(action, selected || '', checked ? '1' : '0').then(unwrap).then(function (status) {
|
||||||
|
if (action === 'download') dirtyProfile = true;
|
||||||
|
display(status);
|
||||||
|
}).catch(function (error) {
|
||||||
|
ui.addNotification(null, E('p', _('Database operation failed') + ': ' + (error.message || error)), 'error');
|
||||||
|
}).finally(function () { pending = false; if (current) display(current); });
|
||||||
|
}
|
||||||
|
profile.addEventListener('change', function () { dirtyProfile = true; });
|
||||||
|
enabled.addEventListener('change', function () { manage('enable', null, enabled.checked); });
|
||||||
|
download.addEventListener('click', function () { manage('download', profile.value); });
|
||||||
|
cancel.addEventListener('click', function () { manage('cancel'); });
|
||||||
|
remove.addEventListener('click', function () {
|
||||||
|
ui.showModal(_('Delete GeoIP databases'), [
|
||||||
|
E('p', _('Remove installed databases and disable IP location lookup?')),
|
||||||
|
E('div', { 'class': 'right' }, [
|
||||||
|
E('button', { type: 'button', 'class': 'btn', click: ui.hideModal }, _('Cancel')), ' ',
|
||||||
|
E('button', { type: 'button', 'class': 'btn cbi-button-negative', click: function () { ui.hideModal(); dirtyProfile = false; manage('delete'); } }, _('Delete'))
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
upload.addEventListener('click', function () {
|
||||||
|
if (!current) return;
|
||||||
|
var limit = typeof current.upload_max_bytes === 'number' ? current.upload_max_bytes : 8 * 1048576;
|
||||||
|
var selection = E('input', { type: 'file', accept: '.mmdb', 'aria-label': _('Upload database') });
|
||||||
|
var uploadError = E('p', { style: 'color:var(--error-color,#c0392b)', role: 'alert' });
|
||||||
|
var uploadAction = E('button', { type: 'button', 'class': 'btn cbi-button-action important', disabled: true }, _('Upload and enable'));
|
||||||
|
selection.addEventListener('change', function () {
|
||||||
|
var file = selection.files[0];
|
||||||
|
uploadAction.disabled = !file || !/\.mmdb$/i.test(file.name) || file.size === 0 || file.size > limit;
|
||||||
|
uploadError.textContent = uploadAction.disabled && file ? _('Choose an uncompressed .mmdb file within the upload size limit.') : '';
|
||||||
|
});
|
||||||
|
uploadAction.addEventListener('click', function () {
|
||||||
|
var file = selection.files[0]; if (!file || uploadAction.disabled) return;
|
||||||
|
pending = true; display(current);
|
||||||
|
var fill = E('div', { style: 'width:0%' });
|
||||||
|
var bar = E('div', { 'class': 'cbi-progressbar' }, fill);
|
||||||
|
ui.showModal(_('Uploading database…'), [bar]);
|
||||||
|
var data = new FormData();
|
||||||
|
data.append('sessionid', rpc.getSessionID());
|
||||||
|
data.append('filename', '/tmp/bandix-geoip-upload.mmdb');
|
||||||
|
data.append('filedata', file);
|
||||||
|
request.post(L.env.cgi_base + '/cgi-upload', data, {
|
||||||
|
timeout: 0,
|
||||||
|
progress: function (event) {
|
||||||
|
if (!event.total) return;
|
||||||
|
var percent = Math.min(100, Math.round(event.loaded * 100 / event.total));
|
||||||
|
fill.style.width = percent + '%'; bar.title = percent + '%';
|
||||||
|
}
|
||||||
|
}).then(function (response) {
|
||||||
|
var reply = response.json();
|
||||||
|
if (!response.ok || reply.failure) throw new Error(reply.message || _('Database upload failed'));
|
||||||
|
ui.hideModal(); return manage('import');
|
||||||
|
}).catch(function (error) {
|
||||||
|
ui.hideModal(); ui.addNotification(null, E('p', _('Database upload failed') + ': ' + (error.message || error)), 'error');
|
||||||
|
}).finally(function () { pending = false; if (current) display(current); });
|
||||||
|
});
|
||||||
|
ui.showModal(_('Upload database'), [
|
||||||
|
E('p', {}, _('Maximum upload size') + ': ' + size(limit)), selection, uploadError,
|
||||||
|
E('div', { 'class': 'right' }, [E('button', { type: 'button', 'class': 'btn', click: ui.hideModal }, _('Cancel')), ' ', uploadAction])
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
root = E('div', { style: 'max-width:780px' }, [
|
||||||
|
E('div', { style: 'display:flex;flex-wrap:wrap;align-items:center;gap:16px;margin-bottom:12px' }, [
|
||||||
|
E('label', { style: 'display:flex;align-items:center;gap:8px' }, [enabled, _('Enable IP location lookup')]), badge
|
||||||
|
]),
|
||||||
|
E('div', { style: 'display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-bottom:10px' }, [profile, download, upload, cancel, remove]),
|
||||||
|
details, progress, message,
|
||||||
|
E('div', { style: 'font-size:0.85em;opacity:0.75;margin-top:8px' }, [
|
||||||
|
_('Data source') + ': ', E('a', { href: 'https://github.com/P3TERX/GeoLite.mmdb', target: '_blank', rel: 'noopener noreferrer' }, 'P3TERX / MaxMind GeoLite2'),
|
||||||
|
' · ', E('a', { href: 'https://www.maxmind.com/en/geolite2/eula', target: '_blank', rel: 'noopener noreferrer' }, _('Data terms')),
|
||||||
|
E('div', {}, _('Upload an uncompressed Country, City or ASN .mmdb file. To add ASN, install a Country or City database first.'))
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
this._root = root;
|
||||||
|
poll.add(refresh, 2);
|
||||||
|
// renderWidget returns before LuCI attaches the form to the document.
|
||||||
|
setTimeout(refresh, 0);
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
'require uci';
|
'require uci';
|
||||||
'require rpc';
|
'require rpc';
|
||||||
'require poll';
|
'require poll';
|
||||||
|
'require bandix.geoip as geoip';
|
||||||
|
|
||||||
var BANDIX_MODAL_BG_OPENWRT2020_LIGHT = '#ffffff';
|
var BANDIX_MODAL_BG_OPENWRT2020_LIGHT = '#ffffff';
|
||||||
var BANDIX_MODAL_BG_OPENWRT2020_DARK = '#2a2a2a';
|
var BANDIX_MODAL_BG_OPENWRT2020_DARK = '#2a2a2a';
|
||||||
@@ -136,15 +137,21 @@ function formatSize(bytes) {
|
|||||||
// IPv6 地址按 RFC 3986 用方括号包起来,一是避免 '2600:...:8000::' + ':' + '80' 拼出
|
// IPv6 地址按 RFC 3986 用方括号包起来,一是避免 '2600:...:8000::' + ':' + '80' 拼出
|
||||||
// ':::80' 这种分不清边界的写法,二是选中复制出来就是 [addr]:port,可以直接粘去用。
|
// ':::80' 这种分不清边界的写法,二是选中复制出来就是 [addr]:port,可以直接粘去用。
|
||||||
// 地址和端口是行内元素,复制时不会被拆成两行;端口自身 nowrap,不会被折断
|
// 地址和端口是行内元素,复制时不会被拆成两行;端口自身 nowrap,不会被折断
|
||||||
function flowEndpointCell(addr, port) {
|
function flowEndpointCell(addr, port, information) {
|
||||||
var text = addr || '-';
|
var text = addr || '-';
|
||||||
if (text.indexOf(':') >= 0) {
|
if (text.indexOf(':') >= 0) {
|
||||||
text = '[' + text + ']';
|
text = '[' + text + ']';
|
||||||
}
|
}
|
||||||
return E('td', { 'class': 'flows-addr-cell' }, [
|
var description = geoip.describe(information);
|
||||||
|
var children = [E('div', {}, [
|
||||||
E('span', { 'class': 'flows-addr' }, text),
|
E('span', { 'class': 'flows-addr' }, text),
|
||||||
E('span', { 'class': 'flows-port' }, port ? ':' + port : '')
|
E('span', { 'class': 'flows-port' }, port ? ':' + port : '')
|
||||||
]);
|
])];
|
||||||
|
if (description) children.push(E('div', {
|
||||||
|
'class': 'flows-geoip', title: description.details,
|
||||||
|
style: 'font-size:0.8rem;opacity:0.75;margin-top:3px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:240px'
|
||||||
|
}, description.summary));
|
||||||
|
return E('td', { 'class': 'flows-addr-cell' }, children);
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatDeviceName(device) {
|
function formatDeviceName(device) {
|
||||||
@@ -1211,7 +1218,8 @@ return view.extend({
|
|||||||
total: res.data.total || 0,
|
total: res.data.total || 0,
|
||||||
page: res.data.page || currentPage,
|
page: res.data.page || currentPage,
|
||||||
page_size: res.data.page_size || currentPageSize,
|
page_size: res.data.page_size || currentPageSize,
|
||||||
total_pages: res.data.total_pages || 1
|
total_pages: res.data.total_pages || 1,
|
||||||
|
geoip: res.data.geoip || {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1233,6 +1241,7 @@ return view.extend({
|
|||||||
tableBody.innerHTML = '';
|
tableBody.innerHTML = '';
|
||||||
var normalized = normalizeFlowsResponse(res);
|
var normalized = normalizeFlowsResponse(res);
|
||||||
var flows = normalized.items;
|
var flows = normalized.items;
|
||||||
|
var information = normalized.geoip || {};
|
||||||
totalItems = normalized.total;
|
totalItems = normalized.total;
|
||||||
currentPage = normalized.page;
|
currentPage = normalized.page;
|
||||||
totalPages = Math.max(1, normalized.total_pages);
|
totalPages = Math.max(1, normalized.total_pages);
|
||||||
@@ -1258,11 +1267,11 @@ return view.extend({
|
|||||||
tableBody.appendChild(E('tr', {}, [
|
tableBody.appendChild(E('tr', {}, [
|
||||||
E('td', { 'class': protoCls }, (f.protocol || '-').toUpperCase()),
|
E('td', { 'class': protoCls }, (f.protocol || '-').toUpperCase()),
|
||||||
E('td', { 'class': 'flows-state-cell ' + stateCls }, f.state || '-'),
|
E('td', { 'class': 'flows-state-cell ' + stateCls }, f.state || '-'),
|
||||||
flowEndpointCell(f.orig && f.orig.src, f.orig && f.orig.sport),
|
flowEndpointCell(f.orig && f.orig.src, f.orig && f.orig.sport, information[f.orig && f.orig.src]),
|
||||||
flowEndpointCell(f.orig && f.orig.dst, f.orig && f.orig.dport),
|
flowEndpointCell(f.orig && f.orig.dst, f.orig && f.orig.dport, information[f.orig && f.orig.dst]),
|
||||||
E('td', {}, sendStr),
|
E('td', {}, sendStr),
|
||||||
flowEndpointCell(f.repl && f.repl.src, f.repl && f.repl.sport),
|
flowEndpointCell(f.repl && f.repl.src, f.repl && f.repl.sport, information[f.repl && f.repl.src]),
|
||||||
flowEndpointCell(f.repl && f.repl.dst, f.repl && f.repl.dport),
|
flowEndpointCell(f.repl && f.repl.dst, f.repl && f.repl.dport, information[f.repl && f.repl.dst]),
|
||||||
E('td', {}, replyStr),
|
E('td', {}, replyStr),
|
||||||
E('td', { 'style': 'font-size: 0.8rem;' }, flagsStr)
|
E('td', { 'style': 'font-size: 0.8rem;' }, flagsStr)
|
||||||
]));
|
]));
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
'require ui';
|
'require ui';
|
||||||
'require uci';
|
'require uci';
|
||||||
'require rpc';
|
'require rpc';
|
||||||
|
'require bandix.geoip as geoip';
|
||||||
'require tools.widgets as widgets';
|
'require tools.widgets as widgets';
|
||||||
|
|
||||||
|
|
||||||
@@ -231,6 +232,13 @@ return view.extend({
|
|||||||
s.description = _('Configure basic parameters for Bandix service');
|
s.description = _('Configure basic parameters for Bandix service');
|
||||||
s.addremove = false;
|
s.addremove = false;
|
||||||
|
|
||||||
|
o = s.option(form.Value, 'host', _('Listen Address'),
|
||||||
|
_('IP address for Bandix to listen on. Use 127.0.0.1 for local access or 0.0.0.0 / :: for all interfaces.'));
|
||||||
|
o.default = '127.0.0.1';
|
||||||
|
o.datatype = 'ipaddr("nomask")';
|
||||||
|
o.placeholder = '127.0.0.1';
|
||||||
|
o.rmempty = false;
|
||||||
|
|
||||||
// 添加端口设置选项
|
// 添加端口设置选项
|
||||||
o = s.option(form.Value, 'port', _('Port'),
|
o = s.option(form.Value, 'port', _('Port'),
|
||||||
_('Port for Bandix service to listen on'));
|
_('Port for Bandix service to listen on'));
|
||||||
@@ -785,6 +793,16 @@ return view.extend({
|
|||||||
o.default = '0';
|
o.default = '0';
|
||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
|
|
||||||
|
o = s.option(form.DummyValue, '_geoip_manager', _('IP location database'));
|
||||||
|
o.renderWidget = function () { return geoip.render(); };
|
||||||
|
|
||||||
|
o = s.option(form.Value, 'geoip_dir', _('Database directory'),
|
||||||
|
_('Use persistent storage or an external drive. Save and apply directory changes before downloading.'));
|
||||||
|
o.default = '/usr/share/bandix-geoip';
|
||||||
|
o.placeholder = '/usr/share/bandix-geoip';
|
||||||
|
o.datatype = 'directory';
|
||||||
|
o.rmempty = false;
|
||||||
|
|
||||||
// 4. DNS监控设置部分 (dns)
|
// 4. DNS监控设置部分 (dns)
|
||||||
s = m.section(form.NamedSection, 'dns', 'dns', _('DNS Monitor Settings'));
|
s = m.section(form.NamedSection, 'dns', 'dns', _('DNS Monitor Settings'));
|
||||||
s.description = _('Configure DNS monitoring related parameters');
|
s.description = _('Configure DNS monitoring related parameters');
|
||||||
|
|||||||
+189
-30
@@ -221,12 +221,6 @@ msgstr "WAN cargado"
|
|||||||
msgid "WAN Downloaded"
|
msgid "WAN Downloaded"
|
||||||
msgstr "WAN descargado"
|
msgstr "WAN descargado"
|
||||||
|
|
||||||
msgid "Total Upload"
|
|
||||||
msgstr "Carga total"
|
|
||||||
|
|
||||||
msgid "Total Download"
|
|
||||||
msgstr "Descarga total"
|
|
||||||
|
|
||||||
msgid "LAN Upload"
|
msgid "LAN Upload"
|
||||||
msgstr "Carga LAN"
|
msgstr "Carga LAN"
|
||||||
|
|
||||||
@@ -422,18 +416,12 @@ msgstr "Página"
|
|||||||
msgid "of"
|
msgid "of"
|
||||||
msgstr "de"
|
msgstr "de"
|
||||||
|
|
||||||
msgid "Total"
|
|
||||||
msgstr "Total"
|
|
||||||
|
|
||||||
msgid "records"
|
msgid "records"
|
||||||
msgstr "registros"
|
msgstr "registros"
|
||||||
|
|
||||||
msgid "Per Page"
|
msgid "Per Page"
|
||||||
msgstr "Por página"
|
msgstr "Por página"
|
||||||
|
|
||||||
msgid ""
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Total Queries"
|
msgid "Total Queries"
|
||||||
msgstr "Total de consultas"
|
msgstr "Total de consultas"
|
||||||
|
|
||||||
@@ -938,18 +926,6 @@ msgstr "Máximo"
|
|||||||
msgid "Minimum"
|
msgid "Minimum"
|
||||||
msgstr "Mínimo"
|
msgstr "Mínimo"
|
||||||
|
|
||||||
msgid "WAN Upload"
|
|
||||||
msgstr "Subida WAN"
|
|
||||||
|
|
||||||
msgid "WAN Download"
|
|
||||||
msgstr "Descarga WAN"
|
|
||||||
|
|
||||||
msgid "WAN Uploaded"
|
|
||||||
msgstr "Subido WAN"
|
|
||||||
|
|
||||||
msgid "WAN Downloaded"
|
|
||||||
msgstr "Descargado WAN"
|
|
||||||
|
|
||||||
msgid "Clear Traffic Data"
|
msgid "Clear Traffic Data"
|
||||||
msgstr "Borrar datos de tráfico"
|
msgstr "Borrar datos de tráfico"
|
||||||
|
|
||||||
@@ -1082,9 +1058,6 @@ msgstr "Descargar e Instalar"
|
|||||||
msgid "Manual Download"
|
msgid "Manual Download"
|
||||||
msgstr "Descarga Manual"
|
msgstr "Descarga Manual"
|
||||||
|
|
||||||
msgid "Close"
|
|
||||||
msgstr "Cerrar"
|
|
||||||
|
|
||||||
msgid "Starting installation... The page will refresh automatically in 30 seconds."
|
msgid "Starting installation... The page will refresh automatically in 30 seconds."
|
||||||
msgstr "Iniciando instalación... La página se actualizará automáticamente en 30 segundos."
|
msgstr "Iniciando instalación... La página se actualizará automáticamente en 30 segundos."
|
||||||
|
|
||||||
@@ -1094,9 +1067,6 @@ msgstr "Por favor, borre manualmente la caché del navegador después de actuali
|
|||||||
msgid "Traffic Statistics"
|
msgid "Traffic Statistics"
|
||||||
msgstr "Estadísticas de Tráfico"
|
msgstr "Estadísticas de Tráfico"
|
||||||
|
|
||||||
msgid "Traffic Statistics"
|
|
||||||
msgstr "Estadísticas de Tráfico"
|
|
||||||
|
|
||||||
msgid "Device Usage Ranking"
|
msgid "Device Usage Ranking"
|
||||||
msgstr "Clasificación de Uso de Dispositivos"
|
msgstr "Clasificación de Uso de Dispositivos"
|
||||||
|
|
||||||
@@ -1351,3 +1321,192 @@ msgstr "No disponible"
|
|||||||
|
|
||||||
msgid "Show reply direction"
|
msgid "Show reply direction"
|
||||||
msgstr "Mostrar la dirección de respuesta"
|
msgstr "Mostrar la dirección de respuesta"
|
||||||
|
|
||||||
|
msgid "Available space"
|
||||||
|
msgstr "Espacio disponible"
|
||||||
|
|
||||||
|
msgid "Cannot prepare storage. Check free space and directory permissions."
|
||||||
|
msgstr "No se pudo preparar el almacenamiento. Compruebe el espacio y los permisos."
|
||||||
|
|
||||||
|
msgid "Checking database version…"
|
||||||
|
msgstr "Comprobando la versión…"
|
||||||
|
|
||||||
|
msgid "Checking storage…"
|
||||||
|
msgstr "Comprobando el almacenamiento…"
|
||||||
|
|
||||||
|
msgid "Choose an uncompressed .mmdb file within the upload size limit."
|
||||||
|
msgstr "Seleccione un archivo .mmdb sin comprimir dentro del límite de tamaño."
|
||||||
|
|
||||||
|
msgid "Country/region + network organization — approximately 12 MiB"
|
||||||
|
msgstr "País/región + organización de red · unos 12 MiB"
|
||||||
|
|
||||||
|
msgid "Country/region — approximately 3.3 MiB"
|
||||||
|
msgstr "País/región · unos 3,3 MiB"
|
||||||
|
|
||||||
|
msgid "Data source"
|
||||||
|
msgstr "Fuente de datos"
|
||||||
|
|
||||||
|
msgid "Data terms"
|
||||||
|
msgstr "Condiciones de uso"
|
||||||
|
|
||||||
|
msgid "Database contents"
|
||||||
|
msgstr "Contenido de la base"
|
||||||
|
|
||||||
|
msgid "Database directory"
|
||||||
|
msgstr "Directorio de la base"
|
||||||
|
|
||||||
|
msgid "Database enabled"
|
||||||
|
msgstr "Base de datos activada"
|
||||||
|
|
||||||
|
msgid "Database is up to date"
|
||||||
|
msgstr "La base está actualizada"
|
||||||
|
|
||||||
|
msgid "Database operation failed"
|
||||||
|
msgstr "Error en la operación de la base"
|
||||||
|
|
||||||
|
msgid "Database operation failed. Retry or upload a local database."
|
||||||
|
msgstr "Error en la operación. Reintente o cargue una base local."
|
||||||
|
|
||||||
|
msgid "Database upload failed"
|
||||||
|
msgstr "Error al cargar la base"
|
||||||
|
|
||||||
|
msgid "Database validation failed. The previous database is unchanged."
|
||||||
|
msgstr "Error de validación. Se conserva la base anterior."
|
||||||
|
|
||||||
|
msgid "Delete"
|
||||||
|
msgstr "Eliminar"
|
||||||
|
|
||||||
|
msgid "Delete GeoIP databases"
|
||||||
|
msgstr "Eliminar bases GeoIP"
|
||||||
|
|
||||||
|
msgid "Disabled"
|
||||||
|
msgstr "Desactivado"
|
||||||
|
|
||||||
|
msgid "Documentation address"
|
||||||
|
msgstr "Dirección de documentación"
|
||||||
|
|
||||||
|
msgid "Download and enable"
|
||||||
|
msgstr "Descargar y activar"
|
||||||
|
|
||||||
|
msgid "Download cancelled"
|
||||||
|
msgstr "Descarga cancelada"
|
||||||
|
|
||||||
|
msgid "Downloading database…"
|
||||||
|
msgstr "Descargando la base…"
|
||||||
|
|
||||||
|
msgid "Enable IP location lookup"
|
||||||
|
msgstr "Activar ubicación de IP"
|
||||||
|
|
||||||
|
msgid "Enabled"
|
||||||
|
msgstr "Activado"
|
||||||
|
|
||||||
|
msgid "GeoIP service is unavailable. Enable connection monitoring and apply settings, or update Bandix."
|
||||||
|
msgstr "GeoIP no está disponible. Active la monitorización de conexiones y aplique los ajustes, o actualice Bandix."
|
||||||
|
|
||||||
|
msgid "IP location database"
|
||||||
|
msgstr "Base de ubicación de IP"
|
||||||
|
|
||||||
|
msgid "Importing database…"
|
||||||
|
msgstr "Importando la base…"
|
||||||
|
|
||||||
|
msgid "Link-local address"
|
||||||
|
msgstr "Dirección de enlace local"
|
||||||
|
|
||||||
|
msgid "Loading database…"
|
||||||
|
msgstr "Cargando la base…"
|
||||||
|
|
||||||
|
msgid "Local network"
|
||||||
|
msgstr "Red local"
|
||||||
|
|
||||||
|
msgid "Loopback"
|
||||||
|
msgstr "Bucle local"
|
||||||
|
|
||||||
|
msgid "Maximum upload size"
|
||||||
|
msgstr "Tamaño máximo de carga"
|
||||||
|
|
||||||
|
msgid "Multicast"
|
||||||
|
msgstr "Multidifusión"
|
||||||
|
|
||||||
|
msgid "Not installed"
|
||||||
|
msgstr "Sin instalar"
|
||||||
|
|
||||||
|
msgid "Private address"
|
||||||
|
msgstr "Dirección privada"
|
||||||
|
|
||||||
|
msgid "Province/city (uploaded database)"
|
||||||
|
msgstr "Provincia/ciudad (base cargada)"
|
||||||
|
|
||||||
|
msgid "Province/city + network organization — approximately 57 MiB"
|
||||||
|
msgstr "Provincia/ciudad + organización de red · unos 57 MiB"
|
||||||
|
|
||||||
|
msgid "Remove installed databases and disable IP location lookup?"
|
||||||
|
msgstr "¿Eliminar las bases y desactivar la ubicación de IP?"
|
||||||
|
|
||||||
|
msgid "Reserved address"
|
||||||
|
msgstr "Dirección reservada"
|
||||||
|
|
||||||
|
msgid "Shared address (CGNAT)"
|
||||||
|
msgstr "Dirección compartida (CGNAT)"
|
||||||
|
|
||||||
|
msgid "Storage used"
|
||||||
|
msgstr "Espacio utilizado"
|
||||||
|
|
||||||
|
msgid "The installed database could not be loaded. Download or upload it again."
|
||||||
|
msgstr "No se pudo cargar la base. Descárguela o cárguela de nuevo."
|
||||||
|
|
||||||
|
msgid "Unknown"
|
||||||
|
msgstr "Desconocido"
|
||||||
|
|
||||||
|
msgid "Unknown location"
|
||||||
|
msgstr "Ubicación desconocida"
|
||||||
|
|
||||||
|
msgid "Unspecified address"
|
||||||
|
msgstr "Dirección sin especificar"
|
||||||
|
|
||||||
|
msgid "Update database"
|
||||||
|
msgstr "Actualizar base"
|
||||||
|
|
||||||
|
msgid "Upload an uncompressed Country, City or ASN .mmdb file. To add ASN, install a Country or City database first."
|
||||||
|
msgstr "Cargue un archivo Country, City o ASN .mmdb sin comprimir. Instale Country o City antes de añadir ASN."
|
||||||
|
|
||||||
|
msgid "Upload and enable"
|
||||||
|
msgstr "Cargar y activar"
|
||||||
|
|
||||||
|
msgid "Upload database"
|
||||||
|
msgstr "Cargar base"
|
||||||
|
|
||||||
|
msgid "Uploading database…"
|
||||||
|
msgstr "Subiendo la base…"
|
||||||
|
|
||||||
|
msgid "Use persistent storage or an external drive. Save and apply directory changes before downloading."
|
||||||
|
msgstr "Use almacenamiento persistente o externo. Guarde y aplique los cambios de directorio antes de descargar."
|
||||||
|
|
||||||
|
msgid "Verifying database…"
|
||||||
|
msgstr "Verificando la base…"
|
||||||
|
|
||||||
|
msgid "Version"
|
||||||
|
msgstr "Versión"
|
||||||
|
|
||||||
|
msgid "Country/region"
|
||||||
|
msgstr "País/región"
|
||||||
|
|
||||||
|
msgid "Province/city"
|
||||||
|
msgstr "Provincia/ciudad"
|
||||||
|
|
||||||
|
msgid "Network organization"
|
||||||
|
msgstr "Organización de red"
|
||||||
|
|
||||||
|
msgid "Not enough free space for this database."
|
||||||
|
msgstr "No hay espacio suficiente para esta base."
|
||||||
|
|
||||||
|
msgid "Required space"
|
||||||
|
msgstr "Espacio necesario"
|
||||||
|
|
||||||
|
msgid "Database ready"
|
||||||
|
msgstr "Base lista"
|
||||||
|
|
||||||
|
msgid "Listen Address"
|
||||||
|
msgstr "Dirección de escucha"
|
||||||
|
|
||||||
|
msgid "IP address for Bandix to listen on. Use 127.0.0.1 for local access or 0.0.0.0 / :: for all interfaces."
|
||||||
|
msgstr "Dirección IP de escucha de Bandix. Use 127.0.0.1 para acceso local o 0.0.0.0 / :: para todas las interfaces."
|
||||||
+189
-30
@@ -221,12 +221,6 @@ msgstr "WAN envoyé"
|
|||||||
msgid "WAN Downloaded"
|
msgid "WAN Downloaded"
|
||||||
msgstr "WAN téléchargé"
|
msgstr "WAN téléchargé"
|
||||||
|
|
||||||
msgid "Total Upload"
|
|
||||||
msgstr "Téléversement total"
|
|
||||||
|
|
||||||
msgid "Total Download"
|
|
||||||
msgstr "Téléchargement total"
|
|
||||||
|
|
||||||
msgid "LAN Upload"
|
msgid "LAN Upload"
|
||||||
msgstr "Débit envoi LAN"
|
msgstr "Débit envoi LAN"
|
||||||
|
|
||||||
@@ -422,18 +416,12 @@ msgstr "Page"
|
|||||||
msgid "of"
|
msgid "of"
|
||||||
msgstr "sur"
|
msgstr "sur"
|
||||||
|
|
||||||
msgid "Total"
|
|
||||||
msgstr "Total"
|
|
||||||
|
|
||||||
msgid "records"
|
msgid "records"
|
||||||
msgstr "enregistrements"
|
msgstr "enregistrements"
|
||||||
|
|
||||||
msgid "Per Page"
|
msgid "Per Page"
|
||||||
msgstr "Par page"
|
msgstr "Par page"
|
||||||
|
|
||||||
msgid ""
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Total Queries"
|
msgid "Total Queries"
|
||||||
msgstr "Total des requêtes"
|
msgstr "Total des requêtes"
|
||||||
|
|
||||||
@@ -938,18 +926,6 @@ msgstr "Maximum"
|
|||||||
msgid "Minimum"
|
msgid "Minimum"
|
||||||
msgstr "Minimum"
|
msgstr "Minimum"
|
||||||
|
|
||||||
msgid "WAN Upload"
|
|
||||||
msgstr "Envoi WAN"
|
|
||||||
|
|
||||||
msgid "WAN Download"
|
|
||||||
msgstr "Téléchargement WAN"
|
|
||||||
|
|
||||||
msgid "WAN Uploaded"
|
|
||||||
msgstr "Envoyé WAN"
|
|
||||||
|
|
||||||
msgid "WAN Downloaded"
|
|
||||||
msgstr "Téléchargé WAN"
|
|
||||||
|
|
||||||
msgid "Clear Traffic Data"
|
msgid "Clear Traffic Data"
|
||||||
msgstr "Effacer les données de trafic"
|
msgstr "Effacer les données de trafic"
|
||||||
|
|
||||||
@@ -1082,9 +1058,6 @@ msgstr "Télécharger et Installer"
|
|||||||
msgid "Manual Download"
|
msgid "Manual Download"
|
||||||
msgstr "Téléchargement Manuel"
|
msgstr "Téléchargement Manuel"
|
||||||
|
|
||||||
msgid "Close"
|
|
||||||
msgstr "Fermer"
|
|
||||||
|
|
||||||
msgid "Starting installation... The page will refresh automatically in 30 seconds."
|
msgid "Starting installation... The page will refresh automatically in 30 seconds."
|
||||||
msgstr "Démarrage de l'installation... La page sera actualisée automatiquement dans 30 secondes."
|
msgstr "Démarrage de l'installation... La page sera actualisée automatiquement dans 30 secondes."
|
||||||
|
|
||||||
@@ -1094,9 +1067,6 @@ msgstr "Veuillez vider manuellement le cache du navigateur après la mise à jou
|
|||||||
msgid "Traffic Statistics"
|
msgid "Traffic Statistics"
|
||||||
msgstr "Statistiques de Trafic"
|
msgstr "Statistiques de Trafic"
|
||||||
|
|
||||||
msgid "Traffic Statistics"
|
|
||||||
msgstr "Statistiques de Trafic"
|
|
||||||
|
|
||||||
msgid "Device Usage Ranking"
|
msgid "Device Usage Ranking"
|
||||||
msgstr "Classement d'Utilisation des Appareils"
|
msgstr "Classement d'Utilisation des Appareils"
|
||||||
|
|
||||||
@@ -1351,3 +1321,192 @@ msgstr "Indisponible"
|
|||||||
|
|
||||||
msgid "Show reply direction"
|
msgid "Show reply direction"
|
||||||
msgstr "Afficher la direction de réponse"
|
msgstr "Afficher la direction de réponse"
|
||||||
|
|
||||||
|
msgid "Available space"
|
||||||
|
msgstr "Espace disponible"
|
||||||
|
|
||||||
|
msgid "Cannot prepare storage. Check free space and directory permissions."
|
||||||
|
msgstr "Impossible de préparer le stockage. Vérifiez l’espace libre et les permissions."
|
||||||
|
|
||||||
|
msgid "Checking database version…"
|
||||||
|
msgstr "Vérification de la version…"
|
||||||
|
|
||||||
|
msgid "Checking storage…"
|
||||||
|
msgstr "Vérification du stockage…"
|
||||||
|
|
||||||
|
msgid "Choose an uncompressed .mmdb file within the upload size limit."
|
||||||
|
msgstr "Choisissez un fichier .mmdb non compressé respectant la taille limite."
|
||||||
|
|
||||||
|
msgid "Country/region + network organization — approximately 12 MiB"
|
||||||
|
msgstr "Pays/région + organisation réseau · environ 12 MiB"
|
||||||
|
|
||||||
|
msgid "Country/region — approximately 3.3 MiB"
|
||||||
|
msgstr "Pays/région · environ 3,3 MiB"
|
||||||
|
|
||||||
|
msgid "Data source"
|
||||||
|
msgstr "Source des données"
|
||||||
|
|
||||||
|
msgid "Data terms"
|
||||||
|
msgstr "Conditions d’utilisation"
|
||||||
|
|
||||||
|
msgid "Database contents"
|
||||||
|
msgstr "Contenu de la base"
|
||||||
|
|
||||||
|
msgid "Database directory"
|
||||||
|
msgstr "Répertoire de la base"
|
||||||
|
|
||||||
|
msgid "Database enabled"
|
||||||
|
msgstr "Base activée"
|
||||||
|
|
||||||
|
msgid "Database is up to date"
|
||||||
|
msgstr "La base est à jour"
|
||||||
|
|
||||||
|
msgid "Database operation failed"
|
||||||
|
msgstr "Échec de l’opération"
|
||||||
|
|
||||||
|
msgid "Database operation failed. Retry or upload a local database."
|
||||||
|
msgstr "Échec de l’opération. Réessayez ou importez une base locale."
|
||||||
|
|
||||||
|
msgid "Database upload failed"
|
||||||
|
msgstr "Échec de l’envoi de la base"
|
||||||
|
|
||||||
|
msgid "Database validation failed. The previous database is unchanged."
|
||||||
|
msgstr "Échec de validation. La base précédente est conservée."
|
||||||
|
|
||||||
|
msgid "Delete"
|
||||||
|
msgstr "Supprimer"
|
||||||
|
|
||||||
|
msgid "Delete GeoIP databases"
|
||||||
|
msgstr "Supprimer les bases GeoIP"
|
||||||
|
|
||||||
|
msgid "Disabled"
|
||||||
|
msgstr "Désactivé"
|
||||||
|
|
||||||
|
msgid "Documentation address"
|
||||||
|
msgstr "Adresse de documentation"
|
||||||
|
|
||||||
|
msgid "Download and enable"
|
||||||
|
msgstr "Télécharger et activer"
|
||||||
|
|
||||||
|
msgid "Download cancelled"
|
||||||
|
msgstr "Téléchargement annulé"
|
||||||
|
|
||||||
|
msgid "Downloading database…"
|
||||||
|
msgstr "Téléchargement de la base…"
|
||||||
|
|
||||||
|
msgid "Enable IP location lookup"
|
||||||
|
msgstr "Activer la géolocalisation IP"
|
||||||
|
|
||||||
|
msgid "Enabled"
|
||||||
|
msgstr "Activé"
|
||||||
|
|
||||||
|
msgid "GeoIP service is unavailable. Enable connection monitoring and apply settings, or update Bandix."
|
||||||
|
msgstr "GeoIP indisponible. Activez le suivi des connexions et appliquez les paramètres, ou mettez Bandix à jour."
|
||||||
|
|
||||||
|
msgid "IP location database"
|
||||||
|
msgstr "Base de géolocalisation IP"
|
||||||
|
|
||||||
|
msgid "Importing database…"
|
||||||
|
msgstr "Importation de la base…"
|
||||||
|
|
||||||
|
msgid "Link-local address"
|
||||||
|
msgstr "Adresse de lien local"
|
||||||
|
|
||||||
|
msgid "Loading database…"
|
||||||
|
msgstr "Chargement de la base…"
|
||||||
|
|
||||||
|
msgid "Local network"
|
||||||
|
msgstr "Réseau local"
|
||||||
|
|
||||||
|
msgid "Loopback"
|
||||||
|
msgstr "Bouclage"
|
||||||
|
|
||||||
|
msgid "Maximum upload size"
|
||||||
|
msgstr "Taille maximale d’envoi"
|
||||||
|
|
||||||
|
msgid "Multicast"
|
||||||
|
msgstr "Multidiffusion"
|
||||||
|
|
||||||
|
msgid "Not installed"
|
||||||
|
msgstr "Non installé"
|
||||||
|
|
||||||
|
msgid "Private address"
|
||||||
|
msgstr "Adresse privée"
|
||||||
|
|
||||||
|
msgid "Province/city (uploaded database)"
|
||||||
|
msgstr "Région/ville (base importée)"
|
||||||
|
|
||||||
|
msgid "Province/city + network organization — approximately 57 MiB"
|
||||||
|
msgstr "Région/ville + organisation réseau · environ 57 MiB"
|
||||||
|
|
||||||
|
msgid "Remove installed databases and disable IP location lookup?"
|
||||||
|
msgstr "Supprimer les bases et désactiver la géolocalisation IP ?"
|
||||||
|
|
||||||
|
msgid "Reserved address"
|
||||||
|
msgstr "Adresse réservée"
|
||||||
|
|
||||||
|
msgid "Shared address (CGNAT)"
|
||||||
|
msgstr "Adresse partagée (CGNAT)"
|
||||||
|
|
||||||
|
msgid "Storage used"
|
||||||
|
msgstr "Espace utilisé"
|
||||||
|
|
||||||
|
msgid "The installed database could not be loaded. Download or upload it again."
|
||||||
|
msgstr "Impossible de charger la base. Téléchargez-la ou importez-la à nouveau."
|
||||||
|
|
||||||
|
msgid "Unknown"
|
||||||
|
msgstr "Inconnu"
|
||||||
|
|
||||||
|
msgid "Unknown location"
|
||||||
|
msgstr "Localisation inconnue"
|
||||||
|
|
||||||
|
msgid "Unspecified address"
|
||||||
|
msgstr "Adresse non spécifiée"
|
||||||
|
|
||||||
|
msgid "Update database"
|
||||||
|
msgstr "Mettre à jour la base"
|
||||||
|
|
||||||
|
msgid "Upload an uncompressed Country, City or ASN .mmdb file. To add ASN, install a Country or City database first."
|
||||||
|
msgstr "Importez un fichier Country, City ou ASN .mmdb non compressé. Installez Country ou City avant d’ajouter ASN."
|
||||||
|
|
||||||
|
msgid "Upload and enable"
|
||||||
|
msgstr "Importer et activer"
|
||||||
|
|
||||||
|
msgid "Upload database"
|
||||||
|
msgstr "Importer une base"
|
||||||
|
|
||||||
|
msgid "Uploading database…"
|
||||||
|
msgstr "Envoi de la base…"
|
||||||
|
|
||||||
|
msgid "Use persistent storage or an external drive. Save and apply directory changes before downloading."
|
||||||
|
msgstr "Utilisez un stockage persistant ou externe. Enregistrez et appliquez le changement de répertoire avant de télécharger."
|
||||||
|
|
||||||
|
msgid "Verifying database…"
|
||||||
|
msgstr "Vérification de la base…"
|
||||||
|
|
||||||
|
msgid "Version"
|
||||||
|
msgstr "Version"
|
||||||
|
|
||||||
|
msgid "Country/region"
|
||||||
|
msgstr "Pays/région"
|
||||||
|
|
||||||
|
msgid "Province/city"
|
||||||
|
msgstr "Région/ville"
|
||||||
|
|
||||||
|
msgid "Network organization"
|
||||||
|
msgstr "Organisation réseau"
|
||||||
|
|
||||||
|
msgid "Not enough free space for this database."
|
||||||
|
msgstr "Espace insuffisant pour cette base."
|
||||||
|
|
||||||
|
msgid "Required space"
|
||||||
|
msgstr "Espace nécessaire"
|
||||||
|
|
||||||
|
msgid "Database ready"
|
||||||
|
msgstr "Base prête"
|
||||||
|
|
||||||
|
msgid "Listen Address"
|
||||||
|
msgstr "Adresse d’écoute"
|
||||||
|
|
||||||
|
msgid "IP address for Bandix to listen on. Use 127.0.0.1 for local access or 0.0.0.0 / :: for all interfaces."
|
||||||
|
msgstr "Adresse IP d’écoute de Bandix. Utilisez 127.0.0.1 pour un accès local ou 0.0.0.0 / :: pour toutes les interfaces."
|
||||||
+189
-30
@@ -221,12 +221,6 @@ msgstr "WAN アップロード済み"
|
|||||||
msgid "WAN Downloaded"
|
msgid "WAN Downloaded"
|
||||||
msgstr "WAN ダウンロード済み"
|
msgstr "WAN ダウンロード済み"
|
||||||
|
|
||||||
msgid "Total Upload"
|
|
||||||
msgstr "総アップロード速度"
|
|
||||||
|
|
||||||
msgid "Total Download"
|
|
||||||
msgstr "総ダウンロード速度"
|
|
||||||
|
|
||||||
msgid "LAN Upload"
|
msgid "LAN Upload"
|
||||||
msgstr "LAN アップロード速度"
|
msgstr "LAN アップロード速度"
|
||||||
|
|
||||||
@@ -422,18 +416,12 @@ msgstr "ページ"
|
|||||||
msgid "of"
|
msgid "of"
|
||||||
msgstr "の"
|
msgstr "の"
|
||||||
|
|
||||||
msgid "Total"
|
|
||||||
msgstr "合計"
|
|
||||||
|
|
||||||
msgid "records"
|
msgid "records"
|
||||||
msgstr "件の記録"
|
msgstr "件の記録"
|
||||||
|
|
||||||
msgid "Per Page"
|
msgid "Per Page"
|
||||||
msgstr "ページあたり"
|
msgstr "ページあたり"
|
||||||
|
|
||||||
msgid ""
|
|
||||||
msgstr "件"
|
|
||||||
|
|
||||||
msgid "Total Queries"
|
msgid "Total Queries"
|
||||||
msgstr "総クエリ数"
|
msgstr "総クエリ数"
|
||||||
|
|
||||||
@@ -938,18 +926,6 @@ msgstr "最大"
|
|||||||
msgid "Minimum"
|
msgid "Minimum"
|
||||||
msgstr "最小"
|
msgstr "最小"
|
||||||
|
|
||||||
msgid "WAN Upload"
|
|
||||||
msgstr "WAN アップロード"
|
|
||||||
|
|
||||||
msgid "WAN Download"
|
|
||||||
msgstr "WAN ダウンロード"
|
|
||||||
|
|
||||||
msgid "WAN Uploaded"
|
|
||||||
msgstr "WAN アップロード量"
|
|
||||||
|
|
||||||
msgid "WAN Downloaded"
|
|
||||||
msgstr "WAN ダウンロード量"
|
|
||||||
|
|
||||||
msgid "Clear Traffic Data"
|
msgid "Clear Traffic Data"
|
||||||
msgstr "トラフィックデータを削除"
|
msgstr "トラフィックデータを削除"
|
||||||
|
|
||||||
@@ -1082,9 +1058,6 @@ msgstr "ダウンロードしてインストール"
|
|||||||
msgid "Manual Download"
|
msgid "Manual Download"
|
||||||
msgstr "手動ダウンロード"
|
msgstr "手動ダウンロード"
|
||||||
|
|
||||||
msgid "Close"
|
|
||||||
msgstr "閉じる"
|
|
||||||
|
|
||||||
msgid "Starting installation... The page will refresh automatically in 30 seconds."
|
msgid "Starting installation... The page will refresh automatically in 30 seconds."
|
||||||
msgstr "インストールを開始しています... ページは30秒後に自動的に更新されます。"
|
msgstr "インストールを開始しています... ページは30秒後に自動的に更新されます。"
|
||||||
|
|
||||||
@@ -1094,9 +1067,6 @@ msgstr "更新後、ブラウザのキャッシュを手動でクリアしてく
|
|||||||
msgid "Traffic Statistics"
|
msgid "Traffic Statistics"
|
||||||
msgstr "トラフィック統計"
|
msgstr "トラフィック統計"
|
||||||
|
|
||||||
msgid "Traffic Statistics"
|
|
||||||
msgstr "トラフィック統計"
|
|
||||||
|
|
||||||
msgid "Device Usage Ranking"
|
msgid "Device Usage Ranking"
|
||||||
msgstr "デバイス使用量ランキング"
|
msgstr "デバイス使用量ランキング"
|
||||||
|
|
||||||
@@ -1351,3 +1321,192 @@ msgstr "利用不可"
|
|||||||
|
|
||||||
msgid "Show reply direction"
|
msgid "Show reply direction"
|
||||||
msgstr "応答方向を表示"
|
msgstr "応答方向を表示"
|
||||||
|
|
||||||
|
msgid "Available space"
|
||||||
|
msgstr "空き容量"
|
||||||
|
|
||||||
|
msgid "Cannot prepare storage. Check free space and directory permissions."
|
||||||
|
msgstr "保存先を準備できません。空き容量と権限を確認してください。"
|
||||||
|
|
||||||
|
msgid "Checking database version…"
|
||||||
|
msgstr "データベースのバージョンを確認中…"
|
||||||
|
|
||||||
|
msgid "Checking storage…"
|
||||||
|
msgstr "空き容量を確認中…"
|
||||||
|
|
||||||
|
msgid "Choose an uncompressed .mmdb file within the upload size limit."
|
||||||
|
msgstr "上限サイズ以内の未圧縮 .mmdb ファイルを選択してください。"
|
||||||
|
|
||||||
|
msgid "Country/region + network organization — approximately 12 MiB"
|
||||||
|
msgstr "国・地域 + ネットワーク組織 · 約 12 MiB"
|
||||||
|
|
||||||
|
msgid "Country/region — approximately 3.3 MiB"
|
||||||
|
msgstr "国・地域 · 約 3.3 MiB"
|
||||||
|
|
||||||
|
msgid "Data source"
|
||||||
|
msgstr "データ提供元"
|
||||||
|
|
||||||
|
msgid "Data terms"
|
||||||
|
msgstr "データ利用規約"
|
||||||
|
|
||||||
|
msgid "Database contents"
|
||||||
|
msgstr "データベースの内容"
|
||||||
|
|
||||||
|
msgid "Database directory"
|
||||||
|
msgstr "データベース保存先"
|
||||||
|
|
||||||
|
msgid "Database enabled"
|
||||||
|
msgstr "データベースを有効にしました"
|
||||||
|
|
||||||
|
msgid "Database is up to date"
|
||||||
|
msgstr "データベースは最新です"
|
||||||
|
|
||||||
|
msgid "Database operation failed"
|
||||||
|
msgstr "データベース操作に失敗しました"
|
||||||
|
|
||||||
|
msgid "Database operation failed. Retry or upload a local database."
|
||||||
|
msgstr "操作に失敗しました。再試行するかローカルのデータベースをアップロードしてください。"
|
||||||
|
|
||||||
|
msgid "Database upload failed"
|
||||||
|
msgstr "データベースのアップロードに失敗しました"
|
||||||
|
|
||||||
|
msgid "Database validation failed. The previous database is unchanged."
|
||||||
|
msgstr "検証に失敗しました。既存のデータベースは保持されています。"
|
||||||
|
|
||||||
|
msgid "Delete"
|
||||||
|
msgstr "削除"
|
||||||
|
|
||||||
|
msgid "Delete GeoIP databases"
|
||||||
|
msgstr "GeoIP データベースを削除"
|
||||||
|
|
||||||
|
msgid "Disabled"
|
||||||
|
msgstr "無効"
|
||||||
|
|
||||||
|
msgid "Documentation address"
|
||||||
|
msgstr "文書用アドレス"
|
||||||
|
|
||||||
|
msgid "Download and enable"
|
||||||
|
msgstr "ダウンロードして有効化"
|
||||||
|
|
||||||
|
msgid "Download cancelled"
|
||||||
|
msgstr "ダウンロードを中止しました"
|
||||||
|
|
||||||
|
msgid "Downloading database…"
|
||||||
|
msgstr "データベースをダウンロード中…"
|
||||||
|
|
||||||
|
msgid "Enable IP location lookup"
|
||||||
|
msgstr "IP 位置情報の検索を有効化"
|
||||||
|
|
||||||
|
msgid "Enabled"
|
||||||
|
msgstr "有効"
|
||||||
|
|
||||||
|
msgid "GeoIP service is unavailable. Enable connection monitoring and apply settings, or update Bandix."
|
||||||
|
msgstr "GeoIP を利用できません。接続監視を有効にして設定を適用するか、Bandix を更新してください。"
|
||||||
|
|
||||||
|
msgid "IP location database"
|
||||||
|
msgstr "IP 位置情報データベース"
|
||||||
|
|
||||||
|
msgid "Importing database…"
|
||||||
|
msgstr "データベースをインポート中…"
|
||||||
|
|
||||||
|
msgid "Link-local address"
|
||||||
|
msgstr "リンクローカルアドレス"
|
||||||
|
|
||||||
|
msgid "Loading database…"
|
||||||
|
msgstr "データベースを読み込み中…"
|
||||||
|
|
||||||
|
msgid "Local network"
|
||||||
|
msgstr "ローカルネットワーク"
|
||||||
|
|
||||||
|
msgid "Loopback"
|
||||||
|
msgstr "ループバック"
|
||||||
|
|
||||||
|
msgid "Maximum upload size"
|
||||||
|
msgstr "最大アップロードサイズ"
|
||||||
|
|
||||||
|
msgid "Multicast"
|
||||||
|
msgstr "マルチキャスト"
|
||||||
|
|
||||||
|
msgid "Not installed"
|
||||||
|
msgstr "未インストール"
|
||||||
|
|
||||||
|
msgid "Private address"
|
||||||
|
msgstr "プライベートアドレス"
|
||||||
|
|
||||||
|
msgid "Province/city (uploaded database)"
|
||||||
|
msgstr "都道府県・都市(アップロード済み)"
|
||||||
|
|
||||||
|
msgid "Province/city + network organization — approximately 57 MiB"
|
||||||
|
msgstr "都道府県・都市 + ネットワーク組織 · 約 57 MiB"
|
||||||
|
|
||||||
|
msgid "Remove installed databases and disable IP location lookup?"
|
||||||
|
msgstr "データベースを削除し、IP 位置情報の検索を無効にしますか?"
|
||||||
|
|
||||||
|
msgid "Reserved address"
|
||||||
|
msgstr "予約済みアドレス"
|
||||||
|
|
||||||
|
msgid "Shared address (CGNAT)"
|
||||||
|
msgstr "共有アドレス(CGNAT)"
|
||||||
|
|
||||||
|
msgid "Storage used"
|
||||||
|
msgstr "使用容量"
|
||||||
|
|
||||||
|
msgid "The installed database could not be loaded. Download or upload it again."
|
||||||
|
msgstr "データベースを読み込めません。再度ダウンロードまたはアップロードしてください。"
|
||||||
|
|
||||||
|
msgid "Unknown"
|
||||||
|
msgstr "不明"
|
||||||
|
|
||||||
|
msgid "Unknown location"
|
||||||
|
msgstr "位置情報不明"
|
||||||
|
|
||||||
|
msgid "Unspecified address"
|
||||||
|
msgstr "未指定アドレス"
|
||||||
|
|
||||||
|
msgid "Update database"
|
||||||
|
msgstr "データベースを更新"
|
||||||
|
|
||||||
|
msgid "Upload an uncompressed Country, City or ASN .mmdb file. To add ASN, install a Country or City database first."
|
||||||
|
msgstr "未圧縮の Country、City、ASN の .mmdb ファイルをアップロードできます。ASN の追加前に Country または City をインストールしてください。"
|
||||||
|
|
||||||
|
msgid "Upload and enable"
|
||||||
|
msgstr "アップロードして有効化"
|
||||||
|
|
||||||
|
msgid "Upload database"
|
||||||
|
msgstr "データベースをアップロード"
|
||||||
|
|
||||||
|
msgid "Uploading database…"
|
||||||
|
msgstr "データベースをアップロード中…"
|
||||||
|
|
||||||
|
msgid "Use persistent storage or an external drive. Save and apply directory changes before downloading."
|
||||||
|
msgstr "永続ストレージまたは外付けドライブを使用してください。保存先の変更を保存・適用してからダウンロードしてください。"
|
||||||
|
|
||||||
|
msgid "Verifying database…"
|
||||||
|
msgstr "データベースを検証中…"
|
||||||
|
|
||||||
|
msgid "Version"
|
||||||
|
msgstr "バージョン"
|
||||||
|
|
||||||
|
msgid "Country/region"
|
||||||
|
msgstr "国・地域"
|
||||||
|
|
||||||
|
msgid "Province/city"
|
||||||
|
msgstr "都道府県・都市"
|
||||||
|
|
||||||
|
msgid "Network organization"
|
||||||
|
msgstr "ネットワーク組織"
|
||||||
|
|
||||||
|
msgid "Not enough free space for this database."
|
||||||
|
msgstr "このデータベースを保存する空き容量が不足しています。"
|
||||||
|
|
||||||
|
msgid "Required space"
|
||||||
|
msgstr "必要な容量"
|
||||||
|
|
||||||
|
msgid "Database ready"
|
||||||
|
msgstr "データベースの準備完了"
|
||||||
|
|
||||||
|
msgid "Listen Address"
|
||||||
|
msgstr "待ち受けアドレス"
|
||||||
|
|
||||||
|
msgid "IP address for Bandix to listen on. Use 127.0.0.1 for local access or 0.0.0.0 / :: for all interfaces."
|
||||||
|
msgstr "Bandix が待ち受ける IP アドレス。ローカルアクセスには 127.0.0.1、すべてのインターフェースには 0.0.0.0 / :: を指定します。"
|
||||||
+189
-30
@@ -221,12 +221,6 @@ msgstr "WAN wysłane"
|
|||||||
msgid "WAN Downloaded"
|
msgid "WAN Downloaded"
|
||||||
msgstr "WAN pobrane"
|
msgstr "WAN pobrane"
|
||||||
|
|
||||||
msgid "Total Upload"
|
|
||||||
msgstr "Całkowite wysyłanie"
|
|
||||||
|
|
||||||
msgid "Total Download"
|
|
||||||
msgstr "Całkowite pobieranie"
|
|
||||||
|
|
||||||
msgid "LAN Upload"
|
msgid "LAN Upload"
|
||||||
msgstr "Wysyłanie LAN"
|
msgstr "Wysyłanie LAN"
|
||||||
|
|
||||||
@@ -422,18 +416,12 @@ msgstr "Strona"
|
|||||||
msgid "of"
|
msgid "of"
|
||||||
msgstr "z"
|
msgstr "z"
|
||||||
|
|
||||||
msgid "Total"
|
|
||||||
msgstr "Całkowity"
|
|
||||||
|
|
||||||
msgid "records"
|
msgid "records"
|
||||||
msgstr "rekordów"
|
msgstr "rekordów"
|
||||||
|
|
||||||
msgid "Per Page"
|
msgid "Per Page"
|
||||||
msgstr "Na stronę"
|
msgstr "Na stronę"
|
||||||
|
|
||||||
msgid ""
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Total Queries"
|
msgid "Total Queries"
|
||||||
msgstr "Całkowite zapytania"
|
msgstr "Całkowite zapytania"
|
||||||
|
|
||||||
@@ -938,18 +926,6 @@ msgstr "Maksimum"
|
|||||||
msgid "Minimum"
|
msgid "Minimum"
|
||||||
msgstr "Minimum"
|
msgstr "Minimum"
|
||||||
|
|
||||||
msgid "WAN Upload"
|
|
||||||
msgstr "Wysyłanie WAN"
|
|
||||||
|
|
||||||
msgid "WAN Download"
|
|
||||||
msgstr "Pobieranie WAN"
|
|
||||||
|
|
||||||
msgid "WAN Uploaded"
|
|
||||||
msgstr "Wysłano WAN"
|
|
||||||
|
|
||||||
msgid "WAN Downloaded"
|
|
||||||
msgstr "Pobrano WAN"
|
|
||||||
|
|
||||||
msgid "Clear Traffic Data"
|
msgid "Clear Traffic Data"
|
||||||
msgstr "Wyczyść dane ruchu"
|
msgstr "Wyczyść dane ruchu"
|
||||||
|
|
||||||
@@ -1082,9 +1058,6 @@ msgstr "Pobierz i Zainstaluj"
|
|||||||
msgid "Manual Download"
|
msgid "Manual Download"
|
||||||
msgstr "Pobieranie Ręczne"
|
msgstr "Pobieranie Ręczne"
|
||||||
|
|
||||||
msgid "Close"
|
|
||||||
msgstr "Zamknij"
|
|
||||||
|
|
||||||
msgid "Starting installation... The page will refresh automatically in 30 seconds."
|
msgid "Starting installation... The page will refresh automatically in 30 seconds."
|
||||||
msgstr "Rozpoczynanie instalacji... Strona odświeży się automatycznie za 30 sekund."
|
msgstr "Rozpoczynanie instalacji... Strona odświeży się automatycznie za 30 sekund."
|
||||||
|
|
||||||
@@ -1094,9 +1067,6 @@ msgstr "Po aktualizacji należy ręcznie wyczyścić pamięć podręczną przegl
|
|||||||
msgid "Traffic Statistics"
|
msgid "Traffic Statistics"
|
||||||
msgstr "Statystyki ruchu"
|
msgstr "Statystyki ruchu"
|
||||||
|
|
||||||
msgid "Traffic Statistics"
|
|
||||||
msgstr "Statystyki ruchu"
|
|
||||||
|
|
||||||
msgid "Device Usage Ranking"
|
msgid "Device Usage Ranking"
|
||||||
msgstr "Ranking Użycia Urządzeń"
|
msgstr "Ranking Użycia Urządzeń"
|
||||||
|
|
||||||
@@ -1351,3 +1321,192 @@ msgstr "Niedostępne"
|
|||||||
|
|
||||||
msgid "Show reply direction"
|
msgid "Show reply direction"
|
||||||
msgstr "Pokaż kierunek odpowiedzi"
|
msgstr "Pokaż kierunek odpowiedzi"
|
||||||
|
|
||||||
|
msgid "Available space"
|
||||||
|
msgstr "Wolne miejsce"
|
||||||
|
|
||||||
|
msgid "Cannot prepare storage. Check free space and directory permissions."
|
||||||
|
msgstr "Nie można przygotować pamięci. Sprawdź wolne miejsce i uprawnienia katalogu."
|
||||||
|
|
||||||
|
msgid "Checking database version…"
|
||||||
|
msgstr "Sprawdzanie wersji bazy…"
|
||||||
|
|
||||||
|
msgid "Checking storage…"
|
||||||
|
msgstr "Sprawdzanie pamięci…"
|
||||||
|
|
||||||
|
msgid "Choose an uncompressed .mmdb file within the upload size limit."
|
||||||
|
msgstr "Wybierz nieskompresowany plik .mmdb mieszczący się w limicie rozmiaru."
|
||||||
|
|
||||||
|
msgid "Country/region + network organization — approximately 12 MiB"
|
||||||
|
msgstr "Kraj/region + organizacja sieciowa · ok. 12 MiB"
|
||||||
|
|
||||||
|
msgid "Country/region — approximately 3.3 MiB"
|
||||||
|
msgstr "Kraj/region · ok. 3,3 MiB"
|
||||||
|
|
||||||
|
msgid "Data source"
|
||||||
|
msgstr "Źródło danych"
|
||||||
|
|
||||||
|
msgid "Data terms"
|
||||||
|
msgstr "Warunki korzystania"
|
||||||
|
|
||||||
|
msgid "Database contents"
|
||||||
|
msgstr "Zawartość bazy"
|
||||||
|
|
||||||
|
msgid "Database directory"
|
||||||
|
msgstr "Katalog bazy"
|
||||||
|
|
||||||
|
msgid "Database enabled"
|
||||||
|
msgstr "Baza danych włączona"
|
||||||
|
|
||||||
|
msgid "Database is up to date"
|
||||||
|
msgstr "Baza danych jest aktualna"
|
||||||
|
|
||||||
|
msgid "Database operation failed"
|
||||||
|
msgstr "Operacja na bazie nie powiodła się"
|
||||||
|
|
||||||
|
msgid "Database operation failed. Retry or upload a local database."
|
||||||
|
msgstr "Operacja nie powiodła się. Spróbuj ponownie lub prześlij lokalną bazę."
|
||||||
|
|
||||||
|
msgid "Database upload failed"
|
||||||
|
msgstr "Przesyłanie bazy nie powiodło się"
|
||||||
|
|
||||||
|
msgid "Database validation failed. The previous database is unchanged."
|
||||||
|
msgstr "Weryfikacja nie powiodła się. Poprzednia baza pozostaje bez zmian."
|
||||||
|
|
||||||
|
msgid "Delete"
|
||||||
|
msgstr "Usuń"
|
||||||
|
|
||||||
|
msgid "Delete GeoIP databases"
|
||||||
|
msgstr "Usuń bazy GeoIP"
|
||||||
|
|
||||||
|
msgid "Disabled"
|
||||||
|
msgstr "Wyłączone"
|
||||||
|
|
||||||
|
msgid "Documentation address"
|
||||||
|
msgstr "Adres dokumentacyjny"
|
||||||
|
|
||||||
|
msgid "Download and enable"
|
||||||
|
msgstr "Pobierz i włącz"
|
||||||
|
|
||||||
|
msgid "Download cancelled"
|
||||||
|
msgstr "Pobieranie anulowane"
|
||||||
|
|
||||||
|
msgid "Downloading database…"
|
||||||
|
msgstr "Pobieranie bazy…"
|
||||||
|
|
||||||
|
msgid "Enable IP location lookup"
|
||||||
|
msgstr "Włącz geolokalizację IP"
|
||||||
|
|
||||||
|
msgid "Enabled"
|
||||||
|
msgstr "Włączone"
|
||||||
|
|
||||||
|
msgid "GeoIP service is unavailable. Enable connection monitoring and apply settings, or update Bandix."
|
||||||
|
msgstr "GeoIP jest niedostępne. Włącz monitorowanie połączeń i zastosuj ustawienia lub zaktualizuj Bandix."
|
||||||
|
|
||||||
|
msgid "IP location database"
|
||||||
|
msgstr "Baza geolokalizacji IP"
|
||||||
|
|
||||||
|
msgid "Importing database…"
|
||||||
|
msgstr "Importowanie bazy…"
|
||||||
|
|
||||||
|
msgid "Link-local address"
|
||||||
|
msgstr "Adres lokalny łącza"
|
||||||
|
|
||||||
|
msgid "Loading database…"
|
||||||
|
msgstr "Wczytywanie bazy…"
|
||||||
|
|
||||||
|
msgid "Local network"
|
||||||
|
msgstr "Sieć lokalna"
|
||||||
|
|
||||||
|
msgid "Loopback"
|
||||||
|
msgstr "Pętla zwrotna"
|
||||||
|
|
||||||
|
msgid "Maximum upload size"
|
||||||
|
msgstr "Maksymalny rozmiar pliku"
|
||||||
|
|
||||||
|
msgid "Multicast"
|
||||||
|
msgstr "Multicast"
|
||||||
|
|
||||||
|
msgid "Not installed"
|
||||||
|
msgstr "Niezainstalowane"
|
||||||
|
|
||||||
|
msgid "Private address"
|
||||||
|
msgstr "Adres prywatny"
|
||||||
|
|
||||||
|
msgid "Province/city (uploaded database)"
|
||||||
|
msgstr "Region/miasto (przesłana baza)"
|
||||||
|
|
||||||
|
msgid "Province/city + network organization — approximately 57 MiB"
|
||||||
|
msgstr "Region/miasto + organizacja sieciowa · ok. 57 MiB"
|
||||||
|
|
||||||
|
msgid "Remove installed databases and disable IP location lookup?"
|
||||||
|
msgstr "Usunąć bazy i wyłączyć geolokalizację IP?"
|
||||||
|
|
||||||
|
msgid "Reserved address"
|
||||||
|
msgstr "Adres zarezerwowany"
|
||||||
|
|
||||||
|
msgid "Shared address (CGNAT)"
|
||||||
|
msgstr "Adres współdzielony (CGNAT)"
|
||||||
|
|
||||||
|
msgid "Storage used"
|
||||||
|
msgstr "Zajęte miejsce"
|
||||||
|
|
||||||
|
msgid "The installed database could not be loaded. Download or upload it again."
|
||||||
|
msgstr "Nie można wczytać bazy. Pobierz lub prześlij ją ponownie."
|
||||||
|
|
||||||
|
msgid "Unknown"
|
||||||
|
msgstr "Nieznane"
|
||||||
|
|
||||||
|
msgid "Unknown location"
|
||||||
|
msgstr "Nieznana lokalizacja"
|
||||||
|
|
||||||
|
msgid "Unspecified address"
|
||||||
|
msgstr "Adres nieokreślony"
|
||||||
|
|
||||||
|
msgid "Update database"
|
||||||
|
msgstr "Aktualizuj bazę"
|
||||||
|
|
||||||
|
msgid "Upload an uncompressed Country, City or ASN .mmdb file. To add ASN, install a Country or City database first."
|
||||||
|
msgstr "Prześlij nieskompresowany plik Country, City lub ASN .mmdb. Przed ASN zainstaluj Country lub City."
|
||||||
|
|
||||||
|
msgid "Upload and enable"
|
||||||
|
msgstr "Prześlij i włącz"
|
||||||
|
|
||||||
|
msgid "Upload database"
|
||||||
|
msgstr "Prześlij bazę"
|
||||||
|
|
||||||
|
msgid "Uploading database…"
|
||||||
|
msgstr "Przesyłanie bazy…"
|
||||||
|
|
||||||
|
msgid "Use persistent storage or an external drive. Save and apply directory changes before downloading."
|
||||||
|
msgstr "Użyj pamięci trwałej lub dysku zewnętrznego. Zapisz i zastosuj zmianę katalogu przed pobieraniem."
|
||||||
|
|
||||||
|
msgid "Verifying database…"
|
||||||
|
msgstr "Weryfikacja bazy…"
|
||||||
|
|
||||||
|
msgid "Version"
|
||||||
|
msgstr "Wersja"
|
||||||
|
|
||||||
|
msgid "Country/region"
|
||||||
|
msgstr "Kraj/region"
|
||||||
|
|
||||||
|
msgid "Province/city"
|
||||||
|
msgstr "Region/miasto"
|
||||||
|
|
||||||
|
msgid "Network organization"
|
||||||
|
msgstr "Organizacja sieciowa"
|
||||||
|
|
||||||
|
msgid "Not enough free space for this database."
|
||||||
|
msgstr "Brak miejsca na tę bazę danych."
|
||||||
|
|
||||||
|
msgid "Required space"
|
||||||
|
msgstr "Wymagane miejsce"
|
||||||
|
|
||||||
|
msgid "Database ready"
|
||||||
|
msgstr "Baza gotowa"
|
||||||
|
|
||||||
|
msgid "Listen Address"
|
||||||
|
msgstr "Adres nasłuchiwania"
|
||||||
|
|
||||||
|
msgid "IP address for Bandix to listen on. Use 127.0.0.1 for local access or 0.0.0.0 / :: for all interfaces."
|
||||||
|
msgstr "Adres IP nasłuchiwania Bandix. Użyj 127.0.0.1 dla dostępu lokalnego lub 0.0.0.0 / :: dla wszystkich interfejsów."
|
||||||
+189
-30
@@ -221,12 +221,6 @@ msgstr "WAN отправлено"
|
|||||||
msgid "WAN Downloaded"
|
msgid "WAN Downloaded"
|
||||||
msgstr "WAN загружено"
|
msgstr "WAN загружено"
|
||||||
|
|
||||||
msgid "Total Upload"
|
|
||||||
msgstr "Всего выгружено"
|
|
||||||
|
|
||||||
msgid "Total Download"
|
|
||||||
msgstr "Всего загружено"
|
|
||||||
|
|
||||||
msgid "LAN Upload"
|
msgid "LAN Upload"
|
||||||
msgstr "Скорость отправки LAN"
|
msgstr "Скорость отправки LAN"
|
||||||
|
|
||||||
@@ -422,18 +416,12 @@ msgstr "Страница"
|
|||||||
msgid "of"
|
msgid "of"
|
||||||
msgstr "из"
|
msgstr "из"
|
||||||
|
|
||||||
msgid "Total"
|
|
||||||
msgstr "Всего"
|
|
||||||
|
|
||||||
msgid "records"
|
msgid "records"
|
||||||
msgstr "записей"
|
msgstr "записей"
|
||||||
|
|
||||||
msgid "Per Page"
|
msgid "Per Page"
|
||||||
msgstr "На странице"
|
msgstr "На странице"
|
||||||
|
|
||||||
msgid ""
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Total Queries"
|
msgid "Total Queries"
|
||||||
msgstr "Всего запросов"
|
msgstr "Всего запросов"
|
||||||
|
|
||||||
@@ -938,18 +926,6 @@ msgstr "Максимум"
|
|||||||
msgid "Minimum"
|
msgid "Minimum"
|
||||||
msgstr "Минимум"
|
msgstr "Минимум"
|
||||||
|
|
||||||
msgid "WAN Upload"
|
|
||||||
msgstr "Отправка WAN"
|
|
||||||
|
|
||||||
msgid "WAN Download"
|
|
||||||
msgstr "Загрузка WAN"
|
|
||||||
|
|
||||||
msgid "WAN Uploaded"
|
|
||||||
msgstr "Отправлено WAN"
|
|
||||||
|
|
||||||
msgid "WAN Downloaded"
|
|
||||||
msgstr "Загружено WAN"
|
|
||||||
|
|
||||||
msgid "Clear Traffic Data"
|
msgid "Clear Traffic Data"
|
||||||
msgstr "Очистить данные трафика"
|
msgstr "Очистить данные трафика"
|
||||||
|
|
||||||
@@ -1082,9 +1058,6 @@ msgstr "Скачать и Установить"
|
|||||||
msgid "Manual Download"
|
msgid "Manual Download"
|
||||||
msgstr "Ручная Загрузка"
|
msgstr "Ручная Загрузка"
|
||||||
|
|
||||||
msgid "Close"
|
|
||||||
msgstr "Закрыть"
|
|
||||||
|
|
||||||
msgid "Starting installation... The page will refresh automatically in 30 seconds."
|
msgid "Starting installation... The page will refresh automatically in 30 seconds."
|
||||||
msgstr "Начало установки... Страница автоматически обновится через 30 секунд."
|
msgstr "Начало установки... Страница автоматически обновится через 30 секунд."
|
||||||
|
|
||||||
@@ -1094,9 +1067,6 @@ msgstr "Пожалуйста, вручную очистите кеш брауз
|
|||||||
msgid "Traffic Statistics"
|
msgid "Traffic Statistics"
|
||||||
msgstr "Статистика трафика"
|
msgstr "Статистика трафика"
|
||||||
|
|
||||||
msgid "Traffic Statistics"
|
|
||||||
msgstr "Статистика трафика"
|
|
||||||
|
|
||||||
msgid "Device Usage Ranking"
|
msgid "Device Usage Ranking"
|
||||||
msgstr "Рейтинг использования устройств"
|
msgstr "Рейтинг использования устройств"
|
||||||
|
|
||||||
@@ -1351,3 +1321,192 @@ msgstr "Недоступно"
|
|||||||
|
|
||||||
msgid "Show reply direction"
|
msgid "Show reply direction"
|
||||||
msgstr "Показывать обратное направление"
|
msgstr "Показывать обратное направление"
|
||||||
|
|
||||||
|
msgid "Available space"
|
||||||
|
msgstr "Свободное место"
|
||||||
|
|
||||||
|
msgid "Cannot prepare storage. Check free space and directory permissions."
|
||||||
|
msgstr "Не удалось подготовить хранилище. Проверьте свободное место и права доступа."
|
||||||
|
|
||||||
|
msgid "Checking database version…"
|
||||||
|
msgstr "Проверка версии базы…"
|
||||||
|
|
||||||
|
msgid "Checking storage…"
|
||||||
|
msgstr "Проверка хранилища…"
|
||||||
|
|
||||||
|
msgid "Choose an uncompressed .mmdb file within the upload size limit."
|
||||||
|
msgstr "Выберите несжатый файл .mmdb, не превышающий ограничение размера."
|
||||||
|
|
||||||
|
msgid "Country/region + network organization — approximately 12 MiB"
|
||||||
|
msgstr "Страна/регион + сетевая организация · около 12 MiB"
|
||||||
|
|
||||||
|
msgid "Country/region — approximately 3.3 MiB"
|
||||||
|
msgstr "Страна/регион · около 3,3 MiB"
|
||||||
|
|
||||||
|
msgid "Data source"
|
||||||
|
msgstr "Источник данных"
|
||||||
|
|
||||||
|
msgid "Data terms"
|
||||||
|
msgstr "Условия использования"
|
||||||
|
|
||||||
|
msgid "Database contents"
|
||||||
|
msgstr "Содержимое базы"
|
||||||
|
|
||||||
|
msgid "Database directory"
|
||||||
|
msgstr "Каталог базы"
|
||||||
|
|
||||||
|
msgid "Database enabled"
|
||||||
|
msgstr "База данных включена"
|
||||||
|
|
||||||
|
msgid "Database is up to date"
|
||||||
|
msgstr "База данных актуальна"
|
||||||
|
|
||||||
|
msgid "Database operation failed"
|
||||||
|
msgstr "Ошибка операции с базой"
|
||||||
|
|
||||||
|
msgid "Database operation failed. Retry or upload a local database."
|
||||||
|
msgstr "Операция не удалась. Повторите попытку или загрузите локальную базу."
|
||||||
|
|
||||||
|
msgid "Database upload failed"
|
||||||
|
msgstr "Не удалось загрузить базу"
|
||||||
|
|
||||||
|
msgid "Database validation failed. The previous database is unchanged."
|
||||||
|
msgstr "Проверка не пройдена. Предыдущая база сохранена."
|
||||||
|
|
||||||
|
msgid "Delete"
|
||||||
|
msgstr "Удалить"
|
||||||
|
|
||||||
|
msgid "Delete GeoIP databases"
|
||||||
|
msgstr "Удалить базы GeoIP"
|
||||||
|
|
||||||
|
msgid "Disabled"
|
||||||
|
msgstr "Отключено"
|
||||||
|
|
||||||
|
msgid "Documentation address"
|
||||||
|
msgstr "Адрес для документации"
|
||||||
|
|
||||||
|
msgid "Download and enable"
|
||||||
|
msgstr "Скачать и включить"
|
||||||
|
|
||||||
|
msgid "Download cancelled"
|
||||||
|
msgstr "Загрузка отменена"
|
||||||
|
|
||||||
|
msgid "Downloading database…"
|
||||||
|
msgstr "Скачивание базы…"
|
||||||
|
|
||||||
|
msgid "Enable IP location lookup"
|
||||||
|
msgstr "Включить геолокацию IP"
|
||||||
|
|
||||||
|
msgid "Enabled"
|
||||||
|
msgstr "Включено"
|
||||||
|
|
||||||
|
msgid "GeoIP service is unavailable. Enable connection monitoring and apply settings, or update Bandix."
|
||||||
|
msgstr "GeoIP недоступен. Включите мониторинг соединений и примените настройки либо обновите Bandix."
|
||||||
|
|
||||||
|
msgid "IP location database"
|
||||||
|
msgstr "База геолокации IP"
|
||||||
|
|
||||||
|
msgid "Importing database…"
|
||||||
|
msgstr "Импорт базы…"
|
||||||
|
|
||||||
|
msgid "Link-local address"
|
||||||
|
msgstr "Локальный адрес канала"
|
||||||
|
|
||||||
|
msgid "Loading database…"
|
||||||
|
msgstr "Подключение базы…"
|
||||||
|
|
||||||
|
msgid "Local network"
|
||||||
|
msgstr "Локальная сеть"
|
||||||
|
|
||||||
|
msgid "Loopback"
|
||||||
|
msgstr "Петлевой адрес"
|
||||||
|
|
||||||
|
msgid "Maximum upload size"
|
||||||
|
msgstr "Максимальный размер загрузки"
|
||||||
|
|
||||||
|
msgid "Multicast"
|
||||||
|
msgstr "Многоадресная рассылка"
|
||||||
|
|
||||||
|
msgid "Not installed"
|
||||||
|
msgstr "Не установлено"
|
||||||
|
|
||||||
|
msgid "Private address"
|
||||||
|
msgstr "Частный адрес"
|
||||||
|
|
||||||
|
msgid "Province/city (uploaded database)"
|
||||||
|
msgstr "Регион/город (загруженная база)"
|
||||||
|
|
||||||
|
msgid "Province/city + network organization — approximately 57 MiB"
|
||||||
|
msgstr "Регион/город + сетевая организация · около 57 MiB"
|
||||||
|
|
||||||
|
msgid "Remove installed databases and disable IP location lookup?"
|
||||||
|
msgstr "Удалить базы и отключить геолокацию IP?"
|
||||||
|
|
||||||
|
msgid "Reserved address"
|
||||||
|
msgstr "Зарезервированный адрес"
|
||||||
|
|
||||||
|
msgid "Shared address (CGNAT)"
|
||||||
|
msgstr "Общий адрес (CGNAT)"
|
||||||
|
|
||||||
|
msgid "Storage used"
|
||||||
|
msgstr "Занято места"
|
||||||
|
|
||||||
|
msgid "The installed database could not be loaded. Download or upload it again."
|
||||||
|
msgstr "Не удалось открыть базу. Скачайте или загрузите её повторно."
|
||||||
|
|
||||||
|
msgid "Unknown"
|
||||||
|
msgstr "Неизвестно"
|
||||||
|
|
||||||
|
msgid "Unknown location"
|
||||||
|
msgstr "Местоположение неизвестно"
|
||||||
|
|
||||||
|
msgid "Unspecified address"
|
||||||
|
msgstr "Неуказанный адрес"
|
||||||
|
|
||||||
|
msgid "Update database"
|
||||||
|
msgstr "Обновить базу"
|
||||||
|
|
||||||
|
msgid "Upload an uncompressed Country, City or ASN .mmdb file. To add ASN, install a Country or City database first."
|
||||||
|
msgstr "Можно загрузить несжатый файл Country, City или ASN .mmdb. Перед ASN установите Country или City."
|
||||||
|
|
||||||
|
msgid "Upload and enable"
|
||||||
|
msgstr "Загрузить и включить"
|
||||||
|
|
||||||
|
msgid "Upload database"
|
||||||
|
msgstr "Загрузить базу"
|
||||||
|
|
||||||
|
msgid "Uploading database…"
|
||||||
|
msgstr "Загрузка базы…"
|
||||||
|
|
||||||
|
msgid "Use persistent storage or an external drive. Save and apply directory changes before downloading."
|
||||||
|
msgstr "Используйте постоянное хранилище или внешний диск. Перед скачиванием сохраните и примените изменение каталога."
|
||||||
|
|
||||||
|
msgid "Verifying database…"
|
||||||
|
msgstr "Проверка базы…"
|
||||||
|
|
||||||
|
msgid "Version"
|
||||||
|
msgstr "Версия"
|
||||||
|
|
||||||
|
msgid "Country/region"
|
||||||
|
msgstr "Страна/регион"
|
||||||
|
|
||||||
|
msgid "Province/city"
|
||||||
|
msgstr "Регион/город"
|
||||||
|
|
||||||
|
msgid "Network organization"
|
||||||
|
msgstr "Сетевая организация"
|
||||||
|
|
||||||
|
msgid "Not enough free space for this database."
|
||||||
|
msgstr "Недостаточно места для этой базы."
|
||||||
|
|
||||||
|
msgid "Required space"
|
||||||
|
msgstr "Требуемое место"
|
||||||
|
|
||||||
|
msgid "Database ready"
|
||||||
|
msgstr "База готова"
|
||||||
|
|
||||||
|
msgid "Listen Address"
|
||||||
|
msgstr "Адрес прослушивания"
|
||||||
|
|
||||||
|
msgid "IP address for Bandix to listen on. Use 127.0.0.1 for local access or 0.0.0.0 / :: for all interfaces."
|
||||||
|
msgstr "IP-адрес прослушивания Bandix. Используйте 127.0.0.1 для локального доступа или 0.0.0.0 / :: для всех интерфейсов."
|
||||||
@@ -221,12 +221,6 @@ msgstr "WAN 已上传"
|
|||||||
msgid "WAN Downloaded"
|
msgid "WAN Downloaded"
|
||||||
msgstr "WAN 已下载"
|
msgstr "WAN 已下载"
|
||||||
|
|
||||||
msgid "Total Upload"
|
|
||||||
msgstr "总上传速率"
|
|
||||||
|
|
||||||
msgid "Total Download"
|
|
||||||
msgstr "总下载速率"
|
|
||||||
|
|
||||||
msgid "LAN Upload"
|
msgid "LAN Upload"
|
||||||
msgstr "LAN 上传速率"
|
msgstr "LAN 上传速率"
|
||||||
|
|
||||||
@@ -419,18 +413,12 @@ msgstr "第"
|
|||||||
msgid "of"
|
msgid "of"
|
||||||
msgstr "页,共"
|
msgstr "页,共"
|
||||||
|
|
||||||
msgid "Total"
|
|
||||||
msgstr "全部"
|
|
||||||
|
|
||||||
msgid "records"
|
msgid "records"
|
||||||
msgstr "条记录"
|
msgstr "条记录"
|
||||||
|
|
||||||
msgid "Per Page"
|
msgid "Per Page"
|
||||||
msgstr "每页显示"
|
msgstr "每页显示"
|
||||||
|
|
||||||
msgid ""
|
|
||||||
msgstr "条"
|
|
||||||
|
|
||||||
msgid "Total Queries"
|
msgid "Total Queries"
|
||||||
msgstr "总查询数"
|
msgstr "总查询数"
|
||||||
|
|
||||||
@@ -852,7 +840,7 @@ msgid "Please select at least one day"
|
|||||||
msgstr "请至少选择一天"
|
msgstr "请至少选择一天"
|
||||||
|
|
||||||
msgid "No scheduled rules yet, click \"Add Rule\" to start setting"
|
msgid "No scheduled rules yet, click \"Add Rule\" to start setting"
|
||||||
msgstr "还没有定时限速规则,点击"添加规则"开始设置"
|
msgstr "还没有定时限速规则,点击\"添加规则\"开始设置"
|
||||||
|
|
||||||
msgid "Loading..."
|
msgid "Loading..."
|
||||||
msgstr "加载中..."
|
msgstr "加载中..."
|
||||||
@@ -935,18 +923,6 @@ msgstr "最大值"
|
|||||||
msgid "Minimum"
|
msgid "Minimum"
|
||||||
msgstr "最小值"
|
msgstr "最小值"
|
||||||
|
|
||||||
msgid "WAN Upload"
|
|
||||||
msgstr "WAN 上传"
|
|
||||||
|
|
||||||
msgid "WAN Download"
|
|
||||||
msgstr "WAN 下载"
|
|
||||||
|
|
||||||
msgid "WAN Uploaded"
|
|
||||||
msgstr "WAN 上传量"
|
|
||||||
|
|
||||||
msgid "WAN Downloaded"
|
|
||||||
msgstr "WAN 下载量"
|
|
||||||
|
|
||||||
msgid "Clear Traffic Data"
|
msgid "Clear Traffic Data"
|
||||||
msgstr "删除流量数据"
|
msgstr "删除流量数据"
|
||||||
|
|
||||||
@@ -1079,9 +1055,6 @@ msgstr "下载并安装"
|
|||||||
msgid "Manual Download"
|
msgid "Manual Download"
|
||||||
msgstr "手动下载"
|
msgstr "手动下载"
|
||||||
|
|
||||||
msgid "Close"
|
|
||||||
msgstr "关闭"
|
|
||||||
|
|
||||||
msgid "Starting installation... The page will refresh automatically in 30 seconds."
|
msgid "Starting installation... The page will refresh automatically in 30 seconds."
|
||||||
msgstr "开始安装... 页面将在30秒后自动刷新。"
|
msgstr "开始安装... 页面将在30秒后自动刷新。"
|
||||||
|
|
||||||
@@ -1097,9 +1070,6 @@ msgstr "有更新,点击前往设置"
|
|||||||
msgid "Traffic Statistics"
|
msgid "Traffic Statistics"
|
||||||
msgstr "流量统计"
|
msgstr "流量统计"
|
||||||
|
|
||||||
msgid "Traffic Statistics"
|
|
||||||
msgstr "流量统计"
|
|
||||||
|
|
||||||
msgid "Device Usage Ranking"
|
msgid "Device Usage Ranking"
|
||||||
msgstr "设备用量排行"
|
msgstr "设备用量排行"
|
||||||
|
|
||||||
@@ -1378,3 +1348,192 @@ msgstr "每分钟"
|
|||||||
|
|
||||||
msgid "Show reply direction"
|
msgid "Show reply direction"
|
||||||
msgstr "显示回复方向"
|
msgstr "显示回复方向"
|
||||||
|
|
||||||
|
msgid "Available space"
|
||||||
|
msgstr "可用空间"
|
||||||
|
|
||||||
|
msgid "Cannot prepare storage. Check free space and directory permissions."
|
||||||
|
msgstr "无法准备存储,请检查剩余空间和目录权限。"
|
||||||
|
|
||||||
|
msgid "Checking database version…"
|
||||||
|
msgstr "正在检查数据库版本…"
|
||||||
|
|
||||||
|
msgid "Checking storage…"
|
||||||
|
msgstr "正在检查存储空间…"
|
||||||
|
|
||||||
|
msgid "Choose an uncompressed .mmdb file within the upload size limit."
|
||||||
|
msgstr "请选择未压缩且未超过上传大小限制的 .mmdb 文件。"
|
||||||
|
|
||||||
|
msgid "Country/region + network organization — approximately 12 MiB"
|
||||||
|
msgstr "国家/地区 + 网络组织 · 约 12 MiB"
|
||||||
|
|
||||||
|
msgid "Country/region — approximately 3.3 MiB"
|
||||||
|
msgstr "国家/地区 · 约 3.3 MiB"
|
||||||
|
|
||||||
|
msgid "Data source"
|
||||||
|
msgstr "数据来源"
|
||||||
|
|
||||||
|
msgid "Data terms"
|
||||||
|
msgstr "数据使用条款"
|
||||||
|
|
||||||
|
msgid "Database contents"
|
||||||
|
msgstr "数据库内容"
|
||||||
|
|
||||||
|
msgid "Database directory"
|
||||||
|
msgstr "数据库目录"
|
||||||
|
|
||||||
|
msgid "Database enabled"
|
||||||
|
msgstr "数据库已启用"
|
||||||
|
|
||||||
|
msgid "Database is up to date"
|
||||||
|
msgstr "数据库已是最新版本"
|
||||||
|
|
||||||
|
msgid "Database operation failed"
|
||||||
|
msgstr "数据库操作失败"
|
||||||
|
|
||||||
|
msgid "Database operation failed. Retry or upload a local database."
|
||||||
|
msgstr "数据库操作失败,请重试或上传本地数据库。"
|
||||||
|
|
||||||
|
msgid "Database upload failed"
|
||||||
|
msgstr "数据库上传失败"
|
||||||
|
|
||||||
|
msgid "Database validation failed. The previous database is unchanged."
|
||||||
|
msgstr "数据库校验失败,原数据库未更改。"
|
||||||
|
|
||||||
|
msgid "Delete"
|
||||||
|
msgstr "删除"
|
||||||
|
|
||||||
|
msgid "Delete GeoIP databases"
|
||||||
|
msgstr "删除 IP 归属地数据库"
|
||||||
|
|
||||||
|
msgid "Disabled"
|
||||||
|
msgstr "已停用"
|
||||||
|
|
||||||
|
msgid "Documentation address"
|
||||||
|
msgstr "文档示例地址"
|
||||||
|
|
||||||
|
msgid "Download and enable"
|
||||||
|
msgstr "下载并启用"
|
||||||
|
|
||||||
|
msgid "Download cancelled"
|
||||||
|
msgstr "下载已取消"
|
||||||
|
|
||||||
|
msgid "Downloading database…"
|
||||||
|
msgstr "正在下载数据库…"
|
||||||
|
|
||||||
|
msgid "Enable IP location lookup"
|
||||||
|
msgstr "启用 IP 归属地查询"
|
||||||
|
|
||||||
|
msgid "Enabled"
|
||||||
|
msgstr "已启用"
|
||||||
|
|
||||||
|
msgid "GeoIP service is unavailable. Enable connection monitoring and apply settings, or update Bandix."
|
||||||
|
msgstr "IP 归属地服务不可用,请启用连接监控并应用设置,或更新 Bandix。"
|
||||||
|
|
||||||
|
msgid "IP location database"
|
||||||
|
msgstr "IP 归属地数据库"
|
||||||
|
|
||||||
|
msgid "Importing database…"
|
||||||
|
msgstr "正在导入数据库…"
|
||||||
|
|
||||||
|
msgid "Link-local address"
|
||||||
|
msgstr "链路本地地址"
|
||||||
|
|
||||||
|
msgid "Loading database…"
|
||||||
|
msgstr "正在加载数据库…"
|
||||||
|
|
||||||
|
msgid "Local network"
|
||||||
|
msgstr "局域网"
|
||||||
|
|
||||||
|
msgid "Loopback"
|
||||||
|
msgstr "回环地址"
|
||||||
|
|
||||||
|
msgid "Maximum upload size"
|
||||||
|
msgstr "上传大小上限"
|
||||||
|
|
||||||
|
msgid "Multicast"
|
||||||
|
msgstr "组播地址"
|
||||||
|
|
||||||
|
msgid "Not installed"
|
||||||
|
msgstr "未安装"
|
||||||
|
|
||||||
|
msgid "Private address"
|
||||||
|
msgstr "私有地址"
|
||||||
|
|
||||||
|
msgid "Province/city (uploaded database)"
|
||||||
|
msgstr "省市(已上传的数据库)"
|
||||||
|
|
||||||
|
msgid "Province/city + network organization — approximately 57 MiB"
|
||||||
|
msgstr "省市 + 网络组织 · 约 57 MiB"
|
||||||
|
|
||||||
|
msgid "Remove installed databases and disable IP location lookup?"
|
||||||
|
msgstr "删除已安装的数据库并停用 IP 归属地查询?"
|
||||||
|
|
||||||
|
msgid "Reserved address"
|
||||||
|
msgstr "保留地址"
|
||||||
|
|
||||||
|
msgid "Shared address (CGNAT)"
|
||||||
|
msgstr "共享地址(CGNAT)"
|
||||||
|
|
||||||
|
msgid "Storage used"
|
||||||
|
msgstr "存储占用"
|
||||||
|
|
||||||
|
msgid "The installed database could not be loaded. Download or upload it again."
|
||||||
|
msgstr "无法加载已安装的数据库,请重新下载或上传。"
|
||||||
|
|
||||||
|
msgid "Unknown"
|
||||||
|
msgstr "未知"
|
||||||
|
|
||||||
|
msgid "Unknown location"
|
||||||
|
msgstr "归属地未知"
|
||||||
|
|
||||||
|
msgid "Unspecified address"
|
||||||
|
msgstr "未指定地址"
|
||||||
|
|
||||||
|
msgid "Update database"
|
||||||
|
msgstr "更新数据库"
|
||||||
|
|
||||||
|
msgid "Upload an uncompressed Country, City or ASN .mmdb file. To add ASN, install a Country or City database first."
|
||||||
|
msgstr "可上传未压缩的 Country、City 或 ASN .mmdb 文件。添加 ASN 前,请先安装 Country 或 City 数据库。"
|
||||||
|
|
||||||
|
msgid "Upload and enable"
|
||||||
|
msgstr "上传并启用"
|
||||||
|
|
||||||
|
msgid "Upload database"
|
||||||
|
msgstr "上传数据库"
|
||||||
|
|
||||||
|
msgid "Uploading database…"
|
||||||
|
msgstr "正在上传数据库…"
|
||||||
|
|
||||||
|
msgid "Use persistent storage or an external drive. Save and apply directory changes before downloading."
|
||||||
|
msgstr "请使用持久存储或外接磁盘。更改目录后,请先保存并应用,再下载数据库。"
|
||||||
|
|
||||||
|
msgid "Verifying database…"
|
||||||
|
msgstr "正在校验数据库…"
|
||||||
|
|
||||||
|
msgid "Version"
|
||||||
|
msgstr "版本"
|
||||||
|
|
||||||
|
msgid "Country/region"
|
||||||
|
msgstr "国家/地区"
|
||||||
|
|
||||||
|
msgid "Province/city"
|
||||||
|
msgstr "省市"
|
||||||
|
|
||||||
|
msgid "Network organization"
|
||||||
|
msgstr "网络组织"
|
||||||
|
|
||||||
|
msgid "Not enough free space for this database."
|
||||||
|
msgstr "没有足够空间安装此数据库。"
|
||||||
|
|
||||||
|
msgid "Required space"
|
||||||
|
msgstr "所需空间"
|
||||||
|
|
||||||
|
msgid "Database ready"
|
||||||
|
msgstr "数据库已就绪"
|
||||||
|
|
||||||
|
msgid "Listen Address"
|
||||||
|
msgstr "监听地址"
|
||||||
|
|
||||||
|
msgid "IP address for Bandix to listen on. Use 127.0.0.1 for local access or 0.0.0.0 / :: for all interfaces."
|
||||||
|
msgstr "Bandix 监听的 IP 地址。127.0.0.1 仅供本机访问,0.0.0.0 / :: 监听所有接口。"
|
||||||
@@ -221,12 +221,6 @@ msgstr "WAN 已上傳"
|
|||||||
msgid "WAN Downloaded"
|
msgid "WAN Downloaded"
|
||||||
msgstr "WAN 已下載"
|
msgstr "WAN 已下載"
|
||||||
|
|
||||||
msgid "Total Upload"
|
|
||||||
msgstr "總上傳速率"
|
|
||||||
|
|
||||||
msgid "Total Download"
|
|
||||||
msgstr "總下載速率"
|
|
||||||
|
|
||||||
msgid "LAN Upload"
|
msgid "LAN Upload"
|
||||||
msgstr "LAN 上傳速率"
|
msgstr "LAN 上傳速率"
|
||||||
|
|
||||||
@@ -422,18 +416,12 @@ msgstr "第"
|
|||||||
msgid "of"
|
msgid "of"
|
||||||
msgstr "頁,共"
|
msgstr "頁,共"
|
||||||
|
|
||||||
msgid "Total"
|
|
||||||
msgstr "全部"
|
|
||||||
|
|
||||||
msgid "records"
|
msgid "records"
|
||||||
msgstr "筆記錄"
|
msgstr "筆記錄"
|
||||||
|
|
||||||
msgid "Per Page"
|
msgid "Per Page"
|
||||||
msgstr "每頁顯示"
|
msgstr "每頁顯示"
|
||||||
|
|
||||||
msgid ""
|
|
||||||
msgstr "筆"
|
|
||||||
|
|
||||||
msgid "Total Queries"
|
msgid "Total Queries"
|
||||||
msgstr "總查詢數"
|
msgstr "總查詢數"
|
||||||
|
|
||||||
@@ -855,7 +843,7 @@ msgid "Please select at least one day"
|
|||||||
msgstr "請至少選擇一天"
|
msgstr "請至少選擇一天"
|
||||||
|
|
||||||
msgid "No scheduled rules yet, click \"Add Rule\" to start setting"
|
msgid "No scheduled rules yet, click \"Add Rule\" to start setting"
|
||||||
msgstr "還沒有定時限速規則,點擊"新增規則"開始設定"
|
msgstr "還沒有定時限速規則,點擊\"新增規則\"開始設定"
|
||||||
|
|
||||||
msgid "Loading..."
|
msgid "Loading..."
|
||||||
msgstr "載入中..."
|
msgstr "載入中..."
|
||||||
@@ -938,18 +926,6 @@ msgstr "最大值"
|
|||||||
msgid "Minimum"
|
msgid "Minimum"
|
||||||
msgstr "最小值"
|
msgstr "最小值"
|
||||||
|
|
||||||
msgid "WAN Upload"
|
|
||||||
msgstr "WAN 上傳"
|
|
||||||
|
|
||||||
msgid "WAN Download"
|
|
||||||
msgstr "WAN 下載"
|
|
||||||
|
|
||||||
msgid "WAN Uploaded"
|
|
||||||
msgstr "WAN 上傳量"
|
|
||||||
|
|
||||||
msgid "WAN Downloaded"
|
|
||||||
msgstr "WAN 下載量"
|
|
||||||
|
|
||||||
msgid "Clear Traffic Data"
|
msgid "Clear Traffic Data"
|
||||||
msgstr "刪除流量數據"
|
msgstr "刪除流量數據"
|
||||||
|
|
||||||
@@ -1082,9 +1058,6 @@ msgstr "下載並安裝"
|
|||||||
msgid "Manual Download"
|
msgid "Manual Download"
|
||||||
msgstr "手動下載"
|
msgstr "手動下載"
|
||||||
|
|
||||||
msgid "Close"
|
|
||||||
msgstr "關閉"
|
|
||||||
|
|
||||||
msgid "Starting installation... The page will refresh automatically in 30 seconds."
|
msgid "Starting installation... The page will refresh automatically in 30 seconds."
|
||||||
msgstr "開始安裝... 頁面將在30秒後自動重新整理。"
|
msgstr "開始安裝... 頁面將在30秒後自動重新整理。"
|
||||||
|
|
||||||
@@ -1094,9 +1067,6 @@ msgstr "更新後請手動清空瀏覽器快取。"
|
|||||||
msgid "Traffic Statistics"
|
msgid "Traffic Statistics"
|
||||||
msgstr "流量統計"
|
msgstr "流量統計"
|
||||||
|
|
||||||
msgid "Traffic Statistics"
|
|
||||||
msgstr "流量統計"
|
|
||||||
|
|
||||||
msgid "Device Usage Ranking"
|
msgid "Device Usage Ranking"
|
||||||
msgstr "設備用量排行"
|
msgstr "設備用量排行"
|
||||||
|
|
||||||
@@ -1351,3 +1321,192 @@ msgstr "無法使用"
|
|||||||
|
|
||||||
msgid "Show reply direction"
|
msgid "Show reply direction"
|
||||||
msgstr "顯示回覆方向"
|
msgstr "顯示回覆方向"
|
||||||
|
|
||||||
|
msgid "Available space"
|
||||||
|
msgstr "可用空間"
|
||||||
|
|
||||||
|
msgid "Cannot prepare storage. Check free space and directory permissions."
|
||||||
|
msgstr "無法準備儲存空間,請檢查剩餘空間和目錄權限。"
|
||||||
|
|
||||||
|
msgid "Checking database version…"
|
||||||
|
msgstr "正在檢查資料庫版本…"
|
||||||
|
|
||||||
|
msgid "Checking storage…"
|
||||||
|
msgstr "正在檢查儲存空間…"
|
||||||
|
|
||||||
|
msgid "Choose an uncompressed .mmdb file within the upload size limit."
|
||||||
|
msgstr "請選擇未壓縮且未超過上傳大小限制的 .mmdb 檔案。"
|
||||||
|
|
||||||
|
msgid "Country/region + network organization — approximately 12 MiB"
|
||||||
|
msgstr "國家/地區 + 網路組織 · 約 12 MiB"
|
||||||
|
|
||||||
|
msgid "Country/region — approximately 3.3 MiB"
|
||||||
|
msgstr "國家/地區 · 約 3.3 MiB"
|
||||||
|
|
||||||
|
msgid "Data source"
|
||||||
|
msgstr "資料來源"
|
||||||
|
|
||||||
|
msgid "Data terms"
|
||||||
|
msgstr "資料使用條款"
|
||||||
|
|
||||||
|
msgid "Database contents"
|
||||||
|
msgstr "資料庫內容"
|
||||||
|
|
||||||
|
msgid "Database directory"
|
||||||
|
msgstr "資料庫目錄"
|
||||||
|
|
||||||
|
msgid "Database enabled"
|
||||||
|
msgstr "資料庫已啟用"
|
||||||
|
|
||||||
|
msgid "Database is up to date"
|
||||||
|
msgstr "資料庫已是最新版本"
|
||||||
|
|
||||||
|
msgid "Database operation failed"
|
||||||
|
msgstr "資料庫操作失敗"
|
||||||
|
|
||||||
|
msgid "Database operation failed. Retry or upload a local database."
|
||||||
|
msgstr "資料庫操作失敗,請重試或上傳本機資料庫。"
|
||||||
|
|
||||||
|
msgid "Database upload failed"
|
||||||
|
msgstr "資料庫上傳失敗"
|
||||||
|
|
||||||
|
msgid "Database validation failed. The previous database is unchanged."
|
||||||
|
msgstr "資料庫驗證失敗,原資料庫未變更。"
|
||||||
|
|
||||||
|
msgid "Delete"
|
||||||
|
msgstr "刪除"
|
||||||
|
|
||||||
|
msgid "Delete GeoIP databases"
|
||||||
|
msgstr "刪除 IP 歸屬地資料庫"
|
||||||
|
|
||||||
|
msgid "Disabled"
|
||||||
|
msgstr "已停用"
|
||||||
|
|
||||||
|
msgid "Documentation address"
|
||||||
|
msgstr "文件範例位址"
|
||||||
|
|
||||||
|
msgid "Download and enable"
|
||||||
|
msgstr "下載並啟用"
|
||||||
|
|
||||||
|
msgid "Download cancelled"
|
||||||
|
msgstr "下載已取消"
|
||||||
|
|
||||||
|
msgid "Downloading database…"
|
||||||
|
msgstr "正在下載資料庫…"
|
||||||
|
|
||||||
|
msgid "Enable IP location lookup"
|
||||||
|
msgstr "啟用 IP 歸屬地查詢"
|
||||||
|
|
||||||
|
msgid "Enabled"
|
||||||
|
msgstr "已啟用"
|
||||||
|
|
||||||
|
msgid "GeoIP service is unavailable. Enable connection monitoring and apply settings, or update Bandix."
|
||||||
|
msgstr "IP 歸屬地服務無法使用,請啟用連線監控並套用設定,或更新 Bandix。"
|
||||||
|
|
||||||
|
msgid "IP location database"
|
||||||
|
msgstr "IP 歸屬地資料庫"
|
||||||
|
|
||||||
|
msgid "Importing database…"
|
||||||
|
msgstr "正在匯入資料庫…"
|
||||||
|
|
||||||
|
msgid "Link-local address"
|
||||||
|
msgstr "鏈路本機位址"
|
||||||
|
|
||||||
|
msgid "Loading database…"
|
||||||
|
msgstr "正在載入資料庫…"
|
||||||
|
|
||||||
|
msgid "Local network"
|
||||||
|
msgstr "區域網路"
|
||||||
|
|
||||||
|
msgid "Loopback"
|
||||||
|
msgstr "回送位址"
|
||||||
|
|
||||||
|
msgid "Maximum upload size"
|
||||||
|
msgstr "上傳大小上限"
|
||||||
|
|
||||||
|
msgid "Multicast"
|
||||||
|
msgstr "多點傳送位址"
|
||||||
|
|
||||||
|
msgid "Not installed"
|
||||||
|
msgstr "未安裝"
|
||||||
|
|
||||||
|
msgid "Private address"
|
||||||
|
msgstr "私有位址"
|
||||||
|
|
||||||
|
msgid "Province/city (uploaded database)"
|
||||||
|
msgstr "省市(已上傳的資料庫)"
|
||||||
|
|
||||||
|
msgid "Province/city + network organization — approximately 57 MiB"
|
||||||
|
msgstr "省市 + 網路組織 · 約 57 MiB"
|
||||||
|
|
||||||
|
msgid "Remove installed databases and disable IP location lookup?"
|
||||||
|
msgstr "刪除已安裝的資料庫並停用 IP 歸屬地查詢?"
|
||||||
|
|
||||||
|
msgid "Reserved address"
|
||||||
|
msgstr "保留位址"
|
||||||
|
|
||||||
|
msgid "Shared address (CGNAT)"
|
||||||
|
msgstr "共用位址(CGNAT)"
|
||||||
|
|
||||||
|
msgid "Storage used"
|
||||||
|
msgstr "儲存用量"
|
||||||
|
|
||||||
|
msgid "The installed database could not be loaded. Download or upload it again."
|
||||||
|
msgstr "無法載入已安裝的資料庫,請重新下載或上傳。"
|
||||||
|
|
||||||
|
msgid "Unknown"
|
||||||
|
msgstr "未知"
|
||||||
|
|
||||||
|
msgid "Unknown location"
|
||||||
|
msgstr "歸屬地未知"
|
||||||
|
|
||||||
|
msgid "Unspecified address"
|
||||||
|
msgstr "未指定位址"
|
||||||
|
|
||||||
|
msgid "Update database"
|
||||||
|
msgstr "更新資料庫"
|
||||||
|
|
||||||
|
msgid "Upload an uncompressed Country, City or ASN .mmdb file. To add ASN, install a Country or City database first."
|
||||||
|
msgstr "可上傳未壓縮的 Country、City 或 ASN .mmdb 檔案。新增 ASN 前,請先安裝 Country 或 City 資料庫。"
|
||||||
|
|
||||||
|
msgid "Upload and enable"
|
||||||
|
msgstr "上傳並啟用"
|
||||||
|
|
||||||
|
msgid "Upload database"
|
||||||
|
msgstr "上傳資料庫"
|
||||||
|
|
||||||
|
msgid "Uploading database…"
|
||||||
|
msgstr "正在上傳資料庫…"
|
||||||
|
|
||||||
|
msgid "Use persistent storage or an external drive. Save and apply directory changes before downloading."
|
||||||
|
msgstr "請使用持續性儲存或外接磁碟。變更目錄後,請先儲存並套用,再下載資料庫。"
|
||||||
|
|
||||||
|
msgid "Verifying database…"
|
||||||
|
msgstr "正在驗證資料庫…"
|
||||||
|
|
||||||
|
msgid "Version"
|
||||||
|
msgstr "版本"
|
||||||
|
|
||||||
|
msgid "Country/region"
|
||||||
|
msgstr "國家/地區"
|
||||||
|
|
||||||
|
msgid "Province/city"
|
||||||
|
msgstr "省市"
|
||||||
|
|
||||||
|
msgid "Network organization"
|
||||||
|
msgstr "網路組織"
|
||||||
|
|
||||||
|
msgid "Not enough free space for this database."
|
||||||
|
msgstr "沒有足夠空間安裝此資料庫。"
|
||||||
|
|
||||||
|
msgid "Required space"
|
||||||
|
msgstr "所需空間"
|
||||||
|
|
||||||
|
msgid "Database ready"
|
||||||
|
msgstr "資料庫已就緒"
|
||||||
|
|
||||||
|
msgid "Listen Address"
|
||||||
|
msgstr "監聽位址"
|
||||||
|
|
||||||
|
msgid "IP address for Bandix to listen on. Use 127.0.0.1 for local access or 0.0.0.0 / :: for all interfaces."
|
||||||
|
msgstr "Bandix 監聽的 IP 位址。127.0.0.1 僅供本機存取,0.0.0.0 / :: 監聽所有介面。"
|
||||||
@@ -3,9 +3,16 @@
|
|||||||
|
|
||||||
. /usr/share/libubox/jshn.sh
|
. /usr/share/libubox/jshn.sh
|
||||||
|
|
||||||
# 从UCI配置读取端口号,如果读取失败则使用默认端口8686
|
# Wildcard listening addresses must be reached through loopback on the router.
|
||||||
BANDIX_PORT=$(uci get bandix.general.port 2>/dev/null || echo "8686")
|
BANDIX_HOST=$(uci -q get bandix.general.host)
|
||||||
readonly BANDIX_API_BASE="http://127.0.0.1:$BANDIX_PORT"
|
BANDIX_PORT=$(uci -q get bandix.general.port)
|
||||||
|
BANDIX_PORT=${BANDIX_PORT:-8686}
|
||||||
|
case "$BANDIX_HOST" in
|
||||||
|
''|0.0.0.0) BANDIX_HOST='127.0.0.1' ;;
|
||||||
|
::) BANDIX_HOST='[::1]' ;;
|
||||||
|
*:*) BANDIX_HOST="[$BANDIX_HOST]" ;;
|
||||||
|
esac
|
||||||
|
readonly BANDIX_API_BASE="http://$BANDIX_HOST:$BANDIX_PORT"
|
||||||
readonly BANDIX_DEVICES_API="$BANDIX_API_BASE/api/traffic/devices"
|
readonly BANDIX_DEVICES_API="$BANDIX_API_BASE/api/traffic/devices"
|
||||||
readonly BANDIX_LIMITS_API="$BANDIX_API_BASE/api/traffic/limits"
|
readonly BANDIX_LIMITS_API="$BANDIX_API_BASE/api/traffic/limits"
|
||||||
readonly BANDIX_METRICS_API="$BANDIX_API_BASE/api/traffic/metrics"
|
readonly BANDIX_METRICS_API="$BANDIX_API_BASE/api/traffic/metrics"
|
||||||
@@ -421,12 +428,13 @@ get_connection_flows() {
|
|||||||
[ -n "$state" ] && query_params="${query_params}state=$(printf '%s' "$state" | sed 's/ /%20/g')&"
|
[ -n "$state" ] && query_params="${query_params}state=$(printf '%s' "$state" | sed 's/ /%20/g')&"
|
||||||
[ -n "$page" ] && query_params="${query_params}page=$page&"
|
[ -n "$page" ] && query_params="${query_params}page=$page&"
|
||||||
[ -n "$page_size" ] && query_params="${query_params}page_size=$page_size&"
|
[ -n "$page_size" ] && query_params="${query_params}page_size=$page_size&"
|
||||||
|
if [ -n "$page" ] || [ -n "$page_size" ]; then query_params="${query_params}geoip=1&"; fi
|
||||||
query_params=$(echo "$query_params" | sed 's/&$//')
|
query_params=$(echo "$query_params" | sed 's/&$//')
|
||||||
|
|
||||||
local url="$BANDIX_CONNECTION_FLOWS_API"
|
local url="$BANDIX_CONNECTION_FLOWS_API"
|
||||||
[ -n "$query_params" ] && url="${url}?${query_params}"
|
[ -n "$query_params" ] && url="${url}?${query_params}"
|
||||||
|
|
||||||
local response=$(curl -s --connect-timeout 2 --max-time 5 -X GET "$url" 2>/dev/null)
|
local response=$(curl -s --connect-timeout 2 --max-time 15 -X GET "$url" 2>/dev/null)
|
||||||
if [ $? -ne 0 ] || [ -z "$response" ]; then
|
if [ $? -ne 0 ] || [ -z "$response" ]; then
|
||||||
make_error "Failed to connect to Bandix service"
|
make_error "Failed to connect to Bandix service"
|
||||||
return
|
return
|
||||||
@@ -434,6 +442,44 @@ get_connection_flows() {
|
|||||||
echo "$response"
|
echo "$response"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# GeoIP management is proxied to the router itself; writes require the LuCI
|
||||||
|
# write ACL. Accept only action/profile identifiers, never arbitrary URLs/paths.
|
||||||
|
get_geoip_status() {
|
||||||
|
local response
|
||||||
|
response=$(curl -s --connect-timeout 2 --max-time 8 "$BANDIX_API_BASE/api/geoip/status" 2>/dev/null)
|
||||||
|
if [ $? -ne 0 ] || [ -z "$response" ]; then
|
||||||
|
make_error "GeoIP service is unavailable"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
printf '%s\n' "$response"
|
||||||
|
}
|
||||||
|
|
||||||
|
manage_geoip() {
|
||||||
|
local action="$1" profile="$2" enabled="$3" payload response
|
||||||
|
json_init
|
||||||
|
case "$action" in
|
||||||
|
download)
|
||||||
|
case "$profile" in country|country_asn|city|city_asn) ;; *) make_error "Invalid database profile"; return ;; esac
|
||||||
|
json_add_string "action" "download"
|
||||||
|
json_add_string "profile" "$profile"
|
||||||
|
;;
|
||||||
|
enable)
|
||||||
|
json_add_string "action" "enable"
|
||||||
|
case "$enabled" in 1|true) json_add_boolean "enabled" 1 ;; 0|false) json_add_boolean "enabled" 0 ;; *) make_error "Invalid enabled value"; return ;; esac
|
||||||
|
;;
|
||||||
|
cancel|delete|import) json_add_string "action" "$action" ;;
|
||||||
|
*) make_error "Invalid GeoIP action"; return ;;
|
||||||
|
esac
|
||||||
|
payload=$(json_dump)
|
||||||
|
json_cleanup
|
||||||
|
response=$(curl -s --connect-timeout 2 --max-time 8 -X POST -H 'Content-Type: application/json' --data-binary "$payload" "$BANDIX_API_BASE/api/geoip/manage" 2>/dev/null)
|
||||||
|
if [ $? -ne 0 ] || [ -z "$response" ]; then
|
||||||
|
make_error "GeoIP service is unavailable"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
printf '%s\n' "$response"
|
||||||
|
}
|
||||||
|
|
||||||
# 获取 DNS 查询记录
|
# 获取 DNS 查询记录
|
||||||
get_dns_queries() {
|
get_dns_queries() {
|
||||||
# 检查 DNS 监控是否启用
|
# 检查 DNS 监控是否启用
|
||||||
@@ -1311,6 +1357,14 @@ case "$1" in
|
|||||||
json_add_string "mac"
|
json_add_string "mac"
|
||||||
json_close_object
|
json_close_object
|
||||||
|
|
||||||
|
json_add_object "getGeoipStatus"
|
||||||
|
json_close_object
|
||||||
|
json_add_object "manageGeoip"
|
||||||
|
json_add_string "action"
|
||||||
|
json_add_string "profile"
|
||||||
|
json_add_string "enabled"
|
||||||
|
json_close_object
|
||||||
|
|
||||||
json_add_object "getConnection"
|
json_add_object "getConnection"
|
||||||
json_close_object
|
json_close_object
|
||||||
|
|
||||||
@@ -1525,6 +1579,20 @@ case "$1" in
|
|||||||
fi
|
fi
|
||||||
delete_device "$mac"
|
delete_device "$mac"
|
||||||
;;
|
;;
|
||||||
|
getGeoipStatus)
|
||||||
|
get_geoip_status
|
||||||
|
;;
|
||||||
|
manageGeoip)
|
||||||
|
input=""
|
||||||
|
read -r input
|
||||||
|
action="$(printf '%s' "$input" | jsonfilter -e '$.action' 2>/dev/null)"
|
||||||
|
[ -z "$action" ] && action="$(printf '%s' "$input" | jsonfilter -e '$[0]' 2>/dev/null)"
|
||||||
|
profile="$(printf '%s' "$input" | jsonfilter -e '$.profile' 2>/dev/null)"
|
||||||
|
[ -z "$profile" ] && profile="$(printf '%s' "$input" | jsonfilter -e '$[1]' 2>/dev/null)"
|
||||||
|
enabled="$(printf '%s' "$input" | jsonfilter -e '$.enabled' 2>/dev/null)"
|
||||||
|
[ -z "$enabled" ] && enabled="$(printf '%s' "$input" | jsonfilter -e '$[2]' 2>/dev/null)"
|
||||||
|
manage_geoip "$action" "$profile" "$enabled"
|
||||||
|
;;
|
||||||
getConnection)
|
getConnection)
|
||||||
# logger "luci.bandix: getConnection called"
|
# logger "luci.bandix: getConnection called"
|
||||||
get_connection_devices
|
get_connection_devices
|
||||||
|
|||||||
@@ -27,7 +27,8 @@
|
|||||||
"getTrafficUsageRanking",
|
"getTrafficUsageRanking",
|
||||||
"getTrafficUsageIncrements",
|
"getTrafficUsageIncrements",
|
||||||
"getRateLimitWhitelist",
|
"getRateLimitWhitelist",
|
||||||
"getTrafficQuotas"
|
"getTrafficQuotas",
|
||||||
|
"getGeoipStatus"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"uci": [
|
"uci": [
|
||||||
@@ -68,12 +69,19 @@
|
|||||||
"updateScheduleLimit",
|
"updateScheduleLimit",
|
||||||
"getTrafficQuotas",
|
"getTrafficQuotas",
|
||||||
"setTrafficQuota",
|
"setTrafficQuota",
|
||||||
"deleteTrafficQuota"
|
"deleteTrafficQuota",
|
||||||
|
"getGeoipStatus",
|
||||||
|
"manageGeoip"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"uci": [
|
"uci": [
|
||||||
"bandix"
|
"bandix"
|
||||||
]
|
],
|
||||||
|
"file": {
|
||||||
|
"/tmp/bandix-geoip-upload.mmdb": [
|
||||||
|
"write"
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk
|
|||||||
|
|
||||||
PKG_NAME:=luci-app-clashoo
|
PKG_NAME:=luci-app-clashoo
|
||||||
PKG_VERSION:=1.31.0
|
PKG_VERSION:=1.31.0
|
||||||
PKG_RELEASE:=8
|
PKG_RELEASE:=9
|
||||||
PKG_MAINTAINER:=kenzok8
|
PKG_MAINTAINER:=kenzok8
|
||||||
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)
|
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)
|
||||||
|
|
||||||
@@ -91,7 +91,7 @@ define Package/luci-i18n-clashoo-zh-cn/postinst
|
|||||||
if [ -z "$${IPKG_INSTROOT}" ]; then
|
if [ -z "$${IPKG_INSTROOT}" ]; then
|
||||||
# just clear the LuCI cache so the new translations load on next request;
|
# just clear the LuCI cache so the new translations load on next request;
|
||||||
# no service restart needed (and none that would drop the login session).
|
# no service restart needed (and none that would drop the login session).
|
||||||
rm -rf /tmp/luci*
|
rm -rf /tmp/luci-indexcache /tmp/luci-modulecache
|
||||||
fi
|
fi
|
||||||
exit 0
|
exit 0
|
||||||
endef
|
endef
|
||||||
|
|||||||
@@ -15,7 +15,9 @@ function mkdtemp(tpl) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const BINARY_PATHS = [
|
const BINARY_PATHS = [
|
||||||
'/usr/bin/mihomo-stable',
|
'/usr/share/clashoo/bin/mihomo-stable',
|
||||||
|
'/usr/libexec/clashoo/mihomo-stable',
|
||||||
|
'/usr/share/clashoo/bin/mihomo-alpha',
|
||||||
'/usr/bin/mihomo',
|
'/usr/bin/mihomo',
|
||||||
'/usr/bin/clash-meta',
|
'/usr/bin/clash-meta',
|
||||||
'/usr/bin/clash',
|
'/usr/bin/clash',
|
||||||
@@ -283,8 +285,8 @@ if (family === 'mihomo') {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let channel = get_core_channel('mihomo');
|
let channel = get_core_channel('mihomo');
|
||||||
if (channel == 'stable' && access('/usr/bin/mihomo-stable', 'x')) return '/usr/bin/mihomo-stable';
|
if (channel == 'stable') return selected_mihomo_stable();
|
||||||
if (channel == 'alpha' && access('/usr/bin/mihomo', 'x')) return '/usr/bin/mihomo';
|
if (channel == 'alpha') return access('/usr/share/clashoo/bin/mihomo-alpha', 'x') ? '/usr/share/clashoo/bin/mihomo-alpha' : '';
|
||||||
if (channel == 'smart') {
|
if (channel == 'smart') {
|
||||||
for (let path in SMART_BINARY_PATHS)
|
for (let path in SMART_BINARY_PATHS)
|
||||||
if (access(path, 'x')) return path;
|
if (access(path, 'x')) return path;
|
||||||
@@ -371,7 +373,7 @@ c.set('clashoo', 'config', 'core', legacy);
|
|||||||
}
|
}
|
||||||
|
|
||||||
function has_mihomo_stable() {
|
function has_mihomo_stable() {
|
||||||
if (access('/usr/bin/mihomo-stable', 'x')) return true;
|
if (selected_mihomo_stable()) return true;
|
||||||
if (system("[ -x /usr/bin/clash-meta ] && [ ! -L /usr/bin/clash-meta ]") != 0) return false;
|
if (system("[ -x /usr/bin/clash-meta ] && [ ! -L /usr/bin/clash-meta ]") != 0) return false;
|
||||||
let version = get_core_version('/usr/bin/clash-meta');
|
let version = get_core_version('/usr/bin/clash-meta');
|
||||||
return !!version && !match(version, /(alpha|beta|rc|pre|smart)/i);
|
return !!version && !match(version, /(alpha|beta|rc|pre|smart)/i);
|
||||||
@@ -387,7 +389,7 @@ if (core == 'mihomo' && dcore == '2') {
|
|||||||
return { error: 'mihomo_stable_missing', message: '未检测到 mihomo 稳定版,请先在“组件更新”中更新稳定版' };
|
return { error: 'mihomo_stable_missing', message: '未检测到 mihomo 稳定版,请先在“组件更新”中更新稳定版' };
|
||||||
}
|
}
|
||||||
if (core == 'mihomo' && dcore == '3') {
|
if (core == 'mihomo' && dcore == '3') {
|
||||||
if (access('/usr/bin/mihomo', 'x')) return null;
|
if (access('/usr/share/clashoo/bin/mihomo-alpha', 'x')) return null;
|
||||||
return { error: 'mihomo_alpha_missing', message: '未检测到 mihomo Alpha,请先在“组件更新”中更新 Alpha' };
|
return { error: 'mihomo_alpha_missing', message: '未检测到 mihomo Alpha,请先在“组件更新”中更新 Alpha' };
|
||||||
}
|
}
|
||||||
if (core == 'singbox' && dcore == '4') {
|
if (core == 'singbox' && dcore == '4') {
|
||||||
@@ -887,6 +889,23 @@ if (m) return m[1];
|
|||||||
return full;
|
return full;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function selected_mihomo_stable() {
|
||||||
|
let bundled = '/usr/libexec/clashoo/mihomo-stable';
|
||||||
|
let manual = '/usr/share/clashoo/bin/mihomo-stable';
|
||||||
|
if (access(manual, 'x') && read_first_line('/usr/share/clashoo/clash_meta_version') == 'custom-url') return manual;
|
||||||
|
if (!access(bundled, 'x')) return access(manual, 'x') ? manual : '';
|
||||||
|
if (!access(manual, 'x')) return bundled;
|
||||||
|
let bv = match(core_short_version(bundled), /^v?([0-9]+)\.([0-9]+)\.([0-9]+)$/);
|
||||||
|
let mv = match(core_short_version(manual), /^v?([0-9]+)\.([0-9]+)\.([0-9]+)$/);
|
||||||
|
if (!bv) return mv ? manual : bundled;
|
||||||
|
if (!mv) return bundled;
|
||||||
|
for (let i = 1; i <= 3; i++) {
|
||||||
|
if (int(mv[i]) > int(bv[i])) return manual;
|
||||||
|
if (int(mv[i]) < int(bv[i])) return bundled;
|
||||||
|
}
|
||||||
|
return manual;
|
||||||
|
}
|
||||||
|
|
||||||
function component_installed_label(id) {
|
function component_installed_label(id) {
|
||||||
if (id == 'clashoo') return pkg_version('clashoo') || '未安装';
|
if (id == 'clashoo') return pkg_version('clashoo') || '未安装';
|
||||||
if (id == 'luci') return pkg_version('luci-app-clashoo') || '未安装';
|
if (id == 'luci') return pkg_version('luci-app-clashoo') || '未安装';
|
||||||
@@ -923,12 +942,11 @@ return !!match(ver || '', /(alpha|beta|rc|pre|smart)/i);
|
|||||||
function component_installed_versions(id) {
|
function component_installed_versions(id) {
|
||||||
let out = {};
|
let out = {};
|
||||||
if (id == 'mihomo') {
|
if (id == 'mihomo') {
|
||||||
let stable = access('/usr/bin/mihomo-stable', 'x') ? read_first_line('/usr/share/clashoo/clash_meta_version') : '';
|
let stable_bin = selected_mihomo_stable();
|
||||||
if (!stable && access('/usr/bin/mihomo-stable', 'x')) stable = core_short_version('/usr/bin/mihomo-stable');
|
let stable = stable_bin ? core_short_version(stable_bin) : '';
|
||||||
if (is_prerelease_label(stable)) stable = '';
|
if (is_prerelease_label(stable)) stable = '';
|
||||||
let alpha = read_first_line('/usr/share/clashoo/mihomo_version');
|
let alpha = access('/usr/share/clashoo/bin/mihomo-alpha', 'x') ? core_short_version('/usr/share/clashoo/bin/mihomo-alpha') : '';
|
||||||
if (match(alpha, /^smart-/)) alpha = '';
|
if (match(alpha, /^smart-/)) alpha = '';
|
||||||
if (!alpha && access('/usr/bin/mihomo', 'x')) alpha = core_short_version('/usr/bin/mihomo');
|
|
||||||
out.stable = stable || '';
|
out.stable = stable || '';
|
||||||
out.alpha = mihomo_alpha_label(alpha);
|
out.alpha = mihomo_alpha_label(alpha);
|
||||||
return out;
|
return out;
|
||||||
@@ -2815,7 +2833,7 @@ let api = get_clash_api_auth();
|
|||||||
health_status, health_detail, has_tun_device,
|
health_status, health_detail, has_tun_device,
|
||||||
core_type: configured_core, dcore, local_ip,
|
core_type: configured_core, dcore, local_ip,
|
||||||
has_mihomo_stable: has_mihomo_stable(),
|
has_mihomo_stable: has_mihomo_stable(),
|
||||||
has_mihomo_alpha: !!access('/usr/bin/mihomo', 'x'),
|
has_mihomo_alpha: !!access('/usr/share/clashoo/bin/mihomo-alpha', 'x'),
|
||||||
has_singbox_stable: !!access(SINGBOX_BINARY_PATHS[0], 'x'),
|
has_singbox_stable: !!access(SINGBOX_BINARY_PATHS[0], 'x'),
|
||||||
has_singbox_alpha: !!access(SINGBOX_BINARY_PATHS[1], 'x'),
|
has_singbox_alpha: !!access(SINGBOX_BINARY_PATHS[1], 'x'),
|
||||||
has_smart: !!find_smart_binary() };
|
has_smart: !!find_smart_binary() };
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ include $(TOPDIR)/rules.mk
|
|||||||
LUCI_TITLE:=LuCI support for fastnet
|
LUCI_TITLE:=LuCI support for fastnet
|
||||||
LUCI_DEPENDS:=+fastnet +luci-lib-linkeaseauth +linkease-app-entry
|
LUCI_DEPENDS:=+fastnet +luci-lib-linkeaseauth +linkease-app-entry
|
||||||
LUCI_PKGARCH:=all
|
LUCI_PKGARCH:=all
|
||||||
PKG_VERSION:=0.7.7-r5
|
PKG_VERSION:=0.7.8-r5
|
||||||
PKG_RELEASE:=4
|
PKG_RELEASE:=5
|
||||||
LUCI_MINIFY_CSS:=0
|
LUCI_MINIFY_CSS:=0
|
||||||
LUCI_MINIFY_JS:=0
|
LUCI_MINIFY_JS:=0
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ include $(TOPDIR)/rules.mk
|
|||||||
LUCI_TITLE:=LuCI support for KSpeeder
|
LUCI_TITLE:=LuCI support for KSpeeder
|
||||||
LUCI_DEPENDS:=+istoreenhance +luci-lib-linkeaseauth +linkease-app-entry
|
LUCI_DEPENDS:=+istoreenhance +luci-lib-linkeaseauth +linkease-app-entry
|
||||||
LUCI_PKGARCH:=all
|
LUCI_PKGARCH:=all
|
||||||
PKG_VERSION:=0.8.4-r1
|
PKG_VERSION:=0.8.5-r1
|
||||||
PKG_RELEASE:=7
|
PKG_RELEASE:=8
|
||||||
|
|
||||||
include $(TOPDIR)/feeds/luci/luci.mk
|
include $(TOPDIR)/feeds/luci/luci.mk
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
include $(TOPDIR)/rules.mk
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
PKG_NAME:=luci-app-open-box
|
PKG_NAME:=luci-app-open-box
|
||||||
PKG_VERSION:=0.1.361
|
PKG_VERSION:=0.1.366
|
||||||
PKG_RELEASE:=47
|
PKG_RELEASE:=48
|
||||||
|
|
||||||
PKG_MAINTAINER:=liandu2024
|
PKG_MAINTAINER:=liandu2024
|
||||||
PKG_LICENSE:=GPL-3.0-or-later
|
PKG_LICENSE:=GPL-3.0-or-later
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ include $(TOPDIR)/rules.mk
|
|||||||
|
|
||||||
PKG_NAME:=luci-app-openvpn-plus
|
PKG_NAME:=luci-app-openvpn-plus
|
||||||
PKG_VERSION:=1.0.0
|
PKG_VERSION:=1.0.0
|
||||||
PKG_RELEASE:=5
|
PKG_RELEASE:=6
|
||||||
PKG_LICENSE:=Apache-2.0
|
PKG_LICENSE:=Apache-2.0
|
||||||
PKG_MAINTAINER:=Manfred Jaider <info@masmbit.com>
|
PKG_MAINTAINER:=Manfred Jaider <info@masmbit.com>
|
||||||
|
|
||||||
|
|||||||
@@ -60,31 +60,6 @@ config openvpn 'instance1'
|
|||||||
option config '/etc/openvpn/luci/instance1.conf'
|
option config '/etc/openvpn/luci/instance1.conf'
|
||||||
```
|
```
|
||||||
|
|
||||||
## Screenshots
|
|
||||||
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<td><img width="2442" height="1844" alt="01" src="https://github.com/user-attachments/assets/5f215704-c18e-42ba-9228-c100635d3836" /></td>
|
|
||||||
<td><img width="2398" height="1700" alt="03" src="https://github.com/user-attachments/assets/10ee2c26-6ca8-42c5-9f1e-04f73fb9c91c" /></td>
|
|
||||||
<td><img width="2384" height="1706" alt="04" src="https://github.com/user-attachments/assets/86f7c341-37e2-4be9-8b7a-458cff1cc722" /></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><img width="1796" height="556" alt="09" src="https://github.com/user-attachments/assets/c2d2b23d-b3b8-40f3-a1bf-b9d57f1ca4e3" /></td>
|
|
||||||
<td><img width="1798" height="496" alt="10" src="https://github.com/user-attachments/assets/30ff6070-def5-4dd8-a5a9-4de11fa14706" /></td>
|
|
||||||
<td><img width="1798" height="754" alt="13" src="https://github.com/user-attachments/assets/9d4d897c-18b1-44dc-93de-035984acf5db" /></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><img width="1800" height="1352" alt="11" src="https://github.com/user-attachments/assets/ae0c5c01-10f3-496a-b687-7b6a734ad322" /></td>
|
|
||||||
<td><img width="1798" height="1728" alt="12" src="https://github.com/user-attachments/assets/f5023929-99b5-438a-94e2-6042c3d352a9" /></td>
|
|
||||||
<td><img width="2412" height="1692" alt="08" src="https://github.com/user-attachments/assets/937b8f11-4743-4aca-bf63-8b5003ce7cda" /></td>
|
|
||||||
</tr>
|
|
||||||
</td>
|
|
||||||
<td><img width="2406" height="1834" alt="14" src="https://github.com/user-attachments/assets/f9d64535-4177-4b86-bd6a-f1611dc41da8" /></td>
|
|
||||||
<td><img width="2394" height="1808" alt="06" src="https://github.com/user-attachments/assets/008c6f5b-0636-43e2-afe6-1de835eab4af" /></td>
|
|
||||||
<td><img width="2382" height="1792" alt="05" src="https://github.com/user-attachments/assets/5694833e-2b03-43a2-b1ff-8f3afe11c8e0" /></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
## Directory Structure
|
## Directory Structure
|
||||||
|
|
||||||
```text
|
```text
|
||||||
|
|||||||
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
@@ -30,7 +30,7 @@ tls-groups secp256r1
|
|||||||
cipher AES-256-GCM
|
cipher AES-256-GCM
|
||||||
data-ciphers AES-256-GCM:AES-128-GCM:CHACHA20-POLY1305
|
data-ciphers AES-256-GCM:AES-128-GCM:CHACHA20-POLY1305
|
||||||
|
|
||||||
# --- OpenVPN Flood Guard ---
|
# --- Flood Guard Filter ---
|
||||||
setenv floodguard on
|
setenv floodguard on
|
||||||
|
|
||||||
# --- Client Push Options ---
|
# --- Client Push Options ---
|
||||||
|
|||||||
@@ -471,16 +471,18 @@ openssldgst() {
|
|||||||
# Manage temporary download links for mobile profiles [create|delete]
|
# Manage temporary download links for mobile profiles [create|delete]
|
||||||
symlink() {
|
symlink() {
|
||||||
shift # Remove "symlink" from the arguments list
|
shift # Remove "symlink" from the arguments list
|
||||||
local ovpn_num="$1"
|
local ovpn_instance="$1"
|
||||||
local action="$2"
|
local action="$2"
|
||||||
local raw_display_name="$3"
|
local raw_display_name="$3"
|
||||||
local export_static_path="/etc/openvpn/luci/instance${ovpn_num}.ovpn"
|
local export_static_path="/etc/openvpn/luci/${ovpn_instance}.ovpn"
|
||||||
local lan_ip=""
|
local lan_ip=""
|
||||||
local link_filename=""
|
local link_filename=""
|
||||||
|
local ram_link_dir="/tmp/luci-ovpn-plus"
|
||||||
|
local www_link_dir="/www/ovpn-downloads"
|
||||||
|
|
||||||
# Check if instance number is a valid digit.
|
# Check if instance name is provided and safe (alphanumeric only).
|
||||||
if [ -z "$ovpn_num" ] || ! echo "$ovpn_num" | grep -q '^[0-9]\+$'; then
|
if [ -z "$ovpn_instance" ] || ! echo "$ovpn_instance" | grep -q '^[A-Za-z0-9]\+$'; then
|
||||||
echo "ERROR: Invalid instance number."
|
echo "ERROR: Invalid instance name."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -491,53 +493,66 @@ symlink() {
|
|||||||
|
|
||||||
# Fallback if the cleaned name is empty.
|
# Fallback if the cleaned name is empty.
|
||||||
if [ -z "$link_filename" ]; then
|
if [ -z "$link_filename" ]; then
|
||||||
link_filename="instance${ovpn_num}"
|
link_filename="${ovpn_instance}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
case "$action" in
|
case "$action" in
|
||||||
create)
|
create)
|
||||||
# Check if the source profile exists on disk.
|
|
||||||
if [ ! -f "$export_static_path" ]; then
|
if [ ! -f "$export_static_path" ]; then
|
||||||
echo "ERROR: Profile file missing at $export_static_path"
|
echo "ERROR: Profile file missing at $export_static_path"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Create a random crypt-token (9 random bytes as hex).
|
# Create a 9-byte random secret token as hex
|
||||||
local secret_token
|
local secret_token
|
||||||
secret_token=$(dd if=/dev/urandom bs=9 count=1 2>/dev/null | hexdump -e '"%02x"')
|
secret_token=$(dd if=/dev/urandom bs=9 count=1 2>/dev/null | hexdump -e '"%02x"')
|
||||||
|
|
||||||
# Path to the temporary session folder.
|
local session_dir="${ram_link_dir}/ovpn-tmp-${secret_token}-${link_filename}"
|
||||||
local session_dir="/www/ovpn-tmp-${secret_token}-${link_filename}"
|
|
||||||
local token_protected_file="${session_dir}/${link_filename}_client.ovpn"
|
|
||||||
|
|
||||||
# Create the directory safely.
|
|
||||||
mkdir -p "$session_dir"
|
mkdir -p "$session_dir"
|
||||||
|
|
||||||
# SECURITY FIX: create token-protected copy for the webserver
|
# Define file path and copy profile with safe webserver permissions
|
||||||
|
local token_protected_file="${session_dir}/${link_filename}.ovpn"
|
||||||
cp -f "$export_static_path" "$token_protected_file" 2>/dev/null
|
cp -f "$export_static_path" "$token_protected_file" 2>/dev/null
|
||||||
chmod 644 "$token_protected_file"
|
chmod 644 "$token_protected_file"
|
||||||
|
|
||||||
# Verify that the file copy exists and is valid.
|
# Create webserver shortcut to the RAM folder
|
||||||
|
if [ ! -L "$www_link_dir" ] && [ ! -d "$www_link_dir" ]; then
|
||||||
|
ln -s "$ram_link_dir" "$www_link_dir"
|
||||||
|
fi
|
||||||
|
|
||||||
if [ -f "$token_protected_file" ]; then
|
if [ -f "$token_protected_file" ]; then
|
||||||
lan_ip=$(lanip)
|
lan_ip=$(lanip)
|
||||||
if [ -z "$lan_ip" ] || [ "$lan_ip" = "0.0.0.0" ]; then
|
if [ -z "$lan_ip" ] || [ "$lan_ip" = "0.0.0.0" ]; then
|
||||||
lan_ip="localhost"
|
lan_ip="localhost"
|
||||||
|
# If the IP contains a colon (:), it is an IPv6 address and needs brackets []
|
||||||
|
elif echo "$lan_ip" | grep -q ':'; then
|
||||||
|
lan_ip="[${lan_ip}]"
|
||||||
fi
|
fi
|
||||||
# Return SUCCESS and the clean download link with sub-folder
|
echo "SUCCESS: http://${lan_ip}/ovpn-downloads/ovpn-tmp-${secret_token}-${link_filename}/${link_filename}.ovpn"
|
||||||
echo "SUCCESS: http://${lan_ip}/ovpn-tmp-${secret_token}-${link_filename}/${link_filename}_client.ovpn"
|
|
||||||
else
|
else
|
||||||
echo "ERROR: Failed to create web export file."
|
echo "ERROR: Failed to create web export file."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
delete)
|
delete)
|
||||||
# Safe clean up using wildcard on the directory name.
|
# Safe deletion inside RAM directory only
|
||||||
# This removes the folder, the temporary copy, and deletes the static file.
|
if [ -n "$link_filename" ] && [ -d "$ram_link_dir" ]; then
|
||||||
rm -rf /www/ovpn-tmp-*-"${link_filename}"
|
rm -rf "${ram_link_dir}"/ovpn-tmp-*-"${link_filename}"
|
||||||
echo "SUCCESS: Link directory and static profile deleted."
|
|
||||||
|
# Remove RAM base directory and the web symlink if it was the last file
|
||||||
|
rmdir "$ram_link_dir" 2>/dev/null
|
||||||
|
if [ ! -d "$ram_link_dir" ] && [ -L "$www_link_dir" ]; then
|
||||||
|
rm -f "$www_link_dir"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "SUCCESS: Link directory and static profile deleted from RAM."
|
||||||
|
else
|
||||||
|
echo "ERROR: Invalid deletion request."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "Usage: /usr/libexec/luci-app-openvpn-plus symlink [num] [create|delete] [display_name]"
|
echo "Usage: /usr/libexec/luci-app-openvpn-plus symlink [instance_id] [create|delete] [display_name]"
|
||||||
return 1
|
return 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
@@ -1357,6 +1372,8 @@ cleanup() {
|
|||||||
local instname
|
local instname
|
||||||
local conf_dir="/etc/openvpn/luci"
|
local conf_dir="/etc/openvpn/luci"
|
||||||
local key_dir="/etc/openvpn/keys"
|
local key_dir="/etc/openvpn/keys"
|
||||||
|
local ram_link_dir="/tmp/luci-ovpn-plus"
|
||||||
|
local www_link_dir="/www/ovpn-downloads"
|
||||||
|
|
||||||
# Step 1: Stop here if there are no instance config files on the router
|
# Step 1: Stop here if there are no instance config files on the router
|
||||||
ls /etc/openvpn/luci/instance*.conf >/dev/null 2>&1 || return 0
|
ls /etc/openvpn/luci/instance*.conf >/dev/null 2>&1 || return 0
|
||||||
@@ -1364,7 +1381,6 @@ cleanup() {
|
|||||||
# Step 2: Loop through all existing instance configuration files
|
# Step 2: Loop through all existing instance configuration files
|
||||||
for conf_file in "$conf_dir"/instance*.conf; do
|
for conf_file in "$conf_dir"/instance*.conf; do
|
||||||
if [ -f "$conf_file" ]; then
|
if [ -f "$conf_file" ]; then
|
||||||
# Extract the pure instance name from the file path
|
|
||||||
instname="${conf_file##*/}"
|
instname="${conf_file##*/}"
|
||||||
instname="${instname%.conf}"
|
instname="${instname%.conf}"
|
||||||
|
|
||||||
@@ -1382,17 +1398,31 @@ cleanup() {
|
|||||||
rm -f "$key_dir/client_${instname}.key"
|
rm -f "$key_dir/client_${instname}.key"
|
||||||
rm -f "$key_dir/server_${instname}.tls2.key"
|
rm -f "$key_dir/server_${instname}.tls2.key"
|
||||||
rm -f "$key_dir/client_${instname}.tls2.key"
|
rm -f "$key_dir/client_${instname}.tls2.key"
|
||||||
|
|
||||||
|
# Delete the static .ovpn file for this specific deleted instance
|
||||||
|
rm -f "$conf_dir/${instname}.ovpn"
|
||||||
|
|
||||||
|
# Clean up the specific RAM files for this deleted instance
|
||||||
|
if [ -d "$ram_link_dir" ]; then
|
||||||
|
rm -rf "${ram_link_dir}"/ovpn-tmp-*-*"${instname}"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# Step 4: Delete all remaining temporary symlink files from the system
|
# Step 4: Safely clean up the empty base directories and the web symlink
|
||||||
rm -rf /www/ovpn-tmp-*
|
if [ -d "$ram_link_dir" ]; then
|
||||||
rm -f "$conf_dir"/instance*.ovpn
|
# Removes the RAM base folder ONLY if it is completely empty
|
||||||
|
rmdir "$ram_link_dir" 2>/dev/null
|
||||||
|
fi
|
||||||
|
# Clean up the web shortcut if the RAM directory does not exist anymore
|
||||||
|
if [ ! -d "$ram_link_dir" ] && [ -L "$www_link_dir" ]; then
|
||||||
|
rm -f "$www_link_dir"
|
||||||
|
fi
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# Validate and extract cryptographic metadata for LuCI
|
# Validate and extract cryptographic metadata for LuCI
|
||||||
keymeta() {
|
keymeta() {
|
||||||
shift # Remove "keymeta" from the arguments list
|
shift # Remove "keymeta" from the arguments list
|
||||||
@@ -1475,7 +1505,7 @@ keymeta() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
echo "TYPE: Custom Raw Data Structure"
|
echo "INVALID: Custom Raw Data Structure"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
echo "ERROR: File is empty or missing."
|
echo "ERROR: File is empty or missing."
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ const TXT = {
|
|||||||
client: _('Client')
|
client: _('Client')
|
||||||
},
|
},
|
||||||
BTN: {
|
BTN: {
|
||||||
|
backup: _('Backup'),
|
||||||
close: _('Close'),
|
close: _('Close'),
|
||||||
cancel: _('Cancel'),
|
cancel: _('Cancel'),
|
||||||
download: _('Download'),
|
download: _('Download'),
|
||||||
@@ -57,6 +58,7 @@ const TXT = {
|
|||||||
generate_unique_keys: _('Yes (Generate Unique Keys)'),
|
generate_unique_keys: _('Yes (Generate Unique Keys)'),
|
||||||
generate_unique_keys_new_instance: _('To be secure, a new instance needs its own key files. Do you want to create them now? (Recommended - usually takes just a few moments)'),
|
generate_unique_keys_new_instance: _('To be secure, a new instance needs its own key files. Do you want to create them now? (Recommended - usually takes just a few moments)'),
|
||||||
generating_client_keys: _('Generating private client key...'),
|
generating_client_keys: _('Generating private client key...'),
|
||||||
|
generating_tls_client_keys: _('Generating TLS crypt v2 client key...'),
|
||||||
key_strength: _('Key Strength'),
|
key_strength: _('Key Strength'),
|
||||||
loading_contents: _('Loading key contents...'),
|
loading_contents: _('Loading key contents...'),
|
||||||
master_ca: _('Master CA'),
|
master_ca: _('Master CA'),
|
||||||
@@ -89,8 +91,9 @@ const TXT = {
|
|||||||
tls_key_server: _('Master Server Secret Key'),
|
tls_key_server: _('Master Server Secret Key'),
|
||||||
tls_key_client: _('Client Key'),
|
tls_key_client: _('Client Key'),
|
||||||
title_main: _('OpenVPN Instance Key Generator'),
|
title_main: _('OpenVPN Instance Key Generator'),
|
||||||
key_allocation: ('Key Allocation'),
|
add_server_instance: ('Add OpenVPN Server Instance'),
|
||||||
unknown_asset: _('Unknown Asset'),
|
unknown_asset: _('Unknown Asset'),
|
||||||
|
unknown_structure: _('Unknown Structure'),
|
||||||
use_unique_name_for_each_office: _('Important: Use a unique name for each office to configure the network routing correctly.'),
|
use_unique_name_for_each_office: _('Important: Use a unique name for each office to configure the network routing correctly.'),
|
||||||
validity_days: _('Validity (Days)'),
|
validity_days: _('Validity (Days)'),
|
||||||
vpn_key_type: _('VPN Key Type:')
|
vpn_key_type: _('VPN Key Type:')
|
||||||
@@ -105,6 +108,7 @@ const TXT = {
|
|||||||
ERROR: {
|
ERROR: {
|
||||||
format_corruption: _('The saved key data contains structural format corruption!'),
|
format_corruption: _('The saved key data contains structural format corruption!'),
|
||||||
polling_threshold: _('ERROR: Safety polling threshold exceeded boundaries. Process deadlocked.'),
|
polling_threshold: _('ERROR: Safety polling threshold exceeded boundaries. Process deadlocked.'),
|
||||||
|
tls_keygen_failed: _('TLS crypt v2 client key generation failed.')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,7 +214,7 @@ const getKeyMeta = async function (keyfile, luci_app_openvpn_plus_callback) {
|
|||||||
const result = {
|
const result = {
|
||||||
valid: false,
|
valid: false,
|
||||||
type: CRYPTO_TYPE.INVALID_KEY,
|
type: CRYPTO_TYPE.INVALID_KEY,
|
||||||
metadata: { type: _('Unknown Structure'), subject: '', expires: '', algo: '', curve: '' },
|
metadata: { type: TXT.KEY.unknown_structure, subject: '', expires: '', algo: '', curve: '' },
|
||||||
raw: ''
|
raw: ''
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -221,10 +225,14 @@ const getKeyMeta = async function (keyfile, luci_app_openvpn_plus_callback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 2. Safely trigger the verified callback mechanism
|
// 2. Safely trigger the verified callback mechanism
|
||||||
const res = await luci_app_openvpn_plus_callback([CFG.LIBEXEC.keymeta, keyfile]);
|
let res = await luci_app_openvpn_plus_callback([CFG.LIBEXEC.keymeta, keyfile]);
|
||||||
|
|
||||||
// Early escape if the backend fails or outputs errors
|
// Early escape if the backend fails or outputs errors
|
||||||
if (!res || res.length === 0 || res.startsWith('ERROR') || res.toUpperCase().indexOf('ERROR') !== -1) {
|
if (!res || res.length === 0) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
res = res.toUpperCase();
|
||||||
|
if (res.startsWith('ERROR') || res.indexOf('ERROR') !== -1 || res.startsWith('INVALID')) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -769,10 +777,11 @@ const openAutomatedPostKeyGenModal = async function (newInstanceItem, appData, w
|
|||||||
|
|
||||||
const yesBtn = E('button', { 'class': 'btn cbi-button cbi-button-action important', 'style': 'margin-right:10px;' }, TXT.KEY.generate_unique_keys);
|
const yesBtn = E('button', { 'class': 'btn cbi-button cbi-button-action important', 'style': 'margin-right:10px;' }, TXT.KEY.generate_unique_keys);
|
||||||
const noBtn = E('button', { 'class': 'btn cbi-button cbi-button-neutral', 'style': 'margin-right:10px;' }, TXT.KEY.clone_default_keys);
|
const noBtn = E('button', { 'class': 'btn cbi-button cbi-button-neutral', 'style': 'margin-right:10px;' }, TXT.KEY.clone_default_keys);
|
||||||
|
const backupBtn = E('button', { 'class': 'btn cbi-button cbi-button-positive important', 'style': 'margin-right:10px;' }, TXT.BTN.backup);
|
||||||
const cancelBtn = E('button', { 'class': 'btn cbi-button cbi-button-neutral', 'style': 'margin-right:10px;' + styleHide }, TXT.BTN.cancel);
|
const cancelBtn = E('button', { 'class': 'btn cbi-button cbi-button-neutral', 'style': 'margin-right:10px;' + styleHide }, TXT.BTN.cancel);
|
||||||
const closeBtn = E('button', { 'class': 'cbi-button cbi-button-action important', 'style': 'display:none;' }, TXT.BTN.close);
|
const closeBtn = E('button', { 'class': 'cbi-button cbi-button-action important', 'style': 'display:none;' }, TXT.BTN.close);
|
||||||
|
|
||||||
const buttonContainer = E('div', { 'style': 'text-align:right; margin-top:15px;' }, [yesBtn, noBtn, cancelBtn, closeBtn]);
|
const buttonContainer = E('div', { 'style': 'text-align:right; margin-top:15px;' }, [yesBtn, noBtn, backupBtn, cancelBtn, closeBtn]);
|
||||||
|
|
||||||
const dashedDivider = E('hr', {
|
const dashedDivider = E('hr', {
|
||||||
'style': 'margin:20px 0; border:0; border-top:1px dashed var(--border-color-medium, #cbd5e1);' + styleHide
|
'style': 'margin:20px 0; border:0; border-top:1px dashed var(--border-color-medium, #cbd5e1);' + styleHide
|
||||||
@@ -803,7 +812,6 @@ const openAutomatedPostKeyGenModal = async function (newInstanceItem, appData, w
|
|||||||
E('span', {}, TXT.INFO.wizard + ' ...')
|
E('span', {}, TXT.INFO.wizard + ' ...')
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
||||||
const wizardButtonWrapperRow = E('div', {
|
const wizardButtonWrapperRow = E('div', {
|
||||||
'style': 'display:flex; justify-content:flex-end; width:100%; margin-top:5px;' + styleHide
|
'style': 'display:flex; justify-content:flex-end; width:100%; margin-top:5px;' + styleHide
|
||||||
}, [openWizardBtn]);
|
}, [openWizardBtn]);
|
||||||
@@ -820,6 +828,12 @@ const openAutomatedPostKeyGenModal = async function (newInstanceItem, appData, w
|
|||||||
callbacks.openWizardBtnClick(appData, null, true);
|
callbacks.openWizardBtnClick(appData, null, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
backupBtn.addEventListener('click', async function (ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
L.ui.hideModal();
|
||||||
|
callbacks.importBackupInstance(appData);
|
||||||
|
});
|
||||||
|
|
||||||
// Declared anonymous handler as ASYNC to authorize internal await callbacks.createInstance
|
// Declared anonymous handler as ASYNC to authorize internal await callbacks.createInstance
|
||||||
noBtn.addEventListener('click', async function (ev) {
|
noBtn.addEventListener('click', async function (ev) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
@@ -889,7 +903,7 @@ const openAutomatedPostKeyGenModal = async function (newInstanceItem, appData, w
|
|||||||
callbacks.uciSaveApplyRestart(instance_id);
|
callbacks.uciSaveApplyRestart(instance_id);
|
||||||
});
|
});
|
||||||
|
|
||||||
L.ui.showModal(TXT.KEY.title_main + ' - ' + TXT.KEY.key_allocation + ' (' + instance_id + ')', [
|
L.ui.showModal(TXT.KEY.add_server_instance + ' (' + instance_id + ')', [
|
||||||
E('div', { 'class': 'cbi-map' }, [
|
E('div', { 'class': 'cbi-map' }, [
|
||||||
E('div', { 'class': 'cbi-section' }, [
|
E('div', { 'class': 'cbi-section' }, [
|
||||||
promptMessageNode,
|
promptMessageNode,
|
||||||
@@ -1148,7 +1162,7 @@ const setupKeyGenEvents = function (ui, instance_id, state, appData, uciSaveAppl
|
|||||||
// Scenario A: If a Site-to-Site client bundle is requested, chain the asymmetric client token derivation instantly
|
// Scenario A: If a Site-to-Site client bundle is requested, chain the asymmetric client token derivation instantly
|
||||||
if (state.mode === KEYTYPE.client_pki) {
|
if (state.mode === KEYTYPE.client_pki) {
|
||||||
const log_separator_line = '\n\n--------------------------------------------------\n\n';
|
const log_separator_line = '\n\n--------------------------------------------------\n\n';
|
||||||
ui.nodes.output.value += log_separator_line + ICON.ARROW + _('Generating TLS crypt v2 client key...') + '\n';
|
ui.nodes.output.value += log_separator_line + ICON.ARROW + TXT.KEY.generating_tls_client_keys + '\n';
|
||||||
ui.nodes.output.scrollTop = ui.nodes.output.scrollHeight;
|
ui.nodes.output.scrollTop = ui.nodes.output.scrollHeight;
|
||||||
|
|
||||||
// STEP 2: Asynchronously generate the client-tls2 credentials into the volatile RAM buffer
|
// STEP 2: Asynchronously generate the client-tls2 credentials into the volatile RAM buffer
|
||||||
@@ -1157,7 +1171,7 @@ const setupKeyGenEvents = function (ui, instance_id, state, appData, uciSaveAppl
|
|||||||
modeSelect.disabled = false;
|
modeSelect.disabled = false;
|
||||||
|
|
||||||
if (!tlsSuccess || !tlsData) {
|
if (!tlsSuccess || !tlsData) {
|
||||||
ui.nodes.output.value += '\n' + ICON.ERROR + ' TLS crypt v2 client key generation failed.';
|
ui.nodes.output.value += '\n' + ICON.ERROR + TXT.ERROR.tls_keygen_failed;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1338,14 +1352,13 @@ const openKeyGenModal = function (instance_id, displayId, role, appData, uciSave
|
|||||||
/**
|
/**
|
||||||
* Export the open wizard modal function to LuCI
|
* Export the open wizard modal function to LuCI
|
||||||
*/
|
*/
|
||||||
return L.Class.extend(
|
return L.Class.extend({
|
||||||
{
|
KEYTYPE: KEYTYPE,
|
||||||
KEYTYPE: KEYTYPE,
|
CRYPTO_TYPE: CRYPTO_TYPE,
|
||||||
CRYPTO_TYPE: CRYPTO_TYPE,
|
getKeyMeta: getKeyMeta,
|
||||||
getKeyMeta: getKeyMeta,
|
executeAsynchronousKeyGen: executeAsynchronousKeyGen,
|
||||||
executeAsynchronousKeyGen: executeAsynchronousKeyGen,
|
openKeyEditorModal: openKeyEditorModal,
|
||||||
openKeyEditorModal: openKeyEditorModal,
|
openAutomatedPostKeyGenModal: openAutomatedPostKeyGenModal,
|
||||||
openAutomatedPostKeyGenModal: openAutomatedPostKeyGenModal,
|
openKeyGenModal: openKeyGenModal
|
||||||
openKeyGenModal: openKeyGenModal
|
});
|
||||||
});
|
|
||||||
|
|
||||||
@@ -142,6 +142,7 @@ const CLIENT_TEMPLATE = Object.freeze({
|
|||||||
timeMs: 0,
|
timeMs: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* --- HELPER ---
|
* --- HELPER ---
|
||||||
*/
|
*/
|
||||||
@@ -153,6 +154,9 @@ const CLIENT_TEMPLATE = Object.freeze({
|
|||||||
const configAssetCache = {};
|
const configAssetCache = {};
|
||||||
const statusFileCache = {};
|
const statusFileCache = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalizes all Windows and Mac line breaks into clean UNIX line breaks for textareas.
|
||||||
|
*/
|
||||||
const sanitizeInputText = function (value) {
|
const sanitizeInputText = function (value) {
|
||||||
const rawText = value ? String(value).trim() : '';
|
const rawText = value ? String(value).trim() : '';
|
||||||
return rawText.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
return rawText.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
||||||
@@ -270,6 +274,9 @@ const isAnyInstanceEnabled = function (ovpnUciSections) {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Caches openVpnInfo
|
||||||
|
*/
|
||||||
const openVpnInfo = {
|
const openVpnInfo = {
|
||||||
macAddress: 0x0000,
|
macAddress: 0x0000,
|
||||||
version: null,
|
version: null,
|
||||||
@@ -327,7 +334,6 @@ const getOpenVpnInfo = async function () {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads system uptime from the proc filesystem asynchronously
|
* Reads system uptime from the proc filesystem asynchronously
|
||||||
*/
|
*/
|
||||||
@@ -1003,13 +1009,16 @@ const parseClientRefresh = function (content, instNum) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if site-to-site configuration is explicitly turned on
|
* Check if site-to-site configuration
|
||||||
*/
|
*/
|
||||||
const parseSiteToSite = function (content) {
|
const parseSiteToSite = function (content) {
|
||||||
const sitetosite_regex = /^[ \t]*setenv\s+sitetosite\s+on/mi;
|
const sitetosite_regex = /^[ \t]*setenv\s+sitetosite\s+on/mi;
|
||||||
return sitetosite_regex.test(content);
|
return sitetosite_regex.test(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if flood guard is turned on
|
||||||
|
*/
|
||||||
const parseFloodGuard = function (content) {
|
const parseFloodGuard = function (content) {
|
||||||
const sitetosite_regex = /^[ \t]*setenv\s+floodguard\s+on/mi;
|
const sitetosite_regex = /^[ \t]*setenv\s+floodguard\s+on/mi;
|
||||||
return sitetosite_regex.test(content);
|
return sitetosite_regex.test(content);
|
||||||
@@ -1282,7 +1291,6 @@ const getProcessStartTime = async function (id, pidVal, currentUptime) {
|
|||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read the connected client status file if necessary or use the statusFileCache
|
* Read the connected client status file if necessary or use the statusFileCache
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -51,7 +51,9 @@ const TXT = {
|
|||||||
MISSING: _('MISSING'),
|
MISSING: _('MISSING'),
|
||||||
WORKING: _('WORKING'),
|
WORKING: _('WORKING'),
|
||||||
mask: _('Mask'),
|
mask: _('Mask'),
|
||||||
openvpn_profile_valid: _('OpenVPN Profile Valid:'),
|
openvpn_profile_valid: _('OpenVPN Profile Valid'),
|
||||||
|
client_profile_detected: _('OpenVPN Client Profile detected'),
|
||||||
|
server_profile_detected: _('OpenVPN Server Profile detected'),
|
||||||
outside_port: _('Outside Port'),
|
outside_port: _('Outside Port'),
|
||||||
port_forwarding: _('Port Forwarding'),
|
port_forwarding: _('Port Forwarding'),
|
||||||
protocol: _('Protocol'),
|
protocol: _('Protocol'),
|
||||||
@@ -261,6 +263,7 @@ const WIZARD_DATA_TEMPLATE = Object.freeze({
|
|||||||
importOvpnClientProfileCallback: null, // Function to parse text, key bundles, and routing layouts from uploaded files
|
importOvpnClientProfileCallback: null, // Function to parse text, key bundles, and routing layouts from uploaded files
|
||||||
instanceNumber: 1, // Stores the current or next available numeric instance identification index
|
instanceNumber: 1, // Stores the current or next available numeric instance identification index
|
||||||
forcedScenario: null, // Bypasses the initial role selection step if the scenario context is locked
|
forcedScenario: null, // Bypasses the initial role selection step if the scenario context is locked
|
||||||
|
forcedScenarioTitle: null // Custrom title for Forced Cenario
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -283,7 +286,8 @@ const WIZARD_PARAMS_TEMPLATE = Object.freeze({
|
|||||||
isStaticIp: false, // Semiautomatic safety override bypassing the active IP migration checks
|
isStaticIp: false, // Semiautomatic safety override bypassing the active IP migration checks
|
||||||
ddnsProvider: '', // Selected dynamic DNS provider configuration key profile token identification
|
ddnsProvider: '', // Selected dynamic DNS provider configuration key profile token identification
|
||||||
ddnsUrl: '', // Generated secure HTTP GET request string dispatched for provider synchronization
|
ddnsUrl: '', // Generated secure HTTP GET request string dispatched for provider synchronization
|
||||||
importedRawOvpn: '' // Temporary payload staging buffer holding raw client file profile contents
|
importedRawOvpn: '', // Temporary payload staging buffer holding raw client file profile contents
|
||||||
|
isServerBackup: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -1166,7 +1170,7 @@ const importClientInstance = async function (elements, wizardData, nextBtn, prev
|
|||||||
// Path B: Full setup for a brand new profile installation
|
// Path B: Full setup for a brand new profile installation
|
||||||
const wizardParams = Object.assign({}, WIZARD_PARAMS_TEMPLATE, {
|
const wizardParams = Object.assign({}, WIZARD_PARAMS_TEMPLATE, {
|
||||||
clients: [],
|
clients: [],
|
||||||
role: OPENVPN.ROLE.CLIENT,
|
role: extractedParams.isServerBackup ? OPENVPN.ROLE.SERVER : OPENVPN.ROLE.CLIENT,
|
||||||
port: extractedParams.port,
|
port: extractedParams.port,
|
||||||
proto: extractedParams.proto,
|
proto: extractedParams.proto,
|
||||||
displayName: extractedParams.cname || cleanCustomName || TXT.INFO.imported_clients,
|
displayName: extractedParams.cname || cleanCustomName || TXT.INFO.imported_clients,
|
||||||
@@ -1175,7 +1179,8 @@ const importClientInstance = async function (elements, wizardData, nextBtn, prev
|
|||||||
remoteServerLan: extractedParams.remoteServerLan,
|
remoteServerLan: extractedParams.remoteServerLan,
|
||||||
isApMode: false,
|
isApMode: false,
|
||||||
ddnsOrPublicIp: '',
|
ddnsOrPublicIp: '',
|
||||||
importedRawOvpn: activeSubParams.importedRawOvpn || ''
|
importedRawOvpn: activeSubParams.importedRawOvpn || '',
|
||||||
|
isServerBackup: extractedParams.isServerBackup
|
||||||
});
|
});
|
||||||
|
|
||||||
if (typeof wizardData.addNewInstanceCallback === 'function') {
|
if (typeof wizardData.addNewInstanceCallback === 'function') {
|
||||||
@@ -1633,14 +1638,17 @@ const renderOptionSiteClient = function (buttons, messageBox) {
|
|||||||
const remoteServerMatch = text.match(/^remote\s+(\S+)\s+(\d+)/m);
|
const remoteServerMatch = text.match(/^remote\s+(\S+)\s+(\d+)/m);
|
||||||
const protoMatch = text.match(/^proto\s+(\S+)/m);
|
const protoMatch = text.match(/^proto\s+(\S+)/m);
|
||||||
const hasCaTag = (text.indexOf('<ca>') !== -1);
|
const hasCaTag = (text.indexOf('<ca>') !== -1);
|
||||||
|
const isServerBackupFile = /^server\s+/m.test(text) || /^server-ipv6\s+/m.test(text);
|
||||||
|
|
||||||
if (remoteServerMatch) {
|
if (remoteServerMatch) {
|
||||||
// PATH A: Valid full configuration profile (.ovpn)
|
// PATH A: Valid full client configuration profile (.ovpn)
|
||||||
uploadedFileContentStr = text;
|
uploadedFileContentStr = text;
|
||||||
|
|
||||||
const successContent = E('div', {}, [
|
const successContent = E('div', {}, [
|
||||||
E('strong', {}, ICON.SUCCESS + ' ' + TXT.INFO.openvpn_profile_valid),
|
E('strong', {}, ICON.SUCCESS + ' ' + TXT.INFO.openvpn_profile_valid),
|
||||||
E('br'),
|
E('br'),
|
||||||
|
E('span', {}, ICON.ARROW + ' ' + TXT.INFO.client_profile_detected),
|
||||||
|
E('br'),
|
||||||
E('span', {}, [
|
E('span', {}, [
|
||||||
ICON.ARROW + ' ' + TXT.INFO.server_address + ': ',
|
ICON.ARROW + ' ' + TXT.INFO.server_address + ': ',
|
||||||
E('code', { 'style': 'font-weight:bold; color:var(--primary-color-high, #1976d2);' },
|
E('code', { 'style': 'font-weight:bold; color:var(--primary-color-high, #1976d2);' },
|
||||||
@@ -1655,8 +1663,24 @@ const renderOptionSiteClient = function (buttons, messageBox) {
|
|||||||
buttons.next.disabled = false;
|
buttons.next.disabled = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (isServerBackupFile === true) {
|
||||||
|
// PATH B: Valid full server configuration profile (.ovpn)
|
||||||
|
uploadedFileContentStr = text;
|
||||||
|
|
||||||
|
const successContent = E('div', {}, [
|
||||||
|
E('strong', {}, ICON.SUCCESS + ' ' + TXT.INFO.openvpn_profile_valid),
|
||||||
|
E('br'),
|
||||||
|
E('span', {}, ICON.ARROW + ' ' + TXT.INFO.server_profile_detected),
|
||||||
|
]);
|
||||||
|
|
||||||
|
showMessage(successContent, MESSAGE_TYPE.OK, messageBox);
|
||||||
|
|
||||||
|
if (buttons && buttons.next) {
|
||||||
|
buttons.next.disabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (isCrtExtension === true || (hasCaTag === true && !remoteServerMatch)) {
|
else if (isCrtExtension === true || (hasCaTag === true && !remoteServerMatch)) {
|
||||||
// PATH B: Valid cryptographic multi-key bundle (.crt)
|
// PATH C: Valid cryptographic multi-key bundle (.crt)
|
||||||
uploadedFileContentStr = text;
|
uploadedFileContentStr = text;
|
||||||
|
|
||||||
const warningContent = E('div', {}, [
|
const warningContent = E('div', {}, [
|
||||||
@@ -1677,7 +1701,7 @@ const renderOptionSiteClient = function (buttons, messageBox) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// PATH C: File is corrupted or missing vital fields
|
// PATH D: File is corrupted or missing vital fields
|
||||||
fileInput.classList.add('cbi-input-invalid');
|
fileInput.classList.add('cbi-input-invalid');
|
||||||
customUploadBtn.classList.add('cbi-input-invalid');
|
customUploadBtn.classList.add('cbi-input-invalid');
|
||||||
fileInput.value = '';
|
fileInput.value = '';
|
||||||
@@ -2215,7 +2239,11 @@ const updateStepVisibility = function (elements, state, buttons, wizardData) {
|
|||||||
scenarioTitle += '\u2003\u2003' + ICON.TAG + elements.inputs.displayName.value;
|
scenarioTitle += '\u2003\u2003' + ICON.TAG + elements.inputs.displayName.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
state.nodes.descr.textContent = scenarioTitle;
|
if (wizardData.forcedScenarioTitle) {
|
||||||
|
state.nodes.descr.textContent = '';
|
||||||
|
} else {
|
||||||
|
state.nodes.descr.textContent = scenarioTitle;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -2815,7 +2843,6 @@ const setupAddressValidator = function (elements, buttons, portForwardingAlert,
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MAIN WIZARD
|
* MAIN WIZARD
|
||||||
*/
|
*/
|
||||||
@@ -2870,8 +2897,15 @@ const openWizardModal = function (wizardData, optionalHideClient) {
|
|||||||
document.head.appendChild(styleNode);
|
document.head.appendChild(styleNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let title;
|
||||||
|
if (wizardData.forcedScenarioTitle) {
|
||||||
|
title = wizardData.forcedScenarioTitle
|
||||||
|
} else {
|
||||||
|
title = ICON.ROCKET + TXT.INFO.setup_wizard;
|
||||||
|
}
|
||||||
|
|
||||||
// Show the modal window on the screen with a simple layout structure
|
// Show the modal window on the screen with a simple layout structure
|
||||||
L.ui.showModal(ICON.ROCKET + TXT.INFO.setup_wizard, [
|
L.ui.showModal(title, [
|
||||||
E('div', {
|
E('div', {
|
||||||
'class': 'cbi-map ovpn-plus-setup-wizard',
|
'class': 'cbi-map ovpn-plus-setup-wizard',
|
||||||
'style': 'width: 100%; margin: 0 auto;'
|
'style': 'width: 100%; margin: 0 auto;'
|
||||||
@@ -2898,12 +2932,12 @@ const openWizardModal = function (wizardData, optionalHideClient) {
|
|||||||
/**
|
/**
|
||||||
* Export the open wizard modal function to LuCI
|
* Export the open wizard modal function to LuCI
|
||||||
*/
|
*/
|
||||||
return L.Class.extend(
|
return L.Class.extend({
|
||||||
{
|
WIZARD_DATA_TEMPLATE: WIZARD_DATA_TEMPLATE,
|
||||||
WIZARD_DATA_TEMPLATE: WIZARD_DATA_TEMPLATE,
|
WIZARD_PARAMS_TEMPLATE: WIZARD_PARAMS_TEMPLATE,
|
||||||
WIZARD_PARAMS_TEMPLATE: WIZARD_PARAMS_TEMPLATE,
|
renderSubnetInputs: renderSubnetInputs,
|
||||||
renderSubnetInputs: renderSubnetInputs,
|
renderPortForwardingAlert: renderPortForwardingAlert,
|
||||||
renderPortForwardingAlert: renderPortForwardingAlert,
|
getValidCommonName: getValidCommonName,
|
||||||
getValidCommonName: getValidCommonName,
|
openWizardModal: openWizardModal
|
||||||
openWizardModal: openWizardModal
|
});
|
||||||
});
|
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -129,6 +129,36 @@ apply_dns_forward() {
|
|||||||
echo "$dns_mode" > "$DNS_MODE_FILE"
|
echo "$dns_mode" > "$DNS_MODE_FILE"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Run 'tailscale set' and recover from one known failure: a daemon built
|
||||||
|
# without auto-update support (ts_omit_clientupdate) rejects every edit whose
|
||||||
|
# result still has AutoUpdate.Apply=true, so while that flag is saved none of
|
||||||
|
# the settings below can be applied. Reset it once and retry. Any other
|
||||||
|
# failure, and the result of the retry, is reported unchanged.
|
||||||
|
run_tailscale_set() {
|
||||||
|
local ts_bin="$1" err ret
|
||||||
|
shift
|
||||||
|
|
||||||
|
err=$("$ts_bin" "$@" 2>&1)
|
||||||
|
ret=$?
|
||||||
|
|
||||||
|
if [ $ret -ne 0 ]; then
|
||||||
|
case "$err" in
|
||||||
|
*'Auto-update support is disabled in this build'*)
|
||||||
|
logger -t "$NAME" "AutoUpdate.Apply=true is unsupported in this build, resetting it"
|
||||||
|
if "$ts_bin" set --auto-update=false; then
|
||||||
|
err=$("$ts_bin" "$@" 2>&1)
|
||||||
|
ret=$?
|
||||||
|
else
|
||||||
|
logger -t "$NAME" "Failed to reset auto-update"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
[ -z "$err" ] || printf '%s\n' "$err" >&2
|
||||||
|
return $ret
|
||||||
|
}
|
||||||
|
|
||||||
apply_settings() {
|
apply_settings() {
|
||||||
local ts_bin
|
local ts_bin
|
||||||
if [ -x /usr/sbin/tailscale ]; then
|
if [ -x /usr/sbin/tailscale ]; then
|
||||||
@@ -253,7 +283,7 @@ apply_settings() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
logger -t "$NAME" "Applying settings"
|
logger -t "$NAME" "Applying settings"
|
||||||
"$ts_bin" "$@"
|
run_tailscale_set "$ts_bin" "$@"
|
||||||
local ret=$?
|
local ret=$?
|
||||||
[ $ret -eq 0 ] \
|
[ $ret -eq 0 ] \
|
||||||
&& logger -t "$NAME" "Settings applied successfully" \
|
&& logger -t "$NAME" "Settings applied successfully" \
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ LUCI_DEPENDS:=+luci-base +USE_APK:wget-any +!USE_APK:wget +jsonfilter +ucode-mod
|
|||||||
# Keep existing translation package names and the argon-config message domain.
|
# Keep existing translation package names and the argon-config message domain.
|
||||||
LUCI_BASENAME:=argon-config
|
LUCI_BASENAME:=argon-config
|
||||||
PKG_VERSION:=3.1.0
|
PKG_VERSION:=3.1.0
|
||||||
PKG_RELEASE:=32
|
PKG_RELEASE:=33
|
||||||
PKG_LICENSE:=Apache-2.0 MIT
|
PKG_LICENSE:=Apache-2.0 MIT
|
||||||
|
|
||||||
# LuCI derives translations independently of the theme version. Keep release
|
# LuCI derives translations independently of the theme version. Keep release
|
||||||
|
|||||||
@@ -2107,7 +2107,6 @@ span.spinning {
|
|||||||
#cbi-wireless > .cbi-section-node,
|
#cbi-wireless > .cbi-section-node,
|
||||||
#cbi-wireless > #wifi_assoclist_table,
|
#cbi-wireless > #wifi_assoclist_table,
|
||||||
[data-tab-title],
|
[data-tab-title],
|
||||||
[data-page^="admin-system-admin"]:not(.node-main-login) .cbi-map:not(#cbi-dropbear),
|
|
||||||
[data-page="admin-system-opkg"] #maincontent > .container {
|
[data-page="admin-system-opkg"] #maincontent > .container {
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
@@ -2128,7 +2127,6 @@ span.spinning {
|
|||||||
#cbi-wireless > .cbi-section-node:last-child,
|
#cbi-wireless > .cbi-section-node:last-child,
|
||||||
#cbi-wireless > #wifi_assoclist_table:last-child,
|
#cbi-wireless > #wifi_assoclist_table:last-child,
|
||||||
[data-tab-title]:last-child,
|
[data-tab-title]:last-child,
|
||||||
[data-page^="admin-system-admin"]:not(.node-main-login) .cbi-map:not(#cbi-dropbear):last-child,
|
|
||||||
[data-page="admin-system-opkg"] #maincontent > .container:last-child {
|
[data-page="admin-system-opkg"] #maincontent > .container:last-child {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
border: 0;
|
border: 0;
|
||||||
@@ -4777,6 +4775,57 @@ pre.command-output {
|
|||||||
gap: var(--control-gap);
|
gap: var(--control-gap);
|
||||||
padding: 0 var(--section-inset) var(--field-gap);
|
padding: 0 var(--section-inset) var(--field-gap);
|
||||||
}
|
}
|
||||||
|
[data-page="admin-system-admin"] .cbi-section > .cbi-section-node:not(.cbi-section-node-tabbed):where(:has(> .cbi-value, > [id] > .cbi-value)),
|
||||||
|
[data-page="admin-system-admin-password"] .cbi-section > .cbi-section-node:not(.cbi-section-node-tabbed):where(:has(> .cbi-value, > [id] > .cbi-value)),
|
||||||
|
[data-page="admin-system-admin-pwpolicy"] .cbi-section > .cbi-section-node:not(.cbi-section-node-tabbed):where(:has(> .cbi-value, > [id] > .cbi-value)),
|
||||||
|
[data-page="admin-system-admin-uhttpd"] .cbi-section > .cbi-section-node:not(.cbi-section-node-tabbed):where(:has(> .cbi-value, > [id] > .cbi-value)) {
|
||||||
|
padding-block: var(--field-gap);
|
||||||
|
}
|
||||||
|
[data-page="admin-system-admin"] .cbi-section > .cbi-section-descr:not(:empty) ~ .cbi-section-node:not(.cbi-section-node-tabbed),
|
||||||
|
[data-page="admin-system-admin-password"] .cbi-section > .cbi-section-descr:not(:empty) ~ .cbi-section-node:not(.cbi-section-node-tabbed),
|
||||||
|
[data-page="admin-system-admin-pwpolicy"] .cbi-section > .cbi-section-descr:not(:empty) ~ .cbi-section-node:not(.cbi-section-node-tabbed),
|
||||||
|
[data-page="admin-system-admin-uhttpd"] .cbi-section > .cbi-section-descr:not(:empty) ~ .cbi-section-node:not(.cbi-section-node-tabbed) {
|
||||||
|
padding-block-start: 0;
|
||||||
|
}
|
||||||
|
[data-page="admin-system-admin"] .cbi-section > .cbi-value,
|
||||||
|
[data-page="admin-system-admin-password"] .cbi-section > .cbi-value,
|
||||||
|
[data-page="admin-system-admin-pwpolicy"] .cbi-section > .cbi-value,
|
||||||
|
[data-page="admin-system-admin-uhttpd"] .cbi-section > .cbi-value {
|
||||||
|
padding-block: var(--field-gap);
|
||||||
|
}
|
||||||
|
[data-page="admin-system-admin"] .cbi-section > .cbi-value ~ .cbi-value,
|
||||||
|
[data-page="admin-system-admin-password"] .cbi-section > .cbi-value ~ .cbi-value,
|
||||||
|
[data-page="admin-system-admin-pwpolicy"] .cbi-section > .cbi-value ~ .cbi-value,
|
||||||
|
[data-page="admin-system-admin-uhttpd"] .cbi-section > .cbi-value ~ .cbi-value,
|
||||||
|
[data-page="admin-system-admin"] .cbi-section > .cbi-section-descr:not(:empty) ~ .cbi-value,
|
||||||
|
[data-page="admin-system-admin-password"] .cbi-section > .cbi-section-descr:not(:empty) ~ .cbi-value,
|
||||||
|
[data-page="admin-system-admin-pwpolicy"] .cbi-section > .cbi-section-descr:not(:empty) ~ .cbi-value,
|
||||||
|
[data-page="admin-system-admin-uhttpd"] .cbi-section > .cbi-section-descr:not(:empty) ~ .cbi-value {
|
||||||
|
padding-block-start: 0;
|
||||||
|
}
|
||||||
|
[data-page="admin-system-admin"] .cbi-section > .cbi-section-create,
|
||||||
|
[data-page="admin-system-admin-password"] .cbi-section > .cbi-section-create,
|
||||||
|
[data-page="admin-system-admin-pwpolicy"] .cbi-section > .cbi-section-create,
|
||||||
|
[data-page="admin-system-admin-uhttpd"] .cbi-section > .cbi-section-create {
|
||||||
|
padding: var(--field-gap) var(--section-inset);
|
||||||
|
}
|
||||||
|
[data-page="admin-system-admin-pwpolicy"] #view > .cbi-map {
|
||||||
|
min-width: 0;
|
||||||
|
border-radius: var(--radius-card);
|
||||||
|
background-color: var(--surface-color);
|
||||||
|
box-shadow: var(--section-shadow);
|
||||||
|
gap: 0;
|
||||||
|
}
|
||||||
|
[data-page="admin-system-admin-pwpolicy"] #view > .cbi-map > h2 {
|
||||||
|
margin: 0;
|
||||||
|
border-end-start-radius: 0;
|
||||||
|
border-end-end-radius: 0;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
[data-page="admin-system-admin-pwpolicy"] #view > .cbi-map > .cbi-section {
|
||||||
|
background: none;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
[data-page="admin-system-admin"] .cbi-map h2,
|
[data-page="admin-system-admin"] .cbi-map h2,
|
||||||
[data-page="admin-system-admin-password"] .cbi-map h2,
|
[data-page="admin-system-admin-password"] .cbi-map h2,
|
||||||
[data-page="admin-system-admin"] .cbi-map .cbi-map-descr,
|
[data-page="admin-system-admin"] .cbi-map .cbi-map-descr,
|
||||||
@@ -4800,10 +4849,17 @@ pre.command-output {
|
|||||||
[data-page="admin-system-admin-password"] [data-name="pw2"] .cbi-value-description span[style="color:green"] {
|
[data-page="admin-system-admin-password"] [data-name="pw2"] .cbi-value-description span[style="color:green"] {
|
||||||
color: var(--strength-strong-text) !important;
|
color: var(--strength-strong-text) !important;
|
||||||
}
|
}
|
||||||
|
[data-page="admin-system-admin-sshkeys"] #view > div:has(> .cbi-section-node > .cbi-dynlist) > .cbi-section-node {
|
||||||
|
min-width: 0;
|
||||||
|
border-radius: var(--radius-card);
|
||||||
|
background-color: var(--surface-color);
|
||||||
|
box-shadow: var(--section-shadow);
|
||||||
|
padding: var(--field-gap) var(--section-inset);
|
||||||
|
}
|
||||||
[data-page="admin-system-admin-sshkeys"] .cbi-dynlist {
|
[data-page="admin-system-admin-sshkeys"] .cbi-dynlist {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-inline: var(--section-inset);
|
margin: 0;
|
||||||
max-width: min(48rem, calc(100% - 2 * var(--section-inset)));
|
max-width: min(48rem, 100%);
|
||||||
gap: var(--field-gap);
|
gap: var(--field-gap);
|
||||||
}
|
}
|
||||||
[data-page="admin-system-admin-sshkeys"] .cbi-dynlist > .item {
|
[data-page="admin-system-admin-sshkeys"] .cbi-dynlist > .item {
|
||||||
@@ -4927,6 +4983,23 @@ pre.command-output {
|
|||||||
overflow-wrap: anywhere;
|
overflow-wrap: anywhere;
|
||||||
user-select: text;
|
user-select: text;
|
||||||
}
|
}
|
||||||
|
[data-page="admin-system-admin-repokeys"] .cbi-section-table {
|
||||||
|
border-collapse: separate;
|
||||||
|
border-spacing: 0;
|
||||||
|
border-radius: inherit;
|
||||||
|
}
|
||||||
|
[data-page="admin-system-admin-repokeys"] .cbi-section-table > .thead,
|
||||||
|
[data-page="admin-system-admin-repokeys"] .cbi-section-table > .thead > .tr {
|
||||||
|
border-start-start-radius: inherit;
|
||||||
|
border-start-end-radius: inherit;
|
||||||
|
background: var(--surface-muted);
|
||||||
|
}
|
||||||
|
[data-page="admin-system-admin-repokeys"] .cbi-section-table > .thead > .tr > .th:first-child {
|
||||||
|
border-start-start-radius: inherit;
|
||||||
|
}
|
||||||
|
[data-page="admin-system-admin-repokeys"] .cbi-section-table .cbi-section-table-row {
|
||||||
|
margin-block: 0;
|
||||||
|
}
|
||||||
[data-page="admin-system-admin-repokeys"] textarea {
|
[data-page="admin-system-admin-repokeys"] textarea {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
@@ -5210,6 +5283,44 @@ pre.command-output {
|
|||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
[data-page="admin-system-package-manager"] #view > #packages,
|
||||||
|
[data-page="admin-system-opkg"] #view > #packages {
|
||||||
|
min-width: 0;
|
||||||
|
border-radius: var(--radius-card);
|
||||||
|
background-color: var(--surface-color);
|
||||||
|
box-shadow: var(--section-shadow);
|
||||||
|
border-collapse: separate;
|
||||||
|
border-spacing: 0;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
[data-page="admin-system-package-manager"] #view > #packages > :is(thead, tbody, .thead, .tbody):first-child,
|
||||||
|
[data-page="admin-system-opkg"] #view > #packages > :is(thead, tbody, .thead, .tbody):first-child,
|
||||||
|
[data-page="admin-system-package-manager"] #view > #packages > :is(thead, tbody, .thead, .tbody):first-child > :is(tr, .tr):first-child,
|
||||||
|
[data-page="admin-system-opkg"] #view > #packages > :is(thead, tbody, .thead, .tbody):first-child > :is(tr, .tr):first-child,
|
||||||
|
[data-page="admin-system-package-manager"] #view > #packages > :is(tr, .tr):first-child,
|
||||||
|
[data-page="admin-system-opkg"] #view > #packages > :is(tr, .tr):first-child {
|
||||||
|
border-start-start-radius: inherit;
|
||||||
|
border-start-end-radius: inherit;
|
||||||
|
}
|
||||||
|
[data-page="admin-system-package-manager"] #view > #packages > :is(thead, tbody, .thead, .tbody):first-child > :is(tr, .tr):first-child > :is(td, th, .td, .th):first-child,
|
||||||
|
[data-page="admin-system-opkg"] #view > #packages > :is(thead, tbody, .thead, .tbody):first-child > :is(tr, .tr):first-child > :is(td, th, .td, .th):first-child,
|
||||||
|
[data-page="admin-system-package-manager"] #view > #packages > :is(tr, .tr):first-child > :is(td, th, .td, .th):first-child,
|
||||||
|
[data-page="admin-system-opkg"] #view > #packages > :is(tr, .tr):first-child > :is(td, th, .td, .th):first-child {
|
||||||
|
border-start-start-radius: inherit;
|
||||||
|
}
|
||||||
|
[data-page="admin-system-package-manager"] #view > #packages > :is(thead, tbody, .thead, .tbody):first-child > :is(tr, .tr):first-child > :is(td, th, .td, .th):last-child,
|
||||||
|
[data-page="admin-system-opkg"] #view > #packages > :is(thead, tbody, .thead, .tbody):first-child > :is(tr, .tr):first-child > :is(td, th, .td, .th):last-child,
|
||||||
|
[data-page="admin-system-package-manager"] #view > #packages > :is(tr, .tr):first-child > :is(td, th, .td, .th):last-child,
|
||||||
|
[data-page="admin-system-opkg"] #view > #packages > :is(tr, .tr):first-child > :is(td, th, .td, .th):last-child {
|
||||||
|
border-start-end-radius: inherit;
|
||||||
|
}
|
||||||
|
@media screen and (max-width: 1152px) {
|
||||||
|
[data-page="admin-system-package-manager"] #view > #packages :is(.table-titles, .cbi-section-table-titles) + .tr,
|
||||||
|
[data-page="admin-system-opkg"] #view > #packages :is(.table-titles, .cbi-section-table-titles) + .tr {
|
||||||
|
border-start-start-radius: inherit;
|
||||||
|
border-start-end-radius: inherit;
|
||||||
|
}
|
||||||
|
}
|
||||||
[data-page="admin-system-package-manager"] .controls,
|
[data-page="admin-system-package-manager"] .controls,
|
||||||
[data-page="admin-system-opkg"] .controls {
|
[data-page="admin-system-opkg"] .controls {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
@@ -5334,7 +5445,17 @@ pre.command-output {
|
|||||||
flex-basis: 50%;
|
flex-basis: 50%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
[data-page="admin-system-crontab"] #view > p:has(> textarea) {
|
||||||
|
min-width: 0;
|
||||||
|
border-radius: var(--radius-card);
|
||||||
|
background-color: var(--surface-color);
|
||||||
|
box-shadow: var(--section-shadow);
|
||||||
|
margin: 0;
|
||||||
|
padding: var(--field-gap) var(--section-inset);
|
||||||
|
line-height: 0;
|
||||||
|
}
|
||||||
[data-page="admin-system-crontab"] #view p textarea {
|
[data-page="admin-system-crontab"] #view p textarea {
|
||||||
|
display: block;
|
||||||
direction: ltr;
|
direction: ltr;
|
||||||
unicode-bidi: plaintext;
|
unicode-bidi: plaintext;
|
||||||
text-align: start;
|
text-align: start;
|
||||||
@@ -6043,6 +6164,9 @@ pre.command-output {
|
|||||||
top: 0.1rem;
|
top: 0.1rem;
|
||||||
left: 1rem;
|
left: 1rem;
|
||||||
}
|
}
|
||||||
|
[data-page="admin-system-reboot"] #view > hr {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
[data-page="admin-system-reboot"] .cbi-button {
|
[data-page="admin-system-reboot"] .cbi-button {
|
||||||
background: var(--warning-action-bg) !important;
|
background: var(--warning-action-bg) !important;
|
||||||
border-color: var(--warning-action-bg) !important;
|
border-color: var(--warning-action-bg) !important;
|
||||||
@@ -6313,6 +6437,14 @@ pre.command-output {
|
|||||||
direction: ltr;
|
direction: ltr;
|
||||||
unicode-bidi: isolate;
|
unicode-bidi: isolate;
|
||||||
}
|
}
|
||||||
|
[data-page="admin-network-diagnostics"] #view > .cbi-map > .table {
|
||||||
|
min-width: 0;
|
||||||
|
border-radius: var(--radius-card);
|
||||||
|
background-color: var(--surface-color);
|
||||||
|
box-shadow: var(--section-shadow);
|
||||||
|
padding: var(--control-gap);
|
||||||
|
border-collapse: separate;
|
||||||
|
}
|
||||||
[data-page="admin-network-diagnostics"] .table {
|
[data-page="admin-network-diagnostics"] .table {
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
@@ -6339,7 +6471,7 @@ pre.command-output {
|
|||||||
}
|
}
|
||||||
[data-page="admin-network-diagnostics"] .tr .td {
|
[data-page="admin-network-diagnostics"] .tr .td {
|
||||||
background-color: var(--surface-color) !important;
|
background-color: var(--surface-color) !important;
|
||||||
border-bottom: 1px solid var(--border-color) !important;
|
border-bottom: 0 !important;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -6468,6 +6600,35 @@ pre.command-output {
|
|||||||
#view > .Dashboard [data-tab="layout"] .cbi-page-actions {
|
#view > .Dashboard [data-tab="layout"] .cbi-page-actions {
|
||||||
padding: var(--field-gap) var(--section-inset);
|
padding: var(--field-gap) var(--section-inset);
|
||||||
}
|
}
|
||||||
|
[data-page="admin-status-processes"] #view > .table {
|
||||||
|
min-width: 0;
|
||||||
|
border-radius: var(--radius-card);
|
||||||
|
background-color: var(--surface-color);
|
||||||
|
box-shadow: var(--section-shadow);
|
||||||
|
border-collapse: separate;
|
||||||
|
border-spacing: 0;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
[data-page="admin-status-processes"] #view > .table > :is(thead, tbody, .thead, .tbody):first-child,
|
||||||
|
[data-page="admin-status-processes"] #view > .table > :is(thead, tbody, .thead, .tbody):first-child > :is(tr, .tr):first-child,
|
||||||
|
[data-page="admin-status-processes"] #view > .table > :is(tr, .tr):first-child {
|
||||||
|
border-start-start-radius: inherit;
|
||||||
|
border-start-end-radius: inherit;
|
||||||
|
}
|
||||||
|
[data-page="admin-status-processes"] #view > .table > :is(thead, tbody, .thead, .tbody):first-child > :is(tr, .tr):first-child > :is(td, th, .td, .th):first-child,
|
||||||
|
[data-page="admin-status-processes"] #view > .table > :is(tr, .tr):first-child > :is(td, th, .td, .th):first-child {
|
||||||
|
border-start-start-radius: inherit;
|
||||||
|
}
|
||||||
|
[data-page="admin-status-processes"] #view > .table > :is(thead, tbody, .thead, .tbody):first-child > :is(tr, .tr):first-child > :is(td, th, .td, .th):last-child,
|
||||||
|
[data-page="admin-status-processes"] #view > .table > :is(tr, .tr):first-child > :is(td, th, .td, .th):last-child {
|
||||||
|
border-start-end-radius: inherit;
|
||||||
|
}
|
||||||
|
@media screen and (max-width: 1152px) {
|
||||||
|
[data-page="admin-status-processes"] #view > .table :is(.table-titles, .cbi-section-table-titles) + .tr {
|
||||||
|
border-start-start-radius: inherit;
|
||||||
|
border-start-end-radius: inherit;
|
||||||
|
}
|
||||||
|
}
|
||||||
[data-page="admin-status-processes"] .th:nth-child(3),
|
[data-page="admin-status-processes"] .th:nth-child(3),
|
||||||
[data-page="admin-status-processes"] .td:nth-child(3) {
|
[data-page="admin-status-processes"] .td:nth-child(3) {
|
||||||
text-align: start;
|
text-align: start;
|
||||||
@@ -7013,9 +7174,35 @@ pre.command-output {
|
|||||||
[data-page="admin-status-nftables"] .alert-message.fade-out {
|
[data-page="admin-status-nftables"] .alert-message.fade-out {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
[data-page="admin-status-logs"] #content_syslog,
|
||||||
|
[data-page="admin-status-logs-syslog"] #content_syslog,
|
||||||
|
[data-page="admin-status-logs-dmesg"] #content_syslog {
|
||||||
|
min-width: 0;
|
||||||
|
border-radius: var(--radius-card);
|
||||||
|
background-color: var(--surface-color);
|
||||||
|
box-shadow: var(--section-shadow);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--field-gap);
|
||||||
|
padding: var(--field-gap) var(--section-inset);
|
||||||
|
}
|
||||||
|
[data-page="admin-status-logs"] #content_syslog > div,
|
||||||
|
[data-page="admin-status-logs-syslog"] #content_syslog > div,
|
||||||
|
[data-page="admin-status-logs-dmesg"] #content_syslog > div {
|
||||||
|
margin-block: 0 !important;
|
||||||
|
}
|
||||||
|
[data-page="admin-status-logs"] #content_syslog > div:has(> .cbi-button),
|
||||||
|
[data-page="admin-status-logs-syslog"] #content_syslog > div:has(> .cbi-button),
|
||||||
|
[data-page="admin-status-logs-dmesg"] #content_syslog > div:has(> .cbi-button) {
|
||||||
|
padding-block: 0 !important;
|
||||||
|
}
|
||||||
[data-page="admin-status-logs"] #syslog,
|
[data-page="admin-status-logs"] #syslog,
|
||||||
[data-page="admin-status-logs-syslog"] #syslog,
|
[data-page="admin-status-logs-syslog"] #syslog,
|
||||||
[data-page="admin-status-logs-dmesg"] #syslog {
|
[data-page="admin-status-logs-dmesg"] #syslog {
|
||||||
|
display: block;
|
||||||
|
margin: 0;
|
||||||
|
border: 1px solid var(--control-border);
|
||||||
|
background-color: var(--input-bg);
|
||||||
direction: ltr;
|
direction: ltr;
|
||||||
unicode-bidi: plaintext;
|
unicode-bidi: plaintext;
|
||||||
text-align: start;
|
text-align: start;
|
||||||
@@ -7027,7 +7214,7 @@ pre.command-output {
|
|||||||
[data-page="admin-status-logs-syslog"] #content_syslog > div:has(> input, > select),
|
[data-page="admin-status-logs-syslog"] #content_syslog > div:has(> input, > select),
|
||||||
[data-page="admin-status-logs-dmesg"] #content_syslog > div:has(> input, > select) {
|
[data-page="admin-status-logs-dmesg"] #content_syslog > div:has(> input, > select) {
|
||||||
line-height: 2.5rem;
|
line-height: 2.5rem;
|
||||||
margin-block-end: var(--control-gap) !important;
|
margin-block-end: 0 !important;
|
||||||
}
|
}
|
||||||
[data-page="admin-status-logs"] #content_syslog > div:has(> input, > select) > label,
|
[data-page="admin-status-logs"] #content_syslog > div:has(> input, > select) > label,
|
||||||
[data-page="admin-status-logs-syslog"] #content_syslog > div:has(> input, > select) > label,
|
[data-page="admin-status-logs-syslog"] #content_syslog > div:has(> input, > select) > label,
|
||||||
@@ -7119,6 +7306,29 @@ pre.command-output {
|
|||||||
[data-page="admin-services-nlbw-display"] .cbi-section > .right {
|
[data-page="admin-services-nlbw-display"] .cbi-section > .right {
|
||||||
margin: 15px 15px 0px 0px;
|
margin: 15px 15px 0px 0px;
|
||||||
}
|
}
|
||||||
|
[data-page="admin-services-wol"] #cbi-luci-wol-defaults-_info {
|
||||||
|
padding-bottom: var(--field-gap);
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
[data-page="admin-services-wol"] #cbi-luci-wol-defaults-_info > output {
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
[data-page="admin-services-wol"] #cbi-luci-wol-defaults-_info output p {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
[data-page="admin-services-wol"] #cbi-luci-wol-defaults-_info .control-group {
|
||||||
|
margin-block-start: var(--control-gap);
|
||||||
|
}
|
||||||
|
[data-page="admin-services-wol"] .cbi-section:has(> #wol-output-text) {
|
||||||
|
padding-bottom: var(--field-gap);
|
||||||
|
}
|
||||||
|
[data-page="admin-services-wol"] #wol-output-text {
|
||||||
|
display: block;
|
||||||
|
width: calc(100% - var(--section-inset) - var(--section-inset)) !important;
|
||||||
|
margin-inline: var(--section-inset);
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
[data-page="admin-services-ddns"] #syslog {
|
[data-page="admin-services-ddns"] #syslog {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
font-size: 12px !important;
|
font-size: 12px !important;
|
||||||
@@ -7205,7 +7415,7 @@ pre.command-output {
|
|||||||
--oc-text: var(--text-color);
|
--oc-text: var(--text-color);
|
||||||
--oc-text-muted: var(--plugin-text-muted);
|
--oc-text-muted: var(--plugin-text-muted);
|
||||||
--oc-neutral: var(--plugin-neutral);
|
--oc-neutral: var(--plugin-neutral);
|
||||||
--oc-scrollbar: var(--plugin-scrollbar);
|
--oc-scrollbar: var(--oc-accent);
|
||||||
}
|
}
|
||||||
[data-page^="admin-services-openclash"] img[src$="arrow-clockwise-light.svg"],
|
[data-page^="admin-services-openclash"] img[src$="arrow-clockwise-light.svg"],
|
||||||
[data-page^="admin-services-openclash"] img[src$="wrench-light.svg"],
|
[data-page^="admin-services-openclash"] img[src$="wrench-light.svg"],
|
||||||
@@ -7256,8 +7466,8 @@ pre.command-output {
|
|||||||
--primary-a60: var(--oc-accent-a60) !important;
|
--primary-a60: var(--oc-accent-a60) !important;
|
||||||
--radio-hover-bg: var(--oc-accent-a10) !important;
|
--radio-hover-bg: var(--oc-accent-a10) !important;
|
||||||
--tab-hover-dark-bg: var(--oc-accent-a10) !important;
|
--tab-hover-dark-bg: var(--oc-accent-a10) !important;
|
||||||
--scrollbar-thumb: var(--oc-neutral) !important;
|
--scrollbar-thumb: var(--oc-scrollbar) !important;
|
||||||
--scrollbar-thumb-hover: var(--oc-text-muted) !important;
|
--scrollbar-thumb-hover: var(--oc-accent-hover) !important;
|
||||||
border-radius: var(--radius-card);
|
border-radius: var(--radius-card);
|
||||||
}
|
}
|
||||||
[data-page^="admin-services-openclash"] .oc .developer-container,
|
[data-page^="admin-services-openclash"] .oc .developer-container,
|
||||||
@@ -7271,6 +7481,22 @@ pre.command-output {
|
|||||||
[data-page^="admin-services-openclash"] .oc .config-upload-content {
|
[data-page^="admin-services-openclash"] .oc .config-upload-content {
|
||||||
background: var(--oc-surface) !important;
|
background: var(--oc-surface) !important;
|
||||||
}
|
}
|
||||||
|
[data-page^="admin-services-openclash"] textarea::-webkit-scrollbar-thumb,
|
||||||
|
[data-page^="admin-services-openclash"] .oc::-webkit-scrollbar-thumb,
|
||||||
|
[data-page^="admin-services-openclash"] .oc *::-webkit-scrollbar-thumb,
|
||||||
|
[data-page^="admin-services-openclash"] .oc .cm-editor .cm-scroller::-webkit-scrollbar-thumb,
|
||||||
|
[data-page^="admin-services-openclash"] .oc #debug-rendered::-webkit-scrollbar-thumb,
|
||||||
|
[data-page^="admin-services-openclash"] .oc #debug-rendered pre::-webkit-scrollbar-thumb {
|
||||||
|
background-color: var(--oc-scrollbar) !important;
|
||||||
|
}
|
||||||
|
[data-page^="admin-services-openclash"] textarea::-webkit-scrollbar-thumb:hover,
|
||||||
|
[data-page^="admin-services-openclash"] .oc::-webkit-scrollbar-thumb:hover,
|
||||||
|
[data-page^="admin-services-openclash"] .oc *::-webkit-scrollbar-thumb:hover,
|
||||||
|
[data-page^="admin-services-openclash"] .oc .cm-editor .cm-scroller::-webkit-scrollbar-thumb:hover,
|
||||||
|
[data-page^="admin-services-openclash"] .oc #debug-rendered::-webkit-scrollbar-thumb:hover,
|
||||||
|
[data-page^="admin-services-openclash"] .oc #debug-rendered pre::-webkit-scrollbar-thumb:hover {
|
||||||
|
background-color: var(--oc-accent-hover) !important;
|
||||||
|
}
|
||||||
[data-page^="admin-services-openclash"] .cbi-button.cbi-button-add,
|
[data-page^="admin-services-openclash"] .cbi-button.cbi-button-add,
|
||||||
[data-page^="admin-services-openclash"] .cbi-button.cbi-button-apply,
|
[data-page^="admin-services-openclash"] .cbi-button.cbi-button-apply,
|
||||||
[data-page^="admin-services-openclash"] .cbi-button.cbi-button-edit,
|
[data-page^="admin-services-openclash"] .cbi-button.cbi-button-edit,
|
||||||
@@ -7435,6 +7661,7 @@ pre.command-output {
|
|||||||
[data-page^="admin-services-openclash"] .oc .config-upload-btn-large:hover {
|
[data-page^="admin-services-openclash"] .oc .config-upload-btn-large:hover {
|
||||||
transform: none !important;
|
transform: none !important;
|
||||||
}
|
}
|
||||||
|
[data-page^="admin-services-openclash"] b.oc-txt-good,
|
||||||
[data-page^="admin-services-openclash"] b[style*="color:green"],
|
[data-page^="admin-services-openclash"] b[style*="color:green"],
|
||||||
[data-page^="admin-services-openclash"] b[style*="color: green"],
|
[data-page^="admin-services-openclash"] b[style*="color: green"],
|
||||||
[data-page^="admin-services-openclash"] b[color="green"] {
|
[data-page^="admin-services-openclash"] b[color="green"] {
|
||||||
@@ -7469,6 +7696,19 @@ pre.command-output {
|
|||||||
padding-top: 0.4375rem;
|
padding-top: 0.4375rem;
|
||||||
padding-bottom: 0.4375rem;
|
padding-bottom: 0.4375rem;
|
||||||
}
|
}
|
||||||
|
[data-page^="admin-services-openclash"][data-page="admin-services-openclash-settings"] [id="container.openclash.config.version_update"] {
|
||||||
|
padding-inline: var(--section-inset);
|
||||||
|
}
|
||||||
|
[data-page^="admin-services-openclash"][data-page="admin-services-openclash-settings"] [id="container.openclash.config.version_update"] > .oc {
|
||||||
|
border-radius: 0;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
[data-page^="admin-services-openclash"][data-page="admin-services-openclash-settings"] [id="container.openclash.config.version_update"] #selectPopup.select-popup-tab {
|
||||||
|
border: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
padding: 0;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
[data-page^="admin-services-openclash"][data-page="admin-services-openclash-config"] .cbi-map > .cbi-section > .cbi-section-node:not(.cbi-section-node-tabbed),
|
[data-page^="admin-services-openclash"][data-page="admin-services-openclash-config"] .cbi-map > .cbi-section > .cbi-section-node:not(.cbi-section-node-tabbed),
|
||||||
[data-page^="admin-services-openclash"][data-page="admin-services-openclash-config-subscribe"] .cbi-map > .cbi-section > .cbi-section-node:not(.cbi-section-node-tabbed),
|
[data-page^="admin-services-openclash"][data-page="admin-services-openclash-config-subscribe"] .cbi-map > .cbi-section > .cbi-section-node:not(.cbi-section-node-tabbed),
|
||||||
[data-page^="admin-services-openclash"][data-page^="admin-services-openclash-custom-dns-edit"] .cbi-map > .cbi-section > .cbi-section-node:not(.cbi-section-node-tabbed),
|
[data-page^="admin-services-openclash"][data-page^="admin-services-openclash-custom-dns-edit"] .cbi-map > .cbi-section > .cbi-section-node:not(.cbi-section-node-tabbed),
|
||||||
@@ -7592,6 +7832,35 @@ pre.command-output {
|
|||||||
[data-page^="admin-services-openclash"] #tab-content .dom {
|
[data-page^="admin-services-openclash"] #tab-content .dom {
|
||||||
padding: 0 1rem 1rem 1rem;
|
padding: 0 1rem 1rem 1rem;
|
||||||
}
|
}
|
||||||
|
[data-page^="admin-services-openclash"][data-page="admin-services-openclash-log"] #tab.oc {
|
||||||
|
height: auto;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
background: transparent;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
[data-page^="admin-services-openclash"][data-page="admin-services-openclash-log"] #tab.oc > #tab-header {
|
||||||
|
padding: var(--field-gap) 0;
|
||||||
|
border: 0;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
[data-page^="admin-services-openclash"][data-page="admin-services-openclash-log"] #tab.oc > #tab-header > ul.oc-tabmenu {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
[data-page^="admin-services-openclash"][data-page="admin-services-openclash-log"] #tab.oc + .oc {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
[data-page^="admin-services-openclash"][data-page="admin-services-openclash-log"] #tab-content > .dom {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
[data-page^="admin-services-openclash"][data-page="admin-services-openclash-log"] .oc .select-popup-footer.log-footer {
|
||||||
|
margin: 0;
|
||||||
|
padding: var(--field-gap) 0;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
background: transparent;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
[data-page^="admin-services-openclash"] #cbi-openclash-dns_servers .cbi-section-table-row > .cbi-section-table-cell:last-child {
|
[data-page^="admin-services-openclash"] #cbi-openclash-dns_servers .cbi-section-table-row > .cbi-section-table-cell:last-child {
|
||||||
min-width: 7.25rem;
|
min-width: 7.25rem;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|||||||
@@ -1,5 +1,13 @@
|
|||||||
// out: false
|
// out: false
|
||||||
|
|
||||||
|
// Opt-in surface for native views that do not render a cbi-section.
|
||||||
|
.native-page-card() {
|
||||||
|
min-width: 0;
|
||||||
|
border-radius: var(--radius-card);
|
||||||
|
background-color: var(--surface-color);
|
||||||
|
box-shadow: var(--section-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
.cbi-section,
|
.cbi-section,
|
||||||
.cbi-section-error,
|
.cbi-section-error,
|
||||||
#iptables,
|
#iptables,
|
||||||
@@ -8,7 +16,6 @@
|
|||||||
#cbi-wireless>.cbi-section-node,
|
#cbi-wireless>.cbi-section-node,
|
||||||
#cbi-wireless>#wifi_assoclist_table,
|
#cbi-wireless>#wifi_assoclist_table,
|
||||||
[data-tab-title],
|
[data-tab-title],
|
||||||
[data-page^="admin-system-admin"]:not(.node-main-login) .cbi-map:not(#cbi-dropbear),
|
|
||||||
[data-page="admin-system-opkg"] #maincontent>.container {
|
[data-page="admin-system-opkg"] #maincontent>.container {
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
|
|||||||
@@ -1,5 +1,40 @@
|
|||||||
// out: false
|
// out: false
|
||||||
|
|
||||||
|
// Standalone native tables have no section to supply their surface or corners.
|
||||||
|
// Round painted cells instead of clipping interactive descendants.
|
||||||
|
.native-table-card() {
|
||||||
|
.native-page-card();
|
||||||
|
border-collapse: separate;
|
||||||
|
border-spacing: 0;
|
||||||
|
overflow: visible;
|
||||||
|
|
||||||
|
> :is(thead, tbody, .thead, .tbody):first-child,
|
||||||
|
> :is(thead, tbody, .thead, .tbody):first-child > :is(tr, .tr):first-child,
|
||||||
|
> :is(tr, .tr):first-child {
|
||||||
|
border-start-start-radius: inherit;
|
||||||
|
border-start-end-radius: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
> :is(thead, tbody, .thead, .tbody):first-child > :is(tr, .tr):first-child > :is(td, th, .td, .th),
|
||||||
|
> :is(tr, .tr):first-child > :is(td, th, .td, .th) {
|
||||||
|
&:first-child {
|
||||||
|
border-start-start-radius: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-start-end-radius: inherit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 1152px) {
|
||||||
|
// Responsive tables hide their title row; the first data row owns the top.
|
||||||
|
:is(.table-titles, .cbi-section-table-titles) + .tr {
|
||||||
|
border-start-start-radius: inherit;
|
||||||
|
border-start-end-radius: inherit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// table
|
// table
|
||||||
|
|
||||||
.table {
|
.table {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
@import "pages/autoreboot.less";
|
@import "pages/autoreboot.less";
|
||||||
@import "pages/system.less";
|
@import "pages/system.less";
|
||||||
@import "pages/syscontrol.less";
|
@import "pages/syscontrol.less";
|
||||||
|
@import "pages/administration.less";
|
||||||
@import "pages/password.less";
|
@import "pages/password.less";
|
||||||
@import "pages/sshkeys.less";
|
@import "pages/sshkeys.less";
|
||||||
@import "pages/repokeys.less";
|
@import "pages/repokeys.less";
|
||||||
@@ -46,6 +47,7 @@
|
|||||||
@import "pages/nlbwmon.less";
|
@import "pages/nlbwmon.less";
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
|
@import "pages/wol.less";
|
||||||
@import "pages/ddns.less";
|
@import "pages/ddns.less";
|
||||||
@import "pages/ttyd.less";
|
@import "pages/ttyd.less";
|
||||||
@import "pages/dockerman.less";
|
@import "pages/dockerman.less";
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
// out: false
|
||||||
|
|
||||||
|
// Match native LuCI maps: a heading, an unpainted page description, then
|
||||||
|
// section cards. Untabbed forms need the inset already supplied by tabs.
|
||||||
|
[data-page="admin-system-admin"],
|
||||||
|
[data-page="admin-system-admin-password"],
|
||||||
|
[data-page="admin-system-admin-pwpolicy"],
|
||||||
|
[data-page="admin-system-admin-uhttpd"] {
|
||||||
|
.legacy-cbi-section-spacing();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Password policy has no page description: join its heading and sole form
|
||||||
|
// into one surface instead of separating two adjacent cards.
|
||||||
|
[data-page="admin-system-admin-pwpolicy"] {
|
||||||
|
#view > .cbi-map {
|
||||||
|
.native-page-card();
|
||||||
|
gap: 0;
|
||||||
|
|
||||||
|
> h2 {
|
||||||
|
margin: 0;
|
||||||
|
border-end-start-radius: 0;
|
||||||
|
border-end-end-radius: 0;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .cbi-section {
|
||||||
|
background: none;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,8 +2,16 @@
|
|||||||
|
|
||||||
// System - Crontab
|
// System - Crontab
|
||||||
[data-page="admin-system-crontab"] {
|
[data-page="admin-system-crontab"] {
|
||||||
|
#view > p:has(> textarea) {
|
||||||
|
.native-page-card();
|
||||||
|
margin: 0;
|
||||||
|
padding: var(--field-gap) var(--section-inset);
|
||||||
|
line-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
#view p {
|
#view p {
|
||||||
textarea {
|
textarea {
|
||||||
|
display: block;
|
||||||
direction: ltr;
|
direction: ltr;
|
||||||
unicode-bidi: plaintext;
|
unicode-bidi: plaintext;
|
||||||
text-align: start;
|
text-align: start;
|
||||||
|
|||||||
@@ -2,6 +2,12 @@
|
|||||||
|
|
||||||
// Network - Diagnostics
|
// Network - Diagnostics
|
||||||
[data-page="admin-network-diagnostics"] {
|
[data-page="admin-network-diagnostics"] {
|
||||||
|
#view > .cbi-map > .table {
|
||||||
|
.native-page-card();
|
||||||
|
padding: var(--control-gap);
|
||||||
|
border-collapse: separate;
|
||||||
|
}
|
||||||
|
|
||||||
.table {
|
.table {
|
||||||
// The toolbar must not become the dropdown's scroll/size boundary.
|
// The toolbar must not become the dropdown's scroll/size boundary.
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
@@ -32,7 +38,7 @@
|
|||||||
|
|
||||||
.td {
|
.td {
|
||||||
background-color: var(--surface-color) !important;
|
background-color: var(--surface-color) !important;
|
||||||
border-bottom: 1px solid var(--border-color) !important;
|
border-bottom: 0 !important;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -3,7 +3,28 @@
|
|||||||
[data-page="admin-status-logs"],
|
[data-page="admin-status-logs"],
|
||||||
[data-page="admin-status-logs-syslog"],
|
[data-page="admin-status-logs-syslog"],
|
||||||
[data-page="admin-status-logs-dmesg"] {
|
[data-page="admin-status-logs-dmesg"] {
|
||||||
|
#content_syslog {
|
||||||
|
.native-page-card();
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--field-gap);
|
||||||
|
padding: var(--field-gap) var(--section-inset);
|
||||||
|
|
||||||
|
> div {
|
||||||
|
margin-block: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// LuCI uses padded wrappers for the two scroll buttons.
|
||||||
|
> div:has(> .cbi-button) {
|
||||||
|
padding-block: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#syslog {
|
#syslog {
|
||||||
|
display: block;
|
||||||
|
margin: 0;
|
||||||
|
border: 1px solid var(--control-border);
|
||||||
|
background-color: var(--input-bg);
|
||||||
direction: ltr;
|
direction: ltr;
|
||||||
unicode-bidi: plaintext;
|
unicode-bidi: plaintext;
|
||||||
text-align: start;
|
text-align: start;
|
||||||
@@ -18,7 +39,7 @@
|
|||||||
[data-page="admin-status-logs-dmesg"] {
|
[data-page="admin-status-logs-dmesg"] {
|
||||||
#content_syslog > div:has(> input, > select) {
|
#content_syslog > div:has(> input, > select) {
|
||||||
line-height: 2.5rem;
|
line-height: 2.5rem;
|
||||||
margin-block-end: var(--control-gap) !important;
|
margin-block-end: 0 !important;
|
||||||
|
|
||||||
> label,
|
> label,
|
||||||
> input,
|
> input,
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
--oc-text: var(--text-color);
|
--oc-text: var(--text-color);
|
||||||
--oc-text-muted: var(--plugin-text-muted);
|
--oc-text-muted: var(--plugin-text-muted);
|
||||||
--oc-neutral: var(--plugin-neutral);
|
--oc-neutral: var(--plugin-neutral);
|
||||||
--oc-scrollbar: var(--plugin-scrollbar);
|
--oc-scrollbar: var(--oc-accent);
|
||||||
|
|
||||||
@supports (color: color-mix(in srgb, red, blue)) {
|
@supports (color: color-mix(in srgb, red, blue)) {
|
||||||
--oc-accent-hover: color-mix(in srgb, var(--oc-accent) 88%, black);
|
--oc-accent-hover: color-mix(in srgb, var(--oc-accent) 88%, black);
|
||||||
@@ -72,8 +72,8 @@
|
|||||||
--primary-a60: var(--oc-accent-a60) !important;
|
--primary-a60: var(--oc-accent-a60) !important;
|
||||||
--radio-hover-bg: var(--oc-accent-a10) !important;
|
--radio-hover-bg: var(--oc-accent-a10) !important;
|
||||||
--tab-hover-dark-bg: var(--oc-accent-a10) !important;
|
--tab-hover-dark-bg: var(--oc-accent-a10) !important;
|
||||||
--scrollbar-thumb: var(--oc-neutral) !important;
|
--scrollbar-thumb: var(--oc-scrollbar) !important;
|
||||||
--scrollbar-thumb-hover: var(--oc-text-muted) !important;
|
--scrollbar-thumb-hover: var(--oc-accent-hover) !important;
|
||||||
border-radius: var(--radius-card);
|
border-radius: var(--radius-card);
|
||||||
|
|
||||||
.developer-container,
|
.developer-container,
|
||||||
@@ -91,6 +91,23 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Editors inject important scrollbar colours. Override only control thumbs,
|
||||||
|
// retaining their existing widths, tracks and clipping.
|
||||||
|
textarea,
|
||||||
|
.oc,
|
||||||
|
.oc *,
|
||||||
|
.oc .cm-editor .cm-scroller,
|
||||||
|
.oc #debug-rendered,
|
||||||
|
.oc #debug-rendered pre {
|
||||||
|
&::-webkit-scrollbar-thumb {
|
||||||
|
background-color: var(--oc-scrollbar) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-thumb:hover {
|
||||||
|
background-color: var(--oc-accent-hover) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Standard LuCI controls
|
// Standard LuCI controls
|
||||||
.cbi-button.cbi-button-add,
|
.cbi-button.cbi-button-add,
|
||||||
.cbi-button.cbi-button-apply,
|
.cbi-button.cbi-button-apply,
|
||||||
@@ -287,6 +304,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Legacy help text is primary emphasis; success and error remain semantic.
|
// Legacy help text is primary emphasis; success and error remain semantic.
|
||||||
|
b.oc-txt-good,
|
||||||
b[style*="color:green"],
|
b[style*="color:green"],
|
||||||
b[style*="color: green"],
|
b[style*="color: green"],
|
||||||
b[color="green"] {
|
b[color="green"] {
|
||||||
@@ -329,6 +347,26 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The version tab reuses the update popup inside an existing Argon card.
|
||||||
|
// Align its content with the tabs without changing standalone popups.
|
||||||
|
&[data-page="admin-services-openclash-settings"] {
|
||||||
|
[id="container.openclash.config.version_update"] {
|
||||||
|
padding-inline: var(--section-inset);
|
||||||
|
|
||||||
|
>.oc {
|
||||||
|
border-radius: 0;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
#selectPopup.select-popup-tab {
|
||||||
|
border: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
padding: 0;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&[data-page="admin-services-openclash-config"],
|
&[data-page="admin-services-openclash-config"],
|
||||||
&[data-page="admin-services-openclash-config-subscribe"],
|
&[data-page="admin-services-openclash-config-subscribe"],
|
||||||
&[data-page^="admin-services-openclash-custom-dns-edit"],
|
&[data-page^="admin-services-openclash-custom-dns-edit"],
|
||||||
@@ -492,6 +530,45 @@
|
|||||||
padding: 0 1rem 1rem 1rem;
|
padding: 0 1rem 1rem 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The surrounding form already provides the log card and horizontal inset.
|
||||||
|
// Retain the editor frame and clipping used to hide inactive log panes.
|
||||||
|
&[data-page="admin-services-openclash-log"] {
|
||||||
|
#tab.oc {
|
||||||
|
height: auto;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
background: transparent;
|
||||||
|
box-shadow: none;
|
||||||
|
|
||||||
|
>#tab-header {
|
||||||
|
padding: var(--field-gap) 0;
|
||||||
|
border: 0;
|
||||||
|
background: transparent;
|
||||||
|
|
||||||
|
>ul.oc-tabmenu {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+.oc {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#tab-content>.dom {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.oc .select-popup-footer.log-footer {
|
||||||
|
margin: 0;
|
||||||
|
padding: var(--field-gap) 0;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
background: transparent;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#cbi-openclash-dns_servers .cbi-section-table-row>
|
#cbi-openclash-dns_servers .cbi-section-table-row>
|
||||||
.cbi-section-table-cell:last-child {
|
.cbi-section-table-cell:last-child {
|
||||||
min-width: 7.25rem;
|
min-width: 7.25rem;
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
// Native package-manager controls inject their own flex rules after the theme.
|
// Native package-manager controls inject their own flex rules after the theme.
|
||||||
[data-page="admin-system-package-manager"],
|
[data-page="admin-system-package-manager"],
|
||||||
[data-page="admin-system-opkg"] {
|
[data-page="admin-system-opkg"] {
|
||||||
|
#view > #packages {
|
||||||
|
.native-table-card();
|
||||||
|
}
|
||||||
|
|
||||||
.controls {
|
.controls {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
@@ -12,6 +12,11 @@
|
|||||||
left: 1rem;
|
left: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The view's section gap already separates the standalone page action.
|
||||||
|
#view > hr {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.cbi-button {
|
.cbi-button {
|
||||||
background: var(--warning-action-bg) !important;
|
background: var(--warning-action-bg) !important;
|
||||||
border-color: var(--warning-action-bg) !important;
|
border-color: var(--warning-action-bg) !important;
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
// out: false
|
// out: false
|
||||||
|
|
||||||
[data-page="admin-status-processes"] {
|
[data-page="admin-status-processes"] {
|
||||||
|
#view > .table {
|
||||||
|
.native-table-card();
|
||||||
|
}
|
||||||
|
|
||||||
.th:nth-child(3),
|
.th:nth-child(3),
|
||||||
.td:nth-child(3) {
|
.td:nth-child(3) {
|
||||||
text-align: start;
|
text-align: start;
|
||||||
|
|||||||
@@ -1,6 +1,29 @@
|
|||||||
// out: false
|
// out: false
|
||||||
|
|
||||||
[data-page="admin-system-admin-repokeys"] {
|
[data-page="admin-system-admin-repokeys"] {
|
||||||
|
.cbi-section-table {
|
||||||
|
border-collapse: separate;
|
||||||
|
border-spacing: 0;
|
||||||
|
border-radius: inherit;
|
||||||
|
|
||||||
|
// The native heading has two cells but data rows also have an action
|
||||||
|
// column. Paint the row itself so the whole card has one continuous top.
|
||||||
|
> .thead,
|
||||||
|
> .thead > .tr {
|
||||||
|
border-start-start-radius: inherit;
|
||||||
|
border-start-end-radius: inherit;
|
||||||
|
background: var(--surface-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
> .thead > .tr > .th:first-child {
|
||||||
|
border-start-start-radius: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cbi-section-table-row {
|
||||||
|
margin-block: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
textarea {
|
textarea {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
|||||||
@@ -1,10 +1,19 @@
|
|||||||
// out: false
|
// out: false
|
||||||
|
|
||||||
[data-page="admin-system-admin-sshkeys"] {
|
[data-page="admin-system-admin-sshkeys"] {
|
||||||
|
#view > div:has(> .cbi-section-node > .cbi-dynlist) {
|
||||||
|
// This view omits cbi-map/cbi-section. Only the key list is a section;
|
||||||
|
// leave its page description between the heading and that card.
|
||||||
|
> .cbi-section-node {
|
||||||
|
.native-page-card();
|
||||||
|
padding: var(--field-gap) var(--section-inset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.cbi-dynlist {
|
.cbi-dynlist {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-inline: var(--section-inset);
|
margin: 0;
|
||||||
max-width: ~"min(48rem, calc(100% - 2 * var(--section-inset)))";
|
max-width: ~"min(48rem, 100%)";
|
||||||
gap: var(--field-gap);
|
gap: var(--field-gap);
|
||||||
|
|
||||||
> .item {
|
> .item {
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
// out: false
|
||||||
|
|
||||||
|
// Wake on LAN uses custom content outside the standard form field layout.
|
||||||
|
[data-page="admin-services-wol"] {
|
||||||
|
#cbi-luci-wol-defaults-_info {
|
||||||
|
padding-bottom: var(--field-gap);
|
||||||
|
line-height: 1.5;
|
||||||
|
|
||||||
|
> output {
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
output p {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-group {
|
||||||
|
margin-block-start: var(--control-gap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cbi-section:has(> #wol-output-text) {
|
||||||
|
padding-bottom: var(--field-gap);
|
||||||
|
}
|
||||||
|
|
||||||
|
#wol-output-text {
|
||||||
|
display: block;
|
||||||
|
// Override the application's inline width while preserving card insets.
|
||||||
|
width: calc(100% - var(--section-inset) - var(--section-inset)) !important;
|
||||||
|
margin-inline: var(--section-inset);
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -11,7 +11,7 @@ PKG_NAME:=qt6base
|
|||||||
PKG_BASE:=6.12
|
PKG_BASE:=6.12
|
||||||
PKG_BUGFIX:=0
|
PKG_BUGFIX:=0
|
||||||
PKG_VERSION:=$(PKG_BASE).$(PKG_BUGFIX)
|
PKG_VERSION:=$(PKG_BASE).$(PKG_BUGFIX)
|
||||||
PKG_RELEASE:=3
|
PKG_RELEASE:=4
|
||||||
|
|
||||||
PKG_SOURCE:=qtbase-everywhere-src-$(PKG_VERSION).tar.xz
|
PKG_SOURCE:=qtbase-everywhere-src-$(PKG_VERSION).tar.xz
|
||||||
PKG_SOURCE_URL:=https://mirrors.tencent.com/qt/archive/qt/$(PKG_BASE)/$(PKG_VERSION)/submodules/ \
|
PKG_SOURCE_URL:=https://mirrors.tencent.com/qt/archive/qt/$(PKG_BASE)/$(PKG_VERSION)/submodules/ \
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
--- a/src/printsupport/dialogs/qprintdialog_unix.cpp
|
||||||
|
+++ b/src/printsupport/dialogs/qprintdialog_unix.cpp
|
||||||
|
@@ -486,8 +486,10 @@ bool QPrintPropertiesDialog::createAdvan
|
||||||
|
PDPK_PpdCustomOption, params);
|
||||||
|
if (hasCustomOption) {
|
||||||
|
auto *customValueEdit = new QLineEdit;
|
||||||
|
+#if QT_CONFIG(accessibility)
|
||||||
|
customValueEdit->setAccessibleName(
|
||||||
|
tr("Custom value for %1").arg(optionText));
|
||||||
|
+#endif
|
||||||
|
choicesCbWithLabelLayout->addWidget(customValueEdit);
|
||||||
|
choicesCb->setProperty(customValueLineEditProperty,
|
||||||
|
QVariant::fromValue(customValueEdit));
|
||||||
Reference in new issue
Block a user