diff --git a/clashoo/Makefile b/clashoo/Makefile index bb561a35d..f25397580 100644 --- a/clashoo/Makefile +++ b/clashoo/Makefile @@ -1,19 +1,20 @@ include $(TOPDIR)/rules.mk PKG_NAME:=clashoo -PKG_SHORT_SHA:=6780eee -PKG_COMMIT_DATE:=2026.10.25 +PKG_UPSTREAM_VERSION:=1.19.33 +PKG_UPSTREAM_TAG:=v$(PKG_UPSTREAM_VERSION) +PKG_SHORT_SHA:=3f27ef3 -PKG_VERSION:=$(PKG_COMMIT_DATE)~$(PKG_SHORT_SHA) -PKG_RELEASE:=18 +PKG_VERSION:=2026.10.25.$(PKG_UPSTREAM_VERSION) +PKG_RELEASE:=19 -PKG_SOURCE:=$(PKG_NAME)-alpha-$(PKG_SHORT_SHA).tar.gz -PKG_SOURCE_SUBDIR:=$(PKG_NAME)-alpha-$(PKG_SHORT_SHA) -PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-alpha-$(PKG_SHORT_SHA) +PKG_SOURCE:=$(PKG_NAME)-stable-$(PKG_UPSTREAM_TAG)-$(PKG_SHORT_SHA).tar.gz +PKG_SOURCE_SUBDIR:=$(PKG_NAME)-stable-$(PKG_UPSTREAM_TAG)-$(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_VERSION:=d831028ec8a3d336ea83295bea36c32fdc973ede +PKG_SOURCE_VERSION:=57468807be288af4250181a024e29c2971eb228c PKG_HASH:=skip -PKG_BUILD_VERSION:=alpha-$(PKG_SHORT_SHA) +PKG_BUILD_VERSION:=$(PKG_UPSTREAM_TAG) PKG_LICENSE:=GPL3.0+ PKG_MAINTAINER:=kenzok8 @@ -50,8 +51,11 @@ endef define Package/clashoo/install $(call GoPackage/Package/Install/Bin,$(1)) + $(INSTALL_DIR) $(1)/usr/libexec/clashoo $(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/config @@ -82,10 +86,44 @@ define Package/clashoo/install $(RM) $(1)/usr/share/clashoo/core_down_complete 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 #!/bin/sh 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/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 diff --git a/clashoo/files/etc/config/clashoo b/clashoo/files/etc/config/clashoo index 1b0e23920..d744fb10f 100644 --- a/clashoo/files/etc/config/clashoo +++ b/clashoo/files/etc/config/clashoo @@ -9,7 +9,7 @@ config clashoo 'config' option dash_port '9090' option dashboard_panel 'zashboard' option level 'info' - option core '3' + option core '2' option allow_lan '1' option dnsforwader '1' option subcri 'meta' @@ -28,7 +28,7 @@ config clashoo 'config' option listen_port '1053' option interf '0' option download_core 'amd64-compatible' - option dcore '3' + option dcore '2' option same_tag '1' option geoip_source '3' option geoip_format 'mmdb' diff --git a/clashoo/files/etc/init.d/clashoo b/clashoo/files/etc/init.d/clashoo index 13e25189c..f926fe636 100755 --- a/clashoo/files/etc/init.d/clashoo +++ b/clashoo/files/etc/init.d/clashoo @@ -169,43 +169,54 @@ cleanup_stale_core_nft() { } _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() { - local legacy=/usr/bin/clash-meta version - [ -x /usr/bin/mihomo-stable ] && return 0 + local legacy=/usr/bin/clash-meta target=/usr/share/clashoo/bin/mihomo-stable version staged + [ -x "$target" ] && return 0 [ -x "$legacy" ] && [ ! -L "$legacy" ] || return 0 version=$("$legacy" -v 2>/dev/null | head -n 1) [ -n "$version" ] || 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() { - local selected fallback - is_singbox_selected && return 0 - selected="$(uci -q get clashoo.config.core 2>/dev/null)" - case "$selected" in - 2) - [ -x /usr/bin/mihomo-stable ] && return 0 - [ -x /usr/bin/mihomo ] || return 0 - fallback=3 - ;; - 3) - [ -x /usr/bin/mihomo ] && return 0 - [ -x /usr/bin/mihomo-stable ] || return 0 - fallback=2 - ;; - *) return 0 ;; - esac - uci -q set clashoo.config.core="$fallback" - uci -q set clashoo.config.dcore="$fallback" - uci -q commit clashoo >/dev/null 2>&1 - if [ "$fallback" = "3" ]; then - log_msg "Stable core missing, falling back to Alpha" "稳定版内核缺失,自动回退到 Alpha" +_mihomo_stable_binary() { + local bundled=/usr/libexec/clashoo/mihomo-stable manual=/usr/share/clashoo/bin/mihomo-stable bundled_ver manual_ver + # A successful custom download is an explicit override, even if its version is older. + if [ -x "$manual" ] && + [ "$(sed -n '1{s/^[[:space:]]*//;s/[[:space:]]*$//;p;}' /usr/share/clashoo/clash_meta_version 2>/dev/null)" = "custom-url" ]; then + echo "$manual" + return 0 + fi + [ -x "$bundled" ] || { [ -x "$manual" ] && { echo "$manual"; return 0; }; return 1; } + [ -x "$manual" ] || { echo "$bundled"; return; } + bundled_ver="$("$bundled" -v 2>/dev/null | awk '{for (i = 1; i < NF; i++) if ($i == "Meta") {print $(i + 1); exit}}')" + manual_ver="$("$manual" -v 2>/dev/null | awk '{for (i = 1; i < NF; i++) if ($i == "Meta") {print $(i + 1); exit}}')" + bundled_ver="${bundled_ver#v}" + manual_ver="${manual_ver#v}" + printf '%s\n' "$bundled_ver" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$' || bundled_ver="" + printf '%s\n' "$manual_ver" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$' || manual_ver="" + if [ -n "$bundled_ver" ] && [ -n "$manual_ver" ]; then + if awk -v manual="$manual_ver" -v bundled="$bundled_ver" 'BEGIN { + split(manual, m, "."); split(bundled, b, "."); + for (i = 1; i <= 3; i++) { + if (m[i] + 0 > b[i] + 0) exit 0; + 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 - log_msg "Alpha core missing, falling back to stable" "Alpha 内核缺失,自动回退到稳定版" + echo "$bundled" fi } @@ -220,11 +231,11 @@ _detect_selected_core() { return 1 ;; 2|mihomo) - [ -x /usr/bin/mihomo-stable ] && echo /usr/bin/mihomo-stable && return - return 1 + _mihomo_stable_binary + return $? ;; 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 ;; esac @@ -233,7 +244,7 @@ _detect_selected_core() { core_supports_advanced_features() { case "$CLASH" in - */mihomo|*/mihomo-stable|*/clash-meta|*/smart) + */mihomo|*/mihomo-stable|*/mihomo-alpha|*/clash-meta|*/smart) return 0 ;; *) @@ -2126,12 +2137,12 @@ _start_validate_core() { echo "Clashoo" > "$REAL_LOG" return 1 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 echo "Clashoo" > "$REAL_LOG" return 1 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 echo "Clashoo" > "$REAL_LOG" 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 [ "$_reload_candidate" = "1" ] && return 1 - if [ -x /usr/bin/mihomo ]; then - uci set clashoo.config.core="3" && uci commit clashoo - elif [ -x /usr/bin/mihomo-stable ]; then + if [ -n "$(_mihomo_stable_binary)" ]; then 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 log_msg "Current config requires mihomo/clash-meta core" "当前配置需要 mihomo 或 clash-meta 内核" backend_remove_rules >/dev/null 2>&1 @@ -2407,7 +2418,6 @@ start_service(){ sync_legacy_core_field _migrate_legacy_mihomo_stable - _fallback_missing_mihomo_core CLASH="$(_detect_selected_core)" cleanup_legacy_startup_backups diff --git a/clashoo/files/usr/share/clashoo/update/core_download.sh b/clashoo/files/usr/share/clashoo/update/core_download.sh index f5aec7ac9..9407fd1a5 100755 --- a/clashoo/files/usr/share/clashoo/update/core_download.sh +++ b/clashoo/files/usr/share/clashoo/update/core_download.sh @@ -1062,10 +1062,10 @@ if [ -n "$CUSTOM_CORE_URL" ]; then write_log "使用自定义内核下载链接" URL="$CUSTOM_CORE_URL" if [ "$CORETYPE" = "3" ]; then - TARGET="/usr/bin/mihomo" + TARGET="/usr/share/clashoo/bin/mihomo-alpha" VERSION_FILE="/usr/share/clashoo/mihomo_version" else - TARGET="/usr/bin/mihomo-stable" + TARGET="/usr/share/clashoo/bin/mihomo-stable" VERSION_FILE="/usr/share/clashoo/clash_meta_version" fi TAG="custom" @@ -1175,7 +1175,7 @@ elif [ "$CORETYPE" = "2" ]; then [ -z "$TAG" ] && write_log "获取稳定版版本号失败" && exit 1 ASSET=$(pick_mihomo_asset "MetaCubeX/mihomo" "$TAG" "$(map_mihomo_arch "$MODELTYPE")" "stable") 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_VALUE="$TAG" 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")" "stable") 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_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" diff --git a/daed/Makefile b/daed/Makefile index 5a828bab0..c98136a04 100644 --- a/daed/Makefile +++ b/daed/Makefile @@ -6,9 +6,9 @@ include $(TOPDIR)/rules.mk PKG_NAME:=daed PKG_VERSION:=2026.10.18 -PKG_RELEASE:=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_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION) PKG_HASH:=skip diff --git a/fastnet/Makefile b/fastnet/Makefile index b9f55b933..abb96f840 100644 --- a/fastnet/Makefile +++ b/fastnet/Makefile @@ -9,8 +9,8 @@ include $(TOPDIR)/rules.mk PKG_ARCH_FASTNET:=$(ARCH) PKG_NAME:=fastnet -PKG_VERSION:=0.7.7 -PKG_RELEASE:=6 +PKG_VERSION:=0.7.8 +PKG_RELEASE:=7 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)/ diff --git a/gately/Makefile b/gately/Makefile index 0f2f0751a..faa025c9b 100644 --- a/gately/Makefile +++ b/gately/Makefile @@ -5,8 +5,8 @@ include $(TOPDIR)/rules.mk PKG_NAME:=gately -PKG_VERSION:=0.5.8 -PKG_RELEASE:=11 +PKG_VERSION:=0.5.9 +PKG_RELEASE:=12 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://codeload.github.com/wangwanjie/gately/tar.gz/v$(PKG_VERSION)? diff --git a/istoreenhance/Makefile b/istoreenhance/Makefile index 3b7c470ce..f573495a0 100644 --- a/istoreenhance/Makefile +++ b/istoreenhance/Makefile @@ -11,8 +11,8 @@ PKG_ARCH_ISTOREENHANCE:=$(ARCH) PKG_NAME:=istoreenhance # use PKG_SOURCE_DATE instead of PKG_VERSION for compitable -PKG_SOURCE_DATE:=0.8.4 -PKG_RELEASE:=13 +PKG_SOURCE_DATE:=0.8.5 +PKG_RELEASE:=14 ARCH_HEXCODE:= ifeq ($(ARCH),x86_64) ARCH_HEXCODE=8664 diff --git a/kai-agent/Makefile b/kai-agent/Makefile index 744061696..34ddc2ff6 100644 --- a/kai-agent/Makefile +++ b/kai-agent/Makefile @@ -1,8 +1,8 @@ include $(TOPDIR)/rules.mk PKG_NAME:=kai-agent -PKG_VERSION:=0.0.26 -PKG_RELEASE:=4 +PKG_VERSION:=0.0.27 +PKG_RELEASE:=5 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_HASH:=skip @@ -17,7 +17,7 @@ define Package/$(PKG_NAME) SECTION:=net CATEGORY:=System TITLE:=KAI OpenCode Agent bundle - URL:=https://github.com/linkease/linkease-skills + URL:=https://github.com/linkease/kai-skills endef define Package/$(PKG_NAME)/description diff --git a/kai/Makefile b/kai/Makefile index e1eb373ba..86aab9513 100644 --- a/kai/Makefile +++ b/kai/Makefile @@ -10,8 +10,8 @@ include $(TOPDIR)/rules.mk PKG_ARCH_kai:=$(ARCH) PKG_NAME:=kai -PKG_VERSION:=0.0.25 -PKG_RELEASE:=9 +PKG_VERSION:=0.0.27 +PKG_RELEASE:=10 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_HASH:=skip @@ -28,11 +28,12 @@ define Package/$(PKG_NAME) SUBMENU:= TITLE:=KAI 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 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 define Package/$(PKG_NAME)/conffiles diff --git a/kai_session/Makefile b/kai_session/Makefile index f8a9f3d30..f1697fe5a 100644 --- a/kai_session/Makefile +++ b/kai_session/Makefile @@ -10,8 +10,8 @@ include $(TOPDIR)/rules.mk PKG_ARCH_kai_session:=$(ARCH) PKG_NAME:=kai_session -PKG_VERSION:=0.0.25 -PKG_RELEASE:=8 +PKG_VERSION:=0.0.27 +PKG_RELEASE:=9 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_HASH:=skip @@ -31,11 +31,11 @@ define Package/$(PKG_NAME) SUBMENU:= TITLE:=KAI_SESSION DEPENDS:=+libstdcpp - URL:=https://github.com/Carseason/openwrt-packages + URL:=https://github.com/linkease/istore-ai-helper endef define Package/$(PKG_NAME)/description - KAI + The OpenCode engine and offline ripgrep runtime for KAI. endef define Package/$(PKG_NAME)/conffiles diff --git a/ks-cli/Makefile b/ks-cli/Makefile index bfba7f491..9da682411 100644 --- a/ks-cli/Makefile +++ b/ks-cli/Makefile @@ -5,8 +5,8 @@ include $(TOPDIR)/rules.mk PKG_ARCH_KS_CLI:=$(ARCH) PKG_NAME:=ks-cli -PKG_VERSION:=0.8.4 -PKG_RELEASE:=2 +PKG_VERSION:=0.8.5 +PKG_RELEASE:=3 PKG_SOURCE:=ks-cli-binary-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://github.com/kspeeder/docker_kspeeder/releases/download/v$(PKG_VERSION)/ diff --git a/luci-app-bandix/Makefile b/luci-app-bandix/Makefile index 34bac53d3..f434174fa 100644 --- a/luci-app-bandix/Makefile +++ b/luci-app-bandix/Makefile @@ -11,8 +11,10 @@ LUCI_DEPENDS:=+luci-base +luci-lib-jsonc +curl +bandix PKG_MAINTAINER:=timsaya 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 -# call BuildPackage - OpenWrt buildroot signature \ No newline at end of file +# call BuildPackage - OpenWrt buildroot signature diff --git a/luci-app-bandix/htdocs/luci-static/resources/bandix/geoip.js b/luci-app-bandix/htdocs/luci-static/resources/bandix/geoip.js new file mode 100644 index 000000000..c4876b507 --- /dev/null +++ b/luci-app-bandix/htdocs/luci-static/resources/bandix/geoip.js @@ -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; + } +}); diff --git a/luci-app-bandix/htdocs/luci-static/resources/view/bandix/connection.js b/luci-app-bandix/htdocs/luci-static/resources/view/bandix/connection.js index 4a3be0d18..24852fb83 100644 --- a/luci-app-bandix/htdocs/luci-static/resources/view/bandix/connection.js +++ b/luci-app-bandix/htdocs/luci-static/resources/view/bandix/connection.js @@ -4,6 +4,7 @@ 'require uci'; 'require rpc'; 'require poll'; +'require bandix.geoip as geoip'; var BANDIX_MODAL_BG_OPENWRT2020_LIGHT = '#ffffff'; var BANDIX_MODAL_BG_OPENWRT2020_DARK = '#2a2a2a'; @@ -136,15 +137,21 @@ function formatSize(bytes) { // IPv6 地址按 RFC 3986 用方括号包起来,一是避免 '2600:...:8000::' + ':' + '80' 拼出 // ':::80' 这种分不清边界的写法,二是选中复制出来就是 [addr]:port,可以直接粘去用。 // 地址和端口是行内元素,复制时不会被拆成两行;端口自身 nowrap,不会被折断 -function flowEndpointCell(addr, port) { +function flowEndpointCell(addr, port, information) { var text = addr || '-'; if (text.indexOf(':') >= 0) { 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-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) { @@ -1211,7 +1218,8 @@ return view.extend({ total: res.data.total || 0, page: res.data.page || currentPage, 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 = ''; var normalized = normalizeFlowsResponse(res); var flows = normalized.items; + var information = normalized.geoip || {}; totalItems = normalized.total; currentPage = normalized.page; totalPages = Math.max(1, normalized.total_pages); @@ -1258,11 +1267,11 @@ return view.extend({ tableBody.appendChild(E('tr', {}, [ E('td', { 'class': protoCls }, (f.protocol || '-').toUpperCase()), 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.dst, f.orig && f.orig.dport), + 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, information[f.orig && f.orig.dst]), E('td', {}, sendStr), - flowEndpointCell(f.repl && f.repl.src, f.repl && f.repl.sport), - flowEndpointCell(f.repl && f.repl.dst, f.repl && f.repl.dport), + 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, information[f.repl && f.repl.dst]), E('td', {}, replyStr), E('td', { 'style': 'font-size: 0.8rem;' }, flagsStr) ])); diff --git a/luci-app-bandix/htdocs/luci-static/resources/view/bandix/settings.js b/luci-app-bandix/htdocs/luci-static/resources/view/bandix/settings.js index 706595604..5b472cc53 100644 --- a/luci-app-bandix/htdocs/luci-static/resources/view/bandix/settings.js +++ b/luci-app-bandix/htdocs/luci-static/resources/view/bandix/settings.js @@ -4,6 +4,7 @@ 'require ui'; 'require uci'; 'require rpc'; +'require bandix.geoip as geoip'; 'require tools.widgets as widgets'; @@ -231,6 +232,13 @@ return view.extend({ s.description = _('Configure basic parameters for Bandix service'); 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'), _('Port for Bandix service to listen on')); @@ -785,6 +793,16 @@ return view.extend({ o.default = '0'; 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) s = m.section(form.NamedSection, 'dns', 'dns', _('DNS Monitor Settings')); s.description = _('Configure DNS monitoring related parameters'); diff --git a/luci-app-bandix/po/es/bandix.po b/luci-app-bandix/po/es/bandix.po index 0f89e6c62..ac2ce1fc9 100644 --- a/luci-app-bandix/po/es/bandix.po +++ b/luci-app-bandix/po/es/bandix.po @@ -221,12 +221,6 @@ msgstr "WAN cargado" msgid "WAN Downloaded" msgstr "WAN descargado" -msgid "Total Upload" -msgstr "Carga total" - -msgid "Total Download" -msgstr "Descarga total" - msgid "LAN Upload" msgstr "Carga LAN" @@ -422,18 +416,12 @@ msgstr "Página" msgid "of" msgstr "de" -msgid "Total" -msgstr "Total" - msgid "records" msgstr "registros" msgid "Per Page" msgstr "Por página" -msgid "" -msgstr "" - msgid "Total Queries" msgstr "Total de consultas" @@ -938,18 +926,6 @@ msgstr "Máximo" msgid "Minimum" 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" msgstr "Borrar datos de tráfico" @@ -1082,9 +1058,6 @@ msgstr "Descargar e Instalar" msgid "Manual Download" msgstr "Descarga Manual" -msgid "Close" -msgstr "Cerrar" - 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." @@ -1094,9 +1067,6 @@ msgstr "Por favor, borre manualmente la caché del navegador después de actuali msgid "Traffic Statistics" msgstr "Estadísticas de Tráfico" -msgid "Traffic Statistics" -msgstr "Estadísticas de Tráfico" - msgid "Device Usage Ranking" msgstr "Clasificación de Uso de Dispositivos" @@ -1351,3 +1321,192 @@ msgstr "No disponible" msgid "Show reply direction" 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." diff --git a/luci-app-bandix/po/fr/bandix.po b/luci-app-bandix/po/fr/bandix.po index 11c212dde..5bdbad4c4 100644 --- a/luci-app-bandix/po/fr/bandix.po +++ b/luci-app-bandix/po/fr/bandix.po @@ -221,12 +221,6 @@ msgstr "WAN envoyé" msgid "WAN Downloaded" msgstr "WAN téléchargé" -msgid "Total Upload" -msgstr "Téléversement total" - -msgid "Total Download" -msgstr "Téléchargement total" - msgid "LAN Upload" msgstr "Débit envoi LAN" @@ -422,18 +416,12 @@ msgstr "Page" msgid "of" msgstr "sur" -msgid "Total" -msgstr "Total" - msgid "records" msgstr "enregistrements" msgid "Per Page" msgstr "Par page" -msgid "" -msgstr "" - msgid "Total Queries" msgstr "Total des requêtes" @@ -938,18 +926,6 @@ msgstr "Maximum" msgid "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" msgstr "Effacer les données de trafic" @@ -1082,9 +1058,6 @@ msgstr "Télécharger et Installer" msgid "Manual Download" msgstr "Téléchargement Manuel" -msgid "Close" -msgstr "Fermer" - 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." @@ -1094,9 +1067,6 @@ msgstr "Veuillez vider manuellement le cache du navigateur après la mise à jou msgid "Traffic Statistics" msgstr "Statistiques de Trafic" -msgid "Traffic Statistics" -msgstr "Statistiques de Trafic" - msgid "Device Usage Ranking" msgstr "Classement d'Utilisation des Appareils" @@ -1351,3 +1321,192 @@ msgstr "Indisponible" msgid "Show reply direction" 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." diff --git a/luci-app-bandix/po/ja/bandix.po b/luci-app-bandix/po/ja/bandix.po index f5fb03d37..f3c1f4c78 100644 --- a/luci-app-bandix/po/ja/bandix.po +++ b/luci-app-bandix/po/ja/bandix.po @@ -221,12 +221,6 @@ msgstr "WAN アップロード済み" msgid "WAN Downloaded" msgstr "WAN ダウンロード済み" -msgid "Total Upload" -msgstr "総アップロード速度" - -msgid "Total Download" -msgstr "総ダウンロード速度" - msgid "LAN Upload" msgstr "LAN アップロード速度" @@ -422,18 +416,12 @@ msgstr "ページ" msgid "of" msgstr "の" -msgid "Total" -msgstr "合計" - msgid "records" msgstr "件の記録" msgid "Per Page" msgstr "ページあたり" -msgid "" -msgstr "件" - msgid "Total Queries" msgstr "総クエリ数" @@ -938,18 +926,6 @@ msgstr "最大" msgid "Minimum" 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" msgstr "トラフィックデータを削除" @@ -1082,9 +1058,6 @@ msgstr "ダウンロードしてインストール" msgid "Manual Download" msgstr "手動ダウンロード" -msgid "Close" -msgstr "閉じる" - msgid "Starting installation... The page will refresh automatically in 30 seconds." msgstr "インストールを開始しています... ページは30秒後に自動的に更新されます。" @@ -1094,9 +1067,6 @@ msgstr "更新後、ブラウザのキャッシュを手動でクリアしてく msgid "Traffic Statistics" msgstr "トラフィック統計" -msgid "Traffic Statistics" -msgstr "トラフィック統計" - msgid "Device Usage Ranking" msgstr "デバイス使用量ランキング" @@ -1351,3 +1321,192 @@ msgstr "利用不可" msgid "Show reply direction" 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 / :: を指定します。" diff --git a/luci-app-bandix/po/pl/bandix.po b/luci-app-bandix/po/pl/bandix.po index 223ec195b..5401ef158 100644 --- a/luci-app-bandix/po/pl/bandix.po +++ b/luci-app-bandix/po/pl/bandix.po @@ -221,12 +221,6 @@ msgstr "WAN wysłane" msgid "WAN Downloaded" msgstr "WAN pobrane" -msgid "Total Upload" -msgstr "Całkowite wysyłanie" - -msgid "Total Download" -msgstr "Całkowite pobieranie" - msgid "LAN Upload" msgstr "Wysyłanie LAN" @@ -422,18 +416,12 @@ msgstr "Strona" msgid "of" msgstr "z" -msgid "Total" -msgstr "Całkowity" - msgid "records" msgstr "rekordów" msgid "Per Page" msgstr "Na stronę" -msgid "" -msgstr "" - msgid "Total Queries" msgstr "Całkowite zapytania" @@ -938,18 +926,6 @@ msgstr "Maksimum" msgid "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" msgstr "Wyczyść dane ruchu" @@ -1082,9 +1058,6 @@ msgstr "Pobierz i Zainstaluj" msgid "Manual Download" msgstr "Pobieranie Ręczne" -msgid "Close" -msgstr "Zamknij" - msgid "Starting installation... The page will refresh automatically in 30 seconds." 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" msgstr "Statystyki ruchu" -msgid "Traffic Statistics" -msgstr "Statystyki ruchu" - msgid "Device Usage Ranking" msgstr "Ranking Użycia Urządzeń" @@ -1351,3 +1321,192 @@ msgstr "Niedostępne" msgid "Show reply direction" 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." diff --git a/luci-app-bandix/po/ru/bandix.po b/luci-app-bandix/po/ru/bandix.po index 725837d2c..62ed7228f 100644 --- a/luci-app-bandix/po/ru/bandix.po +++ b/luci-app-bandix/po/ru/bandix.po @@ -221,12 +221,6 @@ msgstr "WAN отправлено" msgid "WAN Downloaded" msgstr "WAN загружено" -msgid "Total Upload" -msgstr "Всего выгружено" - -msgid "Total Download" -msgstr "Всего загружено" - msgid "LAN Upload" msgstr "Скорость отправки LAN" @@ -422,18 +416,12 @@ msgstr "Страница" msgid "of" msgstr "из" -msgid "Total" -msgstr "Всего" - msgid "records" msgstr "записей" msgid "Per Page" msgstr "На странице" -msgid "" -msgstr "" - msgid "Total Queries" msgstr "Всего запросов" @@ -938,18 +926,6 @@ msgstr "Максимум" msgid "Minimum" 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" msgstr "Очистить данные трафика" @@ -1082,9 +1058,6 @@ msgstr "Скачать и Установить" msgid "Manual Download" msgstr "Ручная Загрузка" -msgid "Close" -msgstr "Закрыть" - msgid "Starting installation... The page will refresh automatically in 30 seconds." msgstr "Начало установки... Страница автоматически обновится через 30 секунд." @@ -1094,9 +1067,6 @@ msgstr "Пожалуйста, вручную очистите кеш брауз msgid "Traffic Statistics" msgstr "Статистика трафика" -msgid "Traffic Statistics" -msgstr "Статистика трафика" - msgid "Device Usage Ranking" msgstr "Рейтинг использования устройств" @@ -1351,3 +1321,192 @@ msgstr "Недоступно" msgid "Show reply direction" 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 / :: для всех интерфейсов." diff --git a/luci-app-bandix/po/zh_Hans/bandix.po b/luci-app-bandix/po/zh_Hans/bandix.po index 1b70bbab5..65379d5bb 100644 --- a/luci-app-bandix/po/zh_Hans/bandix.po +++ b/luci-app-bandix/po/zh_Hans/bandix.po @@ -221,12 +221,6 @@ msgstr "WAN 已上传" msgid "WAN Downloaded" msgstr "WAN 已下载" -msgid "Total Upload" -msgstr "总上传速率" - -msgid "Total Download" -msgstr "总下载速率" - msgid "LAN Upload" msgstr "LAN 上传速率" @@ -419,18 +413,12 @@ msgstr "第" msgid "of" msgstr "页,共" -msgid "Total" -msgstr "全部" - msgid "records" msgstr "条记录" msgid "Per Page" msgstr "每页显示" -msgid "" -msgstr "条" - msgid "Total Queries" msgstr "总查询数" @@ -852,7 +840,7 @@ msgid "Please select at least one day" msgstr "请至少选择一天" msgid "No scheduled rules yet, click \"Add Rule\" to start setting" -msgstr "还没有定时限速规则,点击"添加规则"开始设置" +msgstr "还没有定时限速规则,点击\"添加规则\"开始设置" msgid "Loading..." msgstr "加载中..." @@ -935,18 +923,6 @@ msgstr "最大值" msgid "Minimum" 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" msgstr "删除流量数据" @@ -1079,9 +1055,6 @@ msgstr "下载并安装" msgid "Manual Download" msgstr "手动下载" -msgid "Close" -msgstr "关闭" - msgid "Starting installation... The page will refresh automatically in 30 seconds." msgstr "开始安装... 页面将在30秒后自动刷新。" @@ -1097,9 +1070,6 @@ msgstr "有更新,点击前往设置" msgid "Traffic Statistics" msgstr "流量统计" -msgid "Traffic Statistics" -msgstr "流量统计" - msgid "Device Usage Ranking" msgstr "设备用量排行" @@ -1378,3 +1348,192 @@ msgstr "每分钟" msgid "Show reply direction" 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 / :: 监听所有接口。" diff --git a/luci-app-bandix/po/zh_Hant/bandix.po b/luci-app-bandix/po/zh_Hant/bandix.po index b8555bdd1..e12464726 100644 --- a/luci-app-bandix/po/zh_Hant/bandix.po +++ b/luci-app-bandix/po/zh_Hant/bandix.po @@ -221,12 +221,6 @@ msgstr "WAN 已上傳" msgid "WAN Downloaded" msgstr "WAN 已下載" -msgid "Total Upload" -msgstr "總上傳速率" - -msgid "Total Download" -msgstr "總下載速率" - msgid "LAN Upload" msgstr "LAN 上傳速率" @@ -422,18 +416,12 @@ msgstr "第" msgid "of" msgstr "頁,共" -msgid "Total" -msgstr "全部" - msgid "records" msgstr "筆記錄" msgid "Per Page" msgstr "每頁顯示" -msgid "" -msgstr "筆" - msgid "Total Queries" msgstr "總查詢數" @@ -855,7 +843,7 @@ msgid "Please select at least one day" msgstr "請至少選擇一天" msgid "No scheduled rules yet, click \"Add Rule\" to start setting" -msgstr "還沒有定時限速規則,點擊"新增規則"開始設定" +msgstr "還沒有定時限速規則,點擊\"新增規則\"開始設定" msgid "Loading..." msgstr "載入中..." @@ -938,18 +926,6 @@ msgstr "最大值" msgid "Minimum" 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" msgstr "刪除流量數據" @@ -1082,9 +1058,6 @@ msgstr "下載並安裝" msgid "Manual Download" msgstr "手動下載" -msgid "Close" -msgstr "關閉" - msgid "Starting installation... The page will refresh automatically in 30 seconds." msgstr "開始安裝... 頁面將在30秒後自動重新整理。" @@ -1094,9 +1067,6 @@ msgstr "更新後請手動清空瀏覽器快取。" msgid "Traffic Statistics" msgstr "流量統計" -msgid "Traffic Statistics" -msgstr "流量統計" - msgid "Device Usage Ranking" msgstr "設備用量排行" @@ -1351,3 +1321,192 @@ msgstr "無法使用" msgid "Show reply direction" 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 / :: 監聽所有介面。" diff --git a/luci-app-bandix/root/usr/libexec/rpcd/luci.bandix b/luci-app-bandix/root/usr/libexec/rpcd/luci.bandix index f6a5677eb..d00115a7c 100755 --- a/luci-app-bandix/root/usr/libexec/rpcd/luci.bandix +++ b/luci-app-bandix/root/usr/libexec/rpcd/luci.bandix @@ -3,9 +3,16 @@ . /usr/share/libubox/jshn.sh -# 从UCI配置读取端口号,如果读取失败则使用默认端口8686 -BANDIX_PORT=$(uci get bandix.general.port 2>/dev/null || echo "8686") -readonly BANDIX_API_BASE="http://127.0.0.1:$BANDIX_PORT" +# Wildcard listening addresses must be reached through loopback on the router. +BANDIX_HOST=$(uci -q get bandix.general.host) +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_LIMITS_API="$BANDIX_API_BASE/api/traffic/limits" 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 "$page" ] && query_params="${query_params}page=$page&" [ -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/&$//') local url="$BANDIX_CONNECTION_FLOWS_API" [ -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 make_error "Failed to connect to Bandix service" return @@ -434,6 +442,44 @@ get_connection_flows() { 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 查询记录 get_dns_queries() { # 检查 DNS 监控是否启用 @@ -1311,6 +1357,14 @@ case "$1" in json_add_string "mac" 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_close_object @@ -1525,6 +1579,20 @@ case "$1" in fi 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) # logger "luci.bandix: getConnection called" get_connection_devices diff --git a/luci-app-bandix/root/usr/share/rpcd/acl.d/luci-app-bandix.json b/luci-app-bandix/root/usr/share/rpcd/acl.d/luci-app-bandix.json index 7c8ea3540..573f9d0c7 100644 --- a/luci-app-bandix/root/usr/share/rpcd/acl.d/luci-app-bandix.json +++ b/luci-app-bandix/root/usr/share/rpcd/acl.d/luci-app-bandix.json @@ -27,7 +27,8 @@ "getTrafficUsageRanking", "getTrafficUsageIncrements", "getRateLimitWhitelist", - "getTrafficQuotas" + "getTrafficQuotas", + "getGeoipStatus" ] }, "uci": [ @@ -68,12 +69,19 @@ "updateScheduleLimit", "getTrafficQuotas", "setTrafficQuota", - "deleteTrafficQuota" + "deleteTrafficQuota", + "getGeoipStatus", + "manageGeoip" ] }, "uci": [ "bandix" - ] + ], + "file": { + "/tmp/bandix-geoip-upload.mmdb": [ + "write" + ] + } } } } diff --git a/luci-app-clashoo/Makefile b/luci-app-clashoo/Makefile index 38181dd30..742d691f8 100644 --- a/luci-app-clashoo/Makefile +++ b/luci-app-clashoo/Makefile @@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-clashoo PKG_VERSION:=1.31.0 -PKG_RELEASE:=8 +PKG_RELEASE:=9 PKG_MAINTAINER:=kenzok8 PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME) @@ -91,7 +91,7 @@ define Package/luci-i18n-clashoo-zh-cn/postinst if [ -z "$${IPKG_INSTROOT}" ]; then # 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). - rm -rf /tmp/luci* + rm -rf /tmp/luci-indexcache /tmp/luci-modulecache fi exit 0 endef diff --git a/luci-app-clashoo/root/usr/share/rpcd/ucode/luci.clashoo b/luci-app-clashoo/root/usr/share/rpcd/ucode/luci.clashoo index 52a2ceae5..73ba1c98f 100755 --- a/luci-app-clashoo/root/usr/share/rpcd/ucode/luci.clashoo +++ b/luci-app-clashoo/root/usr/share/rpcd/ucode/luci.clashoo @@ -15,7 +15,9 @@ function mkdtemp(tpl) { } 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/clash-meta', '/usr/bin/clash', @@ -283,8 +285,8 @@ if (family === 'mihomo') { } } let channel = get_core_channel('mihomo'); - if (channel == 'stable' && access('/usr/bin/mihomo-stable', 'x')) return '/usr/bin/mihomo-stable'; - if (channel == 'alpha' && access('/usr/bin/mihomo', 'x')) return '/usr/bin/mihomo'; + if (channel == 'stable') return selected_mihomo_stable(); + if (channel == 'alpha') return access('/usr/share/clashoo/bin/mihomo-alpha', 'x') ? '/usr/share/clashoo/bin/mihomo-alpha' : ''; if (channel == 'smart') { for (let path in SMART_BINARY_PATHS) if (access(path, 'x')) return path; @@ -371,7 +373,7 @@ c.set('clashoo', 'config', 'core', legacy); } 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; let version = get_core_version('/usr/bin/clash-meta'); 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 稳定版,请先在“组件更新”中更新稳定版' }; } 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' }; } if (core == 'singbox' && dcore == '4') { @@ -887,6 +889,23 @@ if (m) return m[1]; 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) { if (id == 'clashoo') return pkg_version('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) { let out = {}; if (id == 'mihomo') { - let stable = access('/usr/bin/mihomo-stable', 'x') ? read_first_line('/usr/share/clashoo/clash_meta_version') : ''; - if (!stable && access('/usr/bin/mihomo-stable', 'x')) stable = core_short_version('/usr/bin/mihomo-stable'); + let stable_bin = selected_mihomo_stable(); + let stable = stable_bin ? core_short_version(stable_bin) : ''; 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 (!alpha && access('/usr/bin/mihomo', 'x')) alpha = core_short_version('/usr/bin/mihomo'); out.stable = stable || ''; out.alpha = mihomo_alpha_label(alpha); return out; @@ -2815,7 +2833,7 @@ let api = get_clash_api_auth(); health_status, health_detail, has_tun_device, core_type: configured_core, dcore, local_ip, 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_alpha: !!access(SINGBOX_BINARY_PATHS[1], 'x'), has_smart: !!find_smart_binary() }; diff --git a/luci-app-fastnet/Makefile b/luci-app-fastnet/Makefile index 7dc5e48ae..f5bcfc05f 100644 --- a/luci-app-fastnet/Makefile +++ b/luci-app-fastnet/Makefile @@ -8,8 +8,8 @@ include $(TOPDIR)/rules.mk LUCI_TITLE:=LuCI support for fastnet LUCI_DEPENDS:=+fastnet +luci-lib-linkeaseauth +linkease-app-entry LUCI_PKGARCH:=all -PKG_VERSION:=0.7.7-r5 -PKG_RELEASE:=4 +PKG_VERSION:=0.7.8-r5 +PKG_RELEASE:=5 LUCI_MINIFY_CSS:=0 LUCI_MINIFY_JS:=0 diff --git a/luci-app-istoreenhance/Makefile b/luci-app-istoreenhance/Makefile index 58e45adb2..929aac162 100644 --- a/luci-app-istoreenhance/Makefile +++ b/luci-app-istoreenhance/Makefile @@ -8,8 +8,8 @@ include $(TOPDIR)/rules.mk LUCI_TITLE:=LuCI support for KSpeeder LUCI_DEPENDS:=+istoreenhance +luci-lib-linkeaseauth +linkease-app-entry LUCI_PKGARCH:=all -PKG_VERSION:=0.8.4-r1 -PKG_RELEASE:=7 +PKG_VERSION:=0.8.5-r1 +PKG_RELEASE:=8 include $(TOPDIR)/feeds/luci/luci.mk diff --git a/luci-app-open-box/Makefile b/luci-app-open-box/Makefile index 37adfa3b4..a3509f70a 100644 --- a/luci-app-open-box/Makefile +++ b/luci-app-open-box/Makefile @@ -1,8 +1,8 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-open-box -PKG_VERSION:=0.1.361 -PKG_RELEASE:=47 +PKG_VERSION:=0.1.366 +PKG_RELEASE:=48 PKG_MAINTAINER:=liandu2024 PKG_LICENSE:=GPL-3.0-or-later diff --git a/luci-app-openvpn-plus/Makefile b/luci-app-openvpn-plus/Makefile index 2d3f8b7cf..db90c47b7 100755 --- a/luci-app-openvpn-plus/Makefile +++ b/luci-app-openvpn-plus/Makefile @@ -10,7 +10,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-openvpn-plus PKG_VERSION:=1.0.0 -PKG_RELEASE:=5 +PKG_RELEASE:=6 PKG_LICENSE:=Apache-2.0 PKG_MAINTAINER:=Manfred Jaider diff --git a/luci-app-openvpn-plus/README.md b/luci-app-openvpn-plus/README.md index 19674365a..af72d3f06 100755 --- a/luci-app-openvpn-plus/README.md +++ b/luci-app-openvpn-plus/README.md @@ -60,31 +60,6 @@ config openvpn 'instance1' option config '/etc/openvpn/luci/instance1.conf' ``` -## Screenshots - - - - - - - - - - - - - - - - - - - - - - -
010304
091013
111208
140605
- ## Directory Structure ```text diff --git a/luci-app-openvpn-plus/po/de/luci-app-openvpn-plus.po b/luci-app-openvpn-plus/po/de/luci-app-openvpn-plus.po index d478459fa..814b1765e 100755 --- a/luci-app-openvpn-plus/po/de/luci-app-openvpn-plus.po +++ b/luci-app-openvpn-plus/po/de/luci-app-openvpn-plus.po @@ -1,23 +1,23 @@ msgid "" msgstr "Content-Type: text/plain; charset=UTF-8\n" -#: root/www/luci-static/resources/view/vpn/openvpn.js:138 +#: root/www/luci-static/resources/view/vpn/openvpn.js:147 msgid "A client connection needs the server keys and config. Select an .ovpn profile to import them automatically, or click Cancel to use the default settings." msgstr "Eine Client-Verbindung benötigt Server-Schlüssel und Konfiguration. Wählen Sie ein .ovpn-Profil aus, um diese automatisch zu importieren, oder klicken Sie auf Abbrechen, um die Standardeinstellungen zu verwenden." -#: root/www/luci-static/resources/view/vpn/openvpn.js:128 +#: root/www/luci-static/resources/view/vpn/openvpn.js:137 msgid "A secure firewall zone for all" msgstr "Eine sichere Firewall-Zone für alle" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:87 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:89 msgid "A unique TLS security key for one client. It encrypts and protects all data sent to the server." msgstr "Ein eindeutiger TLS-Sicherheitsschlüssel für einen Client. Er verschlüsselt und schützt alle an den Server gesendeten Daten." -#: root/www/luci-static/resources/view/vpn/openvpn.js:116 +#: root/www/luci-static/resources/view/vpn/openvpn.js:125 msgid "ACTIVE" msgstr "AKTIV" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:139 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:141 msgid "API Token" msgstr "API-Token" @@ -34,23 +34,23 @@ msgstr "Aktiv" msgid "Active PIDs" msgstr "Aktive PIDs" -#: root/www/luci-static/resources/view/vpn/openvpn.js:2469 +#: root/www/luci-static/resources/view/vpn/openvpn.js:35 msgid "Add" msgstr "Hinzufügen" -#: root/www/luci-static/resources/view/vpn/openvpn.js:61 +#: root/www/luci-static/resources/view/vpn/openvpn.js:63 msgid "Add Client Instance" msgstr "Client-Instanz hinzufügen" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:77 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:79 msgid "Add Office" msgstr "Büro hinzufügen" -#: root/www/luci-static/resources/view/vpn/openvpn.js:62 +#: root/www/luci-static/resources/view/vpn/openvpn.js:64 msgid "Add Server Instance" msgstr "Server-Instanz hinzufügen" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:87 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:89 msgid "Add your other offices below. Please enter the internal LAN network IP (e.g., 192.168.2.0) of each office, NOT their public WAN IP addresses. The server will connect all local networks automatically." msgstr "Fügen Sie unten Ihre anderen Büros hinzu. Bitte geben Sie die interne LAN-Netzwerk-IP (z. B. 192.168.2.0) jedes Büros ein, NICHT deren öffentliche WAN-IP-Adressen. Der Server wird alle lokalen Netzwerke automatisch verbinden." @@ -62,19 +62,19 @@ msgstr "Aggregiertes RX" msgid "Aggregated TX" msgstr "Aggregiertes TX" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:88 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:90 msgid "Another Router detected!" msgstr "Ein anderer Router wurde erkannt!" -#: root/www/luci-static/resources/view/vpn/openvpn.js:89 +#: root/www/luci-static/resources/view/vpn/openvpn.js:95 msgid "Are you sure you want to delete" msgstr "Sind Sie sicher, dass Sie löschen möchten" -#: root/www/luci-static/resources/view/vpn/openvpn.js:118 +#: root/www/luci-static/resources/view/vpn/openvpn.js:127 msgid "Automated Zone Setup:" msgstr "Automatische Zonen-Einrichtung:" -#: root/www/luci-static/resources/view/vpn/openvpn.js:130 +#: root/www/luci-static/resources/view/vpn/openvpn.js:139 msgid "Automatic port scanning is a constant threat on the internet. The OpenVPN flood guard filter detects, bans, and drops these malicious requests. This hides your system, prevents spying, saves CPU power, and guarantees maximum VPN performance." msgstr "Automatische Portscans sind eine ständige Bedrohung im Internet. Der OpenVPN-Flood-Guard-Filter erkennt diese böswilligen Anfragen, sperrt sie und verwirft sie. Dies verbirgt Ihr System, verhindert Spionage, spart CPU-Leistung und garantiert maximale VPN-Performance." @@ -82,65 +82,74 @@ msgstr "Automatische Portscans sind eine ständige Bedrohung im Internet. Der Op msgid "BLOCKED" msgstr "BLOCKIERT" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:78 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:80 msgid "Back" msgstr "Zurück" -#: root/www/luci-static/resources/view/vpn/openvpn.js:83 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:36 +#: root/www/luci-static/resources/view/vpn/openvpn.js:65 +msgid "Backup" +msgstr "Sicherung" + +#: root/www/luci-static/resources/view/vpn/openvpn.js:87 msgid "Backup & Use Wizard (recommended)" msgstr "Sichern & Assistenten nutzen (empfohlen)" +#: root/www/luci-static/resources/view/vpn/openvpn.js:111 +msgid "Backup Server Profile" +msgstr "Backup des Server-Profils" + #: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:47 msgid "CLOSED" msgstr "GESCHLOSSEN" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:37 -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:79 -#: root/www/luci-static/resources/view/vpn/openvpn.js:63 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:38 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:81 +#: root/www/luci-static/resources/view/vpn/openvpn.js:66 msgid "Cancel" msgstr "Abbrechen" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:47 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:48 msgid "Certificate" msgstr "Zertifikat" -#: root/www/luci-static/resources/view/vpn/openvpn.js:137 +#: root/www/luci-static/resources/view/vpn/openvpn.js:146 msgid "Certification Authority" msgstr "Zertifizierungsstelle (CA)" -#: root/www/luci-static/resources/view/vpn/openvpn.js:64 +#: root/www/luci-static/resources/view/vpn/openvpn.js:67 msgid "Change" msgstr "Ändern" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:80 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:82 msgid "Check Address Status" msgstr "Adressstatus prüfen" -#: root/www/luci-static/resources/view/vpn/openvpn.js:119 +#: root/www/luci-static/resources/view/vpn/openvpn.js:128 msgid "Check Traffic Rules:" msgstr "Verkehrsregeln prüfen:" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:94 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:96 msgid "Checking connection status in the background..." msgstr "Verbindungsstatus wird im Hintergrund überprüft..." -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:82 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:84 msgid "Choose File..." msgstr "Datei auswählen..." -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:90 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:92 msgid "Choose how your internet traffic is sent through the VPN. You can use any free OpenVPN app on your phone or laptop to import this profile." msgstr "Wählen Sie aus, wie Ihr Internet-Datenverkehr durch das VPN geleitet wird. Sie können jede kostenlose OpenVPN-App auf Ihrem Smartphone oder Laptop verwenden, um dieses Profil zu importieren." -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:145 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:147 msgid "Classic free provider with exceptional IPv6 and dual-stack support." msgstr "Klassischer kostenloser Anbieter mit hervorragender IPv6- und Dual-Stack-Unterstützung." -#: root/www/luci-static/resources/view/vpn/openvpn.js:43 +#: root/www/luci-static/resources/view/vpn/openvpn.js:45 msgid "Clear Log" msgstr "Protokoll löschen" -#: root/www/luci-static/resources/view/vpn/openvpn.js:35 +#: root/www/luci-static/resources/view/vpn/openvpn.js:36 msgid "Clearing..." msgstr "Lösche..." @@ -149,40 +158,40 @@ msgstr "Lösche..." msgid "Client" msgstr "Client" -#: root/www/luci-static/resources/view/vpn/openvpn.js:149 +#: root/www/luci-static/resources/view/vpn/openvpn.js:158 msgid "Client Certificate" msgstr "Client-Zertifikat" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:48 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:49 msgid "Client Certificate + Private key (issued by CA) + TLS Crypt v2 Client key" msgstr "Client-Zertifikat + privater Schlüssel (von CA ausgestellt) + TLS-Crypt-v2-Client-Schlüssel" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:90 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:92 msgid "Client Key" msgstr "Client-Schlüssel" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:91 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:93 msgid "Client for Branch Office (Branch)" msgstr "Client für Außenstelle (Branch)" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:50 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:51 msgid "Client keys exported successfully." msgstr "Client-Schlüssel erfolgreich exportiert." -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:49 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:50 msgid "Client keys generated successfully!" msgstr "Client-Schlüssel erfolgreich generiert!" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:36 -#: root/www/luci-static/resources/view/vpn/openvpn.js:66 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:37 +#: root/www/luci-static/resources/view/vpn/openvpn.js:69 msgid "Close" msgstr "Schließen" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:52 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:53 msgid "Common Name (CN)" msgstr "Common Name (CN)" -#: root/www/luci-static/resources/view/vpn/openvpn.js:88 +#: root/www/luci-static/resources/view/vpn/openvpn.js:94 msgid "Configuration changed! Applying will temporarily restart the OpenVPN instance." msgstr "Konfiguration geändert! Beim Übernehmen wird die OpenVPN-Instanz neu gestartet." @@ -194,40 +203,44 @@ msgstr "Verbunden" msgid "Connection Type" msgstr "Verbindungstyp" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:81 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:83 msgid "Create VPN" msgstr "VPN erstellen" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:92 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:94 msgid "Create a secure VPN connection in just a few clicks." msgstr "Erstellen Sie mit nur wenigen Klicks eine sichere VPN-Verbindung." -#: root/www/luci-static/resources/view/vpn/openvpn.js:139 -msgid "Create keys for a new office..." -msgstr "Schlüssel für ein neues Büro erstellen..." +#: root/www/luci-static/resources/view/vpn/openvpn.js:149 +msgid "Create keys for a new mobile client..." +msgstr "Schlüssel für einen neuen mobilen Client erstellen..." + +#: root/www/luci-static/resources/view/vpn/openvpn.js:148 +msgid "Create keys for a new remote office..." +msgstr "Schlüssel für eine neue Außenstelle erstellen..." #: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:27 msgid "Creating VPN..." msgstr "VPN wird erstellt..." #: root/www/luci-static/resources/view/vpn/openvpn-status.js:27 -#: root/www/luci-static/resources/view/vpn/openvpn.js:36 +#: root/www/luci-static/resources/view/vpn/openvpn.js:37 msgid "Creating..." msgstr "Erstelle..." -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:82 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:84 msgid "Crypto operation running. Please wait..." msgstr "Krypto-Operation läuft. Bitte warten..." -#: root/www/luci-static/resources/view/vpn/openvpn.js:158 +#: root/www/luci-static/resources/view/vpn/openvpn.js:167 msgid "Cryptographic Key Warning" msgstr "Warnung bezüglich kryptografischer Schlüssel" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:169 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:171 msgid "Custom / Manual Update URL" msgstr "Benutzerdefinierte / Manuelle Update-URL" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:53 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:54 msgid "Custom CLI Options" msgstr "Benutzerdefinierte CLI-Optionen" @@ -235,7 +248,7 @@ msgstr "Benutzerdefinierte CLI-Optionen" msgid "Custom Update URL" msgstr "Benutzerdefinierte Update-URL" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:160 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:162 msgid "DDNS Key" msgstr "DDNS-Schlüssel" @@ -243,7 +256,7 @@ msgstr "DDNS-Schlüssel" msgid "DDNS Provider" msgstr "DDNS-Anbieter" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:161 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:163 msgid "DDNS Username" msgstr "DDNS-Benutzername" @@ -251,32 +264,32 @@ msgstr "DDNS-Benutzername" msgid "Default" msgstr "Standard" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:83 -#: root/www/luci-static/resources/view/vpn/openvpn.js:2470 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:85 +#: root/www/luci-static/resources/view/vpn/openvpn.js:40 msgid "Delete" msgstr "Löschen" -#: root/www/luci-static/resources/view/vpn/openvpn.js:2459 -msgid "Delete Client Profile..." -msgstr "Client-Profil löschen..." - -#: root/www/luci-static/resources/view/vpn/openvpn.js:67 +#: root/www/luci-static/resources/view/vpn/openvpn.js:70 msgid "Delete Instance" msgstr "Instanz löschen" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:95 +#: root/www/luci-static/resources/view/vpn/openvpn.js:112 +msgid "Delete Remote Profile..." +msgstr "Profil der Gegenstelle löschen..." + +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:97 msgid "Delete this office" msgstr "Dieses Büro löschen" -#: root/www/luci-static/resources/view/vpn/openvpn.js:68 +#: root/www/luci-static/resources/view/vpn/openvpn.js:71 msgid "Deleted - Save & Apply" msgstr "Gelöscht - Speichern & Anwenden" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:102 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:105 msgid "Deployment Recommendation:" msgstr "Bereitstellungsempfehlung:" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:101 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:104 msgid "Deprecated TLS-Crypt V1 key (Not supported)" msgstr "Veralteter TLS-Crypt-V1-Schlüssel (Nicht unterstützt)" @@ -284,33 +297,33 @@ msgstr "Veralteter TLS-Crypt-V1-Schlüssel (Nicht unterstützt)" msgid "Destination" msgstr "Ziel" -#: root/www/luci-static/resources/view/vpn/openvpn.js:140 +#: root/www/luci-static/resources/view/vpn/openvpn.js:150 msgid "Diffie - Hellman Parameters" msgstr "Diffie - Hellman-Parameter" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:64 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:66 msgid "Diffie-Hellman Parameters (Perfect Forward Secrecy)" msgstr "Diffie-Hellman-Parameter (Perfect Forward Secrecy)" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:54 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:55 msgid "Diffie-Hellman parameters enforce numeric prime bit boundaries exclusively." msgstr "Diffie-Hellman-Parameter erzwingen ausschließlich numerische Primbit-Grenzen." -#: root/www/luci-static/resources/view/vpn/openvpn.js:37 +#: root/www/luci-static/resources/view/vpn/openvpn.js:38 msgid "Disable" msgstr "Deaktivieren" #: root/www/luci-static/resources/view/status/include/35_openvpn.js:19 #: root/www/luci-static/resources/view/vpn/openvpn-status.js:31 -#: root/www/luci-static/resources/view/vpn/openvpn.js:38 +#: root/www/luci-static/resources/view/vpn/openvpn.js:39 msgid "Disabled" msgstr "Deaktiviert" -#: root/www/luci-static/resources/view/vpn/openvpn.js:157 +#: root/www/luci-static/resources/view/vpn/openvpn.js:166 msgid "Do you want to proceed and save this file anyway?" msgstr "Möchten Sie fortfahren und diese Datei trotzdem speichern?" -#: root/www/luci-static/resources/view/vpn/openvpn.js:164 +#: root/www/luci-static/resources/view/vpn/openvpn.js:173 msgid "Do you want to proceed with the import anyway?" msgstr "Möchten Sie den Import trotzdem fortsetzen?" @@ -318,53 +331,53 @@ msgstr "Möchten Sie den Import trotzdem fortsetzen?" msgid "Domain Name" msgstr "Domainname" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:144 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:146 msgid "Domain Update URL / Key" msgstr "Domain-Update-URL / Schlüssel" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:166 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:168 msgid "Domain Update URL / token" msgstr "Domain-Update-URL / Token" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:38 -#: root/www/luci-static/resources/view/vpn/openvpn.js:69 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:39 +#: root/www/luci-static/resources/view/vpn/openvpn.js:72 msgid "Download" msgstr "Herunterladen" -#: root/www/luci-static/resources/view/vpn/openvpn.js:70 +#: root/www/luci-static/resources/view/vpn/openvpn.js:73 msgid "Download (.ovpn)" msgstr "Herunterladen (.ovpn)" -#: root/www/luci-static/resources/view/vpn/openvpn.js:90 +#: root/www/luci-static/resources/view/vpn/openvpn.js:96 msgid "Download and QR code link are only available while this window is open." msgstr "Download und QR-Code-Link sind nur verfügbar, solange dieses Fenster geöffnet ist." -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:138 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:140 msgid "DuckDNS (duckdns.org)" msgstr "DuckDNS (duckdns.org)" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:153 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:155 msgid "Dynu Systems (dynu.com)" msgstr "Dynu Systems (dynu.com)" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:107 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:110 msgid "ERROR: Safety polling threshold exceeded boundaries. Process deadlocked." msgstr "FEHLER: Sicherheits-Polling-Schwellenwert hat die Grenzen überschritten. Prozess befindet sich im Deadlock." -#: root/www/luci-static/resources/view/vpn/openvpn.js:91 +#: root/www/luci-static/resources/view/vpn/openvpn.js:97 msgid "Edit Config file" msgstr "Konfigurationsdatei bearbeiten" -#: root/www/luci-static/resources/view/vpn/openvpn.js:40 +#: root/www/luci-static/resources/view/vpn/openvpn.js:42 msgid "Enable" msgstr "Aktivieren" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:39 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:40 msgid "Enable Save" msgstr "Speichern aktivieren" #: root/www/luci-static/resources/view/vpn/openvpn-status.js:33 -#: root/www/luci-static/resources/view/vpn/openvpn.js:71 +#: root/www/luci-static/resources/view/vpn/openvpn.js:74 msgid "Enabled" msgstr "Aktiviert" @@ -372,64 +385,64 @@ msgstr "Aktiviert" msgid "Encryption" msgstr "Verschlüsselung" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:167 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:169 msgid "Enter your own manual HTTP update URL structure." msgstr "Tragen Sie Ihre eigene manuelle HTTP-Update-URL-Struktur ein." #: root/www/luci-static/resources/view/status/include/35_openvpn.js:21 #: root/www/luci-static/resources/view/vpn/openvpn-status.js:32 -#: root/www/luci-static/resources/view/vpn/openvpn.js:41 +#: root/www/luci-static/resources/view/vpn/openvpn.js:43 msgid "Error" msgstr "Fehler" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:187 -#: root/www/luci-static/resources/view/vpn/openvpn.js:176 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:189 +#: root/www/luci-static/resources/view/vpn/openvpn.js:186 msgid "Error: An office with this name is already in the list." msgstr "Fehler: Ein Büro mit diesem Namen existiert bereits in der Liste." -#: root/www/luci-static/resources/view/vpn/openvpn.js:170 +#: root/www/luci-static/resources/view/vpn/openvpn.js:179 msgid "Error: Configuration or cryptographic keys are missing on disk." msgstr "Fehler: Konfiguration oder kryptografische Schlüssel fehlen auf dem Datenträger." -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:190 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:192 msgid "Error: File is corrupted or missing vital fields." msgstr "Fehler: Datei ist beschädigt oder es fehlen wichtige Felder." -#: root/www/luci-static/resources/view/vpn/openvpn.js:2665 +#: root/www/luci-static/resources/view/vpn/openvpn.js:192 msgid "Error: Network status check failed." msgstr "Fehler: Netzwerkstatusprüfung fehlgeschlagen." -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:186 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:188 msgid "Error: Network status check failed. Please try again." msgstr "Fehler: Netzwerkstatusprüfung fehlgeschlagen. Bitte versuchen Sie es erneut." -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:182 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:184 msgid "Error: Please add at least one office to the list before you continue." msgstr "Fehler: Bitte fügen Sie mindestens ein Büro zur Liste hinzu, bevor Sie fortfahren." -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:185 -#: root/www/luci-static/resources/view/vpn/openvpn.js:175 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:187 +#: root/www/luci-static/resources/view/vpn/openvpn.js:185 msgid "Error: This IP address is already used by another office. Every office needs a different IP address!" msgstr "Fehler: Diese IP-Adresse wird bereits von einem anderen Büro verwendet. Jedes Büro benötigt eine andere IP-Adresse!" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:188 -#: root/www/luci-static/resources/view/vpn/openvpn.js:177 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:190 +#: root/www/luci-static/resources/view/vpn/openvpn.js:187 msgid "Error: You cannot use this IP network! It is the local network of this router and will block all traffic." msgstr "Fehler: Sie können dieses IP-Netzwerk nicht verwenden! Es ist das lokale Netzwerk dieses Routers und wird den gesamten Datenverkehr blockieren." -#: root/www/luci-static/resources/view/vpn/openvpn.js:93 +#: root/www/luci-static/resources/view/vpn/openvpn.js:99 msgid "Export (.ovpn)" msgstr "Exportieren (.ovpn)" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:55 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:56 msgid "Export Keys" msgstr "Schlüssel exportieren" -#: root/www/luci-static/resources/view/vpn/openvpn.js:94 +#: root/www/luci-static/resources/view/vpn/openvpn.js:100 msgid "Export OpenVPN Connect Client Profile" msgstr "Exportiere OpenVPN Connect Client-Profil" -#: root/www/luci-static/resources/view/vpn/openvpn.js:39 +#: root/www/luci-static/resources/view/vpn/openvpn.js:41 msgid "Export Profile" msgstr "Profil exportieren" @@ -437,47 +450,51 @@ msgstr "Profil exportieren" msgid "External Port" msgstr "Externer Port" -#: root/www/luci-static/resources/view/vpn/openvpn.js:169 +#: root/www/luci-static/resources/view/vpn/openvpn.js:178 msgid "Failed to build profile:" msgstr "Profilerstellung fehlgeschlagen:" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:183 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:185 msgid "Failed to create new OpenVPN server:" msgstr "Erstellung des neuen OpenVPN-Servers fehlgeschlagen:" +#: root/www/luci-static/resources/view/vpn/openvpn.js:190 +msgid "Failed to generate secure download profile. Please check system logs." +msgstr "Das sichere Download-Profil konnte nicht erstellt werden. Bitte überprüfen Sie die Systemprotokolle." + #: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:27 msgid "File Location:" msgstr "Dateipfad:" -#: root/www/luci-static/resources/view/vpn/openvpn.js:121 +#: root/www/luci-static/resources/view/vpn/openvpn.js:130 msgid "Filtered" msgstr "Gefiltert" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:96 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:98 msgid "Finding your public IP address..." msgstr "Ermittle Ihre öffentliche IP-Adresse..." -#: root/www/luci-static/resources/view/vpn/openvpn.js:122 +#: root/www/luci-static/resources/view/vpn/openvpn.js:131 msgid "Firewall" msgstr "Firewall" -#: root/www/luci-static/resources/view/vpn/openvpn.js:123 +#: root/www/luci-static/resources/view/vpn/openvpn.js:132 msgid "Firewall & Routing Information" msgstr "Firewall & Routing-Informationen" -#: root/www/luci-static/resources/view/vpn/openvpn.js:129 +#: root/www/luci-static/resources/view/vpn/openvpn.js:138 msgid "Flood Guard" msgstr "Flood Guard" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:97 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:99 msgid "For Phones & Laptops (OpenVPN Connect App)" msgstr "Für Smartphones & Laptops (OpenVPN Connect App)" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:165 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:167 msgid "FreeMYIP (freemyip.com)" msgstr "FreeMYIP (freemyip.com)" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:65 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:67 msgid "Full PKI Suite (CA + Server Certificate + Private Server Key)" msgstr "Vollständige PKI-Suite (CA + Server-Zertifikat + privater Server-Schlüssel)" @@ -485,31 +502,31 @@ msgstr "Vollständige PKI-Suite (CA + Server-Zertifikat + privater Server-Schlü msgid "Gateway" msgstr "Gateway" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:40 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:41 msgid "Generate" msgstr "Generieren" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:100 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:103 msgid "Generated keys are not saved! Close window and discard keys?" msgstr "Generierte Schlüssel sind nicht gespeichert! Fenster schließen und Schlüssel verwerfen?" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:1054 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:61 msgid "Generating TLS crypt v2 client key..." msgstr "Generiere TLS-Crypt-v2-Client-Schlüssel..." -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:56 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:57 msgid "Generating TLS-Crypt key..." msgstr "Generiere TLS-Crypt-Schlüssel..." -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:59 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:60 msgid "Generating private client key..." msgstr "Generiere privaten Client-Schlüssel..." -#: root/www/luci-static/resources/view/vpn/openvpn.js:72 +#: root/www/luci-static/resources/view/vpn/openvpn.js:75 msgid "Generating..." msgstr "Generiere..." -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:149 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:151 msgid "HTTP Token String" msgstr "HTTP-Token-String" @@ -517,19 +534,19 @@ msgstr "HTTP-Token-String" msgid "Help" msgstr "Hilfe" -#: root/www/luci-static/resources/view/vpn/openvpn.js:99 +#: root/www/luci-static/resources/view/vpn/openvpn.js:105 msgid "Here you can manage multiple OpenVPN Server and Client instances dynamically." msgstr "Hier können Sie mehrere OpenVPN-Server- und Client-Instanzen dynamisch verwalten." -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:143 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:145 msgid "IPv64 (ipv64.net)" msgstr "IPv64 (ipv64.net)" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:98 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:100 msgid "If your internet provides a static IP address." msgstr "Wenn Ihr Internetanschluss eine statische IP-Adresse bereitstellt." -#: root/www/luci-static/resources/view/vpn/openvpn.js:95 +#: root/www/luci-static/resources/view/vpn/openvpn.js:101 msgid "Import (.ovpn)" msgstr "Importieren (.ovpn)" @@ -537,20 +554,20 @@ msgstr "Importieren (.ovpn)" msgid "Import Failed:" msgstr "Import fehlgeschlagen:" -#: root/www/luci-static/resources/view/vpn/openvpn.js:96 -msgid "Import OpenVPN Connect Client Profile" -msgstr "OpenVPN Connect Client-Profil importieren" +#: root/www/luci-static/resources/view/vpn/openvpn.js:102 +msgid "Import OpenVPN Client Profile" +msgstr "OpenVPN-Client-Profil importieren" #: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:35 -#: root/www/luci-static/resources/view/vpn/openvpn.js:97 +#: root/www/luci-static/resources/view/vpn/openvpn.js:103 msgid "Import Profile" msgstr "Profil importieren" -#: root/www/luci-static/resources/view/vpn/openvpn.js:161 +#: root/www/luci-static/resources/view/vpn/openvpn.js:170 msgid "Import cancelled due to missing TLS-Crypt v2 protection." msgstr "Import abgebrochen wegen fehlendem TLS-Crypt-v2-Schutz." -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:94 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:97 msgid "Important: Use a unique name for each office to configure the network routing correctly." msgstr "Wichtig: Verwenden Sie einen eindeutigen Namen für jedes Büro, um das Netzwerk-Routing korrekt zu konfigurieren." @@ -562,7 +579,7 @@ msgstr "Importierter Client" msgid "Importing profile..." msgstr "Importiere Profil..." -#: root/www/luci-static/resources/view/vpn/openvpn.js:124 +#: root/www/luci-static/resources/view/vpn/openvpn.js:133 msgid "Inbound Access:" msgstr "Eingehender Zugriff:" @@ -575,11 +592,11 @@ msgid "Instance" msgstr "Instanz" #: root/www/luci-static/resources/view/vpn/openvpn-status.js:35 -#: root/www/luci-static/resources/view/vpn/openvpn.js:42 +#: root/www/luci-static/resources/view/vpn/openvpn.js:44 msgid "Instance #" msgstr "Instanz #" -#: root/www/luci-static/resources/view/vpn/openvpn.js:51 +#: root/www/luci-static/resources/view/vpn/openvpn.js:53 msgid "Instance Management" msgstr "Instanzverwaltung" @@ -595,27 +612,27 @@ msgstr "Interner Port" msgid "Internet Traffic" msgstr "Internet-Datenverkehr" -#: root/www/luci-static/resources/view/vpn/openvpn.js:171 +#: root/www/luci-static/resources/view/vpn/openvpn.js:180 msgid "Invalid file. Important parts are missing (CA, Cert, or Private Key)." msgstr "Ungültige Datei. Wichtige Teile fehlen (CA, Zertifikat oder privater Schlüssel)." -#: root/www/luci-static/resources/view/vpn/openvpn.js:166 +#: root/www/luci-static/resources/view/vpn/openvpn.js:175 msgid "It is highly recommended not to keep or use the outdated standard default configurations. Instead, you should backup them and use the up-to-date setup Wizard to build new, secure connections." msgstr "Es wird dringend empfohlen, die veralteten Standardkonfigurationen nicht zu behalten oder zu verwenden. Stattdessen sollten Sie diese sichern und den aktuellen Einrichtungsassistenten nutzen, um neue, sichere Verbindungen zu erstellen." -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:103 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:106 msgid "It is highly recommended to use the automated Setup Wizard to deploy a Mobile Server (OpenVPN Connect) or a Headquarters LAN-to-LAN Server. In just a few clicks, the wizard fully configures easy DDNS provider setup, multi-client remote networks, optimal firewall zones, and scan-ready QR codes." msgstr "Es wird dringend empfohlen, den automatischen Einrichtungsassistenten zu verwenden, um einen mobilen Server (OpenVPN Connect) oder einen Hauptsitz-LAN-zu-LAN-Server bereitzustellen. Mit nur wenigen Klicks konfiguriert der Assistent die Einrichtung von DDNS-Anbietern, Multi-Client-Remotenetzwerke, optimale Firewall-Zonen und scannbare QR-Codes vollständig." -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:106 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:108 msgid "Keep IP Setup anyway" msgstr "IP-Einrichtung trotzdem beibehalten" -#: root/www/luci-static/resources/view/vpn/openvpn.js:82 +#: root/www/luci-static/resources/view/vpn/openvpn.js:86 msgid "Keep old samples (to avoid)" msgstr "Alte Vorlagen behalten (nicht empfohlen)" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:60 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:62 msgid "Key Strength" msgstr "Schlüssellänge" @@ -623,35 +640,35 @@ msgstr "Schlüssellänge" msgid "Key Update Active" msgstr "Schlüssel-Update aktiv" -#: root/www/luci-static/resources/view/vpn/openvpn.js:172 +#: root/www/luci-static/resources/view/vpn/openvpn.js:181 msgid "Key check failed. One security key is broken or invalid." msgstr "Schlüsselprüfung fehlgeschlagen. Ein Sicherheits-Schlüssel ist fehlerhaft oder ungültig." -#: root/www/luci-static/resources/view/vpn/openvpn.js:145 +#: root/www/luci-static/resources/view/vpn/openvpn.js:154 msgid "Key file is empty or does not exist on disk yet." msgstr "Schlüsseldatei ist leer oder existiert noch nicht auf dem Datenträger." -#: root/www/luci-static/resources/view/vpn/openvpn.js:173 +#: root/www/luci-static/resources/view/vpn/openvpn.js:182 msgid "Key generation failed." msgstr "Schlüsselgenerierung fehlgeschlagen." -#: root/www/luci-static/resources/view/vpn/openvpn.js:143 +#: root/www/luci-static/resources/view/vpn/openvpn.js:151 msgid "Key generation in progress..." msgstr "Schlüsselgenerierung läuft..." -#: root/www/luci-static/resources/view/vpn/openvpn.js:174 +#: root/www/luci-static/resources/view/vpn/openvpn.js:183 msgid "Key type mismatch" msgstr "Schlüsseltyp-Fehlanpassung" -#: root/www/luci-static/resources/view/vpn/openvpn.js:141 +#: root/www/luci-static/resources/view/vpn/openvpn.js:184 msgid "Key verification failed" msgstr "Schlüsselverifizierung fehlgeschlagen" -#: root/www/luci-static/resources/view/vpn/openvpn.js:142 +#: root/www/luci-static/resources/view/vpn/openvpn.js:76 msgid "KeyGen" msgstr "Schlüsselgenerator" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:107 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:109 msgid "Keys updated successfully!" msgstr "Schlüssel erfolgreich aktualisiert!" @@ -659,15 +676,19 @@ msgstr "Schlüssel erfolgreich aktualisiert!" msgid "LAN IP" msgstr "LAN IP" -#: root/www/luci-static/resources/view/vpn/openvpn.js:98 +#: root/www/luci-static/resources/view/vpn/openvpn.js:104 msgid "LAN-to-LAN Router Profile Selection" msgstr "LAN-zu-LAN-Router Profilauswahl" -#: root/www/luci-static/resources/view/vpn/openvpn.js:52 +#: root/www/luci-static/resources/view/vpn/openvpn.js:54 msgid "LOG" msgstr "Protokoll (Log)" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:61 +#: root/www/luci-static/resources/view/vpn/openvpn.js:153 +msgid "LOG: Certificate pipeline completed. Generating TLS crypt v2 client key..." +msgstr "LOG: Zertifikatspipeline abgeschlossen. Generiere TLS-Crypt-v2-Client-Schlüssel..." + +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:63 msgid "Loading key contents..." msgstr "Lade Schlüsselinhalt..." @@ -675,7 +696,7 @@ msgstr "Lade Schlüsselinhalt..." msgid "Local IP / Port" msgstr "Lokale IP / Port" -#: root/www/luci-static/resources/view/vpn/openvpn.js:44 +#: root/www/luci-static/resources/view/vpn/openvpn.js:46 msgid "Log Cleared" msgstr "Protokoll gelöscht" @@ -687,63 +708,63 @@ msgstr "FEHLT" msgid "Mask" msgstr "Maske" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:62 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:64 msgid "Master CA" msgstr "Haupt-CA" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:89 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:91 msgid "Master Server Secret Key" msgstr "Hauptschlüssel des Servers (Master Secret)" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:140 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:142 msgid "Modern free provider with perfect IPv6 and Dual-Stack support." msgstr "Moderner kostenloser Anbieter mit nativer IPv6- und Dual-Stack-Unterstützung." -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:72 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:74 msgid "N/A - openvpn --genkey accepts no extra flags" msgstr "Nicht verfügbar - openvpn --genkey akzeptiert keine zusätzlichen Flags" #: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:44 -#: root/www/luci-static/resources/view/vpn/openvpn.js:54 +#: root/www/luci-static/resources/view/vpn/openvpn.js:56 msgid "Netmask" msgstr "Netzmaske" -#: root/www/luci-static/resources/view/vpn/openvpn.js:125 +#: root/www/luci-static/resources/view/vpn/openvpn.js:134 msgid "Network" msgstr "Netzwerk" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:184 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:186 msgid "Network Error: DNS/IP query failed." msgstr "Netzwerkfehler: DNS/IP-Abfrage fehlgeschlagen." -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:93 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:95 msgid "Network verification failed / Timeout" msgstr "Netzwerküberprüfung fehlgeschlagen / Zeitüberschreitung" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:84 -#: root/www/luci-static/resources/view/vpn/openvpn.js:73 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:86 +#: root/www/luci-static/resources/view/vpn/openvpn.js:77 msgid "Next" msgstr "Weiter" #: root/www/luci-static/resources/view/vpn/openvpn-status.js:38 -#: root/www/luci-static/resources/view/vpn/openvpn.js:45 +#: root/www/luci-static/resources/view/vpn/openvpn.js:47 msgid "No" msgstr "Nein" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:51 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:52 msgid "No (Clone Default Keys)" msgstr "Nein (Standard-Schlüssel klonen)" -#: root/www/luci-static/resources/view/vpn/openvpn.js:102 +#: root/www/luci-static/resources/view/vpn/openvpn.js:108 msgid "No OpenVPN instances configured yet. Use the Wizard to create new OpenVPN instances." msgstr "Noch keine OpenVPN-Instanzen konfiguriert. Verwenden Sie den Assistenten, um neue OpenVPN-Instanzen zu erstellen." -#: root/www/luci-static/resources/view/vpn/openvpn.js:101 -#: root/www/luci-static/resources/view/vpn/openvpn.js:103 +#: root/www/luci-static/resources/view/vpn/openvpn.js:107 +#: root/www/luci-static/resources/view/vpn/openvpn.js:109 msgid "No active OpenVPN log entries found." msgstr "Keine aktiven OpenVPN-Protokolleinträge gefunden." -#: root/www/luci-static/resources/view/vpn/openvpn.js:46 +#: root/www/luci-static/resources/view/vpn/openvpn.js:48 msgid "No changes detected" msgstr "Keine Änderungen erkannt" @@ -751,7 +772,7 @@ msgstr "Keine Änderungen erkannt" msgid "No clients connected" msgstr "Keine Clients verbunden" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:109 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:111 msgid "No file chosen" msgstr "Keine Datei ausgewählt" @@ -759,7 +780,7 @@ msgstr "Keine Datei ausgewählt" msgid "No instances configured" msgstr "Keine Instanzen konfiguriert" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:108 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:110 msgid "No offices added yet. Use the fields below to add the first office." msgstr "Noch keine Büros hinzugefügt. Verwenden Sie die Felder unten, um das erste Büro hinzuzufügen." @@ -767,20 +788,20 @@ msgstr "Noch keine Büros hinzugefügt. Verwenden Sie die Felder unten, um das e msgid "No server connected" msgstr "Kein Server verbunden" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:159 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:161 msgid "No-IP (noip.com)" msgstr "No-IP (noip.com)" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:63 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:65 msgid "Not applicable" msgstr "Nicht zutreffend" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:135 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:137 msgid "Number 1 for Open-Source and Home-Assistant setups." msgstr "Nummer 1 für Open-Source- und Home-Assistant-Installationen." #: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:50 -#: root/www/luci-static/resources/view/vpn/openvpn.js:74 +#: root/www/luci-static/resources/view/vpn/openvpn.js:78 msgid "OK" msgstr "OK" @@ -788,170 +809,186 @@ msgstr "OK" msgid "OPEN" msgstr "OFFEN" -#: root/www/luci-static/resources/view/vpn/openvpn.js:104 -msgid "Office Profile:" -msgstr "Büroprofil:" - #: root/www/luci-static/resources/view/status/include/35_openvpn.js:17 -#: root/www/luci-static/resources/view/vpn/openvpn.js:47 +#: root/www/luci-static/resources/view/vpn/openvpn.js:49 msgid "OpenVPN" msgstr "OpenVPN" -#: root/www/luci-static/resources/view/vpn/openvpn.js:86 +#: root/www/luci-static/resources/view/vpn/openvpn.js:91 +msgid "OpenVPN Backup Export" +msgstr "OpenVPN-Backup-Export" + +#: root/www/luci-static/resources/view/vpn/openvpn.js:92 +msgid "OpenVPN Backup Import" +msgstr "OpenVPN-Backup-Import" + +#: root/www/luci-static/resources/view/vpn/openvpn.js:90 msgid "OpenVPN Client Export" msgstr "OpenVPN-Client Export" -#: root/www/luci-static/resources/view/vpn/openvpn.js:100 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:55 +msgid "OpenVPN Client Profile detected" +msgstr "OpenVPN-Client-Profil erkannt" + +#: root/www/luci-static/resources/view/vpn/openvpn.js:106 msgid "OpenVPN Connect Mobile Export" msgstr "OpenVPN Connect Export für Mobilgeräte" -#: root/www/luci-static/resources/view/vpn/openvpn.js:87 +#: root/www/luci-static/resources/view/vpn/openvpn.js:93 msgid "OpenVPN Default Configuration Detected" msgstr "OpenVPN-Standardkonfiguration erkannt" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:91 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:93 msgid "OpenVPN Instance Key Generator" msgstr "OpenVPN-Instanz Schlüsselgenerator" -#: root/www/luci-static/resources/view/vpn/openvpn.js:146 +#: root/www/luci-static/resources/view/vpn/openvpn.js:155 msgid "OpenVPN Keys" msgstr "OpenVPN-Schlüssel" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:176 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:178 msgid "OpenVPN Keys Detected:" msgstr "OpenVPN-Schlüssel erkannt:" #: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:54 -msgid "OpenVPN Profile Valid:" -msgstr "OpenVPN-Profil gültig:" +msgid "OpenVPN Profile Valid" +msgstr "OpenVPN-Profil gültig" -#: root/www/luci-static/resources/view/vpn/openvpn.js:53 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:56 +msgid "OpenVPN Server Profile detected" +msgstr "OpenVPN-Server-Profil erkannt" + +#: root/www/luci-static/resources/view/vpn/openvpn.js:55 msgid "OpenVPN Server/Client" msgstr "OpenVPN-Server/Client" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:66 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:68 msgid "OpenVPN Setup Wizard" msgstr "OpenVPN-Einrichtungsassistent" -#: root/www/luci-static/resources/view/vpn/openvpn.js:126 +#: root/www/luci-static/resources/view/vpn/openvpn.js:135 msgid "OpenVPN tunnel interfaces (tun0, tun1, tun3, etc.)" msgstr "OpenVPN-Tunnel-Schnittstellen (tun0, tun1, tun3, etc.)" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:55 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:57 msgid "Outside Port" msgstr "Externer Port" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:73 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:75 msgid "PKI Suite components saved successfully." msgstr "PKI-Suite-Komponenten erfolgreich gespeichert." -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:154 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:156 msgid "Password as plain text (Auto-SHA256)" msgstr "Passwort als Klartext (Auto-SHA256)" #: root/www/luci-static/resources/view/status/include/35_openvpn.js:20 #: root/www/luci-static/resources/view/vpn/openvpn-status.js:41 -#: root/www/luci-static/resources/view/vpn/openvpn.js:48 +#: root/www/luci-static/resources/view/vpn/openvpn.js:50 msgid "Pending..." msgstr "Ausstehend..." -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:114 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:116 msgid "Please add static routing inside your Main Internet Router (IP: %s) using this OpenVPN Router (IP: %s) as Gateway." msgstr "Bitte fügen Sie in Ihrem Haupt-Internet-Router (IP: %s) eine statische Route hinzu, die diesen OpenVPN-Router (IP: %s) als Gateway nutzt." -#: root/www/luci-static/resources/view/vpn/openvpn.js:106 +#: root/www/luci-static/resources/view/vpn/openvpn.js:114 msgid "Please assign a unique device name (Common Name) for this mobile profile:" msgstr "Bitte vergeben Sie einen eindeutigen Gerätenamen (Common Name) für dieses mobile Profil:" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:115 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:117 msgid "Please check your profile file. A valid configuration file needs a remote target endpoint and key blocks." msgstr "Bitte überprüfen Sie Ihre Profildatei. Eine gültige Konfigurationsdatei benötigt einen Remote-Zielendpunkt und Schlüsselblöcke." -#: root/www/luci-static/resources/view/vpn/openvpn.js:65 +#: root/www/luci-static/resources/view/vpn/openvpn.js:68 msgid "Please click «Save & Apply»." msgstr "Bitte klicken Sie auf «Speichern & Anwenden»." -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:118 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:120 msgid "Please create a port forwarding rule inside your Main Internet Router (IP: %s) using this OpenVPN Router (IP: %s) as receiver." msgstr "Bitte erstellen Sie in Ihrem Haupt-Internet-Router (IP: %s) eine Portweiterleitungsregel, die diesen OpenVPN-Router (IP: %s) als Empfänger nutzt." -#: root/www/luci-static/resources/view/vpn/openvpn.js:144 +#: root/www/luci-static/resources/view/vpn/openvpn.js:152 msgid "Please wait while secure cryptographic, router-unique default assets are being generated." msgstr "Bitte warten Sie, während sichere, kryptografische und router-eindeutige Standard-Ressourcen generiert werden." -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:56 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:58 msgid "Port Forwarding" msgstr "Portweiterleitung" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:111 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:113 msgid "Port Open Success" msgstr "Port erfolgreich geöffnet" -#: root/www/luci-static/resources/view/vpn/openvpn.js:150 +#: root/www/luci-static/resources/view/vpn/openvpn.js:159 msgid "Private Client Key" msgstr "Privater Client-Schlüssel" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:77 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:79 msgid "Private Key" msgstr "Privater Schlüssel" -#: root/www/luci-static/resources/view/vpn/openvpn.js:148 +#: root/www/luci-static/resources/view/vpn/openvpn.js:157 msgid "Private Server Key" msgstr "Privater Server-Schlüssel" -#: root/www/luci-static/resources/view/vpn/openvpn.js:156 +#: root/www/luci-static/resources/view/vpn/openvpn.js:165 msgid "Proceeding with invalid cryptographic keys will cause total connection failure and may lead to service instability or infinite daemon crash loops." msgstr "Das Fortfahren mit ungültigen kryptografischen Schlüsseln führt zum totalen Verbindungsausfall und kann zu Dienstinstabilität oder unendlichen Daemon-Absturzkreisläufen führen." -#: root/www/luci-static/resources/view/vpn/openvpn.js:75 +#: root/www/luci-static/resources/view/vpn/openvpn.js:79 msgid "Processing..." msgstr "Verarbeitung läuft..." -#: root/www/luci-static/resources/view/vpn/openvpn.js:159 +#: root/www/luci-static/resources/view/vpn/openvpn.js:191 +msgid "Profile Generation Error" +msgstr "Fehler bei der Profilerstellung" + +#: root/www/luci-static/resources/view/vpn/openvpn.js:168 msgid "Profile link active (Native uqr framework (QR-Code) not loaded)." msgstr "Profil-Link aktiv (Natives uqr-Framework (QR-Code) nicht geladen)." -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:78 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:80 msgid "Progress & Output" msgstr "Fortschritt & Ausgabe" -#: root/www/luci-static/resources/view/vpn/openvpn.js:132 +#: root/www/luci-static/resources/view/vpn/openvpn.js:141 msgid "Protected since" msgstr "Geschützt seit" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:57 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:59 msgid "Protocol" msgstr "Protokoll" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:59 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:61 msgid "Public IP Address or Domain" msgstr "Öffentliche IP-Adresse oder Domain" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:60 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:62 msgid "Public Static IP" msgstr "Öffentliche statische IP" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:79 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:81 msgid "Pure ECC" msgstr "Reines ECC" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:80 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:82 msgid "Pure RSA" msgstr "Reines RSA" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:81 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:83 msgid "Ready for key generation." msgstr "Bereit für die Schlüsselgenerierung." -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:120 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:122 msgid "Ready for openvpn connections." msgstr "Bereit für OpenVPN-Verbindungen." -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:85 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:87 msgid "Recommended: RSA-2048 + ECC-Prime256v1. RSA ensures CA compatibility, while ECC accelerates the data tunnel." msgstr "Empfohlen: RSA-2048 + ECC-Prime256v1. RSA stellt die CA-Kompatibilität sicher, während ECC den Datentunnel beschleunigt." -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:150 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:152 msgid "Reliable free provider. Never expires, perfect for IPv6 and Dual-Stack." msgstr "Zuverlässiger kostenloser Anbieter. Läuft nie ab, perfekt für IPv6 und Dual-Stack." @@ -960,24 +997,28 @@ msgid "Remote IP / Port" msgstr "Entfernte IP / Port" #: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:45 -#: root/www/luci-static/resources/view/vpn/openvpn.js:55 +#: root/www/luci-static/resources/view/vpn/openvpn.js:57 msgid "Remote LAN Subnet" msgstr "Entferntes LAN-Subnetz" #: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:46 -#: root/www/luci-static/resources/view/vpn/openvpn.js:56 +#: root/www/luci-static/resources/view/vpn/openvpn.js:58 msgid "Remote Name" msgstr "Entfernter Name" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:61 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:63 msgid "Remote Office Name" msgstr "Name des Remote-Büros" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:62 +#: root/www/luci-static/resources/view/vpn/openvpn.js:110 +msgid "Remote Profile" +msgstr "Profil der Gegenstelle" + +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:64 msgid "Resolving Target..." msgstr "Löse Zieladresse auf..." -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:63 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:65 msgid "Reveal/hide password" msgstr "Passwort anzeigen/ausblenden" @@ -985,62 +1026,62 @@ msgstr "Passwort anzeigen/ausblenden" msgid "Running" msgstr "Aktiv (Läuft)" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:41 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:42 msgid "Save & Apply" msgstr "Speichern & Anwenden" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:42 -#: root/www/luci-static/resources/view/vpn/openvpn.js:76 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:43 +#: root/www/luci-static/resources/view/vpn/openvpn.js:80 msgid "Save Config" msgstr "Konfiguration speichern" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:44 -#: root/www/luci-static/resources/view/vpn/openvpn.js:78 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:45 +#: root/www/luci-static/resources/view/vpn/openvpn.js:82 msgid "Saved" msgstr "Gespeichert" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:43 -#: root/www/luci-static/resources/view/vpn/openvpn.js:77 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:44 +#: root/www/luci-static/resources/view/vpn/openvpn.js:81 msgid "Saving..." msgstr "Speichere..." -#: root/www/luci-static/resources/view/vpn/openvpn.js:108 +#: root/www/luci-static/resources/view/vpn/openvpn.js:116 msgid "Scan QR Code with Mobile Phone Camera:" msgstr "Scannen Sie den QR-Code mit Ihrer Smartphone-Kamera:" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:83 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:85 msgid "Secret CA Key" msgstr "Privater CA-Schlüssel" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:67 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:69 msgid "Secure Full Tunnel (Route all traffic over VPN)" msgstr "Sicherer Volltunnel (Gesamten Datenverkehr über das VPN leiten)" -#: root/www/luci-static/resources/view/vpn/openvpn.js:109 +#: root/www/luci-static/resources/view/vpn/openvpn.js:117 msgid "Secure Temporary Profile URL Field:" msgstr "Sicheres Feld für temporäre Profil-URL:" -#: root/www/luci-static/resources/view/vpn/openvpn.js:160 +#: root/www/luci-static/resources/view/vpn/openvpn.js:169 msgid "Security Notice:" msgstr "Sicherheitshinweis:" -#: root/www/luci-static/resources/view/vpn/openvpn.js:165 +#: root/www/luci-static/resources/view/vpn/openvpn.js:174 msgid "Security Notice: Outdated factory default samples detected!" msgstr "Sicherheitshinweis: Veraltete Werks-Standardvorlagen erkannt!" -#: root/www/luci-static/resources/view/vpn/openvpn.js:162 +#: root/www/luci-static/resources/view/vpn/openvpn.js:171 msgid "Security Warning!" msgstr "Sicherheitswarnung!" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:64 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:66 msgid "Select VPN Type" msgstr "VPN-Typ auswählen" -#: root/www/luci-static/resources/view/vpn/openvpn.js:2507 +#: root/www/luci-static/resources/view/vpn/openvpn.js:118 msgid "Select clients to remove:" msgstr "Zu entfernende Clients auswählen:" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:124 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:126 msgid "Select the (.ovpn) profile or (.crt) multi-key bundle. Settings will be configured or updated automatically." msgstr "Wählen Sie das (.ovpn)-Profil oder das (.crt) Multi-Schlüssel-Paket aus. Die Einstellungen werden automatisch konfiguriert oder aktualisiert." @@ -1049,15 +1090,15 @@ msgstr "Wählen Sie das (.ovpn)-Profil oder das (.crt) Multi-Schlüssel-Paket au msgid "Server" msgstr "Server" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:68 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:70 msgid "Server Address" msgstr "Serveradresse" -#: root/www/luci-static/resources/view/vpn/openvpn.js:147 +#: root/www/luci-static/resources/view/vpn/openvpn.js:156 msgid "Server Certificate" msgstr "Server-Zertifikat" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:125 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:127 msgid "Server for Main Office (Headquarters)" msgstr "Server für das Hauptbüro (Zentrale)" @@ -1065,19 +1106,19 @@ msgstr "Server für das Hauptbüro (Zentrale)" msgid "Session" msgstr "Sitzung" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:65 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:67 msgid "Setup" msgstr "Einrichtung" -#: root/www/luci-static/resources/view/vpn/openvpn.js:79 +#: root/www/luci-static/resources/view/vpn/openvpn.js:83 msgid "Show" msgstr "Anzeigen" -#: root/www/luci-static/resources/view/vpn/openvpn.js:131 +#: root/www/luci-static/resources/view/vpn/openvpn.js:140 msgid "Show flood guard drops" msgstr "Flood-Guard-Drops anzeigen" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:84 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:86 msgid "Signing client certificate using server CA..." msgstr "Signiere Client-Zertifikat mit der Server-CA..." @@ -1085,162 +1126,170 @@ msgstr "Signiere Client-Zertifikat mit der Server-CA..." msgid "Since" msgstr "Seit" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:126 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:128 msgid "Skip Check & Unlock Create Button" msgstr "Prüfung überspringen & Erstellen-Button freischalten" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:69 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:71 msgid "Split Tunnel (Access home LAN only, faster web browsing)" msgstr "Split-Tunnel (Nur Zugriff auf das Heim-LAN, schnelleres Websurfen)" -#: root/www/luci-static/resources/view/vpn/openvpn.js:49 +#: root/www/luci-static/resources/view/vpn/openvpn.js:51 msgid "Starting..." msgstr "Starte..." -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:99 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:101 msgid "Static IP check bypassed by administrator." msgstr "Überprüfung der statischen IP durch Administrator umgangen." -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:100 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:102 msgid "Static IP now checked by administrator." msgstr "Statische IP wird jetzt vom Administrator überprüft." -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:70 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:72 msgid "Static Route" msgstr "Statische Route" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:123 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:125 msgid "Static Routing Success" msgstr "Statisches Routing erfolgreich" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:122 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:124 msgid "Static Routing missing!" msgstr "Statisches Routing fehlt!" #: root/www/luci-static/resources/view/vpn/openvpn-status.js:44 -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:71 -#: root/www/luci-static/resources/view/vpn/openvpn.js:50 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:73 +#: root/www/luci-static/resources/view/vpn/openvpn.js:52 msgid "Status" msgstr "Status" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:74 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:76 msgid "Step 1/3: Generating Certificate Authority (CA)..." msgstr "Schritt 1/3: Generiere Zertifizierungsstelle (CA)..." -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:75 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:77 msgid "Step 2/3: Generating Private Server Key..." msgstr "Schritt 2/3: Generiere privaten Server-Schlüssel..." -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:76 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:78 msgid "Step 3/3: Generating & Signing Server Certificate..." msgstr "Schritt 3/3: Generiere & Signiere Server-Zertifikat..." -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:103 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:105 msgid "Success: The IP address is valid and fully resolvable!" msgstr "Erfolg: Die IP-Adresse ist gültig und vollständig auflösbar!" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:162 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:164 msgid "Super fast setup without registration. WARNING: Blocked in some networks because it is completely anonymous." msgstr "Super schnelle Einrichtung ohne Registrierung. WARNUNG: In einigen Netzwerken blockiert, da sie komplett anonym ist." -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:127 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:129 msgid "Switch to Dynamic DNS (DDNS)" msgstr "Zu Dynamischem DNS (DDNS) wechseln" -#: root/www/luci-static/resources/view/vpn/openvpn.js:111 +#: root/www/luci-static/resources/view/vpn/openvpn.js:120 msgid "System logs ..." msgstr "Systemprotokolle..." -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:129 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:131 msgid "TCP (Good for traveling - works behind firewalls)" msgstr "TCP (Gut für Reisen - funktioniert hinter Firewalls)" -#: root/www/luci-static/resources/view/vpn/openvpn.js:152 +#: root/www/luci-static/resources/view/vpn/openvpn.js:161 msgid "TLS Crypt v2 Client Key" msgstr "TLS-Crypt-v2-Client-Schlüssel" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:67 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:69 msgid "TLS Crypt v2 Client Key (Anti-DoS / Port-Scan Protection)" msgstr "TLS-Crypt-v2-Client-Schlüssel (Anti-DoS- / Portscan-Schutz)" -#: root/www/luci-static/resources/view/vpn/openvpn.js:151 +#: root/www/luci-static/resources/view/vpn/openvpn.js:160 msgid "TLS Crypt v2 Server Master Key" msgstr "TLS-Crypt-v2-Server-Hauptschlüssel" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:66 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:68 msgid "TLS Crypt v2 Server Master Key (Anti-DoS / Port-Scan Protection)" msgstr "TLS-Crypt-v2-Server-Hauptschlüssel (Anti-DoS- / Portscan-Schutz)" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:88 +#: root/www/luci-static/resources/view/vpn/openvpn.js:193 +msgid "TLS crypt v2 client key generation failed" +msgstr "Schlüsselgenerierung für TLS-Crypt-v2-Client fehlgeschlagen" + +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:111 +msgid "TLS crypt v2 client key generation failed." +msgstr "Schlüsselgenerierung für TLS-Crypt-v2-Client fehlgeschlagen." + +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:90 msgid "TLS-Crypt key saved successfully." msgstr "TLS-Crypt-Schlüssel erfolgreich gespeichert." -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:121 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:123 msgid "The VPN routing via Main Internet Router (IP: %s) to remote subnet(s) (Net: %s)" msgstr "Das VPN-Routing über den Haupt-Internet-Router (IP: %s) zu entfernten Subnetz(en) (Netz: %s)" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:86 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:88 msgid "The main TLS security key on the server. It encrypts the connection and creates the keys for all clients." msgstr "Der Haupt-TLS-Sicherheitsschlüssel auf dem Server. Er verschlüsselt die Verbindung und erstellt die Schlüssel für alle Clients." -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:128 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:130 msgid "The port forward" msgstr "Die Portweiterleitung" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:117 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:119 msgid "The port used on this OpenWrt router." msgstr "Der auf diesem OpenWrt-Router verwendete Port." -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:116 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:118 msgid "The port used on your main internet router." msgstr "Der auf Ihrem Haupt-Internet-Router verwendete Port." -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:119 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:121 msgid "The public IP address or your own external domain name." msgstr "Die öffentliche IP-Adresse oder Ihr eigener externe Domainname." -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:106 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:109 msgid "The saved key data contains structural format corruption!" msgstr "Die gespeicherten Schlüsseldaten enthalten strukturelle Formatbeschädigungen!" -#: root/www/luci-static/resources/view/vpn/openvpn.js:178 +#: root/www/luci-static/resources/view/vpn/openvpn.js:188 msgid "The uploaded file is empty." msgstr "Die hochgeladene Datei ist leer." -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:89 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:91 msgid "This OpenVPN Router is not the Main Internet Router." msgstr "Dieser OpenVPN-Router ist nicht der Haupt-Internet-Router." -#: root/www/luci-static/resources/view/vpn/openvpn.js:107 +#: root/www/luci-static/resources/view/vpn/openvpn.js:115 msgid "This automated initialization process can take a few minutes on your device..." msgstr "Dieser automatische Initialisierungsprozess kann auf Ihrem Gerät einige Minuten dauern..." -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:110 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:112 msgid "This file contains only VPN client keys." msgstr "Diese Datei enthält nur VPN-Client-Schlüssel." -#: root/www/luci-static/resources/view/vpn/openvpn.js:92 +#: root/www/luci-static/resources/view/vpn/openvpn.js:98 msgid "This is the address your VPN clients will use to connect from the internet. Scan the QR code with your phone camera or use the link below to download your connection profile." msgstr "Dies ist die Adresse, die Ihre VPN-Clients verwenden, um sich aus dem Internet zu verbinden. Scannen Sie den QR-Code mit Ihrer Smartphone-Kamera oder verwenden Sie den unten stehenden Link, um Ihr Verbindungsprofil herunterzuladen." -#: root/www/luci-static/resources/view/vpn/openvpn.js:163 +#: root/www/luci-static/resources/view/vpn/openvpn.js:172 msgid "This profile does not use TLS-Crypt v2. It is highly recommended to use TLS-Crypt v2 for maximum security, better performance, and protection against unauthorized scanning." msgstr "Dieses Profil verwendet kein TLS-Crypt v2. Es wird dringend empfohlen, TLS-Crypt v2 zu verwenden, um maximale Sicherheit, bessere Performance und Schutz vor unbefugten Scans zu gewährleisten." -#: root/www/luci-static/resources/view/vpn/openvpn.js:110 +#: root/www/luci-static/resources/view/vpn/openvpn.js:119 msgid "This server handles network rules for multiple remote offices. Please choose an existing office name or write a name to make a new connection profile." msgstr "Dieser Server verwaltet Netzwerkregeln für mehrere entfernte Büros. Bitte wählen Sie einen bestehenden Büronamen oder tragen Sie einen neuen Namen ein, um ein neues Verbindungsprofil zu erstellen." -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:58 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:59 msgid "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)" msgstr "Um sicher zu sein, benötigt eine neue Instanz eigene Schlüsseldateien. Möchten Sie diese jetzt erstellen? (Empfohlen - dauert meist nur wenige Augenblicke)" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:72 -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:170 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:74 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:172 msgid "Token / Password" msgstr "Token / Passwort" -#: root/www/luci-static/resources/view/vpn/openvpn.js:133 +#: root/www/luci-static/resources/view/vpn/openvpn.js:142 msgid "Traffic Rules" msgstr "Filterregeln" @@ -1253,43 +1302,47 @@ msgstr "Übertragung" msgid "Type" msgstr "Typ" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:130 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:132 msgid "UDP (High Performance - maximum throughput)" msgstr "UDP (Hohe Leistung - maximaler Durchsatz)" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:93 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:95 msgid "Unknown Asset" msgstr "Unbekannte Ressource" +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:96 +msgid "Unknown Structure" +msgstr "Unbekannte Struktur" + #: root/www/luci-static/resources/view/vpn/openvpn-status.js:52 msgid "UpTime" msgstr "Laufzeit (Uptime)" -#: root/www/luci-static/resources/view/vpn/openvpn.js:80 +#: root/www/luci-static/resources/view/vpn/openvpn.js:84 msgid "Upload" msgstr "Hochladen" -#: root/www/luci-static/resources/view/vpn/openvpn.js:112 +#: root/www/luci-static/resources/view/vpn/openvpn.js:121 msgid "Uploaded file is invalid or corrupt!" msgstr "Hochgeladene Datei ist ungültig oder beschädigt!" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:131 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:133 msgid "Use Dynamic DNS (DDNS) Service" msgstr "Dynamischen DNS-Dienst (DDNS) verwenden" -#: root/www/luci-static/resources/view/vpn/openvpn.js:81 +#: root/www/luci-static/resources/view/vpn/openvpn.js:85 msgid "Use Fallback" msgstr "Fallback verwenden" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:132 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:134 msgid "Use Public IP Address / Domain" msgstr "Öffentliche IP-Adresse / Domain verwenden" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:73 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:75 msgid "Username" msgstr "Benutzername" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:155 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:157 msgid "Username (Optional)" msgstr "Benutzername (Optional)" @@ -1297,15 +1350,15 @@ msgstr "Benutzername (Optional)" msgid "VPN" msgstr "VPN" -#: root/www/luci-static/resources/view/vpn/openvpn.js:113 +#: root/www/luci-static/resources/view/vpn/openvpn.js:122 msgid "VPN Client Connection Domain or Address:" msgstr "VPN-Client Verbindungs-Domain oder -Adresse:" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:96 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:99 msgid "VPN Key Type:" msgstr "VPN-Schlüsseltyp:" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:74 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:76 msgid "VPN Name" msgstr "VPN-Name" @@ -1313,31 +1366,31 @@ msgstr "VPN-Name" msgid "Valid Until" msgstr "Gültig bis" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:189 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:191 msgid "Validation Error: The VPN Name must contain only letters, numbers, hyphens or underscores." msgstr "Validierungsfehler: Der VPN-Name darf nur Buchstaben, Zahlen, Bindestriche oder Unterstriche enthalten." -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:101 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:103 msgid "Validation bypassed after network failure." msgstr "Validierung nach Netzwerkausfall umgangen." -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:102 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:104 msgid "Validation bypassed by administrator." msgstr "Validierung durch Administrator umgangen." -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:95 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:98 msgid "Validity (Days)" msgstr "Gültigkeit (Tage)" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:156 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:158 msgid "Very popular provider. WARNING: Free accounts require manual activation/renewal every 30 days via email." msgstr "Sehr beliebter Anbieter. WARNUNG: Kostenlose Konten erfordern alle 30 Tage eine manuelle Aktivierung/Verlängerung per E-Mail." -#: root/www/luci-static/resources/view/vpn/openvpn.js:134 +#: root/www/luci-static/resources/view/vpn/openvpn.js:143 msgid "WAN ports" msgstr "WAN-Ports" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:99 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:102 msgid "WARNING: Clicking save will instantly break all active VPN tunnels. You must generate and share new client profiles (.ovpn) to restore the connections." msgstr "WARNUNG: Das Klicken auf Speichern trennt sofort alle aktiven VPN-Tunnel. Sie müssen neue Client-Profile (.ovpn) generieren und verteilen, um die Verbindungen wiederherzustellen." @@ -1345,32 +1398,32 @@ msgstr "WARNUNG: Das Klicken auf Speichern trennt sofort alle aktiven VPN-Tunnel msgid "WORKING" msgstr "AKTIV" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:178 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:180 msgid "Warning: If you upload wrong keys, your connection will stop!" msgstr "Warnung: Wenn Sie falsche Schlüssel hochladen, wird Ihre Verbindung unterbrochen!" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:174 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:176 msgid "Warning: The IP address cannot be resolved. Your router might be offline." msgstr "Warnung: Die IP-Adresse kann nicht aufgelöst werden. Ihr Router ist möglicherweise offline." -#: root/www/luci-static/resources/view/vpn/openvpn.js:155 +#: root/www/luci-static/resources/view/vpn/openvpn.js:164 msgid "Warning: The uploaded file structure does not match the expected key type." msgstr "Warnung: Die Struktur der hochgeladenen Datei entspricht nicht dem erwarteten Schlüsseltyp." -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:175 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:177 msgid "Warning: You did not select «Public Static IP». If your public IP address changes, your VPN tunnel will crash." msgstr "Warnung: Sie haben «Öffentliche statische IP» nicht ausgewählt. Wenn sich Ihre öffentliche IP-Adresse ändert, bricht Ihr VPN-Tunnel ab." -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:177 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:179 msgid "Warning: You have un-saved text in the input fields. Please click the blue «Add Office» button to save it, or clear the fields to continue." msgstr "Warnung: Sie haben ungespeicherten Text in den Eingabefeldern. Bitte klicken Sie auf den blauen Button «Büro hinzufügen», um ihn zu speichern, oder leeren Sie die Felder, um fortzufahren." #: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:31 -#: root/www/luci-static/resources/view/vpn/openvpn.js:57 +#: root/www/luci-static/resources/view/vpn/openvpn.js:59 msgid "Wizard" msgstr "Assistent" -#: root/www/luci-static/resources/view/vpn/openvpn.js:179 +#: root/www/luci-static/resources/view/vpn/openvpn.js:189 msgid "Wrong key type! Please upload the correct cryptographic file." msgstr "Falscher Schlüsseltyp! Bitte laden Sie die korrekte kryptografische Datei hoch." @@ -1379,101 +1432,99 @@ msgid "Years" msgstr "Jahre" #: root/www/luci-static/resources/view/vpn/openvpn-status.js:37 -#: root/www/luci-static/resources/view/vpn/openvpn.js:58 +#: root/www/luci-static/resources/view/vpn/openvpn.js:60 msgid "Yes" msgstr "Ja" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:57 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:58 msgid "Yes (Generate Unique Keys)" msgstr "Ja (Eindeutige Schlüssel generieren)" -#: root/www/luci-static/resources/view/vpn/openvpn.js:117 +#: root/www/luci-static/resources/view/vpn/openvpn.js:126 msgid "are opened automatically for secure OpenVPN connections." msgstr "werden automatisch für sichere OpenVPN-Verbindungen geöffnet." -#: root/www/luci-static/resources/view/vpn/openvpn.js:120 +#: root/www/luci-static/resources/view/vpn/openvpn.js:129 msgid "devices is created automatically." msgstr "Geräte wird automatisch erstellt." -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:148 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:150 msgid "dynv6 (dynv6.com)" msgstr "dynv6 (dynv6.com)" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:58 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:60 msgid "e.g." msgstr "e.g." -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:70 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:72 msgid "e.g. -sha256 ; -noout ; -sha256" msgstr "z. B. -sha256 ; -noout ; -sha256" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:69 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:71 msgid "e.g. -text (shows hex output)" msgstr "z.B. -text (zeigt Hex-Ausgabe)" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:71 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:73 msgid "e.g. -traditional ; ; -sha256" msgstr "z.B. -traditional ; ; -sha256" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:113 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:115 msgid "e.g. Mobile-Server, Main-Office" msgstr "z.B. Mobile-Server, Haupt-Buero" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:68 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:70 msgid "e.g. Remote Office 1" msgstr "z.B. Externes Büro 1" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:171 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:173 msgid "e.g. https://custom-provider.com?domain=...&key=..." msgstr "z.B. https://eigener-anbieter.com?domain=...&key=..." -#: root/www/luci-static/resources/view/vpn/openvpn.js:105 +#: root/www/luci-static/resources/view/vpn/openvpn.js:113 msgid "e.g. my-smartphone" msgstr "e.g. mein-smartphone" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:112 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:114 msgid "e.g. mydomain.com or your public IP" msgstr "z.B. meinewebsite.de oder deine öffentliche IP" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:168 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:170 msgid "e.g. myname-custom-provider.com" msgstr "z.B. meinname-eigener-anbieter.com" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:158 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:160 msgid "e.g. myname.ddns.net" msgstr "z.B. meinname.ddns.net" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:137 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:139 msgid "e.g. myname.duckdns.org" msgstr "z.B. meinname.duckdns.org" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:152 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:154 msgid "e.g. myname.dynu.net" msgstr "z.B. meinname.dynu.net" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:147 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:149 msgid "e.g. myname.dynv6.net" msgstr "z.B. meinname.dynv6.net" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:164 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:166 msgid "e.g. myname.freemyip.com" msgstr "z.B. meinname.freemyip.com" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:142 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:144 msgid "e.g. myname.ipv64.net" msgstr "z.B. meinname.ipv64.net" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:104 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:106 msgid "is already" msgstr "ist bereits" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:105 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:107 msgid "is currently" msgstr "ist aktuell" -#: root/www/luci-static/resources/view/vpn/openvpn.js:127 +#: root/www/luci-static/resources/view/vpn/openvpn.js:136 msgid "packets" msgstr "Pakete" -#~ msgid "Smart Firewall" -#~ msgstr "Intelligente Firewall" diff --git a/luci-app-openvpn-plus/po/templates/luci-app-openvpn-plus.pot b/luci-app-openvpn-plus/po/templates/luci-app-openvpn-plus.pot index e93089f01..5d03ac3c1 100755 --- a/luci-app-openvpn-plus/po/templates/luci-app-openvpn-plus.pot +++ b/luci-app-openvpn-plus/po/templates/luci-app-openvpn-plus.pot @@ -1,23 +1,23 @@ msgid "" msgstr "Content-Type: text/plain; charset=UTF-8" -#: root/www/luci-static/resources/view/vpn/openvpn.js:138 +#: root/www/luci-static/resources/view/vpn/openvpn.js:147 msgid "A client connection needs the server keys and config. Select an .ovpn profile to import them automatically, or click Cancel to use the default settings." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:128 +#: root/www/luci-static/resources/view/vpn/openvpn.js:137 msgid "A secure firewall zone for all" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:87 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:89 msgid "A unique TLS security key for one client. It encrypts and protects all data sent to the server." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:116 +#: root/www/luci-static/resources/view/vpn/openvpn.js:125 msgid "ACTIVE" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:139 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:141 msgid "API Token" msgstr "" @@ -34,23 +34,23 @@ msgstr "" msgid "Active PIDs" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:2469 +#: root/www/luci-static/resources/view/vpn/openvpn.js:35 msgid "Add" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:61 +#: root/www/luci-static/resources/view/vpn/openvpn.js:63 msgid "Add Client Instance" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:77 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:79 msgid "Add Office" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:62 +#: root/www/luci-static/resources/view/vpn/openvpn.js:64 msgid "Add Server Instance" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:87 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:89 msgid "Add your other offices below. Please enter the internal LAN network IP (e.g., 192.168.2.0) of each office, NOT their public WAN IP addresses. The server will connect all local networks automatically." msgstr "" @@ -62,19 +62,19 @@ msgstr "" msgid "Aggregated TX" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:88 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:90 msgid "Another Router detected!" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:89 +#: root/www/luci-static/resources/view/vpn/openvpn.js:95 msgid "Are you sure you want to delete" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:118 +#: root/www/luci-static/resources/view/vpn/openvpn.js:127 msgid "Automated Zone Setup:" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:130 +#: root/www/luci-static/resources/view/vpn/openvpn.js:139 msgid "Automatic port scanning is a constant threat on the internet. The OpenVPN flood guard filter detects, bans, and drops these malicious requests. This hides your system, prevents spying, saves CPU power, and guarantees maximum VPN performance." msgstr "" @@ -82,65 +82,74 @@ msgstr "" msgid "BLOCKED" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:78 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:80 msgid "Back" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:83 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:36 +#: root/www/luci-static/resources/view/vpn/openvpn.js:65 +msgid "Backup" +msgstr "" + +#: root/www/luci-static/resources/view/vpn/openvpn.js:87 msgid "Backup & Use Wizard (recommended)" msgstr "" +#: root/www/luci-static/resources/view/vpn/openvpn.js:111 +msgid "Backup Server Profile" +msgstr "" + #: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:47 msgid "CLOSED" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:37 -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:79 -#: root/www/luci-static/resources/view/vpn/openvpn.js:63 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:38 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:81 +#: root/www/luci-static/resources/view/vpn/openvpn.js:66 msgid "Cancel" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:47 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:48 msgid "Certificate" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:137 +#: root/www/luci-static/resources/view/vpn/openvpn.js:146 msgid "Certification Authority" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:64 +#: root/www/luci-static/resources/view/vpn/openvpn.js:67 msgid "Change" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:80 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:82 msgid "Check Address Status" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:119 +#: root/www/luci-static/resources/view/vpn/openvpn.js:128 msgid "Check Traffic Rules:" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:94 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:96 msgid "Checking connection status in the background..." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:82 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:84 msgid "Choose File..." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:90 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:92 msgid "Choose how your internet traffic is sent through the VPN. You can use any free OpenVPN app on your phone or laptop to import this profile." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:145 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:147 msgid "Classic free provider with exceptional IPv6 and dual-stack support." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:43 +#: root/www/luci-static/resources/view/vpn/openvpn.js:45 msgid "Clear Log" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:35 +#: root/www/luci-static/resources/view/vpn/openvpn.js:36 msgid "Clearing..." msgstr "" @@ -149,40 +158,40 @@ msgstr "" msgid "Client" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:149 +#: root/www/luci-static/resources/view/vpn/openvpn.js:158 msgid "Client Certificate" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:48 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:49 msgid "Client Certificate + Private key (issued by CA) + TLS Crypt v2 Client key" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:90 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:92 msgid "Client Key" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:91 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:93 msgid "Client for Branch Office (Branch)" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:50 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:51 msgid "Client keys exported successfully." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:49 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:50 msgid "Client keys generated successfully!" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:36 -#: root/www/luci-static/resources/view/vpn/openvpn.js:66 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:37 +#: root/www/luci-static/resources/view/vpn/openvpn.js:69 msgid "Close" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:52 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:53 msgid "Common Name (CN)" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:88 +#: root/www/luci-static/resources/view/vpn/openvpn.js:94 msgid "Configuration changed! Applying will temporarily restart the OpenVPN instance." msgstr "" @@ -194,16 +203,20 @@ msgstr "" msgid "Connection Type" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:81 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:83 msgid "Create VPN" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:92 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:94 msgid "Create a secure VPN connection in just a few clicks." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:139 -msgid "Create keys for a new office..." +#: root/www/luci-static/resources/view/vpn/openvpn.js:149 +msgid "Create keys for a new mobile client..." +msgstr "" + +#: root/www/luci-static/resources/view/vpn/openvpn.js:148 +msgid "Create keys for a new remote office..." msgstr "" #: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:27 @@ -211,23 +224,23 @@ msgid "Creating VPN..." msgstr "" #: root/www/luci-static/resources/view/vpn/openvpn-status.js:27 -#: root/www/luci-static/resources/view/vpn/openvpn.js:36 +#: root/www/luci-static/resources/view/vpn/openvpn.js:37 msgid "Creating..." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:82 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:84 msgid "Crypto operation running. Please wait..." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:158 +#: root/www/luci-static/resources/view/vpn/openvpn.js:167 msgid "Cryptographic Key Warning" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:169 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:171 msgid "Custom / Manual Update URL" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:53 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:54 msgid "Custom CLI Options" msgstr "" @@ -235,7 +248,7 @@ msgstr "" msgid "Custom Update URL" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:160 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:162 msgid "DDNS Key" msgstr "" @@ -243,7 +256,7 @@ msgstr "" msgid "DDNS Provider" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:161 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:163 msgid "DDNS Username" msgstr "" @@ -251,32 +264,32 @@ msgstr "" msgid "Default" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:83 -#: root/www/luci-static/resources/view/vpn/openvpn.js:2470 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:85 +#: root/www/luci-static/resources/view/vpn/openvpn.js:40 msgid "Delete" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:2459 -msgid "Delete Client Profile..." -msgstr "" - -#: root/www/luci-static/resources/view/vpn/openvpn.js:67 +#: root/www/luci-static/resources/view/vpn/openvpn.js:70 msgid "Delete Instance" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:95 +#: root/www/luci-static/resources/view/vpn/openvpn.js:112 +msgid "Delete Remote Profile..." +msgstr "" + +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:97 msgid "Delete this office" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:68 +#: root/www/luci-static/resources/view/vpn/openvpn.js:71 msgid "Deleted - Save & Apply" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:102 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:105 msgid "Deployment Recommendation:" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:101 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:104 msgid "Deprecated TLS-Crypt V1 key (Not supported)" msgstr "" @@ -284,33 +297,33 @@ msgstr "" msgid "Destination" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:140 +#: root/www/luci-static/resources/view/vpn/openvpn.js:150 msgid "Diffie - Hellman Parameters" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:64 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:66 msgid "Diffie-Hellman Parameters (Perfect Forward Secrecy)" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:54 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:55 msgid "Diffie-Hellman parameters enforce numeric prime bit boundaries exclusively." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:37 +#: root/www/luci-static/resources/view/vpn/openvpn.js:38 msgid "Disable" msgstr "" #: root/www/luci-static/resources/view/status/include/35_openvpn.js:19 #: root/www/luci-static/resources/view/vpn/openvpn-status.js:31 -#: root/www/luci-static/resources/view/vpn/openvpn.js:38 +#: root/www/luci-static/resources/view/vpn/openvpn.js:39 msgid "Disabled" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:157 +#: root/www/luci-static/resources/view/vpn/openvpn.js:166 msgid "Do you want to proceed and save this file anyway?" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:164 +#: root/www/luci-static/resources/view/vpn/openvpn.js:173 msgid "Do you want to proceed with the import anyway?" msgstr "" @@ -318,53 +331,53 @@ msgstr "" msgid "Domain Name" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:144 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:146 msgid "Domain Update URL / Key" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:166 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:168 msgid "Domain Update URL / token" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:38 -#: root/www/luci-static/resources/view/vpn/openvpn.js:69 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:39 +#: root/www/luci-static/resources/view/vpn/openvpn.js:72 msgid "Download" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:70 +#: root/www/luci-static/resources/view/vpn/openvpn.js:73 msgid "Download (.ovpn)" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:90 +#: root/www/luci-static/resources/view/vpn/openvpn.js:96 msgid "Download and QR code link are only available while this window is open." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:138 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:140 msgid "DuckDNS (duckdns.org)" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:153 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:155 msgid "Dynu Systems (dynu.com)" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:107 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:110 msgid "ERROR: Safety polling threshold exceeded boundaries. Process deadlocked." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:91 +#: root/www/luci-static/resources/view/vpn/openvpn.js:97 msgid "Edit Config file" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:40 +#: root/www/luci-static/resources/view/vpn/openvpn.js:42 msgid "Enable" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:39 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:40 msgid "Enable Save" msgstr "" #: root/www/luci-static/resources/view/vpn/openvpn-status.js:33 -#: root/www/luci-static/resources/view/vpn/openvpn.js:71 +#: root/www/luci-static/resources/view/vpn/openvpn.js:74 msgid "Enabled" msgstr "" @@ -372,64 +385,64 @@ msgstr "" msgid "Encryption" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:167 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:169 msgid "Enter your own manual HTTP update URL structure." msgstr "" #: root/www/luci-static/resources/view/status/include/35_openvpn.js:21 #: root/www/luci-static/resources/view/vpn/openvpn-status.js:32 -#: root/www/luci-static/resources/view/vpn/openvpn.js:41 +#: root/www/luci-static/resources/view/vpn/openvpn.js:43 msgid "Error" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:187 -#: root/www/luci-static/resources/view/vpn/openvpn.js:176 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:189 +#: root/www/luci-static/resources/view/vpn/openvpn.js:186 msgid "Error: An office with this name is already in the list." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:170 +#: root/www/luci-static/resources/view/vpn/openvpn.js:179 msgid "Error: Configuration or cryptographic keys are missing on disk." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:190 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:192 msgid "Error: File is corrupted or missing vital fields." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:2665 +#: root/www/luci-static/resources/view/vpn/openvpn.js:192 msgid "Error: Network status check failed." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:186 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:188 msgid "Error: Network status check failed. Please try again." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:182 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:184 msgid "Error: Please add at least one office to the list before you continue." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:185 -#: root/www/luci-static/resources/view/vpn/openvpn.js:175 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:187 +#: root/www/luci-static/resources/view/vpn/openvpn.js:185 msgid "Error: This IP address is already used by another office. Every office needs a different IP address!" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:188 -#: root/www/luci-static/resources/view/vpn/openvpn.js:177 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:190 +#: root/www/luci-static/resources/view/vpn/openvpn.js:187 msgid "Error: You cannot use this IP network! It is the local network of this router and will block all traffic." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:93 +#: root/www/luci-static/resources/view/vpn/openvpn.js:99 msgid "Export (.ovpn)" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:55 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:56 msgid "Export Keys" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:94 +#: root/www/luci-static/resources/view/vpn/openvpn.js:100 msgid "Export OpenVPN Connect Client Profile" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:39 +#: root/www/luci-static/resources/view/vpn/openvpn.js:41 msgid "Export Profile" msgstr "" @@ -437,47 +450,51 @@ msgstr "" msgid "External Port" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:169 +#: root/www/luci-static/resources/view/vpn/openvpn.js:178 msgid "Failed to build profile:" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:183 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:185 msgid "Failed to create new OpenVPN server:" msgstr "" +#: root/www/luci-static/resources/view/vpn/openvpn.js:190 +msgid "Failed to generate secure download profile. Please check system logs." +msgstr "" + #: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:27 msgid "File Location:" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:121 +#: root/www/luci-static/resources/view/vpn/openvpn.js:130 msgid "Filtered" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:96 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:98 msgid "Finding your public IP address..." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:122 +#: root/www/luci-static/resources/view/vpn/openvpn.js:131 msgid "Firewall" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:123 +#: root/www/luci-static/resources/view/vpn/openvpn.js:132 msgid "Firewall & Routing Information" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:129 +#: root/www/luci-static/resources/view/vpn/openvpn.js:138 msgid "Flood Guard" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:97 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:99 msgid "For Phones & Laptops (OpenVPN Connect App)" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:165 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:167 msgid "FreeMYIP (freemyip.com)" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:65 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:67 msgid "Full PKI Suite (CA + Server Certificate + Private Server Key)" msgstr "" @@ -485,31 +502,31 @@ msgstr "" msgid "Gateway" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:40 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:41 msgid "Generate" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:100 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:103 msgid "Generated keys are not saved! Close window and discard keys?" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:1054 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:61 msgid "Generating TLS crypt v2 client key..." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:56 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:57 msgid "Generating TLS-Crypt key..." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:59 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:60 msgid "Generating private client key..." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:72 +#: root/www/luci-static/resources/view/vpn/openvpn.js:75 msgid "Generating..." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:149 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:151 msgid "HTTP Token String" msgstr "" @@ -517,19 +534,19 @@ msgstr "" msgid "Help" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:99 +#: root/www/luci-static/resources/view/vpn/openvpn.js:105 msgid "Here you can manage multiple OpenVPN Server and Client instances dynamically." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:143 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:145 msgid "IPv64 (ipv64.net)" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:98 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:100 msgid "If your internet provides a static IP address." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:95 +#: root/www/luci-static/resources/view/vpn/openvpn.js:101 msgid "Import (.ovpn)" msgstr "" @@ -537,20 +554,20 @@ msgstr "" msgid "Import Failed:" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:96 -msgid "Import OpenVPN Connect Client Profile" +#: root/www/luci-static/resources/view/vpn/openvpn.js:102 +msgid "Import OpenVPN Client Profile" msgstr "" #: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:35 -#: root/www/luci-static/resources/view/vpn/openvpn.js:97 +#: root/www/luci-static/resources/view/vpn/openvpn.js:103 msgid "Import Profile" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:161 +#: root/www/luci-static/resources/view/vpn/openvpn.js:170 msgid "Import cancelled due to missing TLS-Crypt v2 protection." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:94 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:97 msgid "Important: Use a unique name for each office to configure the network routing correctly." msgstr "" @@ -562,7 +579,7 @@ msgstr "" msgid "Importing profile..." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:124 +#: root/www/luci-static/resources/view/vpn/openvpn.js:133 msgid "Inbound Access:" msgstr "" @@ -575,11 +592,11 @@ msgid "Instance" msgstr "" #: root/www/luci-static/resources/view/vpn/openvpn-status.js:35 -#: root/www/luci-static/resources/view/vpn/openvpn.js:42 +#: root/www/luci-static/resources/view/vpn/openvpn.js:44 msgid "Instance #" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:51 +#: root/www/luci-static/resources/view/vpn/openvpn.js:53 msgid "Instance Management" msgstr "" @@ -595,27 +612,27 @@ msgstr "" msgid "Internet Traffic" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:171 +#: root/www/luci-static/resources/view/vpn/openvpn.js:180 msgid "Invalid file. Important parts are missing (CA, Cert, or Private Key)." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:166 +#: root/www/luci-static/resources/view/vpn/openvpn.js:175 msgid "It is highly recommended not to keep or use the outdated standard default configurations. Instead, you should backup them and use the up-to-date setup Wizard to build new, secure connections." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:103 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:106 msgid "It is highly recommended to use the automated Setup Wizard to deploy a Mobile Server (OpenVPN Connect) or a Headquarters LAN-to-LAN Server. In just a few clicks, the wizard fully configures easy DDNS provider setup, multi-client remote networks, optimal firewall zones, and scan-ready QR codes." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:106 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:108 msgid "Keep IP Setup anyway" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:82 +#: root/www/luci-static/resources/view/vpn/openvpn.js:86 msgid "Keep old samples (to avoid)" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:60 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:62 msgid "Key Strength" msgstr "" @@ -623,35 +640,35 @@ msgstr "" msgid "Key Update Active" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:172 +#: root/www/luci-static/resources/view/vpn/openvpn.js:181 msgid "Key check failed. One security key is broken or invalid." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:145 +#: root/www/luci-static/resources/view/vpn/openvpn.js:154 msgid "Key file is empty or does not exist on disk yet." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:173 +#: root/www/luci-static/resources/view/vpn/openvpn.js:182 msgid "Key generation failed." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:143 +#: root/www/luci-static/resources/view/vpn/openvpn.js:151 msgid "Key generation in progress..." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:174 +#: root/www/luci-static/resources/view/vpn/openvpn.js:183 msgid "Key type mismatch" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:141 +#: root/www/luci-static/resources/view/vpn/openvpn.js:184 msgid "Key verification failed" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:142 +#: root/www/luci-static/resources/view/vpn/openvpn.js:76 msgid "KeyGen" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:107 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:109 msgid "Keys updated successfully!" msgstr "" @@ -659,15 +676,19 @@ msgstr "" msgid "LAN IP" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:98 +#: root/www/luci-static/resources/view/vpn/openvpn.js:104 msgid "LAN-to-LAN Router Profile Selection" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:52 +#: root/www/luci-static/resources/view/vpn/openvpn.js:54 msgid "LOG" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:61 +#: root/www/luci-static/resources/view/vpn/openvpn.js:153 +msgid "LOG: Certificate pipeline completed. Generating TLS crypt v2 client key..." +msgstr "" + +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:63 msgid "Loading key contents..." msgstr "" @@ -675,7 +696,7 @@ msgstr "" msgid "Local IP / Port" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:44 +#: root/www/luci-static/resources/view/vpn/openvpn.js:46 msgid "Log Cleared" msgstr "" @@ -687,63 +708,63 @@ msgstr "" msgid "Mask" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:62 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:64 msgid "Master CA" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:89 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:91 msgid "Master Server Secret Key" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:140 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:142 msgid "Modern free provider with perfect IPv6 and Dual-Stack support." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:72 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:74 msgid "N/A - openvpn --genkey accepts no extra flags" msgstr "" #: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:44 -#: root/www/luci-static/resources/view/vpn/openvpn.js:54 +#: root/www/luci-static/resources/view/vpn/openvpn.js:56 msgid "Netmask" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:125 +#: root/www/luci-static/resources/view/vpn/openvpn.js:134 msgid "Network" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:184 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:186 msgid "Network Error: DNS/IP query failed." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:93 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:95 msgid "Network verification failed / Timeout" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:84 -#: root/www/luci-static/resources/view/vpn/openvpn.js:73 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:86 +#: root/www/luci-static/resources/view/vpn/openvpn.js:77 msgid "Next" msgstr "" #: root/www/luci-static/resources/view/vpn/openvpn-status.js:38 -#: root/www/luci-static/resources/view/vpn/openvpn.js:45 +#: root/www/luci-static/resources/view/vpn/openvpn.js:47 msgid "No" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:51 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:52 msgid "No (Clone Default Keys)" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:102 +#: root/www/luci-static/resources/view/vpn/openvpn.js:108 msgid "No OpenVPN instances configured yet. Use the Wizard to create new OpenVPN instances." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:101 -#: root/www/luci-static/resources/view/vpn/openvpn.js:103 +#: root/www/luci-static/resources/view/vpn/openvpn.js:107 +#: root/www/luci-static/resources/view/vpn/openvpn.js:109 msgid "No active OpenVPN log entries found." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:46 +#: root/www/luci-static/resources/view/vpn/openvpn.js:48 msgid "No changes detected" msgstr "" @@ -751,7 +772,7 @@ msgstr "" msgid "No clients connected" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:109 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:111 msgid "No file chosen" msgstr "" @@ -759,7 +780,7 @@ msgstr "" msgid "No instances configured" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:108 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:110 msgid "No offices added yet. Use the fields below to add the first office." msgstr "" @@ -767,20 +788,20 @@ msgstr "" msgid "No server connected" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:159 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:161 msgid "No-IP (noip.com)" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:63 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:65 msgid "Not applicable" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:135 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:137 msgid "Number 1 for Open-Source and Home-Assistant setups." msgstr "" #: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:50 -#: root/www/luci-static/resources/view/vpn/openvpn.js:74 +#: root/www/luci-static/resources/view/vpn/openvpn.js:78 msgid "OK" msgstr "" @@ -788,170 +809,186 @@ msgstr "" msgid "OPEN" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:104 -msgid "Office Profile:" -msgstr "" - #: root/www/luci-static/resources/view/status/include/35_openvpn.js:17 -#: root/www/luci-static/resources/view/vpn/openvpn.js:47 +#: root/www/luci-static/resources/view/vpn/openvpn.js:49 msgid "OpenVPN" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:86 +#: root/www/luci-static/resources/view/vpn/openvpn.js:91 +msgid "OpenVPN Backup Export" +msgstr "" + +#: root/www/luci-static/resources/view/vpn/openvpn.js:92 +msgid "OpenVPN Backup Import" +msgstr "" + +#: root/www/luci-static/resources/view/vpn/openvpn.js:90 msgid "OpenVPN Client Export" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:100 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:55 +msgid "OpenVPN Client Profile detected" +msgstr "" + +#: root/www/luci-static/resources/view/vpn/openvpn.js:106 msgid "OpenVPN Connect Mobile Export" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:87 +#: root/www/luci-static/resources/view/vpn/openvpn.js:93 msgid "OpenVPN Default Configuration Detected" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:91 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:93 msgid "OpenVPN Instance Key Generator" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:146 +#: root/www/luci-static/resources/view/vpn/openvpn.js:155 msgid "OpenVPN Keys" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:176 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:178 msgid "OpenVPN Keys Detected:" msgstr "" #: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:54 -msgid "OpenVPN Profile Valid:" +msgid "OpenVPN Profile Valid" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:53 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:56 +msgid "OpenVPN Server Profile detected" +msgstr "" + +#: root/www/luci-static/resources/view/vpn/openvpn.js:55 msgid "OpenVPN Server/Client" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:66 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:68 msgid "OpenVPN Setup Wizard" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:126 +#: root/www/luci-static/resources/view/vpn/openvpn.js:135 msgid "OpenVPN tunnel interfaces (tun0, tun1, tun3, etc.)" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:55 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:57 msgid "Outside Port" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:73 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:75 msgid "PKI Suite components saved successfully." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:154 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:156 msgid "Password as plain text (Auto-SHA256)" msgstr "" #: root/www/luci-static/resources/view/status/include/35_openvpn.js:20 #: root/www/luci-static/resources/view/vpn/openvpn-status.js:41 -#: root/www/luci-static/resources/view/vpn/openvpn.js:48 +#: root/www/luci-static/resources/view/vpn/openvpn.js:50 msgid "Pending..." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:114 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:116 msgid "Please add static routing inside your Main Internet Router (IP: %s) using this OpenVPN Router (IP: %s) as Gateway." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:106 +#: root/www/luci-static/resources/view/vpn/openvpn.js:114 msgid "Please assign a unique device name (Common Name) for this mobile profile:" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:115 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:117 msgid "Please check your profile file. A valid configuration file needs a remote target endpoint and key blocks." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:65 +#: root/www/luci-static/resources/view/vpn/openvpn.js:68 msgid "Please click «Save & Apply»." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:118 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:120 msgid "Please create a port forwarding rule inside your Main Internet Router (IP: %s) using this OpenVPN Router (IP: %s) as receiver." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:144 +#: root/www/luci-static/resources/view/vpn/openvpn.js:152 msgid "Please wait while secure cryptographic, router-unique default assets are being generated." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:56 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:58 msgid "Port Forwarding" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:111 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:113 msgid "Port Open Success" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:150 +#: root/www/luci-static/resources/view/vpn/openvpn.js:159 msgid "Private Client Key" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:77 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:79 msgid "Private Key" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:148 +#: root/www/luci-static/resources/view/vpn/openvpn.js:157 msgid "Private Server Key" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:156 +#: root/www/luci-static/resources/view/vpn/openvpn.js:165 msgid "Proceeding with invalid cryptographic keys will cause total connection failure and may lead to service instability or infinite daemon crash loops." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:75 +#: root/www/luci-static/resources/view/vpn/openvpn.js:79 msgid "Processing..." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:159 +#: root/www/luci-static/resources/view/vpn/openvpn.js:191 +msgid "Profile Generation Error" +msgstr "" + +#: root/www/luci-static/resources/view/vpn/openvpn.js:168 msgid "Profile link active (Native uqr framework (QR-Code) not loaded)." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:78 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:80 msgid "Progress & Output" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:132 +#: root/www/luci-static/resources/view/vpn/openvpn.js:141 msgid "Protected since" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:57 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:59 msgid "Protocol" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:59 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:61 msgid "Public IP Address or Domain" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:60 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:62 msgid "Public Static IP" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:79 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:81 msgid "Pure ECC" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:80 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:82 msgid "Pure RSA" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:81 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:83 msgid "Ready for key generation." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:120 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:122 msgid "Ready for openvpn connections." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:85 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:87 msgid "Recommended: RSA-2048 + ECC-Prime256v1. RSA ensures CA compatibility, while ECC accelerates the data tunnel." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:150 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:152 msgid "Reliable free provider. Never expires, perfect for IPv6 and Dual-Stack." msgstr "" @@ -960,24 +997,28 @@ msgid "Remote IP / Port" msgstr "" #: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:45 -#: root/www/luci-static/resources/view/vpn/openvpn.js:55 +#: root/www/luci-static/resources/view/vpn/openvpn.js:57 msgid "Remote LAN Subnet" msgstr "" #: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:46 -#: root/www/luci-static/resources/view/vpn/openvpn.js:56 +#: root/www/luci-static/resources/view/vpn/openvpn.js:58 msgid "Remote Name" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:61 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:63 msgid "Remote Office Name" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:62 +#: root/www/luci-static/resources/view/vpn/openvpn.js:110 +msgid "Remote Profile" +msgstr "" + +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:64 msgid "Resolving Target..." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:63 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:65 msgid "Reveal/hide password" msgstr "" @@ -985,62 +1026,62 @@ msgstr "" msgid "Running" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:41 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:42 msgid "Save & Apply" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:42 -#: root/www/luci-static/resources/view/vpn/openvpn.js:76 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:43 +#: root/www/luci-static/resources/view/vpn/openvpn.js:80 msgid "Save Config" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:44 -#: root/www/luci-static/resources/view/vpn/openvpn.js:78 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:45 +#: root/www/luci-static/resources/view/vpn/openvpn.js:82 msgid "Saved" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:43 -#: root/www/luci-static/resources/view/vpn/openvpn.js:77 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:44 +#: root/www/luci-static/resources/view/vpn/openvpn.js:81 msgid "Saving..." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:108 +#: root/www/luci-static/resources/view/vpn/openvpn.js:116 msgid "Scan QR Code with Mobile Phone Camera:" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:83 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:85 msgid "Secret CA Key" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:67 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:69 msgid "Secure Full Tunnel (Route all traffic over VPN)" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:109 +#: root/www/luci-static/resources/view/vpn/openvpn.js:117 msgid "Secure Temporary Profile URL Field:" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:160 +#: root/www/luci-static/resources/view/vpn/openvpn.js:169 msgid "Security Notice:" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:165 +#: root/www/luci-static/resources/view/vpn/openvpn.js:174 msgid "Security Notice: Outdated factory default samples detected!" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:162 +#: root/www/luci-static/resources/view/vpn/openvpn.js:171 msgid "Security Warning!" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:64 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:66 msgid "Select VPN Type" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:2507 +#: root/www/luci-static/resources/view/vpn/openvpn.js:118 msgid "Select clients to remove:" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:124 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:126 msgid "Select the (.ovpn) profile or (.crt) multi-key bundle. Settings will be configured or updated automatically." msgstr "" @@ -1049,15 +1090,15 @@ msgstr "" msgid "Server" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:68 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:70 msgid "Server Address" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:147 +#: root/www/luci-static/resources/view/vpn/openvpn.js:156 msgid "Server Certificate" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:125 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:127 msgid "Server for Main Office (Headquarters)" msgstr "" @@ -1065,19 +1106,19 @@ msgstr "" msgid "Session" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:65 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:67 msgid "Setup" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:79 +#: root/www/luci-static/resources/view/vpn/openvpn.js:83 msgid "Show" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:131 +#: root/www/luci-static/resources/view/vpn/openvpn.js:140 msgid "Show flood guard drops" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:84 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:86 msgid "Signing client certificate using server CA..." msgstr "" @@ -1085,162 +1126,170 @@ msgstr "" msgid "Since" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:126 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:128 msgid "Skip Check & Unlock Create Button" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:69 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:71 msgid "Split Tunnel (Access home LAN only, faster web browsing)" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:49 +#: root/www/luci-static/resources/view/vpn/openvpn.js:51 msgid "Starting..." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:99 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:101 msgid "Static IP check bypassed by administrator." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:100 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:102 msgid "Static IP now checked by administrator." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:70 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:72 msgid "Static Route" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:123 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:125 msgid "Static Routing Success" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:122 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:124 msgid "Static Routing missing!" msgstr "" #: root/www/luci-static/resources/view/vpn/openvpn-status.js:44 -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:71 -#: root/www/luci-static/resources/view/vpn/openvpn.js:50 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:73 +#: root/www/luci-static/resources/view/vpn/openvpn.js:52 msgid "Status" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:74 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:76 msgid "Step 1/3: Generating Certificate Authority (CA)..." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:75 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:77 msgid "Step 2/3: Generating Private Server Key..." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:76 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:78 msgid "Step 3/3: Generating & Signing Server Certificate..." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:103 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:105 msgid "Success: The IP address is valid and fully resolvable!" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:162 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:164 msgid "Super fast setup without registration. WARNING: Blocked in some networks because it is completely anonymous." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:127 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:129 msgid "Switch to Dynamic DNS (DDNS)" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:111 +#: root/www/luci-static/resources/view/vpn/openvpn.js:120 msgid "System logs ..." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:129 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:131 msgid "TCP (Good for traveling - works behind firewalls)" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:152 +#: root/www/luci-static/resources/view/vpn/openvpn.js:161 msgid "TLS Crypt v2 Client Key" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:67 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:69 msgid "TLS Crypt v2 Client Key (Anti-DoS / Port-Scan Protection)" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:151 +#: root/www/luci-static/resources/view/vpn/openvpn.js:160 msgid "TLS Crypt v2 Server Master Key" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:66 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:68 msgid "TLS Crypt v2 Server Master Key (Anti-DoS / Port-Scan Protection)" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:88 +#: root/www/luci-static/resources/view/vpn/openvpn.js:193 +msgid "TLS crypt v2 client key generation failed" +msgstr "" + +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:111 +msgid "TLS crypt v2 client key generation failed." +msgstr "" + +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:90 msgid "TLS-Crypt key saved successfully." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:121 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:123 msgid "The VPN routing via Main Internet Router (IP: %s) to remote subnet(s) (Net: %s)" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:86 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:88 msgid "The main TLS security key on the server. It encrypts the connection and creates the keys for all clients." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:128 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:130 msgid "The port forward" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:117 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:119 msgid "The port used on this OpenWrt router." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:116 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:118 msgid "The port used on your main internet router." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:119 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:121 msgid "The public IP address or your own external domain name." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:106 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:109 msgid "The saved key data contains structural format corruption!" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:178 +#: root/www/luci-static/resources/view/vpn/openvpn.js:188 msgid "The uploaded file is empty." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:89 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:91 msgid "This OpenVPN Router is not the Main Internet Router." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:107 +#: root/www/luci-static/resources/view/vpn/openvpn.js:115 msgid "This automated initialization process can take a few minutes on your device..." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:110 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:112 msgid "This file contains only VPN client keys." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:92 +#: root/www/luci-static/resources/view/vpn/openvpn.js:98 msgid "This is the address your VPN clients will use to connect from the internet. Scan the QR code with your phone camera or use the link below to download your connection profile." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:163 +#: root/www/luci-static/resources/view/vpn/openvpn.js:172 msgid "This profile does not use TLS-Crypt v2. It is highly recommended to use TLS-Crypt v2 for maximum security, better performance, and protection against unauthorized scanning." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:110 +#: root/www/luci-static/resources/view/vpn/openvpn.js:119 msgid "This server handles network rules for multiple remote offices. Please choose an existing office name or write a name to make a new connection profile." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:58 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:59 msgid "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)" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:72 -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:170 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:74 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:172 msgid "Token / Password" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:133 +#: root/www/luci-static/resources/view/vpn/openvpn.js:142 msgid "Traffic Rules" msgstr "" @@ -1253,43 +1302,47 @@ msgstr "" msgid "Type" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:130 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:132 msgid "UDP (High Performance - maximum throughput)" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:93 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:95 msgid "Unknown Asset" msgstr "" +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:96 +msgid "Unknown Structure" +msgstr "" + #: root/www/luci-static/resources/view/vpn/openvpn-status.js:52 msgid "UpTime" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:80 +#: root/www/luci-static/resources/view/vpn/openvpn.js:84 msgid "Upload" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:112 +#: root/www/luci-static/resources/view/vpn/openvpn.js:121 msgid "Uploaded file is invalid or corrupt!" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:131 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:133 msgid "Use Dynamic DNS (DDNS) Service" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:81 +#: root/www/luci-static/resources/view/vpn/openvpn.js:85 msgid "Use Fallback" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:132 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:134 msgid "Use Public IP Address / Domain" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:73 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:75 msgid "Username" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:155 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:157 msgid "Username (Optional)" msgstr "" @@ -1297,15 +1350,15 @@ msgstr "" msgid "VPN" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:113 +#: root/www/luci-static/resources/view/vpn/openvpn.js:122 msgid "VPN Client Connection Domain or Address:" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:96 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:99 msgid "VPN Key Type:" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:74 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:76 msgid "VPN Name" msgstr "" @@ -1313,31 +1366,31 @@ msgstr "" msgid "Valid Until" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:189 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:191 msgid "Validation Error: The VPN Name must contain only letters, numbers, hyphens or underscores." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:101 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:103 msgid "Validation bypassed after network failure." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:102 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:104 msgid "Validation bypassed by administrator." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:95 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:98 msgid "Validity (Days)" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:156 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:158 msgid "Very popular provider. WARNING: Free accounts require manual activation/renewal every 30 days via email." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:134 +#: root/www/luci-static/resources/view/vpn/openvpn.js:143 msgid "WAN ports" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:99 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:102 msgid "WARNING: Clicking save will instantly break all active VPN tunnels. You must generate and share new client profiles (.ovpn) to restore the connections." msgstr "" @@ -1345,32 +1398,32 @@ msgstr "" msgid "WORKING" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:178 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:180 msgid "Warning: If you upload wrong keys, your connection will stop!" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:174 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:176 msgid "Warning: The IP address cannot be resolved. Your router might be offline." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:155 +#: root/www/luci-static/resources/view/vpn/openvpn.js:164 msgid "Warning: The uploaded file structure does not match the expected key type." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:175 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:177 msgid "Warning: You did not select «Public Static IP». If your public IP address changes, your VPN tunnel will crash." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:177 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:179 msgid "Warning: You have un-saved text in the input fields. Please click the blue «Add Office» button to save it, or clear the fields to continue." msgstr "" #: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:31 -#: root/www/luci-static/resources/view/vpn/openvpn.js:57 +#: root/www/luci-static/resources/view/vpn/openvpn.js:59 msgid "Wizard" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:179 +#: root/www/luci-static/resources/view/vpn/openvpn.js:189 msgid "Wrong key type! Please upload the correct cryptographic file." msgstr "" @@ -1379,98 +1432,98 @@ msgid "Years" msgstr "" #: root/www/luci-static/resources/view/vpn/openvpn-status.js:37 -#: root/www/luci-static/resources/view/vpn/openvpn.js:58 +#: root/www/luci-static/resources/view/vpn/openvpn.js:60 msgid "Yes" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:57 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:58 msgid "Yes (Generate Unique Keys)" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:117 +#: root/www/luci-static/resources/view/vpn/openvpn.js:126 msgid "are opened automatically for secure OpenVPN connections." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:120 +#: root/www/luci-static/resources/view/vpn/openvpn.js:129 msgid "devices is created automatically." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:148 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:150 msgid "dynv6 (dynv6.com)" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:58 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:60 msgid "e.g." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:70 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:72 msgid "e.g. -sha256 ; -noout ; -sha256" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:69 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:71 msgid "e.g. -text (shows hex output)" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:71 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:73 msgid "e.g. -traditional ; ; -sha256" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:113 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:115 msgid "e.g. Mobile-Server, Main-Office" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:68 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:70 msgid "e.g. Remote Office 1" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:171 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:173 msgid "e.g. https://custom-provider.com?domain=...&key=..." msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:105 +#: root/www/luci-static/resources/view/vpn/openvpn.js:113 msgid "e.g. my-smartphone" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:112 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:114 msgid "e.g. mydomain.com or your public IP" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:168 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:170 msgid "e.g. myname-custom-provider.com" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:158 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:160 msgid "e.g. myname.ddns.net" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:137 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:139 msgid "e.g. myname.duckdns.org" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:152 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:154 msgid "e.g. myname.dynu.net" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:147 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:149 msgid "e.g. myname.dynv6.net" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:164 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:166 msgid "e.g. myname.freemyip.com" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:142 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:144 msgid "e.g. myname.ipv64.net" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:104 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:106 msgid "is already" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:105 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:107 msgid "is currently" msgstr "" -#: root/www/luci-static/resources/view/vpn/openvpn.js:127 +#: root/www/luci-static/resources/view/vpn/openvpn.js:136 msgid "packets" msgstr "" diff --git a/luci-app-openvpn-plus/po/zh-cn/luci-app-openvpn-plus.po b/luci-app-openvpn-plus/po/zh-cn/luci-app-openvpn-plus.po index 9e646126b..83829d4bb 100755 --- a/luci-app-openvpn-plus/po/zh-cn/luci-app-openvpn-plus.po +++ b/luci-app-openvpn-plus/po/zh-cn/luci-app-openvpn-plus.po @@ -1,23 +1,23 @@ msgid "" msgstr "Content-Type: text/plain; charset=UTF-8\n" -#: root/www/luci-static/resources/view/vpn/openvpn.js:138 +#: root/www/luci-static/resources/view/vpn/openvpn.js:147 msgid "A client connection needs the server keys and config. Select an .ovpn profile to import them automatically, or click Cancel to use the default settings." msgstr "客户端连接需要服务器密钥和配置。选择一个 .ovpn 配置文件来自动导入,或点击取消以使用默认设置。" -#: root/www/luci-static/resources/view/vpn/openvpn.js:128 +#: root/www/luci-static/resources/view/vpn/openvpn.js:137 msgid "A secure firewall zone for all" msgstr "适用于所有组件的安全防火墙区域" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:87 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:89 msgid "A unique TLS security key for one client. It encrypts and protects all data sent to the server." msgstr "用于单个客户端 brain 的唯一 TLS 安全密钥。它用于加密并保护发送到服务器的所有数据。" -#: root/www/luci-static/resources/view/vpn/openvpn.js:116 +#: root/www/luci-static/resources/view/vpn/openvpn.js:125 msgid "ACTIVE" msgstr "活动" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:139 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:141 msgid "API Token" msgstr "API 令牌" @@ -34,23 +34,23 @@ msgstr "活动" msgid "Active PIDs" msgstr "活动 PID" -#: root/www/luci-static/resources/view/vpn/openvpn.js:2469 +#: root/www/luci-static/resources/view/vpn/openvpn.js:35 msgid "Add" msgstr "添加" -#: root/www/luci-static/resources/view/vpn/openvpn.js:61 +#: root/www/luci-static/resources/view/vpn/openvpn.js:63 msgid "Add Client Instance" msgstr "添加客户端实例" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:77 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:79 msgid "Add Office" msgstr "添加办公室" -#: root/www/luci-static/resources/view/vpn/openvpn.js:62 +#: root/www/luci-static/resources/view/vpn/openvpn.js:64 msgid "Add Server Instance" msgstr "添加服务器实例" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:87 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:89 msgid "Add your other offices below. Please enter the internal LAN network IP (e.g., 192.168.2.0) of each office, NOT their public WAN IP addresses. The server will connect all local networks automatically." msgstr "在下方添加您的其他办公室。请输入每个办公室 regional 的内部 LAN 网络 IP(例如 192.168.2.0),而**不是**它们的公网 WAN IP 地址。服务器将自动连接所有本地网络。" @@ -62,19 +62,19 @@ msgstr "总计接收 (RX)" msgid "Aggregated TX" msgstr "总计发送 (TX)" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:88 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:90 msgid "Another Router detected!" msgstr "检测到另一个路由器!" -#: root/www/luci-static/resources/view/vpn/openvpn.js:89 +#: root/www/luci-static/resources/view/vpn/openvpn.js:95 msgid "Are you sure you want to delete" msgstr "您确定要删除吗" -#: root/www/luci-static/resources/view/vpn/openvpn.js:118 +#: root/www/luci-static/resources/view/vpn/openvpn.js:127 msgid "Automated Zone Setup:" msgstr "自动区域设置:" -#: root/www/luci-static/resources/view/vpn/openvpn.js:130 +#: root/www/luci-static/resources/view/vpn/openvpn.js:139 msgid "Automatic port scanning is a constant threat on the internet. The OpenVPN flood guard filter detects, bans, and drops these malicious requests. This hides your system, prevents spying, saves CPU power, and guarantees maximum VPN performance." msgstr "自动端口扫描是互联网上的持续威胁。OpenVPN Flood Guard 过滤器可以检测、封禁并丢弃这些恶意请求。这能隐藏您的系统、防止间谍活动、节省 CPU 算力并确保最大的 VPN 性能。" @@ -82,65 +82,74 @@ msgstr "自动端口扫描是互联网上的持续威胁。OpenVPN Flood Guard msgid "BLOCKED" msgstr "已拦截" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:78 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:80 msgid "Back" msgstr "返回" -#: root/www/luci-static/resources/view/vpn/openvpn.js:83 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:36 +#: root/www/luci-static/resources/view/vpn/openvpn.js:65 +msgid "Backup" +msgstr "备份" + +#: root/www/luci-static/resources/view/vpn/openvpn.js:87 msgid "Backup & Use Wizard (recommended)" msgstr "备份并使用向导(推荐)" +#: root/www/luci-static/resources/view/vpn/openvpn.js:111 +msgid "Backup Server Profile" +msgstr "备份服务器配置文件" + #: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:47 msgid "CLOSED" msgstr "已关闭" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:37 -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:79 -#: root/www/luci-static/resources/view/vpn/openvpn.js:63 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:38 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:81 +#: root/www/luci-static/resources/view/vpn/openvpn.js:66 msgid "Cancel" msgstr "取消" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:47 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:48 msgid "Certificate" msgstr "证书" -#: root/www/luci-static/resources/view/vpn/openvpn.js:137 +#: root/www/luci-static/resources/view/vpn/openvpn.js:146 msgid "Certification Authority" msgstr "证书颁发机构 (CA)" -#: root/www/luci-static/resources/view/vpn/openvpn.js:64 +#: root/www/luci-static/resources/view/vpn/openvpn.js:67 msgid "Change" msgstr "修改" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:80 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:82 msgid "Check Address Status" msgstr "检查地址状态" -#: root/www/luci-static/resources/view/vpn/openvpn.js:119 +#: root/www/luci-static/resources/view/vpn/openvpn.js:128 msgid "Check Traffic Rules:" msgstr "检查流量规则:" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:94 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:96 msgid "Checking connection status in the background..." msgstr "正在后台检查连接状态..." -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:82 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:84 msgid "Choose File..." msgstr "选择文件..." -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:90 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:92 msgid "Choose how your internet traffic is sent through the VPN. You can use any free OpenVPN app on your phone or laptop to import this profile." msgstr "选择您的互联网流量如何通过 VPN 发送。您可以在手机或笔记本电脑上使用任何免费的 OpenVPN 应用程序来导入此配置文件。" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:145 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:147 msgid "Classic free provider with exceptional IPv6 and dual-stack support." msgstr "经典的免费服务商,支持极佳的 IPv6 和双栈。" -#: root/www/luci-static/resources/view/vpn/openvpn.js:43 +#: root/www/luci-static/resources/view/vpn/openvpn.js:45 msgid "Clear Log" msgstr "清除日志" -#: root/www/luci-static/resources/view/vpn/openvpn.js:35 +#: root/www/luci-static/resources/view/vpn/openvpn.js:36 msgid "Clearing..." msgstr "正在清除..." @@ -149,40 +158,40 @@ msgstr "正在清除..." msgid "Client" msgstr "客户端" -#: root/www/luci-static/resources/view/vpn/openvpn.js:149 +#: root/www/luci-static/resources/view/vpn/openvpn.js:158 msgid "Client Certificate" msgstr "客户端证书" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:48 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:49 msgid "Client Certificate + Private key (issued by CA) + TLS Crypt v2 Client key" msgstr "客户端证书 + 私钥(由 CA 颁发)+ TLS Crypt v2 客户端密钥" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:90 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:92 msgid "Client Key" msgstr "客户端密钥" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:91 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:93 msgid "Client for Branch Office (Branch)" msgstr "分支机构客户端 (Branch)" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:50 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:51 msgid "Client keys exported successfully." msgstr "客户端密钥成功导出。" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:49 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:50 msgid "Client keys generated successfully!" msgstr "客户端密钥成功生成!" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:36 -#: root/www/luci-static/resources/view/vpn/openvpn.js:66 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:37 +#: root/www/luci-static/resources/view/vpn/openvpn.js:69 msgid "Close" msgstr "关闭" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:52 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:53 msgid "Common Name (CN)" msgstr "通用名称 (CN)" -#: root/www/luci-static/resources/view/vpn/openvpn.js:88 +#: root/www/luci-static/resources/view/vpn/openvpn.js:94 msgid "Configuration changed! Applying will temporarily restart the OpenVPN instance." msgstr "配置已更改!应用设置将暂时重启 OpenVPN 实例。" @@ -194,40 +203,44 @@ msgstr "已连接" msgid "Connection Type" msgstr "连接类型" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:81 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:83 msgid "Create VPN" msgstr "创建 VPN" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:92 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:94 msgid "Create a secure VPN connection in just a few clicks." msgstr "只需点击几次即可创建安全的 VPN 连接。" -#: root/www/luci-static/resources/view/vpn/openvpn.js:139 -msgid "Create keys for a new office..." -msgstr "为新办公室创建密钥..." +#: root/www/luci-static/resources/view/vpn/openvpn.js:149 +msgid "Create keys for a new mobile client..." +msgstr "为新移动客户端创建密钥..." + +#: root/www/luci-static/resources/view/vpn/openvpn.js:148 +msgid "Create keys for a new remote office..." +msgstr "为新远程办公室创建密钥..." #: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:27 msgid "Creating VPN..." msgstr "正在创建 VPN..." #: root/www/luci-static/resources/view/vpn/openvpn-status.js:27 -#: root/www/luci-static/resources/view/vpn/openvpn.js:36 +#: root/www/luci-static/resources/view/vpn/openvpn.js:37 msgid "Creating..." msgstr "正在创建..." -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:82 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:84 msgid "Crypto operation running. Please wait..." msgstr "加密操作正在运行。请稍候..." -#: root/www/luci-static/resources/view/vpn/openvpn.js:158 +#: root/www/luci-static/resources/view/vpn/openvpn.js:167 msgid "Cryptographic Key Warning" msgstr "加密密钥警告" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:169 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:171 msgid "Custom / Manual Update URL" msgstr "自定义 / 手动更新 URL" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:53 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:54 msgid "Custom CLI Options" msgstr "自定义 CLI 选项" @@ -235,7 +248,7 @@ msgstr "自定义 CLI 选项" msgid "Custom Update URL" msgstr "自定义更新 URL" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:160 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:162 msgid "DDNS Key" msgstr "DDNS 密钥" @@ -243,7 +256,7 @@ msgstr "DDNS 密钥" msgid "DDNS Provider" msgstr "DDNS 服务商" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:161 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:163 msgid "DDNS Username" msgstr "DDNS 用户名" @@ -251,32 +264,32 @@ msgstr "DDNS 用户名" msgid "Default" msgstr "默认" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:83 -#: root/www/luci-static/resources/view/vpn/openvpn.js:2470 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:85 +#: root/www/luci-static/resources/view/vpn/openvpn.js:40 msgid "Delete" msgstr "删除" -#: root/www/luci-static/resources/view/vpn/openvpn.js:2459 -msgid "Delete Client Profile..." -msgstr "删除客户端配置文件..." - -#: root/www/luci-static/resources/view/vpn/openvpn.js:67 +#: root/www/luci-static/resources/view/vpn/openvpn.js:70 msgid "Delete Instance" msgstr "删除实例" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:95 +#: root/www/luci-static/resources/view/vpn/openvpn.js:112 +msgid "Delete Remote Profile..." +msgstr "删除远程配置文件..." + +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:97 msgid "Delete this office" msgstr "删除此办公室" -#: root/www/luci-static/resources/view/vpn/openvpn.js:68 +#: root/www/luci-static/resources/view/vpn/openvpn.js:71 msgid "Deleted - Save & Apply" msgstr "已删除 - 保存并应用" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:102 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:105 msgid "Deployment Recommendation:" msgstr "部署推荐:" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:101 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:104 msgid "Deprecated TLS-Crypt V1 key (Not supported)" msgstr "已弃用的 TLS-Crypt V1 密钥(不支持)" @@ -284,33 +297,33 @@ msgstr "已弃用的 TLS-Crypt V1 密钥(不支持)" msgid "Destination" msgstr "目的地" -#: root/www/luci-static/resources/view/vpn/openvpn.js:140 +#: root/www/luci-static/resources/view/vpn/openvpn.js:150 msgid "Diffie - Hellman Parameters" msgstr "Diffie-Hellman 参数" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:64 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:66 msgid "Diffie-Hellman Parameters (Perfect Forward Secrecy)" msgstr "Diffie-Hellman 参数 (完全 forward 保密/PFS)" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:54 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:55 msgid "Diffie-Hellman parameters enforce numeric prime bit boundaries exclusively." msgstr "Diffie-Hellman 参数仅强制执行数字质数位边界。" -#: root/www/luci-static/resources/view/vpn/openvpn.js:37 +#: root/www/luci-static/resources/view/vpn/openvpn.js:38 msgid "Disable" msgstr "禁用" #: root/www/luci-static/resources/view/status/include/35_openvpn.js:19 #: root/www/luci-static/resources/view/vpn/openvpn-status.js:31 -#: root/www/luci-static/resources/view/vpn/openvpn.js:38 +#: root/www/luci-static/resources/view/vpn/openvpn.js:39 msgid "Disabled" msgstr "已禁用" -#: root/www/luci-static/resources/view/vpn/openvpn.js:157 +#: root/www/luci-static/resources/view/vpn/openvpn.js:166 msgid "Do you want to proceed and save this file anyway?" msgstr "您确定要继续并保存此文件吗?" -#: root/www/luci-static/resources/view/vpn/openvpn.js:164 +#: root/www/luci-static/resources/view/vpn/openvpn.js:173 msgid "Do you want to proceed with the import anyway?" msgstr "您确定要继续导入吗?" @@ -318,53 +331,53 @@ msgstr "您确定要继续导入吗?" msgid "Domain Name" msgstr "域名" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:144 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:146 msgid "Domain Update URL / Key" msgstr "域名更新 URL / 密钥" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:166 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:168 msgid "Domain Update URL / token" msgstr "域名更新 URL / 令牌" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:38 -#: root/www/luci-static/resources/view/vpn/openvpn.js:69 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:39 +#: root/www/luci-static/resources/view/vpn/openvpn.js:72 msgid "Download" msgstr "下载" -#: root/www/luci-static/resources/view/vpn/openvpn.js:70 +#: root/www/luci-static/resources/view/vpn/openvpn.js:73 msgid "Download (.ovpn)" msgstr "下载 (.ovpn)" -#: root/www/luci-static/resources/view/vpn/openvpn.js:90 +#: root/www/luci-static/resources/view/vpn/openvpn.js:96 msgid "Download and QR code link are only available while this window is open." msgstr "下载和二维码链接仅在此窗口打开时可用。" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:138 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:140 msgid "DuckDNS (duckdns.org)" msgstr "DuckDNS (duckdns.org)" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:153 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:155 msgid "Dynu Systems (dynu.com)" msgstr "Dynu Systems (dynu.com)" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:107 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:110 msgid "ERROR: Safety polling threshold exceeded boundaries. Process deadlocked." msgstr "错误: 安全轮询阈值超过边界。进程死锁。" -#: root/www/luci-static/resources/view/vpn/openvpn.js:91 +#: root/www/luci-static/resources/view/vpn/openvpn.js:97 msgid "Edit Config file" msgstr "编辑配置文件" -#: root/www/luci-static/resources/view/vpn/openvpn.js:40 +#: root/www/luci-static/resources/view/vpn/openvpn.js:42 msgid "Enable" msgstr "启用" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:39 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:40 msgid "Enable Save" msgstr "启用保存" #: root/www/luci-static/resources/view/vpn/openvpn-status.js:33 -#: root/www/luci-static/resources/view/vpn/openvpn.js:71 +#: root/www/luci-static/resources/view/vpn/openvpn.js:74 msgid "Enabled" msgstr "已启用" @@ -372,64 +385,64 @@ msgstr "已启用" msgid "Encryption" msgstr "加密" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:167 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:169 msgid "Enter your own manual HTTP update URL structure." msgstr "输入您自己的手动 HTTP 更新 URL 结构。" #: root/www/luci-static/resources/view/status/include/35_openvpn.js:21 #: root/www/luci-static/resources/view/vpn/openvpn-status.js:32 -#: root/www/luci-static/resources/view/vpn/openvpn.js:41 +#: root/www/luci-static/resources/view/vpn/openvpn.js:43 msgid "Error" msgstr "错误" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:187 -#: root/www/luci-static/resources/view/vpn/openvpn.js:176 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:189 +#: root/www/luci-static/resources/view/vpn/openvpn.js:186 msgid "Error: An office with this name is already in the list." msgstr "错误: 列表中已存在此名称的办公室。" -#: root/www/luci-static/resources/view/vpn/openvpn.js:170 +#: root/www/luci-static/resources/view/vpn/openvpn.js:179 msgid "Error: Configuration or cryptographic keys are missing on disk." msgstr "错误: 磁盘上缺少配置或加密密钥。" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:190 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:192 msgid "Error: File is corrupted or missing vital fields." msgstr "错误: 文件损坏或缺少重要字段。" -#: root/www/luci-static/resources/view/vpn/openvpn.js:2665 +#: root/www/luci-static/resources/view/vpn/openvpn.js:192 msgid "Error: Network status check failed." msgstr "错误:网络状态检查失败。" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:186 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:188 msgid "Error: Network status check failed. Please try again." msgstr "错误: 网络状态检查失败。请重试。" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:182 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:184 msgid "Error: Please add at least one office to the list before you continue." msgstr "错误: 请在继续之前向列表中至少添加一个办公室。" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:185 -#: root/www/luci-static/resources/view/vpn/openvpn.js:175 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:187 +#: root/www/luci-static/resources/view/vpn/openvpn.js:185 msgid "Error: This IP address is already used by another office. Every office needs a different IP address!" msgstr "错误: 此 IP 地址已被另一个办公室使用。每个办公室都需要不同的 IP 地址!" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:188 -#: root/www/luci-static/resources/view/vpn/openvpn.js:177 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:190 +#: root/www/luci-static/resources/view/vpn/openvpn.js:187 msgid "Error: You cannot use this IP network! It is the local network of this router and will block all traffic." msgstr "错误: 您不能使用此 IP 网络!它是此路由器的本地网络,将阻塞所有流量。" -#: root/www/luci-static/resources/view/vpn/openvpn.js:93 +#: root/www/luci-static/resources/view/vpn/openvpn.js:99 msgid "Export (.ovpn)" msgstr "导出 (.ovpn)" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:55 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:56 msgid "Export Keys" msgstr "导出密钥" -#: root/www/luci-static/resources/view/vpn/openvpn.js:94 +#: root/www/luci-static/resources/view/vpn/openvpn.js:100 msgid "Export OpenVPN Connect Client Profile" msgstr "导出 OpenVPN Connect 客户端配置文件" -#: root/www/luci-static/resources/view/vpn/openvpn.js:39 +#: root/www/luci-static/resources/view/vpn/openvpn.js:41 msgid "Export Profile" msgstr "导出配置文件" @@ -437,47 +450,51 @@ msgstr "导出配置文件" msgid "External Port" msgstr "外部端口" -#: root/www/luci-static/resources/view/vpn/openvpn.js:169 +#: root/www/luci-static/resources/view/vpn/openvpn.js:178 msgid "Failed to build profile:" msgstr "构建配置文件失败:" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:183 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:185 msgid "Failed to create new OpenVPN server:" msgstr "创建新 OpenVPN 服务器失败:" +#: root/www/luci-static/resources/view/vpn/openvpn.js:190 +msgid "Failed to generate secure download profile. Please check system logs." +msgstr "生成安全下载配置文件失败。请检查系统日志。" + #: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:27 msgid "File Location:" msgstr "文件位置:" -#: root/www/luci-static/resources/view/vpn/openvpn.js:121 +#: root/www/luci-static/resources/view/vpn/openvpn.js:130 msgid "Filtered" msgstr "已过滤" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:96 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:98 msgid "Finding your public IP address..." msgstr "正在寻找您的公网 IP 地址..." -#: root/www/luci-static/resources/view/vpn/openvpn.js:122 +#: root/www/luci-static/resources/view/vpn/openvpn.js:131 msgid "Firewall" msgstr "防火墙" -#: root/www/luci-static/resources/view/vpn/openvpn.js:123 +#: root/www/luci-static/resources/view/vpn/openvpn.js:132 msgid "Firewall & Routing Information" msgstr "防火墙和路由信息" -#: root/www/luci-static/resources/view/vpn/openvpn.js:129 +#: root/www/luci-static/resources/view/vpn/openvpn.js:138 msgid "Flood Guard" msgstr "Flood Guard" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:97 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:99 msgid "For Phones & Laptops (OpenVPN Connect App)" msgstr "适用于手机和笔记本电脑 (OpenVPN Connect 应用)" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:165 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:167 msgid "FreeMYIP (freemyip.com)" msgstr "FreeMYIP (freemyip.com)" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:65 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:67 msgid "Full PKI Suite (CA + Server Certificate + Private Server Key)" msgstr "完整 PKI 套件 (CA + 服务器证书 + 服务器私钥)" @@ -485,31 +502,31 @@ msgstr "完整 PKI 套件 (CA + 服务器证书 + 服务器私钥)" msgid "Gateway" msgstr "网关" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:40 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:41 msgid "Generate" msgstr "生成" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:100 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:103 msgid "Generated keys are not saved! Close window and discard keys?" msgstr "生成的密钥未保存!是否关闭窗口并放弃密钥?" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:1054 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:61 msgid "Generating TLS crypt v2 client key..." msgstr "正在生成 TLS crypt v2 客户端密钥..." -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:56 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:57 msgid "Generating TLS-Crypt key..." msgstr "正在生成 TLS-Crypt 密钥..." -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:59 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:60 msgid "Generating private client key..." msgstr "正在生成客户端私钥..." -#: root/www/luci-static/resources/view/vpn/openvpn.js:72 +#: root/www/luci-static/resources/view/vpn/openvpn.js:75 msgid "Generating..." msgstr "正在生成..." -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:149 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:151 msgid "HTTP Token String" msgstr "HTTP 令牌字符串" @@ -517,19 +534,19 @@ msgstr "HTTP 令牌字符串" msgid "Help" msgstr "帮助" -#: root/www/luci-static/resources/view/vpn/openvpn.js:99 +#: root/www/luci-static/resources/view/vpn/openvpn.js:105 msgid "Here you can manage multiple OpenVPN Server and Client instances dynamically." msgstr "您可以在这里动态管理多个 OpenVPN 服务器和客户端实例。" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:143 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:145 msgid "IPv64 (ipv64.net)" msgstr "IPv64 (ipv64.net)" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:98 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:100 msgid "If your internet provides a static IP address." msgstr "如果您的互联网提供静态 IP 地址。" -#: root/www/luci-static/resources/view/vpn/openvpn.js:95 +#: root/www/luci-static/resources/view/vpn/openvpn.js:101 msgid "Import (.ovpn)" msgstr "导入 (.ovpn)" @@ -537,20 +554,20 @@ msgstr "导入 (.ovpn)" msgid "Import Failed:" msgstr "导入失败:" -#: root/www/luci-static/resources/view/vpn/openvpn.js:96 -msgid "Import OpenVPN Connect Client Profile" -msgstr "导入 OpenVPN Connect 客户端配置文件" +#: root/www/luci-static/resources/view/vpn/openvpn.js:102 +msgid "Import OpenVPN Client Profile" +msgstr "导入 OpenVPN 客户端配置文件" #: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:35 -#: root/www/luci-static/resources/view/vpn/openvpn.js:97 +#: root/www/luci-static/resources/view/vpn/openvpn.js:103 msgid "Import Profile" msgstr "导入配置文件" -#: root/www/luci-static/resources/view/vpn/openvpn.js:161 +#: root/www/luci-static/resources/view/vpn/openvpn.js:170 msgid "Import cancelled due to missing TLS-Crypt v2 protection." msgstr "由于缺少 TLS-Crypt v2 保护,导入已取消。" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:94 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:97 msgid "Important: Use a unique name for each office to configure the network routing correctly." msgstr "重要: 为每个办公室使用唯一的名称,以便正确配置网络路由。" @@ -562,7 +579,7 @@ msgstr "已导入的客户端" msgid "Importing profile..." msgstr "正在导入配置文件..." -#: root/www/luci-static/resources/view/vpn/openvpn.js:124 +#: root/www/luci-static/resources/view/vpn/openvpn.js:133 msgid "Inbound Access:" msgstr "入站访问:" @@ -575,11 +592,11 @@ msgid "Instance" msgstr "实例" #: root/www/luci-static/resources/view/vpn/openvpn-status.js:35 -#: root/www/luci-static/resources/view/vpn/openvpn.js:42 +#: root/www/luci-static/resources/view/vpn/openvpn.js:44 msgid "Instance #" msgstr "实例 #" -#: root/www/luci-static/resources/view/vpn/openvpn.js:51 +#: root/www/luci-static/resources/view/vpn/openvpn.js:53 msgid "Instance Management" msgstr "实例管理" @@ -595,27 +612,27 @@ msgstr "内部端口" msgid "Internet Traffic" msgstr "互联网流量" -#: root/www/luci-static/resources/view/vpn/openvpn.js:171 +#: root/www/luci-static/resources/view/vpn/openvpn.js:180 msgid "Invalid file. Important parts are missing (CA, Cert, or Private Key)." msgstr "无效文件。缺少重要部分 (CA、证书或私钥)。" -#: root/www/luci-static/resources/view/vpn/openvpn.js:166 +#: root/www/luci-static/resources/view/vpn/openvpn.js:175 msgid "It is highly recommended not to keep or use the outdated standard default configurations. Instead, you should backup them and use the up-to-date setup Wizard to build new, secure connections." msgstr "强烈建议不要保留或使用过时的标准默认配置。相反,您应该对它们进行备份,并使用最新的设置向导来建立新的安全连接。" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:103 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:106 msgid "It is highly recommended to use the automated Setup Wizard to deploy a Mobile Server (OpenVPN Connect) or a Headquarters LAN-to-LAN Server. In just a few clicks, the wizard fully configures easy DDNS provider setup, multi-client remote networks, optimal firewall zones, and scan-ready QR codes." msgstr "强烈建议使用自动设置向导来部署移动服务器 (OpenVPN Connect) 或总部 LAN-to-LAN 服务器。只需点击几下,向导即可完全配置简易的 DDNS 服务商设置、多客户端远程网络、优化的防火墙区域以及支持扫描的二维码。" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:106 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:108 msgid "Keep IP Setup anyway" msgstr "仍然保留 IP 设置" -#: root/www/luci-static/resources/view/vpn/openvpn.js:82 +#: root/www/luci-static/resources/view/vpn/openvpn.js:86 msgid "Keep old samples (to avoid)" msgstr "保留旧示例(不推荐)" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:60 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:62 msgid "Key Strength" msgstr "密钥强度" @@ -623,35 +640,35 @@ msgstr "密钥强度" msgid "Key Update Active" msgstr "密钥更新激活" -#: root/www/luci-static/resources/view/vpn/openvpn.js:172 +#: root/www/luci-static/resources/view/vpn/openvpn.js:181 msgid "Key check failed. One security key is broken or invalid." msgstr "密钥检查失败。一个安全密钥损坏或无效。" -#: root/www/luci-static/resources/view/vpn/openvpn.js:145 +#: root/www/luci-static/resources/view/vpn/openvpn.js:154 msgid "Key file is empty or does not exist on disk yet." msgstr "密钥文件为空或在磁盘上尚不存在。" -#: root/www/luci-static/resources/view/vpn/openvpn.js:173 +#: root/www/luci-static/resources/view/vpn/openvpn.js:182 msgid "Key generation failed." msgstr "密钥生成失败。" -#: root/www/luci-static/resources/view/vpn/openvpn.js:143 +#: root/www/luci-static/resources/view/vpn/openvpn.js:151 msgid "Key generation in progress..." msgstr "正在生成密钥..." -#: root/www/luci-static/resources/view/vpn/openvpn.js:174 +#: root/www/luci-static/resources/view/vpn/openvpn.js:183 msgid "Key type mismatch" msgstr "密钥类型不匹配" -#: root/www/luci-static/resources/view/vpn/openvpn.js:141 +#: root/www/luci-static/resources/view/vpn/openvpn.js:184 msgid "Key verification failed" msgstr "密钥验证失败" -#: root/www/luci-static/resources/view/vpn/openvpn.js:142 +#: root/www/luci-static/resources/view/vpn/openvpn.js:76 msgid "KeyGen" msgstr "密钥生成" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:107 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:109 msgid "Keys updated successfully!" msgstr "密钥更新成功!" @@ -659,15 +676,19 @@ msgstr "密钥更新成功!" msgid "LAN IP" msgstr "LAN IP" -#: root/www/luci-static/resources/view/vpn/openvpn.js:98 +#: root/www/luci-static/resources/view/vpn/openvpn.js:104 msgid "LAN-to-LAN Router Profile Selection" msgstr "LAN-to-LAN 路由器配置文件选择" -#: root/www/luci-static/resources/view/vpn/openvpn.js:52 +#: root/www/luci-static/resources/view/vpn/openvpn.js:54 msgid "LOG" msgstr "日志" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:61 +#: root/www/luci-static/resources/view/vpn/openvpn.js:153 +msgid "LOG: Certificate pipeline completed. Generating TLS crypt v2 client key..." +msgstr "日志:证书流水线完成。正在生成 TLS crypt v2 客户端密钥..." + +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:63 msgid "Loading key contents..." msgstr "正在加载密钥内容..." @@ -675,7 +696,7 @@ msgstr "正在加载密钥内容..." msgid "Local IP / Port" msgstr "本地 IP / 端口" -#: root/www/luci-static/resources/view/vpn/openvpn.js:44 +#: root/www/luci-static/resources/view/vpn/openvpn.js:46 msgid "Log Cleared" msgstr "日志已清除" @@ -687,63 +708,63 @@ msgstr "缺失" msgid "Mask" msgstr "掩码" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:62 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:64 msgid "Master CA" msgstr "主 CA" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:89 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:91 msgid "Master Server Secret Key" msgstr "服务器主密钥" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:140 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:142 msgid "Modern free provider with perfect IPv6 and Dual-Stack support." msgstr "现代免费服务商,支持完善的 IPv6 和双栈(Dual-Stack)。" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:72 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:74 msgid "N/A - openvpn --genkey accepts no extra flags" msgstr "不适用 - openvpn --genkey 不接受额外的标记" #: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:44 -#: root/www/luci-static/resources/view/vpn/openvpn.js:54 +#: root/www/luci-static/resources/view/vpn/openvpn.js:56 msgid "Netmask" msgstr "子网掩码" -#: root/www/luci-static/resources/view/vpn/openvpn.js:125 +#: root/www/luci-static/resources/view/vpn/openvpn.js:134 msgid "Network" msgstr "网络" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:184 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:186 msgid "Network Error: DNS/IP query failed." msgstr "网络错误: DNS/IP 查询失败。" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:93 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:95 msgid "Network verification failed / Timeout" msgstr "网络验证失败 / 超时" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:84 -#: root/www/luci-static/resources/view/vpn/openvpn.js:73 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:86 +#: root/www/luci-static/resources/view/vpn/openvpn.js:77 msgid "Next" msgstr "下一步" #: root/www/luci-static/resources/view/vpn/openvpn-status.js:38 -#: root/www/luci-static/resources/view/vpn/openvpn.js:45 +#: root/www/luci-static/resources/view/vpn/openvpn.js:47 msgid "No" msgstr "否" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:51 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:52 msgid "No (Clone Default Keys)" msgstr "否 (克隆默认密钥)" -#: root/www/luci-static/resources/view/vpn/openvpn.js:102 +#: root/www/luci-static/resources/view/vpn/openvpn.js:108 msgid "No OpenVPN instances configured yet. Use the Wizard to create new OpenVPN instances." msgstr "尚未配置 OpenVPN 实例。使用向导创建新的 OpenVPN 实例。" -#: root/www/luci-static/resources/view/vpn/openvpn.js:101 -#: root/www/luci-static/resources/view/vpn/openvpn.js:103 +#: root/www/luci-static/resources/view/vpn/openvpn.js:107 +#: root/www/luci-static/resources/view/vpn/openvpn.js:109 msgid "No active OpenVPN log entries found." msgstr "未找到活动的 OpenVPN 日志条目。" -#: root/www/luci-static/resources/view/vpn/openvpn.js:46 +#: root/www/luci-static/resources/view/vpn/openvpn.js:48 msgid "No changes detected" msgstr "未检测到更改" @@ -751,7 +772,7 @@ msgstr "未检测到更改" msgid "No clients connected" msgstr "无客户端连接" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:109 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:111 msgid "No file chosen" msgstr "未选择文件" @@ -759,7 +780,7 @@ msgstr "未选择文件" msgid "No instances configured" msgstr "未配置实例" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:108 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:110 msgid "No offices added yet. Use the fields below to add the first office." msgstr "尚未添加办公室。使用下方的字段添加第一个办公室。" @@ -767,20 +788,20 @@ msgstr "尚未添加办公室。使用下方的字段添加第一个办公室。 msgid "No server connected" msgstr "未连接服务器" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:159 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:161 msgid "No-IP (noip.com)" msgstr "No-IP (noip.com)" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:63 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:65 msgid "Not applicable" msgstr "不适用" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:135 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:137 msgid "Number 1 for Open-Source and Home-Assistant setups." msgstr "开源和 Home-Assistant 设置的首选。" #: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:50 -#: root/www/luci-static/resources/view/vpn/openvpn.js:74 +#: root/www/luci-static/resources/view/vpn/openvpn.js:78 msgid "OK" msgstr "确定" @@ -788,171 +809,188 @@ msgstr "确定" msgid "OPEN" msgstr "打开" -#: root/www/luci-static/resources/view/vpn/openvpn.js:104 -msgid "Office Profile:" -msgstr "办公室配置文件:" - #: root/www/luci-static/resources/view/status/include/35_openvpn.js:17 -#: root/www/luci-static/resources/view/vpn/openvpn.js:47 +#: root/www/luci-static/resources/view/vpn/openvpn.js:49 msgid "OpenVPN" msgstr "OpenVPN" -#: root/www/luci-static/resources/view/vpn/openvpn.js:86 +#: root/www/luci-static/resources/view/vpn/openvpn.js:91 +msgid "OpenVPN Backup Export" +msgstr "OpenVPN 备份导出" + +# #: root/www/luci-static/resources/view/vpn/openvpn.js:92 +#: root/www/luci-static/resources/view/vpn/openvpn.js:92 +msgid "OpenVPN Backup Import" +msgstr "OpenVPN 备份导入" + +#: root/www/luci-static/resources/view/vpn/openvpn.js:90 msgid "OpenVPN Client Export" msgstr "OpenVPN 客户端导出" -#: root/www/luci-static/resources/view/vpn/openvpn.js:100 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:55 +msgid "OpenVPN Client Profile detected" +msgstr "检测到 OpenVPN 客户端配置文件" + +#: root/www/luci-static/resources/view/vpn/openvpn.js:106 msgid "OpenVPN Connect Mobile Export" msgstr "OpenVPN Connect 移动端导出" -#: root/www/luci-static/resources/view/vpn/openvpn.js:87 +#: root/www/luci-static/resources/view/vpn/openvpn.js:93 msgid "OpenVPN Default Configuration Detected" msgstr "检测到 OpenVPN 默认配置" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:91 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:93 msgid "OpenVPN Instance Key Generator" msgstr "OpenVPN 实例密钥生成器" -#: root/www/luci-static/resources/view/vpn/openvpn.js:146 +#: root/www/luci-static/resources/view/vpn/openvpn.js:155 msgid "OpenVPN Keys" msgstr "OpenVPN 密钥" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:176 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:178 msgid "OpenVPN Keys Detected:" msgstr "检测到 OpenVPN 密钥:" #: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:54 -msgid "OpenVPN Profile Valid:" -msgstr "OpenVPN 配置文件有效:" +msgid "OpenVPN Profile Valid" +msgstr "OpenVPN 配置文件有效" -#: root/www/luci-static/resources/view/vpn/openvpn.js:53 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:56 +msgid "OpenVPN Server Profile detected" +msgstr "检测到 OpenVPN 服务器配置文件" + +#: root/www/luci-static/resources/view/vpn/openvpn.js:55 msgid "OpenVPN Server/Client" msgstr "OpenVPN 服务器/客户端" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:66 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:68 msgid "OpenVPN Setup Wizard" msgstr "OpenVPN 设置向导" -#: root/www/luci-static/resources/view/vpn/openvpn.js:126 +#: root/www/luci-static/resources/view/vpn/openvpn.js:135 msgid "OpenVPN tunnel interfaces (tun0, tun1, tun3, etc.)" msgstr "OpenVPN 隧道接口 (tun0, tun1, tun3 等)" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:55 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:57 msgid "Outside Port" msgstr "外部端口" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:73 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:75 msgid "PKI Suite components saved successfully." msgstr "PKI 套件组件成功保存。" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:154 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:156 msgid "Password as plain text (Auto-SHA256)" msgstr "明文密码(自动 SHA256)" #: root/www/luci-static/resources/view/status/include/35_openvpn.js:20 #: root/www/luci-static/resources/view/vpn/openvpn-status.js:41 -#: root/www/luci-static/resources/view/vpn/openvpn.js:48 +#: root/www/luci-static/resources/view/vpn/openvpn.js:50 msgid "Pending..." msgstr "等待中..." -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:114 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:116 msgid "Please add static routing inside your Main Internet Router (IP: %s) using this OpenVPN Router (IP: %s) as Gateway." msgstr "请在您的主互联网路由器(IP: %s)中添加一条静态路由,并将此 OpenVPN 路由器(IP: %s)用作网关。" -#: root/www/luci-static/resources/view/vpn/openvpn.js:106 +#: root/www/luci-static/resources/view/vpn/openvpn.js:114 msgid "Please assign a unique device name (Common Name) for this mobile profile:" msgstr "请为此移动端配置文件分配一个唯一的设备名称(通用名称/CN):" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:115 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:117 msgid "Please check your profile file. A valid configuration file needs a remote target endpoint and key blocks." msgstr "请检查您的配置文件。有效的配置文件需要一个远程目标端点和密钥块。" -#: root/www/luci-static/resources/view/vpn/openvpn.js:65 +#: root/www/luci-static/resources/view/vpn/openvpn.js:68 msgid "Please click «Save & Apply»." msgstr "请点击 «保存并应用»。" # #: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:118 -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:118 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:120 msgid "Please create a port forwarding rule inside your Main Internet Router (IP: %s) using this OpenVPN Router (IP: %s) as receiver." msgstr "请在您的主互联网路由器(IP: %s)中创建一条端口转发规则,并将此 OpenVPN 路由器(IP: %s)设 for 接收端。" -#: root/www/luci-static/resources/view/vpn/openvpn.js:144 +#: root/www/luci-static/resources/view/vpn/openvpn.js:152 msgid "Please wait while secure cryptographic, router-unique default assets are being generated." msgstr "请稍候,正在生成安全的、路由器唯一的默认加密资产。" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:56 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:58 msgid "Port Forwarding" msgstr "端口转发" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:111 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:113 msgid "Port Open Success" msgstr "端口打开成功" -#: root/www/luci-static/resources/view/vpn/openvpn.js:150 +#: root/www/luci-static/resources/view/vpn/openvpn.js:159 msgid "Private Client Key" msgstr "客户端私钥" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:77 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:79 msgid "Private Key" msgstr "私钥" -#: root/www/luci-static/resources/view/vpn/openvpn.js:148 +#: root/www/luci-static/resources/view/vpn/openvpn.js:157 msgid "Private Server Key" msgstr "服务器私钥" -#: root/www/luci-static/resources/view/vpn/openvpn.js:156 +#: root/www/luci-static/resources/view/vpn/openvpn.js:165 msgid "Proceeding with invalid cryptographic keys will cause total connection failure and may lead to service instability or infinite daemon crash loops." msgstr "使用无效的加密密钥继续操作将导致彻底的连接失败,并可能导致服务不稳定或守护进程的无限崩溃循环。" -#: root/www/luci-static/resources/view/vpn/openvpn.js:75 +#: root/www/luci-static/resources/view/vpn/openvpn.js:79 msgid "Processing..." msgstr "正在处理..." -#: root/www/luci-static/resources/view/vpn/openvpn.js:159 +#: root/www/luci-static/resources/view/vpn/openvpn.js:191 +msgid "Profile Generation Error" +msgstr "配置文件生成错误" + +#: root/www/luci-static/resources/view/vpn/openvpn.js:168 msgid "Profile link active (Native uqr framework (QR-Code) not loaded)." msgstr "配置文件链接已激活 (未加载本地 uqr 框架/二维码)。" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:78 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:80 msgid "Progress & Output" msgstr "进度与输出" -#: root/www/luci-static/resources/view/vpn/openvpn.js:132 +#: root/www/luci-static/resources/view/vpn/openvpn.js:141 msgid "Protected since" msgstr "保护始于" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:57 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:59 msgid "Protocol" msgstr "协议" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:59 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:61 msgid "Public IP Address or Domain" msgstr "公网 IP 地址或域名" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:60 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:62 msgid "Public Static IP" msgstr "公网静态 IP" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:79 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:81 msgid "Pure ECC" msgstr "纯 ECC" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:80 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:82 msgid "Pure RSA" msgstr "纯 RSA" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:81 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:83 msgid "Ready for key generation." msgstr "准备生成密钥。" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:120 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:122 msgid "Ready for openvpn connections." msgstr "已准备好进行 OpenVPN 连接。" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:85 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:87 msgid "Recommended: RSA-2048 + ECC-Prime256v1. RSA ensures CA compatibility, while ECC accelerates the data tunnel." msgstr "推荐: RSA-2048 + ECC-Prime256v1。RSA 确保 CA 兼容性,而 ECC 加速数据隧道。" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:150 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:152 msgid "Reliable free provider. Never expires, perfect for IPv6 and Dual-Stack." msgstr "可靠的免费服务商。永不过期,非常适合 IPv6 和双栈。" @@ -961,24 +999,28 @@ msgid "Remote IP / Port" msgstr "远程 IP / 端口" #: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:45 -#: root/www/luci-static/resources/view/vpn/openvpn.js:55 +#: root/www/luci-static/resources/view/vpn/openvpn.js:57 msgid "Remote LAN Subnet" msgstr "远程 LAN 子网" #: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:46 -#: root/www/luci-static/resources/view/vpn/openvpn.js:56 +#: root/www/luci-static/resources/view/vpn/openvpn.js:58 msgid "Remote Name" msgstr "远程名称" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:61 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:63 msgid "Remote Office Name" msgstr "远程办公室名称" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:62 +#: root/www/luci-static/resources/view/vpn/openvpn.js:110 +msgid "Remote Profile" +msgstr "远程配置文件" + +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:64 msgid "Resolving Target..." msgstr "正在解析目标..." -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:63 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:65 msgid "Reveal/hide password" msgstr "显示/隐藏密码" @@ -986,62 +1028,62 @@ msgstr "显示/隐藏密码" msgid "Running" msgstr "正在运行" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:41 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:42 msgid "Save & Apply" msgstr "保存并应用" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:42 -#: root/www/luci-static/resources/view/vpn/openvpn.js:76 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:43 +#: root/www/luci-static/resources/view/vpn/openvpn.js:80 msgid "Save Config" msgstr "保存配置" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:44 -#: root/www/luci-static/resources/view/vpn/openvpn.js:78 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:45 +#: root/www/luci-static/resources/view/vpn/openvpn.js:82 msgid "Saved" msgstr "已保存" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:43 -#: root/www/luci-static/resources/view/vpn/openvpn.js:77 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:44 +#: root/www/luci-static/resources/view/vpn/openvpn.js:81 msgid "Saving..." msgstr "正在保存..." -#: root/www/luci-static/resources/view/vpn/openvpn.js:108 +#: root/www/luci-static/resources/view/vpn/openvpn.js:116 msgid "Scan QR Code with Mobile Phone Camera:" msgstr "使用手机摄像头扫描二维码:" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:83 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:85 msgid "Secret CA Key" msgstr "CA 私钥" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:67 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:69 msgid "Secure Full Tunnel (Route all traffic over VPN)" msgstr "安全全局隧道 (通过 VPN 路由所有流量)" -#: root/www/luci-static/resources/view/vpn/openvpn.js:109 +#: root/www/luci-static/resources/view/vpn/openvpn.js:117 msgid "Secure Temporary Profile URL Field:" msgstr "安全临时配置文件 URL 字段:" -#: root/www/luci-static/resources/view/vpn/openvpn.js:160 +#: root/www/luci-static/resources/view/vpn/openvpn.js:169 msgid "Security Notice:" msgstr "安全通知:" -#: root/www/luci-static/resources/view/vpn/openvpn.js:165 +#: root/www/luci-static/resources/view/vpn/openvpn.js:174 msgid "Security Notice: Outdated factory default samples detected!" msgstr "安全通知:检测到过时的出厂默认示例!" -#: root/www/luci-static/resources/view/vpn/openvpn.js:162 +#: root/www/luci-static/resources/view/vpn/openvpn.js:171 msgid "Security Warning!" msgstr "安全警告!" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:64 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:66 msgid "Select VPN Type" msgstr "选择 VPN 类型" -#: root/www/luci-static/resources/view/vpn/openvpn.js:2507 +#: root/www/luci-static/resources/view/vpn/openvpn.js:118 msgid "Select clients to remove:" msgstr "选择要删除的客户端:" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:124 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:126 msgid "Select the (.ovpn) profile or (.crt) multi-key bundle. Settings will be configured or updated automatically." msgstr "选择 (.ovpn) 配置文件或 (.crt) 多密钥捆绑包。设置将自动配置或更新。" @@ -1050,15 +1092,15 @@ msgstr "选择 (.ovpn) 配置文件或 (.crt) 多密钥捆绑包。设置将自 msgid "Server" msgstr "服务器" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:68 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:70 msgid "Server Address" msgstr "服务器地址" -#: root/www/luci-static/resources/view/vpn/openvpn.js:147 +#: root/www/luci-static/resources/view/vpn/openvpn.js:156 msgid "Server Certificate" msgstr "服务器证书" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:125 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:127 msgid "Server for Main Office (Headquarters)" msgstr "主办公室服务器 (总部)" @@ -1066,19 +1108,19 @@ msgstr "主办公室服务器 (总部)" msgid "Session" msgstr "会话" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:65 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:67 msgid "Setup" msgstr "设置" -#: root/www/luci-static/resources/view/vpn/openvpn.js:79 +#: root/www/luci-static/resources/view/vpn/openvpn.js:83 msgid "Show" msgstr "显示" -#: root/www/luci-static/resources/view/vpn/openvpn.js:131 +#: root/www/luci-static/resources/view/vpn/openvpn.js:140 msgid "Show flood guard drops" msgstr "显示 Flood Guard 丢弃项" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:84 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:86 msgid "Signing client certificate using server CA..." msgstr "正在使用服务器 CA 签名客户端证书..." @@ -1086,162 +1128,170 @@ msgstr "正在使用服务器 CA 签名客户端证书..." msgid "Since" msgstr "自" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:126 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:128 msgid "Skip Check & Unlock Create Button" msgstr "跳过检查并解锁创建按钮" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:69 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:71 msgid "Split Tunnel (Access home LAN only, faster web browsing)" msgstr "分流隧道 (仅访问家庭局域网,网页浏览更快)" -#: root/www/luci-static/resources/view/vpn/openvpn.js:49 +#: root/www/luci-static/resources/view/vpn/openvpn.js:51 msgid "Starting..." msgstr "正在启动..." -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:99 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:101 msgid "Static IP check bypassed by administrator." msgstr "管理员已绕过静态 IP 检查。" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:100 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:102 msgid "Static IP now checked by administrator." msgstr "管理员现已检查静态 IP。" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:70 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:72 msgid "Static Route" msgstr "静态路由" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:123 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:125 msgid "Static Routing Success" msgstr "静态路由成功" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:122 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:124 msgid "Static Routing missing!" msgstr "缺少静态路由!" #: root/www/luci-static/resources/view/vpn/openvpn-status.js:44 -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:71 -#: root/www/luci-static/resources/view/vpn/openvpn.js:50 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:73 +#: root/www/luci-static/resources/view/vpn/openvpn.js:52 msgid "Status" msgstr "状态" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:74 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:76 msgid "Step 1/3: Generating Certificate Authority (CA)..." msgstr "步骤 1/3: 正在生成证书颁发机构 (CA)..." -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:75 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:77 msgid "Step 2/3: Generating Private Server Key..." msgstr "步骤 2/3: 正在生成服务器私钥..." -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:76 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:78 msgid "Step 3/3: Generating & Signing Server Certificate..." msgstr "步骤 3/3: 正在生成并签名服务器证书..." -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:103 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:105 msgid "Success: The IP address is valid and fully resolvable!" msgstr "成功: IP 地址有效且可完全解析!" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:162 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:164 msgid "Super fast setup without registration. WARNING: Blocked in some networks because it is completely anonymous." msgstr "超快速设置,无需注册。警告:在某些网络中可能被拦截,因其完全匿名。" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:127 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:129 msgid "Switch to Dynamic DNS (DDNS)" msgstr "切换到动态 DNS (DDNS)" -#: root/www/luci-static/resources/view/vpn/openvpn.js:111 +#: root/www/luci-static/resources/view/vpn/openvpn.js:120 msgid "System logs ..." msgstr "系统日志..." -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:129 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:131 msgid "TCP (Good for traveling - works behind firewalls)" msgstr "TCP (适合旅行 - 可在防火墙后工作)" -#: root/www/luci-static/resources/view/vpn/openvpn.js:152 +#: root/www/luci-static/resources/view/vpn/openvpn.js:161 msgid "TLS Crypt v2 Client Key" msgstr "TLS Crypt v2 客户端密钥" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:67 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:69 msgid "TLS Crypt v2 Client Key (Anti-DoS / Port-Scan Protection)" msgstr "TLS Crypt v2 客户端密钥 (防御 DoS / 端口扫描保护)" -#: root/www/luci-static/resources/view/vpn/openvpn.js:151 +#: root/www/luci-static/resources/view/vpn/openvpn.js:160 msgid "TLS Crypt v2 Server Master Key" msgstr "TLS Crypt v2 服务器主密钥" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:66 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:68 msgid "TLS Crypt v2 Server Master Key (Anti-DoS / Port-Scan Protection)" msgstr "TLS Crypt v2 服务器主密钥 (防御 DoS / 端口扫描保护)" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:88 +#: root/www/luci-static/resources/view/vpn/openvpn.js:193 +msgid "TLS crypt v2 client key generation failed" +msgstr "TLS crypt v2 客户端密钥生成失败" + +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:111 +msgid "TLS crypt v2 client key generation failed." +msgstr "TLS crypt v2 客户端密钥生成失败。" + +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:90 msgid "TLS-Crypt key saved successfully." msgstr "TLS-Crypt 密钥保存成功。" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:121 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:123 msgid "The VPN routing via Main Internet Router (IP: %s) to remote subnet(s) (Net: %s)" msgstr "经由主互联网路由器(IP: %s)到远程子网(网络: %s)的 VPN 路由" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:86 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:88 msgid "The main TLS security key on the server. It encrypts the connection and creates the keys for all clients." msgstr "服务器上的主 TLS 安全密钥。它用于加密连接并为所有客户端创建密钥。" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:128 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:130 msgid "The port forward" msgstr "端口转发" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:117 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:119 msgid "The port used on this OpenWrt router." msgstr "此 OpenWrt 路由器上使用的端口。" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:116 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:118 msgid "The port used on your main internet router." msgstr "您的主 internet 路由器上使用的端口。" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:119 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:121 msgid "The public IP address or your own external domain name." msgstr "公网 IP 地址或您自己的外部域名。" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:106 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:109 msgid "The saved key data contains structural format corruption!" msgstr "保存的密钥数据包含结构格式损坏!" -#: root/www/luci-static/resources/view/vpn/openvpn.js:178 +#: root/www/luci-static/resources/view/vpn/openvpn.js:188 msgid "The uploaded file is empty." msgstr "上传的文件为空。" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:89 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:91 msgid "This OpenVPN Router is not the Main Internet Router." msgstr "此 OpenVPN 路由器不是主 internet 路由器。" -#: root/www/luci-static/resources/view/vpn/openvpn.js:107 +#: root/www/luci-static/resources/view/vpn/openvpn.js:115 msgid "This automated initialization process can take a few minutes on your device..." msgstr "此自动初始化过程可能需要几分钟时间..." -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:110 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:112 msgid "This file contains only VPN client keys." msgstr "此文件仅包含 VPN 客户端密钥。" -#: root/www/luci-static/resources/view/vpn/openvpn.js:92 +#: root/www/luci-static/resources/view/vpn/openvpn.js:98 msgid "This is the address your VPN clients will use to connect from the internet. Scan the QR code with your phone camera or use the link below to download your connection profile." msgstr "这是您的 VPN 客户端从互联网连接时使用的地址。使用手机摄像头扫描二维码或点击下方链接下载您的连接配置文件。" -#: root/www/luci-static/resources/view/vpn/openvpn.js:163 +#: root/www/luci-static/resources/view/vpn/openvpn.js:172 msgid "This profile does not use TLS-Crypt v2. It is highly recommended to use TLS-Crypt v2 for maximum security, better performance, and protection against unauthorized scanning." msgstr "此配置文件未使用 TLS-Crypt v2。强烈建议使用 TLS-Crypt v2 以获得最高安全性、更好的性能以及防御未经授权的扫描保护。" -#: root/www/luci-static/resources/view/vpn/openvpn.js:110 +#: root/www/luci-static/resources/view/vpn/openvpn.js:119 msgid "This server handles network rules for multiple remote offices. Please choose an existing office name or write a name to make a new connection profile." msgstr "此服务器处理多个远程办公室的网络规则。请选择现有的办公室名称或写入一个新名称以建立新的连接配置文件。" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:58 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:59 msgid "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)" msgstr "为了安全起见,新实例需要自己的密钥文件。是否现在创建?(推荐 - 通常只需几分钟)" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:72 -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:170 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:74 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:172 msgid "Token / Password" msgstr "令牌 / 密码" -#: root/www/luci-static/resources/view/vpn/openvpn.js:133 +#: root/www/luci-static/resources/view/vpn/openvpn.js:142 msgid "Traffic Rules" msgstr "流量规则" @@ -1254,43 +1304,47 @@ msgstr "传输" msgid "Type" msgstr "类型" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:130 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:132 msgid "UDP (High Performance - maximum throughput)" msgstr "UDP(高性能 - 最大吞吐量)" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:93 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:95 msgid "Unknown Asset" msgstr "未知资产" +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:96 +msgid "Unknown Structure" +msgstr "未知结构" + #: root/www/luci-static/resources/view/vpn/openvpn-status.js:52 msgid "UpTime" msgstr "运行时间" -#: root/www/luci-static/resources/view/vpn/openvpn.js:80 +#: root/www/luci-static/resources/view/vpn/openvpn.js:84 msgid "Upload" msgstr "上传" -#: root/www/luci-static/resources/view/vpn/openvpn.js:112 +#: root/www/luci-static/resources/view/vpn/openvpn.js:121 msgid "Uploaded file is invalid or corrupt!" msgstr "上传的文件无效或已损坏!" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:131 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:133 msgid "Use Dynamic DNS (DDNS) Service" msgstr "使用动态 DNS (DDNS) 服务" -#: root/www/luci-static/resources/view/vpn/openvpn.js:81 +#: root/www/luci-static/resources/view/vpn/openvpn.js:85 msgid "Use Fallback" msgstr "使用备用 (Fallback)" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:132 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:134 msgid "Use Public IP Address / Domain" msgstr "使用公网 IP 地址 / 域名" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:73 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:75 msgid "Username" msgstr "用户名" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:155 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:157 msgid "Username (Optional)" msgstr "用户名 (可选)" @@ -1298,15 +1352,15 @@ msgstr "用户名 (可选)" msgid "VPN" msgstr "VPN" -#: root/www/luci-static/resources/view/vpn/openvpn.js:113 +#: root/www/luci-static/resources/view/vpn/openvpn.js:122 msgid "VPN Client Connection Domain or Address:" msgstr "VPN 客户端连接域名或地址:" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:96 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:99 msgid "VPN Key Type:" msgstr "VPN 密钥类型:" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:74 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:76 msgid "VPN Name" msgstr "VPN 名称" @@ -1314,31 +1368,31 @@ msgstr "VPN 名称" msgid "Valid Until" msgstr "有效期至" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:189 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:191 msgid "Validation Error: The VPN Name must contain only letters, numbers, hyphens or underscores." msgstr "验证错误: VPN 名称只能包含字母、数字、连字符或下划线。" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:101 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:103 msgid "Validation bypassed after network failure." msgstr "网络故障后已绕过验证。" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:102 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:104 msgid "Validation bypassed by administrator." msgstr "管理员已绕过验证。" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:95 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:98 msgid "Validity (Days)" msgstr "有效期 (天)" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:156 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:158 msgid "Very popular provider. WARNING: Free accounts require manual activation/renewal every 30 days via email." msgstr "非常受欢迎的服务商。警告:免费账户需要每 30 天通过电子邮件手动激活/续期一次。" -#: root/www/luci-static/resources/view/vpn/openvpn.js:134 +#: root/www/luci-static/resources/view/vpn/openvpn.js:143 msgid "WAN ports" msgstr "WAN 端口" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:99 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:102 msgid "WARNING: Clicking save will instantly break all active VPN tunnels. You must generate and share new client profiles (.ovpn) to restore the connections." msgstr "警告: 点击保存将立即中断所有活动的 VPN 隧道。您必须生成并共享新的客户端配置文件 (.ovpn) 才能恢复连接。" @@ -1346,32 +1400,32 @@ msgstr "警告: 点击保存将立即中断所有活动的 VPN 隧道。您必 msgid "WORKING" msgstr "正常" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:178 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:180 msgid "Warning: If you upload wrong keys, your connection will stop!" msgstr "警告: 如果您上传了错误的密钥,您的连接将会停止!" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:174 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:176 msgid "Warning: The IP address cannot be resolved. Your router might be offline." msgstr "警告: 无法解析该 IP 地址。您的路由器可能处于离线状态。" -#: root/www/luci-static/resources/view/vpn/openvpn.js:155 +#: root/www/luci-static/resources/view/vpn/openvpn.js:164 msgid "Warning: The uploaded file structure does not match the expected key type." msgstr "警告: 上传的文件结构与预期的密钥类型不匹配。" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:175 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:177 msgid "Warning: You did not select «Public Static IP». If your public IP address changes, your VPN tunnel will crash." msgstr "警告: 您未选择 «公网静态 IP»。如果您的公网 IP 地址发生更改,您的 VPN 隧道将会崩溃。" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:177 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:179 msgid "Warning: You have un-saved text in the input fields. Please click the blue «Add Office» button to save it, or clear the fields to continue." msgstr "警告: 输入框中有未保存的文本。请点击蓝色的 «添加办公室» 按钮进行保存,或清空字段以继续。" #: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:31 -#: root/www/luci-static/resources/view/vpn/openvpn.js:57 +#: root/www/luci-static/resources/view/vpn/openvpn.js:59 msgid "Wizard" msgstr "向导" -#: root/www/luci-static/resources/view/vpn/openvpn.js:179 +#: root/www/luci-static/resources/view/vpn/openvpn.js:189 msgid "Wrong key type! Please upload the correct cryptographic file." msgstr "错误的密钥类型!请上传正确的加密文件。" @@ -1380,116 +1434,99 @@ msgid "Years" msgstr "年" #: root/www/luci-static/resources/view/vpn/openvpn-status.js:37 -#: root/www/luci-static/resources/view/vpn/openvpn.js:58 +#: root/www/luci-static/resources/view/vpn/openvpn.js:60 msgid "Yes" msgstr "是" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:57 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:58 msgid "Yes (Generate Unique Keys)" msgstr "是 (生成唯一密钥)" -#: root/www/luci-static/resources/view/vpn/openvpn.js:117 +#: root/www/luci-static/resources/view/vpn/openvpn.js:126 msgid "are opened automatically for secure OpenVPN connections." msgstr "会自动开启以用于安全的 OpenVPN 连接。" -#: root/www/luci-static/resources/view/vpn/openvpn.js:120 +#: root/www/luci-static/resources/view/vpn/openvpn.js:129 msgid "devices is created automatically." msgstr "设备将自动创建。" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:148 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:150 msgid "dynv6 (dynv6.com)" msgstr "dynv6 (dynv6.com)" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:58 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:60 msgid "e.g." msgstr "例如:" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:70 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:72 msgid "e.g. -sha256 ; -noout ; -sha256" msgstr "例如:-sha256 ; -noout ; -sha256" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:69 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:71 msgid "e.g. -text (shows hex output)" msgstr "例如:-text(显示十六进制输出)" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:71 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:73 msgid "e.g. -traditional ; ; -sha256" msgstr "例如:-traditional ; ; -sha256" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:113 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:115 msgid "e.g. Mobile-Server, Main-Office" msgstr "例如: Mobile-Server, Main-Office" -#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:68 +#: root/www/luci-static/resources/view/vpn/openvpn-keygen.js:70 msgid "e.g. Remote Office 1" msgstr "例如:远程办公室 1" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:171 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:173 msgid "e.g. https://custom-provider.com?domain=...&key=..." msgstr "例如: https://custom-provider.com?domain=...&key=..." -#: root/www/luci-static/resources/view/vpn/openvpn.js:105 +#: root/www/luci-static/resources/view/vpn/openvpn.js:113 msgid "e.g. my-smartphone" msgstr "例如: my-smartphone" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:112 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:114 msgid "e.g. mydomain.com or your public IP" msgstr "例如: mydomain.com 或您的公网 IP" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:168 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:170 msgid "e.g. myname-custom-provider.com" msgstr "例如: myname-custom-provider.com" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:158 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:160 msgid "e.g. myname.ddns.net" msgstr "例如: myname.ddns.net" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:137 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:139 msgid "e.g. myname.duckdns.org" msgstr "例如: myname.duckdns.org" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:152 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:154 msgid "e.g. myname.dynu.net" msgstr "例如: myname.dynu.net" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:147 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:149 msgid "e.g. myname.dynv6.net" msgstr "例如: myname.dynv6.net" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:164 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:166 msgid "e.g. myname.freemyip.com" msgstr "例如: myname.freemyip.com" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:142 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:144 msgid "e.g. myname.ipv64.net" msgstr "例如: myname.ipv64.net" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:104 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:106 msgid "is already" msgstr "已经" -#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:105 +#: root/www/luci-static/resources/view/vpn/openvpn-wizard.js:107 msgid "is currently" msgstr "当前正在" -#: root/www/luci-static/resources/view/vpn/openvpn.js:127 +#: root/www/luci-static/resources/view/vpn/openvpn.js:136 msgid "packets" msgstr "数据包" -#~ msgid "Smart Firewall" -#~ msgstr "智能防火墙" - -#~ msgid "Actual" -#~ msgstr "实际" - -#~ msgid "The external port" -#~ msgstr "外部端口" - -#~ msgid "as Gateway." -#~ msgstr "作为网关。" - -#~ msgid "as receiver." -#~ msgstr "作为接收方。" - -#~ msgid "using this OpenVPN Router" -#~ msgstr "使用此 OpenVPN 路由器" diff --git a/luci-app-openvpn-plus/root/etc/openvpn/luci/server.default.conf b/luci-app-openvpn-plus/root/etc/openvpn/luci/server.default.conf index 643478cfc..307daf3c9 100755 --- a/luci-app-openvpn-plus/root/etc/openvpn/luci/server.default.conf +++ b/luci-app-openvpn-plus/root/etc/openvpn/luci/server.default.conf @@ -30,7 +30,7 @@ tls-groups secp256r1 cipher AES-256-GCM data-ciphers AES-256-GCM:AES-128-GCM:CHACHA20-POLY1305 -# --- OpenVPN Flood Guard --- +# --- Flood Guard Filter --- setenv floodguard on # --- Client Push Options --- diff --git a/luci-app-openvpn-plus/root/usr/libexec/luci-app-openvpn-plus b/luci-app-openvpn-plus/root/usr/libexec/luci-app-openvpn-plus index e22b0b138..55bb34a3b 100755 --- a/luci-app-openvpn-plus/root/usr/libexec/luci-app-openvpn-plus +++ b/luci-app-openvpn-plus/root/usr/libexec/luci-app-openvpn-plus @@ -471,16 +471,18 @@ openssldgst() { # Manage temporary download links for mobile profiles [create|delete] symlink() { shift # Remove "symlink" from the arguments list - local ovpn_num="$1" + local ovpn_instance="$1" local action="$2" 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 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. - if [ -z "$ovpn_num" ] || ! echo "$ovpn_num" | grep -q '^[0-9]\+$'; then - echo "ERROR: Invalid instance number." + # Check if instance name is provided and safe (alphanumeric only). + if [ -z "$ovpn_instance" ] || ! echo "$ovpn_instance" | grep -q '^[A-Za-z0-9]\+$'; then + echo "ERROR: Invalid instance name." return 1 fi @@ -491,53 +493,66 @@ symlink() { # Fallback if the cleaned name is empty. if [ -z "$link_filename" ]; then - link_filename="instance${ovpn_num}" + link_filename="${ovpn_instance}" fi case "$action" in create) - # Check if the source profile exists on disk. if [ ! -f "$export_static_path" ]; then echo "ERROR: Profile file missing at $export_static_path" return 1 fi - # Create a random crypt-token (9 random bytes as hex). + # Create a 9-byte random secret token as hex local secret_token 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="/www/ovpn-tmp-${secret_token}-${link_filename}" - local token_protected_file="${session_dir}/${link_filename}_client.ovpn" - - # Create the directory safely. + local session_dir="${ram_link_dir}/ovpn-tmp-${secret_token}-${link_filename}" 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 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 lan_ip=$(lanip) if [ -z "$lan_ip" ] || [ "$lan_ip" = "0.0.0.0" ]; then 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 - # Return SUCCESS and the clean download link with sub-folder - echo "SUCCESS: http://${lan_ip}/ovpn-tmp-${secret_token}-${link_filename}/${link_filename}_client.ovpn" + echo "SUCCESS: http://${lan_ip}/ovpn-downloads/ovpn-tmp-${secret_token}-${link_filename}/${link_filename}.ovpn" else echo "ERROR: Failed to create web export file." return 1 fi ;; delete) - # Safe clean up using wildcard on the directory name. - # This removes the folder, the temporary copy, and deletes the static file. - rm -rf /www/ovpn-tmp-*-"${link_filename}" - echo "SUCCESS: Link directory and static profile deleted." + # Safe deletion inside RAM directory only + if [ -n "$link_filename" ] && [ -d "$ram_link_dir" ]; then + rm -rf "${ram_link_dir}"/ovpn-tmp-*-"${link_filename}" + + # 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 ;; esac @@ -1357,6 +1372,8 @@ cleanup() { local instname local conf_dir="/etc/openvpn/luci" 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 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 for conf_file in "$conf_dir"/instance*.conf; do if [ -f "$conf_file" ]; then - # Extract the pure instance name from the file path instname="${conf_file##*/}" instname="${instname%.conf}" @@ -1382,17 +1398,31 @@ cleanup() { rm -f "$key_dir/client_${instname}.key" rm -f "$key_dir/server_${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 done - # Step 4: Delete all remaining temporary symlink files from the system - rm -rf /www/ovpn-tmp-* - rm -f "$conf_dir"/instance*.ovpn + # Step 4: Safely clean up the empty base directories and the web symlink + if [ -d "$ram_link_dir" ]; then + # 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 } - # Validate and extract cryptographic metadata for LuCI keymeta() { shift # Remove "keymeta" from the arguments list @@ -1475,7 +1505,7 @@ keymeta() { fi fi else - echo "TYPE: Custom Raw Data Structure" + echo "INVALID: Custom Raw Data Structure" fi else echo "ERROR: File is empty or missing." diff --git a/luci-app-openvpn-plus/root/www/luci-static/resources/view/vpn/openvpn-keygen.js b/luci-app-openvpn-plus/root/www/luci-static/resources/view/vpn/openvpn-keygen.js index ffb5f48aa..8a5636024 100755 --- a/luci-app-openvpn-plus/root/www/luci-static/resources/view/vpn/openvpn-keygen.js +++ b/luci-app-openvpn-plus/root/www/luci-static/resources/view/vpn/openvpn-keygen.js @@ -33,6 +33,7 @@ const TXT = { client: _('Client') }, BTN: { + backup: _('Backup'), close: _('Close'), cancel: _('Cancel'), download: _('Download'), @@ -57,6 +58,7 @@ const TXT = { 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)'), generating_client_keys: _('Generating private client key...'), + generating_tls_client_keys: _('Generating TLS crypt v2 client key...'), key_strength: _('Key Strength'), loading_contents: _('Loading key contents...'), master_ca: _('Master CA'), @@ -89,8 +91,9 @@ const TXT = { tls_key_server: _('Master Server Secret Key'), tls_key_client: _('Client Key'), title_main: _('OpenVPN Instance Key Generator'), - key_allocation: ('Key Allocation'), + add_server_instance: ('Add OpenVPN Server Instance'), 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.'), validity_days: _('Validity (Days)'), vpn_key_type: _('VPN Key Type:') @@ -105,6 +108,7 @@ const TXT = { ERROR: { format_corruption: _('The saved key data contains structural format corruption!'), 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 = { valid: false, type: CRYPTO_TYPE.INVALID_KEY, - metadata: { type: _('Unknown Structure'), subject: '', expires: '', algo: '', curve: '' }, + metadata: { type: TXT.KEY.unknown_structure, subject: '', expires: '', algo: '', curve: '' }, raw: '' }; @@ -221,10 +225,14 @@ const getKeyMeta = async function (keyfile, luci_app_openvpn_plus_callback) { } // 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 - 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; } @@ -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 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 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', { '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 + ' ...') ]); - const wizardButtonWrapperRow = E('div', { 'style': 'display:flex; justify-content:flex-end; width:100%; margin-top:5px;' + styleHide }, [openWizardBtn]); @@ -820,6 +828,12 @@ const openAutomatedPostKeyGenModal = async function (newInstanceItem, appData, w 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 noBtn.addEventListener('click', async function (ev) { ev.preventDefault(); @@ -889,7 +903,7 @@ const openAutomatedPostKeyGenModal = async function (newInstanceItem, appData, w 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-section' }, [ 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 if (state.mode === KEYTYPE.client_pki) { 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; // 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; 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; } @@ -1338,14 +1352,13 @@ const openKeyGenModal = function (instance_id, displayId, role, appData, uciSave /** * Export the open wizard modal function to LuCI */ -return L.Class.extend( - { - KEYTYPE: KEYTYPE, - CRYPTO_TYPE: CRYPTO_TYPE, - getKeyMeta: getKeyMeta, - executeAsynchronousKeyGen: executeAsynchronousKeyGen, - openKeyEditorModal: openKeyEditorModal, - openAutomatedPostKeyGenModal: openAutomatedPostKeyGenModal, - openKeyGenModal: openKeyGenModal - }); +return L.Class.extend({ + KEYTYPE: KEYTYPE, + CRYPTO_TYPE: CRYPTO_TYPE, + getKeyMeta: getKeyMeta, + executeAsynchronousKeyGen: executeAsynchronousKeyGen, + openKeyEditorModal: openKeyEditorModal, + openAutomatedPostKeyGenModal: openAutomatedPostKeyGenModal, + openKeyGenModal: openKeyGenModal +}); diff --git a/luci-app-openvpn-plus/root/www/luci-static/resources/view/vpn/openvpn-status.js b/luci-app-openvpn-plus/root/www/luci-static/resources/view/vpn/openvpn-status.js index 4bf3fca4a..17188479d 100755 --- a/luci-app-openvpn-plus/root/www/luci-static/resources/view/vpn/openvpn-status.js +++ b/luci-app-openvpn-plus/root/www/luci-static/resources/view/vpn/openvpn-status.js @@ -142,6 +142,7 @@ const CLIENT_TEMPLATE = Object.freeze({ timeMs: 0, }); + /* * --- HELPER --- */ @@ -153,6 +154,9 @@ const CLIENT_TEMPLATE = Object.freeze({ const configAssetCache = {}; const statusFileCache = {}; +/** + * Normalizes all Windows and Mac line breaks into clean UNIX line breaks for textareas. + */ const sanitizeInputText = function (value) { const rawText = value ? String(value).trim() : ''; return rawText.replace(/\r\n/g, '\n').replace(/\r/g, '\n'); @@ -270,6 +274,9 @@ const isAnyInstanceEnabled = function (ovpnUciSections) { return false; }; +/** + * Caches openVpnInfo + */ const openVpnInfo = { macAddress: 0x0000, version: null, @@ -327,7 +334,6 @@ const getOpenVpnInfo = async function () { } }; - /** * 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 sitetosite_regex = /^[ \t]*setenv\s+sitetosite\s+on/mi; return sitetosite_regex.test(content); } +/** + * Check if flood guard is turned on + */ const parseFloodGuard = function (content) { const sitetosite_regex = /^[ \t]*setenv\s+floodguard\s+on/mi; return sitetosite_regex.test(content); @@ -1282,7 +1291,6 @@ const getProcessStartTime = async function (id, pidVal, currentUptime) { return 0; }; - /** * Read the connected client status file if necessary or use the statusFileCache */ diff --git a/luci-app-openvpn-plus/root/www/luci-static/resources/view/vpn/openvpn-wizard.js b/luci-app-openvpn-plus/root/www/luci-static/resources/view/vpn/openvpn-wizard.js index 1248ba174..06fa42007 100755 --- a/luci-app-openvpn-plus/root/www/luci-static/resources/view/vpn/openvpn-wizard.js +++ b/luci-app-openvpn-plus/root/www/luci-static/resources/view/vpn/openvpn-wizard.js @@ -51,7 +51,9 @@ const TXT = { MISSING: _('MISSING'), WORKING: _('WORKING'), 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'), port_forwarding: _('Port Forwarding'), 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 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 + 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 ddnsProvider: '', // Selected dynamic DNS provider configuration key profile token identification 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 const wizardParams = Object.assign({}, WIZARD_PARAMS_TEMPLATE, { clients: [], - role: OPENVPN.ROLE.CLIENT, + role: extractedParams.isServerBackup ? OPENVPN.ROLE.SERVER : OPENVPN.ROLE.CLIENT, port: extractedParams.port, proto: extractedParams.proto, displayName: extractedParams.cname || cleanCustomName || TXT.INFO.imported_clients, @@ -1175,7 +1179,8 @@ const importClientInstance = async function (elements, wizardData, nextBtn, prev remoteServerLan: extractedParams.remoteServerLan, isApMode: false, ddnsOrPublicIp: '', - importedRawOvpn: activeSubParams.importedRawOvpn || '' + importedRawOvpn: activeSubParams.importedRawOvpn || '', + isServerBackup: extractedParams.isServerBackup }); 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 protoMatch = text.match(/^proto\s+(\S+)/m); const hasCaTag = (text.indexOf('') !== -1); + const isServerBackupFile = /^server\s+/m.test(text) || /^server-ipv6\s+/m.test(text); if (remoteServerMatch) { - // PATH A: Valid full configuration profile (.ovpn) + // PATH A: Valid full client 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.client_profile_detected), + E('br'), E('span', {}, [ ICON.ARROW + ' ' + TXT.INFO.server_address + ': ', 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; } } + 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)) { - // PATH B: Valid cryptographic multi-key bundle (.crt) + // PATH C: Valid cryptographic multi-key bundle (.crt) uploadedFileContentStr = text; const warningContent = E('div', {}, [ @@ -1677,7 +1701,7 @@ const renderOptionSiteClient = function (buttons, messageBox) { } } 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'); customUploadBtn.classList.add('cbi-input-invalid'); fileInput.value = ''; @@ -2215,7 +2239,11 @@ const updateStepVisibility = function (elements, state, buttons, wizardData) { 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 */ @@ -2870,8 +2897,15 @@ const openWizardModal = function (wizardData, optionalHideClient) { 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 - L.ui.showModal(ICON.ROCKET + TXT.INFO.setup_wizard, [ + L.ui.showModal(title, [ E('div', { 'class': 'cbi-map ovpn-plus-setup-wizard', 'style': 'width: 100%; margin: 0 auto;' @@ -2898,12 +2932,12 @@ const openWizardModal = function (wizardData, optionalHideClient) { /** * Export the open wizard modal function to LuCI */ -return L.Class.extend( - { - WIZARD_DATA_TEMPLATE: WIZARD_DATA_TEMPLATE, - WIZARD_PARAMS_TEMPLATE: WIZARD_PARAMS_TEMPLATE, - renderSubnetInputs: renderSubnetInputs, - renderPortForwardingAlert: renderPortForwardingAlert, - getValidCommonName: getValidCommonName, - openWizardModal: openWizardModal - }); +return L.Class.extend({ + WIZARD_DATA_TEMPLATE: WIZARD_DATA_TEMPLATE, + WIZARD_PARAMS_TEMPLATE: WIZARD_PARAMS_TEMPLATE, + renderSubnetInputs: renderSubnetInputs, + renderPortForwardingAlert: renderPortForwardingAlert, + getValidCommonName: getValidCommonName, + openWizardModal: openWizardModal +}); + diff --git a/luci-app-openvpn-plus/root/www/luci-static/resources/view/vpn/openvpn.js b/luci-app-openvpn-plus/root/www/luci-static/resources/view/vpn/openvpn.js index 2744d9d94..5760073fb 100755 --- a/luci-app-openvpn-plus/root/www/luci-static/resources/view/vpn/openvpn.js +++ b/luci-app-openvpn-plus/root/www/luci-static/resources/view/vpn/openvpn.js @@ -32,10 +32,12 @@ const view = L.view; const TXT = { INFO: { active: _('Active'), + add: _('Add'), clearing: _('Clearing...'), creating: _('Creating...'), disable: _('Disable'), disabled: _('Disabled'), + delete: _('Delete'), export_profile: _('Export Profile'), enable: _('Enable'), error: _('Error'), @@ -60,6 +62,7 @@ const TXT = { BTN: { add_client: _('Add Client Instance'), add_server: _('Add Server Instance'), + backup: _('Backup'), cancel: _('Cancel'), change: _('Change'), click_save_apply: _('Please click «Save & Apply».'), @@ -70,6 +73,7 @@ const TXT = { download_ovpn: _('Download (.ovpn)'), enabled: _('Enabled'), generating: _('Generating...'), + keygen: _('KeyGen'), next: _('Next'), ok: _('OK'), processing: _('Processing...'), @@ -84,6 +88,8 @@ const TXT = { }, MSG: { client_export: _('OpenVPN Client Export'), + backup_export: _('OpenVPN Backup Export'), + backup_import: _('OpenVPN Backup Import'), default_config_detected: _('OpenVPN Default Configuration Detected'), config_changed_reload: _('Configuration changed! Applying will temporarily restart the OpenVPN instance.'), confirm_del: _('Are you sure you want to delete '), @@ -93,7 +99,7 @@ const TXT = { export_ovpn: _('Export (.ovpn)'), export_openvpn_connect_client_profile: _('Export OpenVPN Connect Client Profile'), import_ovpn: _('Import (.ovpn)'), - import_openvpn_connect_client_profile: _('Import OpenVPN Connect Client Profile'), + import_openvpn_client_profile: _('Import OpenVPN Client Profile'), import_profile: _('Import Profile'), lan_to_lan_profile_selection: _('LAN-to-LAN Router Profile Selection'), manage_instance: _('Here you can manage multiple OpenVPN Server and Client instances dynamically.'), @@ -101,12 +107,15 @@ const TXT = { no_active_log_entries: _('No active OpenVPN log entries found.'), no_vpn_configured: _('No OpenVPN instances configured yet. Use the Wizard to create new OpenVPN instances.'), no_vpn_log: _('No active OpenVPN log entries found.'), - office_profile: _('Office Profile:'), + remote_profile: _('Remote Profile'), + backup_server_profile: _('Backup Server Profile'), + delete_remote_profile: _('Delete Remote Profile...'), placeholder_cn_mobile: _('e.g. my-smartphone'), please_assign_cn_name: _('Please assign a unique device name (Common Name) for this mobile profile:'), process_take_few_minutes: _('This automated initialization process can take a few minutes on your device...'), scan_qr_code_with_camera: _('Scan QR Code with Mobile Phone Camera:'), secure_temporary_profile_url: _('Secure Temporary Profile URL Field:'), + select_clients_to_remove: _('Select clients to remove:'), select_remote_office_to_export: _('This server handles network rules for multiple remote offices. Please choose an existing office name or write a name to make a new connection profile.'), system_logs: _('System logs ...'), uploaded_file_invalid: _('Uploaded file is invalid or corrupt!'), @@ -136,12 +145,12 @@ const TXT = { KEY: { ca: _('Certification Authority'), client_connection_needs_server_key_and_config: _('A client connection needs the server keys and config. Select an .ovpn profile to import them automatically, or click Cancel to use the default settings.'), - create_key_new_office: _('Create keys for a new office...'), + create_key_new_office: _('Create keys for a new remote office...'), + create_key_new_mobile: _('Create keys for a new mobile client...'), dh: _('Diffie - Hellman Parameters'), - key_verification_failed: _('Key verification failed'), - keygen: _('KeyGen'), keygen_in_progress: _('Key generation in progress...'), keygen_wait: _('Please wait while secure cryptographic, router-unique default assets are being generated.'), + log_crt_completed_tls_client_keygen: _('LOG: Certificate pipeline completed. Generating TLS crypt v2 client key...'), keyfile_not_exist: _('Key file is empty or does not exist on disk yet.'), openvpn_keys: _('OpenVPN Keys'), server_crt: _('Server Certificate'), @@ -172,11 +181,16 @@ const TXT = { key_check_failed: _('Key check failed. One security key is broken or invalid.'), keygen_failed: _('Key generation failed.'), key_type_mismatch: _('Key type mismatch'), + key_verification_failed: _('Key verification failed'), ip_used_on_other_office: _('Error: This IP address is already used by another office. Every office needs a different IP address!'), office_name_already_exist: _('Error: An office with this name is already in the list.'), use_local_ipnetwork: _('Error: You cannot use this IP network! It is the local network of this router and will block all traffic.'), upload_key_empty: _('The uploaded file is empty.'), - wrong_key_type: _('Wrong key type! Please upload the correct cryptographic file.') + wrong_key_type: _('Wrong key type! Please upload the correct cryptographic file.'), + failed_generate_profile_download: _('Failed to generate secure download profile. Please check system logs.'), + profile_generation_error: _('Profile Generation Error'), + network_status_check_failed: _('Error: Network status check failed.'), + tls_client_keygen_failed: _('TLS crypt v2 client key generation failed') } } @@ -281,6 +295,7 @@ const CFG = Object.freeze({ const ICON = Object.freeze({ ARROW: '➔ ', + BACKUP: '📦 ', CHECK: '✓ ', CHANGE: '✏️ ', OFFICE: '🏢 ', @@ -498,13 +513,16 @@ const secureFileCallbacks = Object.freeze({ luci_app_openvpn_plus: luci_app_openvpn_plus }); - -// Checks if a specific OpenVPN instance is enabled directly in the live UCI buffer +/** + * Checks if a specific OpenVPN instance is enabled directly in the live UCI buffer + */ const isInstanceEnabled = function (instance_id) { return L.uci.get(CFG.CMD.openvpn, instance_id, 'enabled') === '1'; }; -// Checks if at least one OpenVPN instance is active inside the provided configuration array +/** + * Checks if at least one OpenVPN instance is active inside the provided configuration array + */ const isAnyInstanceEnabled = function (ovpnUciSections) { const ovpnSections = Array.isArray(ovpnUciSections) ? ovpnUciSections : []; @@ -514,6 +532,21 @@ const isAnyInstanceEnabled = function (ovpnUciSections) { }); }; +/** + * Get InstanceID with instance number + */ +const getInstanceIdFromNumber = function (instances, instNumber) { + let inst = null; + if (instances) { + for (let i = 0; i < instances.length; i++) { + if (instances[i].instNum === instNumber) { + return instances[i].id; + } + } + } + return inst; +} + /** * Calculates a SHA256 hash using the privileged backend helper */ @@ -570,7 +603,7 @@ const getHostnameFallback = function (returnMask) { }; /** - * Memory buffer and timestamp to cache the land and public IP string + * Memory buffer and timestamp to cache the lan and public IP string */ const netCache = { LanIp: null, @@ -841,7 +874,6 @@ const updateDdnsProvider = async function (updateUrl, domain) { }; }; - /** * checkNetworkStructure cached data storage */ @@ -1096,7 +1128,6 @@ const checkOptimalDataCipherAES = async function () { } }; - /** * Compresses and formats the configuration text. Optionally removes dynamic blocks defined by start and end identifiers. */ @@ -1468,67 +1499,96 @@ const generateClientConfig = async function (appData, newInstanceItem, wizardPar /** * Compiles the final standalone .ovpn profile text with embedded keys */ -const compileOvpnProfileText = async function (cname, targetHost, targetPort, proto, cryptoAssets, appData, instNum, isSiteToSite) { +const compileOvpnProfileText = async function (cname, targetHost, targetPort, proto, cryptoAssets, appData, instNum, isSiteToSite, backup) { - const optimalDataCipherAES = await checkOptimalDataCipherAES(); + let ovpn = ''; - let new_remote = 'remote ' + targetHost + ' ' + targetPort; - if (isSiteToSite) { - const localLan = await getLanIpMask(true); - const localSubnet = appData.statusClass.netIPCIDR(localLan.ip, localLan.mask); - new_remote = new_remote + '\nsetenv remotelan ' + localSubnet; - } + if (backup) { - // Remove old crypto key references - ovpn profile comes with embedded keys - let ovpn = appData.clientTemplate - .replace(CFG.CONF.modern_vpn_client, CFG.CONF.modern_vpn_client + ' #' + instNum + ' (' + cname + ')') - .replace(CFG.CONF.certificate_and_keys_comment, '') - .replace(/^ca\s+\S+\r?\n/m, '') - .replace(/^cert\s+\S+\r?\n/m, '') - .replace(/^key\s+\S+\r?\n/m, '') - .replace(/^tls-crypt-v2\s+\S+\r?\n/m, '') - .replace(/^remote\s+\S+\s+\d+/m, new_remote) - .replace(/^proto\s+\S+/m, 'proto ' + proto); + if (cryptoAssets.conf) { + ovpn += cryptoAssets.conf + '\n\n' + } + if (cryptoAssets.ca) { + ovpn += '\n' + cryptoAssets.ca.trim() + '\n\n\n'; + } + if (cryptoAssets.cert) { + ovpn += '\n' + cryptoAssets.cert.trim() + '\n\n\n'; + } + if (cryptoAssets.key) { + ovpn += '\n' + cryptoAssets.key.trim() + '\n\n\n'; + } + if (cryptoAssets.tlsCrypt) { + ovpn += '\n' + cryptoAssets.tlsCrypt.trim() + '\n\n\n'; + } + const instance_id = getInstanceIdFromNumber(appData.instances, instNum); + let dh = await secureReadFile(CFG.FILE.dir_keys + 'dh_' + instance_id + '.pem'); + if (dh) { + dh = dh.trim(); + if (dh.length > 0) { + ovpn += '\n' + dh + '\n\n\n'; + } + } + } else { - if (isSiteToSite !== true) { - ovpn = ovpn.replace(/^.*openvpn\.instance\.status.*\n/m, '') - ovpn = ovpn.replace(/^.*status-version.*\n/m, '') - } + const optimalDataCipherAES = await checkOptimalDataCipherAES(); - if (!optimalDataCipherAES) { - ovpn = ovpn.replace(CFG.CONF.data_ciphers_aes, CFG.CONF.data_ciphers_comment + '\n' + CFG.CONF.data_ciphers_chacha); - } + let new_remote = 'remote ' + targetHost + ' ' + targetPort; + if (isSiteToSite) { + const localLan = await getLanIpMask(true); + const localSubnet = appData.statusClass.netIPCIDR(localLan.ip, localLan.mask); + new_remote = new_remote + '\nsetenv remotelan ' + localSubnet; + } - if (proto === OPENVPN.PROTO.tcp) { - ovpn = ovpn.replace(CFG.CONF.mssfix_1360, ''); - ovpn = ovpn.replace(CFG.CONF.tun_mtu_1420, ''); - } + // Remove old crypto key references - ovpn profile comes with embedded keys + ovpn = appData.clientTemplate + .replace(CFG.CONF.modern_vpn_client, CFG.CONF.modern_vpn_client + ' #' + instNum + ' (' + cname + ')') + .replace(CFG.CONF.certificate_and_keys_comment, '') + .replace(/^ca\s+\S+\r?\n/m, '') + .replace(/^cert\s+\S+\r?\n/m, '') + .replace(/^key\s+\S+\r?\n/m, '') + .replace(/^tls-crypt-v2\s+\S+\r?\n/m, '') + .replace(/^remote\s+\S+\s+\d+/m, new_remote) + .replace(/^proto\s+\S+/m, 'proto ' + proto); - // Collapse three or more consecutive newlines down to exactly one empty line - ovpn = ovpn.replace(/\n{3,}/g, '\n\n'); - if (!ovpn.endsWith('\n')) { - ovpn += '\n'; - } + if (isSiteToSite !== true) { + ovpn = ovpn.replace(/^.*openvpn\.instance\.status.*\n/m, '') + ovpn = ovpn.replace(/^.*status-version.*\n/m, '') + } - ovpn += '\n' + 'setenv client-cname ' + cname + '\n\n'; + if (!optimalDataCipherAES) { + ovpn = ovpn.replace(CFG.CONF.data_ciphers_aes, CFG.CONF.data_ciphers_comment + '\n' + CFG.CONF.data_ciphers_chacha); + } - if (cryptoAssets.ca && typeof cryptoAssets.ca.trim === 'function') { - const rawCa = cryptoAssets.ca.trim(); - const cleanCa = rawCa.replace(/-----BEGIN[^\n]*PRIVATE KEY-----[\s\S]*?-----END[^\n]*PRIVATE KEY-----\n*/g, ''); - ovpn += '\n' + cleanCa.trim() + '\n\n\n'; - } + if (proto === OPENVPN.PROTO.tcp) { + ovpn = ovpn.replace(CFG.CONF.mssfix_1360, ''); + ovpn = ovpn.replace(CFG.CONF.tun_mtu_1420, ''); + } - if (cryptoAssets.cert && typeof cryptoAssets.cert.trim === 'function') { - ovpn += '\n' + cryptoAssets.cert.trim() + '\n\n\n'; - } + // Collapse three or more consecutive newlines down to exactly one empty line + ovpn = ovpn.replace(/\n{3,}/g, '\n\n'); + if (!ovpn.endsWith('\n')) { + ovpn += '\n'; + } - if (cryptoAssets.key && typeof cryptoAssets.key.trim === 'function') { - ovpn += '\n' + cryptoAssets.key.trim() + '\n\n\n'; - } + ovpn += '\n' + 'setenv client-cname ' + cname + '\n\n'; - // Embedded client asset delivery wrapped into modern asymmetric tls-crypt-v2 XML layout tags - if (cryptoAssets.tlsCrypt && typeof cryptoAssets.tlsCrypt.trim === 'function' && cryptoAssets.tlsCrypt.trim()) { - ovpn += '\n' + cryptoAssets.tlsCrypt.trim() + '\n\n'; + if (cryptoAssets.ca) { + const rawCa = cryptoAssets.ca.trim(); + const cleanCa = rawCa.replace(/-----BEGIN[^\n]*PRIVATE KEY-----[\s\S]*?-----END[^\n]*PRIVATE KEY-----\n*/g, ''); + ovpn += '\n' + cleanCa.trim() + '\n\n\n'; + } + + if (cryptoAssets.cert) { + ovpn += '\n' + cryptoAssets.cert.trim() + '\n\n\n'; + } + + if (cryptoAssets.key) { + ovpn += '\n' + cryptoAssets.key.trim() + '\n\n\n'; + } + + if (cryptoAssets.tlsCrypt) { + ovpn += '\n' + cryptoAssets.tlsCrypt.trim() + '\n\n'; + } } return ovpn; @@ -1703,7 +1763,6 @@ const loadInstanceData = async function (appData) { }; - /** * --- SAVE AND RESTART --- */ @@ -1750,7 +1809,7 @@ const ovpnPending = { _t: null, init: function (appData) { if (appData) { - this._t = this._t = appData.statusClass.ovpnPending.trigger; + this._t = appData.statusClass.ovpnPending.trigger; } }, trigger: function () { @@ -2027,6 +2086,7 @@ const importOvpnClientProfile = async function (ovpnContent, instanceId, appData return { isCryptoUpdateOnly: false, + isServerBackup: false, remoteServer: remoteServerMatch[1].trim(), remoteServerLan: remoteServerLanMatch ? remoteServerLanMatch[1].trim() : '', port: parseInt(remoteServerMatch[2], 10) || OPENVPN.PORT.n1194, @@ -2035,6 +2095,119 @@ const importOvpnClientProfile = async function (ovpnContent, instanceId, appData }; }; +/** + * Imports a server backup profile, extracts keys, validates them, and prepares the configuration. + */ +const importOvpnBackupProfile = async function (ovpnContent, instanceId, appData) { + if (!ovpnContent || !ovpnContent.trim()) { + throw new Error(ICON.ERROR + ' ' + TXT.ERROR.upload_key_empty); + } + + // Fix line endings for Windows, Mac, and Linux instantly + const content = sanitizeInputText(ovpnContent); + + // Setup empty variables for the server assets + let confContent = ""; + let caContent = ""; + let certContent = ""; + let keyContent = ""; + let tlsCryptContent = ""; + let dhContent = ""; + + // 1. Extract the embedded keys using regular expressions (XML tags) + const caXml = content.match(/([\s\S]*?)<\/ca>/); + const certXml = content.match(/([\s\S]*?)<\/cert>/); + const keyXml = content.match(/([\s\S]*?)<\/key>/); + const tlsCryptXml = content.match(/([\s\S]*?)<\/tls-crypt-v2>/); + const dhXml = content.match(/([\s\S]*?)<\/dh>/); + + const blocksToScan = []; + + if (caXml && caXml[1] && caXml[1].trim()) { blocksToScan.push({ type: 'ca', text: caXml[1].trim() }); } + if (certXml && certXml[1] && certXml[1].trim()) { blocksToScan.push({ type: 'cert', text: certXml[1].trim() }); } + if (keyXml && keyXml[1] && keyXml[1].trim()) { blocksToScan.push({ type: 'key', text: keyXml[1].trim() }); } + if (tlsCryptXml && tlsCryptXml[1] && tlsCryptXml[1].trim()) { blocksToScan.push({ type: 'tlscrypt-v2', text: tlsCryptXml[1].trim() }); } + if (dhXml && dhXml[1] && dhXml[1].trim()) { blocksToScan.push({ type: 'dh', text: dhXml[1].trim() }); } + + // Safety check: A valid backup must have at least CA, Cert and Key tagged + if (!caXml || !certXml || !keyXml) { + throw new Error(ICON.ERROR + ' ' + TXT.ERROR.invalid_file_parts_missing); + } + + // SCAN LOOP: Test every single key block with the keymeta tool in the backend + for (let i = 0; i < blocksToScan.length; i++) { + const currentItem = blocksToScan[i]; + const currentBlock = currentItem ? currentItem.text : ''; + if (!currentBlock) { + continue; + } + + // Save the current block to a quick temporary file + const tmpFile = 'tmp_backup_scan_' + i + '.pem'; + await secureWriteFile(CFG.FILE.dir_keys + tmpFile, currentBlock + '\n'); + + // Run keymeta to check if this key is good or bad + const keyInfo = await appData.keygenClass.getKeyMeta(tmpFile, luci_app_openvpn_plus); + await secureRemoveFile(CFG.FILE.dir_keys + tmpFile); + + // Catch backend errors or empty responses immediately before parsing + if (keyInfo.valid === false) { + throw new Error(ICON.ERROR + ' ' + TXT.ERROR.key_check_failed); + } + + // SORTING: Trust the XML tags from backup but assign verified content to variables + if (currentItem.type === 'ca') { caContent = currentBlock; } + else if (currentItem.type === 'cert') { certContent = currentBlock; } + else if (currentItem.type === 'key') { keyContent = currentBlock; } + else if (currentItem.type === 'tlscrypt-v2') { tlsCryptContent = currentBlock; } + else if (currentItem.type === 'dh') { dhContent = currentBlock; } + } + + // Double check that the necessary fragments survived validation + if (!caContent || !certContent || !keyContent) { + throw new Error(ICON.ERROR + ' ' + TXT.ERROR.invalid_file_parts_missing); + } + + // 2. Clean the configuration part (Remove the XML blocks from the text) + confContent = content + .replace(/[\s\S]*?<\/ca>\n*/g, '') + .replace(/[\s\S]*?<\/cert>\n*/g, '') + .replace(/[\s\S]*?<\/key>\n*/g, '') + .replace(/[\s\S]*?<\/tls-crypt-v2>\n*/g, '') + .replace(/[\s\S]*?<\/dh>\n*/g, '') + .trim() + '\n'; + + // Helper function to strip any accidental inner tags + const stripXmlTags = function (str) { + return str.replace(/<\/?(ca|cert|key|tls-crypt|dh)/g, '').trim(); + }; + + // 3. Save clean files to the router folders in parallel + await Promise.all([ + secureWriteFile(CFG.FILE.dir_cfg + instanceId + '.conf', confContent), + secureWriteFile(CFG.FILE.dir_keys + 'ca_' + instanceId + '.crt', stripXmlTags(caContent) + '\n'), + secureWriteFile(CFG.FILE.dir_keys + 'server_' + instanceId + '.crt', stripXmlTags(certContent) + '\n'), + secureWriteFile(CFG.FILE.dir_keys + 'server_' + instanceId + '.key', stripXmlTags(keyContent) + '\n'), + dhContent ? secureWriteFile(CFG.FILE.dir_keys + 'dh_' + instanceId + '.pem', stripXmlTags(dhContent) + '\n') : Promise.resolve(), + tlsCryptContent ? secureWriteFile(CFG.FILE.dir_keys + 'server_' + instanceId + '.tls2.key', stripXmlTags(tlsCryptContent) + '\n') : Promise.resolve() + ]); + + // 4. Parse connection settings from the configuration text to help the wizard + const portMatch = confContent.match(/^port\s+(\d+)/m); + const protoMatch = confContent.match(/^proto\s+(\S+)/m); + const matchPattern = CFG.CONF.modern_vpn_server + '[^\\(]*\\(([^)]+)\\)'; + const cnameMatch = confContent.match(new RegExp(matchPattern)); + const isServerBackupFile = /^server\s+/m.test(confContent) || /^server-ipv6\s+/m.test(confContent); + + return { + isCryptoUpdateOnly: false, + isServerBackup: isServerBackupFile, + port: portMatch ? parseInt(portMatch[1], 10) : OPENVPN.PORT.s1194, + proto: protoMatch ? protoMatch[1].trim().toLowerCase() : OPENVPN.PROTO.udp, + cname: cnameMatch ? cnameMatch[1].trim() : "" + }; +}; + /** * Displays the mobile QR code for OpenVPN profiles */ @@ -2076,41 +2249,60 @@ const renderClientOvpnProfileQr = function (containerNode, downloadUrl) { /** * Displays the mobile layout container for OpenVPN profiles */ -const renderClientOvpnProfileModal = function (ipFieldWrapper, urlContainer, qrContainer, dlBtn, closeBtn) { - return E('div', { 'class': 'cbi-map' }, [ - E('div', { 'class': 'cbi-section' }, [ +const renderClientOvpnProfileModal = function (ipFieldWrapper, urlContainer, qrContainer, dlBtn, closeBtn, backup) { + const innerSectionElements = []; + + // Only add the address and description if it is NOT a backup + if (backup !== true) { + innerSectionElements.push( E('div', { 'class': 'cbi-section-descr', 'style': 'margin-bottom:15px; border-bottom:1px solid var(--border-color-medium, #cbd5e1); padding-bottom:8px;' }, TXT.MSG.export_connection_address_qr_code - ), + ) + ); + innerSectionElements.push( E('div', { 'style': 'display:flex; flex-direction:column; align-items:center; width:100%; text-align:center; margin-bottom:15px; background: var(--background-color-medium, #f4f4f5); padding:12px; border:1px solid var(--border-color-medium, #cbd5e1); border-radius:4px;' }, [ E('strong', { 'style': 'display:block; font-size:12px; color:color-mix(in srgb, var(--text-color-medium, #71717a) 85%, #0055ff); text-transform:uppercase; letter-spacing:0.5px;' }, TXT.MSG.vpn_client_address), ipFieldWrapper + ]) + ); + } + + // Always add the URL, QR-Code and warning block + innerSectionElements.push( + E('div', { 'style': 'display:flex; flex-direction:column; align-items:center; gap:15px; background: var(--background-color-medium, #f4f4f5); padding:20px; border-radius:6px; border:1px solid var(--border-color-medium, #cbd5e1); margin-bottom:15px;' }, [ + E('div', { 'style': 'width:100%; text-align:center;' }, [ + E('strong', { 'style': 'display:block; margin-bottom:6px; font-size:12px; color:color-mix(in srgb, var(--text-color-medium, #71717a) 85%, #0055ff); text-transform:uppercase; letter-spacing:0.5px;' }, TXT.MSG.secure_temporary_profile_url), + urlContainer ]), - E('div', { 'style': 'display:flex; flex-direction:column; align-items:center; gap:15px; background: var(--background-color-medium, #f4f4f5); padding:20px; border-radius:6px; border:1px solid var(--border-color-medium, #cbd5e1); margin-bottom:15px;' }, [ - E('div', { 'style': 'width:100%; text-align:center;' }, [ - E('strong', { 'style': 'display:block; margin-bottom:6px; font-size:12px; color:color-mix(in srgb, var(--text-color-medium, #71717a) 85%, #0055ff); text-transform:uppercase; letter-spacing:0.5px;' }, TXT.MSG.secure_temporary_profile_url), - urlContainer - ]), - E('div', { 'style': 'width:100%; text-align:center; margin-top:5px;' }, [ - E('strong', { 'style': 'display:block; margin-bottom:2px; font-size:12px; color:color-mix(in srgb, var(--text-color-medium, #71717a) 85%, #0055ff); text-transform:uppercase; letter-spacing:0.5px;' }, TXT.MSG.scan_qr_code_with_camera), - qrContainer - ]), - E('div', { 'style': 'width:100%; text-align:center; margin-top:8px; font-size:12px; color:color-mix(in srgb, var(--text-color-medium, #71717a) 85%, #0055ff); padding-top:10px; border-top:1px dashed var(--border-color-medium, #cbd5e1); font-weight:500;' }, [ - E('span', { 'style': 'var(--success-color-high, #00ac59); font-weight:bold; margin-right:4px;' }, ICON.WARNING + TXT.WARNING.security_notice + ' '), - TXT.MSG.download_only_available_window_open - ]) + E('div', { 'style': 'width:100%; text-align:center; margin-top:5px;' }, [ + E('strong', { 'style': 'display:block; margin-bottom:2px; font-size:12px; color:color-mix(in srgb, var(--text-color-medium, #71717a) 85%, #0055ff); text-transform:uppercase; letter-spacing:0.5px;' }, TXT.MSG.scan_qr_code_with_camera), + qrContainer ]), - E('div', { 'style': 'text-align:right; border-top:1px solid var(--border-color-medium, #cbd5e1); padding-top:12px; margin-top:15px;' }, [ - dlBtn, E('span', { 'style': 'margin-right:10px;' }), closeBtn + E('div', { 'style': 'width:100%; text-align:center; margin-top:8px; font-size:12px; color:color-mix(in srgb, var(--text-color-medium, #71717a) 85%, #0055ff); padding-top:10px; border-top:1px dashed var(--border-color-medium, #cbd5e1); font-weight:500;' }, [ + E('span', { 'style': 'var(--success-color-high, #00ac59); font-weight:bold; margin-right:4px;' }, ICON.WARNING + TXT.WARNING.security_notice + ' '), + TXT.MSG.download_only_available_window_open ]) ]) + ); + + // Always add the button bar at the bottom + innerSectionElements.push( + E('div', { 'style': 'text-align:right; border-top:1px solid var(--border-color-medium, #cbd5e1); padding-top:12px; margin-top:15px;' }, [ + dlBtn, E('span', { 'style': 'margin-right:10px;' }), closeBtn + ]) + ); + + // Build and return the final modal structure + const modalElement = E('div', { 'class': 'cbi-map' }, [ + E('div', { 'class': 'cbi-section' }, innerSectionElements) ]); + return modalElement; }; /** * Creates the basic screen elements for the mobile QR window */ -const createQrBoxElements = function (initialHost) { +const createQrBoxElements = function (initialHost, backup) { // Create the link field that users can click to open or download files const urlOutput = E('a', { 'target': '_blank', 'download': '', 'onclick': 'event.stopPropagation();', @@ -2140,7 +2332,7 @@ const createQrBoxElements = function (initialHost) { /** * Setup all click actions and update the view when things change */ -const setupQrBoxEvents = function (elements, ovpnParams, cryptoAssets, appData, isSiteToSite) { +const setupQrBoxEvents = function (elements, ovpnParams, cryptoAssets, appData, isSiteToSite, backup) { // Update data, links and the QR code asynchronously when the host changes const refreshModalState = async function () { @@ -2149,13 +2341,15 @@ const setupQrBoxEvents = function (elements, ovpnParams, cryptoAssets, appData, const instNumber = appData.statusClass.getInstanceNumber(ovpnParams.nextId); // 1. Await the dynamic profile compilation text block - const fullProfileText = await compileOvpnProfileText(ovpnParams.displayId, activeHost, ovpnParams.port, ovpnParams.proto, cryptoAssets, appData, instNumber, isSiteToSite); + const fullProfileText = await compileOvpnProfileText(ovpnParams.displayId, activeHost, ovpnParams.port, ovpnParams.proto, cryptoAssets, appData, instNumber, isSiteToSite, backup); // 2. Write the compiled text payload directly into the RAM configuration folder await secureWriteFile(ovpnParams.exportPath, fullProfileText); + const link_id = backup ? ovpnParams.displayId + '_backup' : ovpnParams.displayId + '_client'; + // 3. Trigger the backend symlink engine execution pipeline - const backendResponse = await luci_app_openvpn_plus([CFG.LIBEXEC.symlink, instNumber.toString(), 'create', ovpnParams.displayId]); + const backendResponse = await luci_app_openvpn_plus([CFG.LIBEXEC.symlink, ovpnParams.nextId, 'create', link_id]); // 4. Parse the secret URL from the backend response if (backendResponse && backendResponse.startsWith("SUCCESS: ")) { @@ -2172,11 +2366,11 @@ const setupQrBoxEvents = function (elements, ovpnParams, cryptoAssets, appData, elements.nodes.qr.innerHTML = ''; elements.nodes.qr.appendChild( E('div', { 'style': 'color:var(--error-color-high, #f62b12); font-size:12px; font-weight:bold; padding:20px;' }, [ - ICON.ERROR + ' ' + _('Failed to generate secure download profile. Please check system logs.') + ICON.ERROR + ' ' + TXT.ERROR.failed_generate_profile_download ]) ); elements.nodes.url.href = '#'; - elements.nodes.url.textContent = _('Profile Generation Error'); + elements.nodes.url.textContent = TXT.ERROR.profile_generation_error; } @@ -2214,11 +2408,11 @@ const setupQrBoxEvents = function (elements, ovpnParams, cryptoAssets, appData, elements.buttons.download.addEventListener('click', function () { const activeHost = elements.nodes.input.value ? sanitizeInputLine(elements.nodes.input.value) : window.location.hostname; const instNumber = appData.statusClass.getInstanceNumber(ovpnParams.nextId); - compileOvpnProfileText(ovpnParams.displayId, activeHost, ovpnParams.port, ovpnParams.proto, cryptoAssets, appData, instNumber, isSiteToSite).then(function (fullProfileText) { + compileOvpnProfileText(ovpnParams.displayId, activeHost, ovpnParams.port, ovpnParams.proto, cryptoAssets, appData, instNumber, isSiteToSite, backup).then(function (fullProfileText) { const blob = new Blob([fullProfileText], { type: 'application/x-openvpn-profile' }); const link = document.createElement('a'); link.href = URL.createObjectURL(blob); - link.download = ovpnParams.displayId.replace(/\s+/g, '_') + '_client.ovpn'; + link.download = ovpnParams.displayId.replace(/\s+/g, '_') + (backup ? '_backup.ovpn' : '_client.ovpn'); document.body.appendChild(link); link.click(); document.body.removeChild(link); @@ -2227,13 +2421,14 @@ const setupQrBoxEvents = function (elements, ovpnParams, cryptoAssets, appData, // Delete temporary files and close the window when clicking the close button elements.buttons.close.addEventListener('click', async function () { - const instNumber = appData.statusClass.getInstanceNumber(ovpnParams.nextId); try { // 1. Remove the temporary export file await secureRemoveFile(ovpnParams.exportPath); + const link_id = backup ? ovpnParams.displayId + '_backup' : ovpnParams.displayId + '_client'; + // 2. Call the universal script to delete the symlink with wildcard cleanup - await luci_app_openvpn_plus([CFG.LIBEXEC.symlink, instNumber.toString(), 'delete', ovpnParams.displayId]); + await luci_app_openvpn_plus([CFG.LIBEXEC.symlink, ovpnParams.nextId, 'delete', link_id]); } catch (err) { // Log the error but do not block the UI from closing console.error("Error during cleanup:", err); @@ -2264,7 +2459,14 @@ const SERVER_FILES_TEMPLATE = { /** * Opens a simplified modal for mobile apps clients with a dynamic targetCnName input field and triggers asynchronous client cryptographic generation. */ -const openMobileExportModal = function (instance_id, nextId, files, finalHost, triggerStandardExportFlow, appData) { +const openMobileExportModal = function (instance_id, nextId, serverAssets, finalHost, triggerStandardExportFlow, appData, displayId) { + + // Create a dropdown to select between creating a client or backing up the server + const selectDropdown = E('select', { 'class': 'cbi-input-select', 'style': 'width:100%; font-weight:bold; margin-top:10px; margin-bottom:5px;' }); + selectDropdown.appendChild(E('option', { 'value': 'create_new_client' }, [ICON.PLUS + ' ' + TXT.KEY.create_key_new_mobile])); + selectDropdown.appendChild(E('option', { 'disabled': true }, ['- - -'])); + selectDropdown.appendChild(E('option', { 'value': 'backup_server_profile' }, [ICON.BACKUP + TXT.MSG.backup_server_profile + ': ' + displayId])); + const cnInput = E('input', { 'type': 'text', 'class': 'cbi-input-text', @@ -2274,6 +2476,20 @@ const openMobileExportModal = function (instance_id, nextId, files, finalHost, t cnInput.addEventListener('input', function () { cnInput.classList.remove('cbi-input-invalid'); }); + + // Hide or show both the label and the text input field based on selection + selectDropdown.addEventListener('change', function () { + const descNode = document.getElementById('mobile-description-node'); + if (selectDropdown.value === 'backup_server_profile') { + cnInput.style.display = 'none'; + cnInput.classList.remove('cbi-input-invalid'); + if (descNode) { descNode.style.display = 'none'; } + } else { + cnInput.style.display = 'block'; + if (descNode) { descNode.style.display = 'block'; } + } + }); + const modalConfirmBtn = E('button', { 'class': 'cbi-button cbi-button-action important' }, [TXT.BTN.next + ' ' + ICON.ARROW]); const modalCancelBtn = E('button', { 'class': 'cbi-button cbi-button-neutral', 'style': 'margin-right:10px;' }, [TXT.BTN.cancel]); const statusFeedbackNode = E('textarea', { @@ -2288,6 +2504,11 @@ const openMobileExportModal = function (instance_id, nextId, files, finalHost, t L.ui.showModal(ICON.MOBILE + ' ' + TXT.MSG.export_openvpn_connect_client_profile, [ E('div', { 'class': 'cbi-map' }, [ E('div', { 'class': 'cbi-section' }, [ + E('div', { 'class': 'cbi-value', 'style': 'border:none; padding:0; margin-bottom:12px;' }, [ + E('div', { 'class': 'cbi-value-field', 'style': 'width:100%; margin:0; padding:0;' }, [ + selectDropdown + ]) + ]), E('div', { 'id': 'mobile-description-node', 'class': 'cbi-section-descr', 'style': 'margin-bottom:12px; font-size:12px; color:color-mix(in srgb, var(--text-color-medium, #71717a) 85%, #0055ff);' }, [ TXT.MSG.please_assign_cn_name ]), @@ -2307,9 +2528,18 @@ const openMobileExportModal = function (instance_id, nextId, files, finalHost, t modalConfirmBtn.addEventListener('click', function (ev) { ev.preventDefault(); + + // Check if the user selected the backup option + if (selectDropdown.value === 'backup_server_profile') { + L.ui.hideModal(); + // Trigger the standard export function to build and download the .ovpn file + triggerStandardExportFlow(serverAssets, displayId, saveApplyOpenVPN, true); + return; + } + if (forgedAssetsBundle && finalizedCnName) { L.ui.hideModal(); - triggerStandardExportFlow(forgedAssetsBundle, finalizedCnName, saveApplyOpenVPN); + triggerStandardExportFlow(forgedAssetsBundle, finalizedCnName, saveApplyOpenVPN, false); return; } cnInput.classList.remove('cbi-input-invalid'); @@ -2322,6 +2552,8 @@ const openMobileExportModal = function (instance_id, nextId, files, finalHost, t finalizedCnName = cleanCn; const descNode = document.getElementById('mobile-description-node'); if (descNode) { descNode.style.display = 'none'; } + + selectDropdown.style.display = 'none'; cnInput.style.display = 'none'; modalConfirmBtn.disabled = true; modalConfirmBtn.textContent = ICON.LOADING + ' ' + TXT.BTN.processing; @@ -2337,7 +2569,7 @@ const openMobileExportModal = function (instance_id, nextId, files, finalHost, t statusFeedbackNode.value += '\n' + ICON.ERROR + ' ' + TXT.ERROR.keygen_failed; return; } - statusFeedbackNode.value += '\nLOG: Certificate pipeline completed. Generating TLS crypt v2 client key...'; + statusFeedbackNode.value += '\n' + TXT.KEY.log_crt_completed_tls_client_keygen; // Step 2: Directly chain the asymmetric client tls generation appData.keygenClass.executeAsynchronousKeyGen(nextId, appData.keygenClass.KEYTYPE.client_tls2, 'none', '0', finalizedCnName, '', statusFeedbackNode, secureFileCallbacks, function (tlsSuccess, nullData, tlsPayload) { @@ -2345,13 +2577,13 @@ const openMobileExportModal = function (instance_id, nextId, files, finalHost, t modalCancelBtn.style.display = 'inline-block'; modalConfirmBtn.disabled = false; modalConfirmBtn.textContent = TXT.BTN.next + ' ' + ICON.ARROW; - statusFeedbackNode.value += '\n' + ICON.ERROR + ' TLS crypt v2 client key generation failed.'; + statusFeedbackNode.value += '\n' + ICON.ERROR + TXT.ERROR.tls_client_keygen_failed; return; } // Pack all 5 crypto components cleanly into the finalized delivery bundle forgedAssetsBundle = Object.assign({}, SERVER_FILES_TEMPLATE, { - conf: files.conf, + conf: serverAssets.conf, ca: pkiPayload.ca, cert: pkiPayload.cert, key: pkiPayload.key, @@ -2537,18 +2769,20 @@ const siteToSiteExportUpdateClients = async function (instance_id, nextId, targe await secureWriteFile(CFG.FILE.dir_cfg + instance_id + '.conf', formatOpenVpnConfig(configOutput)); }; - /** * Opens the selection modal for site-to-site clients and handles configuration updates */ -const openSiteToSiteExportModal = function (instance_id, nextId, files, activeBranchNames, initialHost, triggerStandardExportFlow, appData) { +const openSiteToSiteExportModal = function (instance_id, nextId, serverAssets, activeBranchNames, initialHost, triggerStandardExportFlow, appData, displayId) { const selectDropdown = E('select', { 'class': 'cbi-input-select', 'style': 'width:100%; font-weight:bold;' }); activeBranchNames.forEach(function (name) { - selectDropdown.appendChild(E('option', { 'value': name }, [ICON.OFFICE + TXT.MSG.office_profile + ' ' + name])); + selectDropdown.appendChild(E('option', { 'value': name }, [ICON.OFFICE + TXT.MSG.remote_profile + ': ' + name])); }); selectDropdown.appendChild(E('option', { 'value': 'create_new_client' }, [ICON.PLUS + TXT.KEY.create_key_new_office])); - selectDropdown.appendChild(E('option', { 'value': 'delete_existing_clients' }, [ICON.MINUS + _('Delete Client Profile...')])); + selectDropdown.appendChild(E('option', { 'value': 'delete_existing_clients' }, [ICON.MINUS + TXT.MSG.delete_remote_profile])); + + selectDropdown.appendChild(E('option', { 'disabled': true }, ['- - -'])); + selectDropdown.appendChild(E('option', { 'value': 'backup_server_profile' }, [ICON.BACKUP + TXT.MSG.backup_server_profile + ': ' + displayId])); const subnetInputs = appData.wizardClass.renderSubnetInputs(); @@ -2558,8 +2792,8 @@ const openSiteToSiteExportModal = function (instance_id, nextId, files, activeBr }); // Create Add and Delete buttons - const addOfficeBtn = E('button', { 'class': 'cbi-button cbi-button-add', 'style': 'margin-top:8px; font-weight:bold;' }, [ICON.PLUS + _('Add')]); - const delOfficeBtn = E('button', { 'class': 'cbi-button cbi-button-remove disabled', 'style': 'margin-top:8px; font-weight:bold; text-align: right; display: none;', 'disabled': true }, [ICON.REMOVE + _('Delete')]); + const addOfficeBtn = E('button', { 'class': 'cbi-button cbi-button-add', 'style': 'margin-top:8px; font-weight:bold;' }, [ICON.PLUS + TXT.INFO.add]); + const delOfficeBtn = E('button', { 'class': 'cbi-button cbi-button-remove disabled', 'style': 'margin-top:8px; font-weight:bold; text-align: right; display: none;', 'disabled': true }, [ICON.REMOVE + TXT.INFO.delete]); // Creates a small preview table to show the validated network data const networkPreviewTable = E('table', { @@ -2584,7 +2818,6 @@ const openSiteToSiteExportModal = function (instance_id, nextId, files, activeBr delOfficeBtn ]); - // Combine input fields, buttons and error box into one container const inputRowContainer = E('div', { 'style': 'margin-top:4px; display:none;' }, [ subnetInputs.node, @@ -2596,7 +2829,7 @@ const openSiteToSiteExportModal = function (instance_id, nextId, files, activeBr const deleteChecklistContainer = E('div', { 'style': 'margin-top:12px; padding:15px; background:var(--background-color-medium, #f4f4f5); border:1px dashed var(--border-color-medium, #f4f4f5); border-radius:4px; width:100%; display: none;' }, [ - E('strong', { 'style': 'display:block; margin-bottom:10px; font-size:12px; color:var(--error-color-high, #f62b12); text-transform:uppercase; font-family:var(--font-sans);' }, [_('Select clients to remove:')]) + E('strong', { 'style': 'display:block; margin-bottom:10px; font-size:12px; color:var(--error-color-high, #f62b12); text-transform:uppercase; font-family:var(--font-sans);' }, [TXT.MSG.select_clients_to_remove]) ]); // Generate dynamic matrix layout checklist row objects inside the memory tree @@ -2754,7 +2987,7 @@ const openSiteToSiteExportModal = function (instance_id, nextId, files, activeBr // Unlock the main Next button modalConfirmBtn.disabled = false; }).catch(function () { - errorDisplayNode.innerHTML = ICON.WARNING + ' ' + _('Error: Network status check failed.'); + errorDisplayNode.innerHTML = ICON.WARNING + ' ' + TXT.ERROR.network_status_check_failed; errorDisplayNode.style.display = 'block'; }); }); @@ -2820,11 +3053,20 @@ const openSiteToSiteExportModal = function (instance_id, nextId, files, activeBr ev.preventDefault(); if (forgedAssetsBundle && finalizedCnName) { L.ui.hideModal(); - triggerStandardExportFlow(forgedAssetsBundle, finalizedCnName, saveApplyOpenVPN); + triggerStandardExportFlow(forgedAssetsBundle, finalizedCnName, saveApplyOpenVPN, false); return; } const targetMode = selectDropdown.value; + + // Check if the user selected the backup option + if (targetMode === 'backup_server_profile') { + L.ui.hideModal(); + // Trigger the standard export function to build and download the .ovpn file + triggerStandardExportFlow(serverAssets, displayId, saveApplyOpenVPN, true); + return; + } + let targetCnName = ''; let targetSub = ''; let targetMask = ''; @@ -2886,7 +3128,7 @@ const openSiteToSiteExportModal = function (instance_id, nextId, files, activeBr statusFeedbackNode.value += '\n' + ICON.ERROR + ' ' + TXT.ERROR.keygen_failed; return; } - statusFeedbackNode.value += '\nLOG: Certificate pipeline completed. Generating TLS crypt v2 client key...'; + statusFeedbackNode.value += '\n' + TXT.KEY.log_crt_completed_tls_client_keygen; // Step 2: Directly chain the asymmetric client tls generation appData.keygenClass.executeAsynchronousKeyGen(nextId, appData.keygenClass.KEYTYPE.client_tls2, 'none', '0', targetCnName, '', statusFeedbackNode, secureFileCallbacks, async function (tlsSuccess, nullData, tlsPayload) { @@ -2894,10 +3136,10 @@ const openSiteToSiteExportModal = function (instance_id, nextId, files, activeBr modalCancelBtn.style.display = 'inline-block'; modalConfirmBtn.disabled = false; modalConfirmBtn.textContent = TXT.BTN.next + ' ' + ICON.ARROW; - statusFeedbackNode.value += '\n' + ICON.ERROR + ' TLS crypt v2 client key generation failed.'; + statusFeedbackNode.value += '\n' + ICON.ERROR + TXT.ERROR.tls_client_keygen_failed; return; } - const activeConfigContentText = files; + const activeConfigContentText = serverAssets; if (isNewClient === true) { await siteToSiteExportUpdateClients(instance_id, nextId, targetCnName, targetSub, targetMask); saveApplyOpenVPN = true; @@ -2920,10 +3162,10 @@ const openSiteToSiteExportModal = function (instance_id, nextId, files, activeBr /** * Exporter for OpenVPN profiles with embedded client crypto data */ -const downloadClientOvpnProfile = async function (instance_id, instObj, customUciName, appData) { +const downloadClientOvpnProfile = async function (instance_id, instObj, customUciName, appData, directBackup) { const nextId = instance_id; const displayId = customUciName || instance_id; - const currentRole = instObj.role || 'client'; + const currentRole = instObj.role || OPENVPN.ROLE.SERVER; const rolePrefix = currentRole + '_'; // Note: Profile will be compiled in: triggerStandardExportFlow -> setupQrBoxEvents -> compileOvpnProfileText @@ -2962,7 +3204,6 @@ const downloadClientOvpnProfile = async function (instance_id, instObj, customUc tlsCrypt: rawFilesArray[4] });; - // Check if this server configuration is Site-To-Site const isSiteToSite = appData.statusClass.parseSiteToSite(serverAssets.conf); @@ -3006,10 +3247,13 @@ const downloadClientOvpnProfile = async function (instance_id, instObj, customUc }; // --- TYPE A: Flow for standard devices (Mobile Apps) --- - const triggerStandardExportFlow = function (finalCryptoBundle, overrideCn, saveApplyOpenVPN) { + const triggerStandardExportFlow = function (finalCryptoBundle, overrideCn, saveApplyOpenVPN, backup) { const ovpnParamsUpdated = Object.assign({}, ovpnDownloadParams); let title; - if (overrideCn) { + if (backup) { + title = ICON.BACKUP + TXT.MSG.backup_export; + } + else if (overrideCn) { title = ICON.OFFICE + TXT.MSG.client_export; ovpnParamsUpdated.displayId = overrideCn; } else { @@ -3019,15 +3263,21 @@ const downloadClientOvpnProfile = async function (instance_id, instObj, customUc ovpnParamsUpdated.saveApplyOpenVPN = saveApplyOpenVPN; } const ui = createQrBoxElements(finalHost); - setupQrBoxEvents(ui, ovpnParamsUpdated, finalCryptoBundle, appData, isSiteToSite); + setupQrBoxEvents(ui, ovpnParamsUpdated, finalCryptoBundle, appData, isSiteToSite, backup); L.ui.showModal(title + ': ' + ovpnParamsUpdated.displayId, [ - renderClientOvpnProfileModal(ui.nodes.wrapper, ui.nodes.url, ui.nodes.qr, ui.buttons.download, ui.buttons.close) + renderClientOvpnProfileModal(ui.nodes.wrapper, ui.nodes.url, ui.nodes.qr, ui.buttons.download, ui.buttons.close, backup) ]); }; + // On direct backup show the export modal with qr-code and download immediatly + if (directBackup) { + triggerStandardExportFlow(serverAssets, displayId, false, true); + return; + } + // If it is a standard mobile server, open the Mobile Export modal to generate unique client_pki keys first if (!isSiteToSite) { - openMobileExportModal(instance_id, nextId, serverAssets, finalHost, triggerStandardExportFlow, appData); + openMobileExportModal(instance_id, nextId, serverAssets, finalHost, triggerStandardExportFlow, appData, displayId); return; } @@ -3047,7 +3297,7 @@ const downloadClientOvpnProfile = async function (instance_id, instObj, customUc } // --- TYPE B: Open the clean isolated Site-to-Site Selection Modal UI Sub-Routine --- - openSiteToSiteExportModal(instance_id, nextId, serverAssets, activeBranchNames, finalHost, triggerStandardExportFlow, appData); + openSiteToSiteExportModal(instance_id, nextId, serverAssets, activeBranchNames, finalHost, triggerStandardExportFlow, appData, displayId); } catch (err) { // Central error interception handler cleanly logs all file system crashes @@ -3497,7 +3747,7 @@ const renderKeyButtons = function (label, filename, instance_id, displayId, defa statusMsg.className = 'text-danger'; statusMsg.textContent = ICON.ERROR + ' ' + TXT.MSG.uploaded_file_invalid; uploadBtn.classList.remove('disabled'); - throw new Error(TXT.KEY.key_verification_failed); + throw new Error(TXT.ERROR.key_verification_failed); } let typeMismatch = false; @@ -3567,7 +3817,7 @@ const renderKeyButtons = function (label, filename, instance_id, displayId, defa uciSaveApplyRestart(instance_id); } catch (err) { - if (err.message !== TXT.KEY.key_verification_failed && err.message !== TXT.ERROR.key_type_mismatch) { + if (err.message !== TXT.ERROR.key_verification_failed && err.message !== TXT.ERROR.key_type_mismatch) { statusMsg.className = 'text-danger'; statusMsg.textContent = ICON.ERROR + ' ' + TXT.INFO.error + ': ' + err.message; } @@ -3771,7 +4021,7 @@ const handleInstanceDeletion = function (instance_id, displayId, delBtn, dNotice /** * Shows the collapsible box containing all cryptographic key files, keeping control buttons always visible. */ -const renderKeysBox = function (instance_id, displayId, role, ovpnProfileBtn, keygenBtn, appData) { +const renderKeysBox = function (instance_id, displayId, role, ovpnProfileBtn, keygenBackupBtn, appData) { const isServer = (role === OPENVPN.ROLE.SERVER); const certLabel = isServer ? TXT.KEY.server_crt : TXT.KEY.client_crt; const keyLabel = isServer ? TXT.KEY.server_key : TXT.KEY.client_key; @@ -3820,13 +4070,15 @@ const renderKeysBox = function (instance_id, displayId, role, ovpnProfileBtn, ke if (isHidden) { keysContentContainer.style.display = 'block'; - keygenBtn.style.display = 'block'; - inlineSummaryLine.style.display = 'none'; // Hide text row when box expands + keygenBackupBtn.style.display = 'block'; + // Hide text row when box expands + inlineSummaryLine.style.display = 'none'; toggleArrow.textContent = '▼ '; } else { keysContentContainer.style.display = 'none'; - keygenBtn.style.display = 'none'; - inlineSummaryLine.style.display = 'block'; // Show text row when box collapses + keygenBackupBtn.style.display = 'none'; + // Show text row when box collapses + inlineSummaryLine.style.display = 'block'; toggleArrow.textContent = '▶ '; } } @@ -3849,7 +4101,7 @@ const renderKeysBox = function (instance_id, displayId, role, ovpnProfileBtn, ke // The action buttons container remains outside the collapsible block (always visible) E('div', { 'style': 'width:100%; display:block; overflow:hidden; border-top:1px dashed var(--border-color-medium, #cbd5e1); padding-top:5px; margin-top:0px;' }, [ ovpnProfileBtn, - ((role === OPENVPN.ROLE.SERVER) ? keygenBtn : '') + keygenBackupBtn ]) ]) ]); @@ -4102,15 +4354,23 @@ const renderInstanceBox = function (s, idx, appData) { 'style': 'display: inline-flex; align-items: center; cursor: pointer; font-size: 13px; font-weight: bold;' }, [instanceCheckbox, E('span', {}, TXT.BTN.enabled)]); - // Create the key generator button - const keygenBtn = E('button', { + const keygenLabel = (role === OPENVPN.ROLE.SERVER) ? TXT.BTN.keygen : TXT.BTN.backup + + // Create the keygen/backup button + const keygenBackupBtn = E('button', { 'class': 'cbi-button cbi-button-apply important', 'style': 'float: right; margin: 10px 10px 0 0; display: none' - }, TXT.KEY.keygen); + }, keygenLabel); - keygenBtn.addEventListener('click', function (ev) { + + keygenBackupBtn.addEventListener('click', function (ev) { ev.preventDefault(); - appData.keygenClass.openKeyGenModal(instance_id, instObj.displayName, role, appData, uciSaveApplyRestart, secureFileCallbacks); + // keygen on server profiles and backup on client profiles + if (role === OPENVPN.ROLE.SERVER) { + appData.keygenClass.openKeyGenModal(instance_id, instObj.displayName, role, appData, uciSaveApplyRestart, secureFileCallbacks); + } else { + downloadClientOvpnProfile(instance_id, instObj, instObj.displayName, appData, true); + } }); // Create the main configuration text box field @@ -4134,7 +4394,7 @@ const renderInstanceBox = function (s, idx, appData) { }, TXT.MSG.export_ovpn); ovpnProfileBtn.addEventListener('click', function (ev) { ev.preventDefault(); - downloadClientOvpnProfile(instance_id, instObj, customDisplayName, appData); + downloadClientOvpnProfile(instance_id, instObj, customDisplayName, appData, false); }); } else { ovpnProfileBtn = E('button', { @@ -4150,7 +4410,8 @@ const renderInstanceBox = function (s, idx, appData) { uciSaveApplyRestartCallback: uciSaveApplyRestart, importOvpnClientProfileCallback: importOvpnClientProfile, instanceNumber: instNum, - forcedScenario: OPENVPN.SCENARIO.SITE_TO_SITE_CLIENT + forcedScenario: OPENVPN.SCENARIO.SITE_TO_SITE_CLIENT, + forcedScenarioTitle: ICON.IMPORT + TXT.MSG.import_openvpn_client_profile }); appData.wizardClass.openWizardModal(wizardData); }); @@ -4174,7 +4435,7 @@ const renderInstanceBox = function (s, idx, appData) { renderPortAlertBox(instObj, role, appData), - renderKeysBox(instance_id, displayId, role, ovpnProfileBtn, keygenBtn, appData), + renderKeysBox(instance_id, displayId, role, ovpnProfileBtn, keygenBackupBtn, appData), renderConfigEditor(instance_id, txtArea, saveBtn), @@ -4199,6 +4460,24 @@ const renderInstanceBox = function (s, idx, appData) { return sectionRootNode; }; +/** + * Open Import Backup Instance Wizard + */ +const importBackupInstance = function (appData) { + const newInstNumber = getNextInstanceNumber(appData); + const wizardData = Object.assign({}, appData.wizardClass.WIZARD_DATA_TEMPLATE, { + appData: appData, + addNewInstanceCallback: addNewInstance, + networkCallbacks: networkCallbacks, + uciSaveApplyRestartCallback: uciSaveApplyRestart, + importOvpnClientProfileCallback: importOvpnBackupProfile, + instanceNumber: newInstNumber, + forcedScenario: OPENVPN.SCENARIO.SITE_TO_SITE_CLIENT, + forcedScenarioTitle: ICON.BACKUP + TXT.MSG.backup_import + }); + appData.wizardClass.openWizardModal(wizardData, false); +} + /** * Subroutine of addNewInstance to create a new OpenVPN instance and its firewall rules safely. */ @@ -4271,16 +4550,28 @@ const addNewInstance = async function (roleType, appData, wizardParams, optional connectedClients: [] }); - // Trigger the automated key allocation modal for ALL server installations - if ((newInstanceItem.role === OPENVPN.ROLE.SERVER) || ((wizardParams) && (wizardParams.role === OPENVPN.ROLE.SERVER))) { + let openKeygenModal = false; + if (newInstanceItem.role === OPENVPN.ROLE.SERVER) { + openKeygenModal = true; + } + if ((wizardParams) && (wizardParams.role === OPENVPN.ROLE.SERVER)) { + if (wizardParams.isServerBackup === true) { + openKeygenModal = false; + } else { + openKeygenModal = true; + } + } + + // Trigger the automated key allocation modal for ALL server installations + if (openKeygenModal) { const callbacks = ({ openWizardBtnClick: openWizardBtnClick, secureFileCallbacks: secureFileCallbacks, uciSaveApplyRestart: uciSaveApplyRestart, createInstance: createInstance, + importBackupInstance: importBackupInstance, }); - appData.keygenClass.openAutomatedPostKeyGenModal(newInstanceItem, appData, wizardParams, callbacks, optionalShowBtnCancel); } else { @@ -4336,7 +4627,8 @@ const openManualClientImportModal = function (appData) { uciSaveApplyRestartCallback: uciSaveApplyRestart, importOvpnClientProfileCallback: importOvpnClientProfile, instanceNumber: nextNum, - forcedScenario: OPENVPN.SCENARIO.SITE_TO_SITE_CLIENT + forcedScenario: OPENVPN.SCENARIO.SITE_TO_SITE_CLIENT, + forcedScenarioTitle: ICON.IMPORT + TXT.MSG.import_openvpn_client_profile }); appData.wizardClass.openWizardModal(wizardData, false); }); @@ -4346,7 +4638,7 @@ const openManualClientImportModal = function (appData) { cancelBtn.addEventListener('click', handlePureCancel); // Render the complete client import window layout - L.ui.showModal(ICON.IMPORT + TXT.MSG.import_openvpn_connect_client_profile, [ + L.ui.showModal(ICON.IMPORT + TXT.MSG.import_openvpn_client_profile, [ E('div', { 'class': 'cbi-map' }, [ E('div', { 'class': 'cbi-section' }, [ infoText, @@ -4972,7 +5264,7 @@ const renderFirewallInfoBox = function (appData) { }; /** - * Create the RPC call to run system commands. + * Create the RPC call to run logread system command. */ const logreadNativeRpc = L.rpc.declare({ object: 'file', @@ -5630,7 +5922,7 @@ return view.extend({ await appData.statusClass.onLoad(); ovpnPending.init(appData); - // Gets the openvon sections from LuCI + // Gets the openvpn sections from LuCI const ovpnSections = L.uci.sections(CFG.CMD.openvpn, CFG.CMD.openvpn) || []; // Sorts the array alphabetically based on the internal '.name' (instance1, instance2, ...) ovpnSections.sort(function (a, b) { @@ -5662,8 +5954,6 @@ return view.extend({ */ render: function () { try { - - const appData = this.APP_DATA; const openvpnState = isAnyInstanceEnabled(appData.ovpnUciSections) ? OPENVPN.STATE.active : OPENVPN.STATE.disabled; const openvpnStatus = appData.statusClass; @@ -5711,7 +6001,7 @@ return view.extend({ renderStatusControlBox(openvpnState, masterServerBtn, masterClientBtn, appData) ]); - const renderOpenVPNView = E('div', { 'class': 'cbi-map', 'style': 'position:relative; min-height:300px;' }, [ + const renderOpenVpnView = E('div', { 'class': 'cbi-map', 'style': 'position:relative; min-height:300px;' }, [ startupLockOverlay, statusControlBox, @@ -5733,7 +6023,7 @@ return view.extend({ setupMainRefresh(appData, statusTable); }); - return renderOpenVPNView; + return renderOpenVpnView; } catch (err) { console.error('Error rendering main OpenVPN dashboard: ', err); diff --git a/luci-app-tailscale-community/root/etc/init.d/tailscale-settings b/luci-app-tailscale-community/root/etc/init.d/tailscale-settings index 69144feef..af252d6da 100755 --- a/luci-app-tailscale-community/root/etc/init.d/tailscale-settings +++ b/luci-app-tailscale-community/root/etc/init.d/tailscale-settings @@ -129,6 +129,36 @@ apply_dns_forward() { 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() { local ts_bin if [ -x /usr/sbin/tailscale ]; then @@ -253,7 +283,7 @@ apply_settings() { fi logger -t "$NAME" "Applying settings" - "$ts_bin" "$@" + run_tailscale_set "$ts_bin" "$@" local ret=$? [ $ret -eq 0 ] \ && logger -t "$NAME" "Settings applied successfully" \ diff --git a/luci-theme-argon/Makefile b/luci-theme-argon/Makefile index 0375b95ed..5b4d81d47 100644 --- a/luci-theme-argon/Makefile +++ b/luci-theme-argon/Makefile @@ -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. LUCI_BASENAME:=argon-config PKG_VERSION:=3.1.0 -PKG_RELEASE:=32 +PKG_RELEASE:=33 PKG_LICENSE:=Apache-2.0 MIT # LuCI derives translations independently of the theme version. Keep release diff --git a/luci-theme-argon/htdocs/luci-static/argon/css/cascade.css b/luci-theme-argon/htdocs/luci-static/argon/css/cascade.css index 6037dde77..8c8fb034a 100644 --- a/luci-theme-argon/htdocs/luci-static/argon/css/cascade.css +++ b/luci-theme-argon/htdocs/luci-static/argon/css/cascade.css @@ -2107,7 +2107,6 @@ span.spinning { #cbi-wireless > .cbi-section-node, #cbi-wireless > #wifi_assoclist_table, [data-tab-title], -[data-page^="admin-system-admin"]:not(.node-main-login) .cbi-map:not(#cbi-dropbear), [data-page="admin-system-opkg"] #maincontent > .container { font-family: inherit; font-weight: normal; @@ -2128,7 +2127,6 @@ span.spinning { #cbi-wireless > .cbi-section-node:last-child, #cbi-wireless > #wifi_assoclist_table: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 { margin: 0; border: 0; @@ -4777,6 +4775,57 @@ pre.command-output { gap: var(--control-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-password"] .cbi-map h2, [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"] { 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 { width: 100%; - margin-inline: var(--section-inset); - max-width: min(48rem, calc(100% - 2 * var(--section-inset))); + margin: 0; + max-width: min(48rem, 100%); gap: var(--field-gap); } [data-page="admin-system-admin-sshkeys"] .cbi-dynlist > .item { @@ -4927,6 +4983,23 @@ pre.command-output { overflow-wrap: anywhere; 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 { width: 100%; min-width: 0; @@ -5210,6 +5283,44 @@ pre.command-output { object-fit: contain; 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-opkg"] .controls { min-width: 0; @@ -5334,7 +5445,17 @@ pre.command-output { 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 { + display: block; direction: ltr; unicode-bidi: plaintext; text-align: start; @@ -6043,6 +6164,9 @@ pre.command-output { top: 0.1rem; left: 1rem; } +[data-page="admin-system-reboot"] #view > hr { + display: none; +} [data-page="admin-system-reboot"] .cbi-button { background: var(--warning-action-bg) !important; border-color: var(--warning-action-bg) !important; @@ -6313,6 +6437,14 @@ pre.command-output { direction: ltr; 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 { overflow: visible; box-shadow: none; @@ -6339,7 +6471,7 @@ pre.command-output { } [data-page="admin-network-diagnostics"] .tr .td { background-color: var(--surface-color) !important; - border-bottom: 1px solid var(--border-color) !important; + border-bottom: 0 !important; display: flex; flex-direction: row; align-items: center; @@ -6468,6 +6600,35 @@ pre.command-output { #view > .Dashboard [data-tab="layout"] .cbi-page-actions { 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"] .td:nth-child(3) { text-align: start; @@ -7013,9 +7174,35 @@ pre.command-output { [data-page="admin-status-nftables"] .alert-message.fade-out { 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"] #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; unicode-bidi: plaintext; 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-dmesg"] #content_syslog > div:has(> input, > select) { 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-syslog"] #content_syslog > div:has(> input, > select) > label, @@ -7119,6 +7306,29 @@ pre.command-output { [data-page="admin-services-nlbw-display"] .cbi-section > .right { 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 { overflow: auto; font-size: 12px !important; @@ -7205,7 +7415,7 @@ pre.command-output { --oc-text: var(--text-color); --oc-text-muted: var(--plugin-text-muted); --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$="wrench-light.svg"], @@ -7256,8 +7466,8 @@ pre.command-output { --primary-a60: var(--oc-accent-a60) !important; --radio-hover-bg: var(--oc-accent-a10) !important; --tab-hover-dark-bg: var(--oc-accent-a10) !important; - --scrollbar-thumb: var(--oc-neutral) !important; - --scrollbar-thumb-hover: var(--oc-text-muted) !important; + --scrollbar-thumb: var(--oc-scrollbar) !important; + --scrollbar-thumb-hover: var(--oc-accent-hover) !important; border-radius: var(--radius-card); } [data-page^="admin-services-openclash"] .oc .developer-container, @@ -7271,6 +7481,22 @@ pre.command-output { [data-page^="admin-services-openclash"] .oc .config-upload-content { 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-apply, [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 { 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[color="green"] { @@ -7469,6 +7696,19 @@ pre.command-output { padding-top: 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-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), @@ -7592,6 +7832,35 @@ pre.command-output { [data-page^="admin-services-openclash"] #tab-content .dom { 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 { min-width: 7.25rem; white-space: nowrap; diff --git a/luci-theme-argon/less/components/sections.less b/luci-theme-argon/less/components/sections.less index 89c77ded3..ca897e47b 100644 --- a/luci-theme-argon/less/components/sections.less +++ b/luci-theme-argon/less/components/sections.less @@ -1,5 +1,13 @@ // 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-error, #iptables, @@ -8,7 +16,6 @@ #cbi-wireless>.cbi-section-node, #cbi-wireless>#wifi_assoclist_table, [data-tab-title], -[data-page^="admin-system-admin"]:not(.node-main-login) .cbi-map:not(#cbi-dropbear), [data-page="admin-system-opkg"] #maincontent>.container { font-family: inherit; font-weight: normal; diff --git a/luci-theme-argon/less/components/tables.less b/luci-theme-argon/less/components/tables.less index d90804002..33c0eae19 100644 --- a/luci-theme-argon/less/components/tables.less +++ b/luci-theme-argon/less/components/tables.less @@ -1,5 +1,40 @@ // 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 { diff --git a/luci-theme-argon/less/page-fix.less b/luci-theme-argon/less/page-fix.less index d3e0b0b70..302ffcfa9 100644 --- a/luci-theme-argon/less/page-fix.less +++ b/luci-theme-argon/less/page-fix.less @@ -6,6 +6,7 @@ @import "pages/autoreboot.less"; @import "pages/system.less"; @import "pages/syscontrol.less"; +@import "pages/administration.less"; @import "pages/password.less"; @import "pages/sshkeys.less"; @import "pages/repokeys.less"; @@ -46,6 +47,7 @@ @import "pages/nlbwmon.less"; // Services +@import "pages/wol.less"; @import "pages/ddns.less"; @import "pages/ttyd.less"; @import "pages/dockerman.less"; diff --git a/luci-theme-argon/less/pages/administration.less b/luci-theme-argon/less/pages/administration.less new file mode 100644 index 000000000..77bfd5dd2 --- /dev/null +++ b/luci-theme-argon/less/pages/administration.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; + } + } +} diff --git a/luci-theme-argon/less/pages/crontab.less b/luci-theme-argon/less/pages/crontab.less index 8eaf8238f..e087b41b2 100644 --- a/luci-theme-argon/less/pages/crontab.less +++ b/luci-theme-argon/less/pages/crontab.less @@ -2,8 +2,16 @@ // 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 { textarea { + display: block; direction: ltr; unicode-bidi: plaintext; text-align: start; diff --git a/luci-theme-argon/less/pages/diagnostics.less b/luci-theme-argon/less/pages/diagnostics.less index 42025d597..9fb96a025 100644 --- a/luci-theme-argon/less/pages/diagnostics.less +++ b/luci-theme-argon/less/pages/diagnostics.less @@ -2,6 +2,12 @@ // Network - Diagnostics [data-page="admin-network-diagnostics"] { + #view > .cbi-map > .table { + .native-page-card(); + padding: var(--control-gap); + border-collapse: separate; + } + .table { // The toolbar must not become the dropdown's scroll/size boundary. overflow: visible; @@ -32,7 +38,7 @@ .td { background-color: var(--surface-color) !important; - border-bottom: 1px solid var(--border-color) !important; + border-bottom: 0 !important; display: flex; flex-direction: row; align-items: center; diff --git a/luci-theme-argon/less/pages/logs.less b/luci-theme-argon/less/pages/logs.less index 6806a13fc..dfc4cdd0b 100644 --- a/luci-theme-argon/less/pages/logs.less +++ b/luci-theme-argon/less/pages/logs.less @@ -3,7 +3,28 @@ [data-page="admin-status-logs"], [data-page="admin-status-logs-syslog"], [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 { + display: block; + margin: 0; + border: 1px solid var(--control-border); + background-color: var(--input-bg); direction: ltr; unicode-bidi: plaintext; text-align: start; @@ -18,7 +39,7 @@ [data-page="admin-status-logs-dmesg"] { #content_syslog > div:has(> input, > select) { line-height: 2.5rem; - margin-block-end: var(--control-gap) !important; + margin-block-end: 0 !important; > label, > input, diff --git a/luci-theme-argon/less/pages/openclash.less b/luci-theme-argon/less/pages/openclash.less index ed0a1e4a8..64e86f442 100644 --- a/luci-theme-argon/less/pages/openclash.less +++ b/luci-theme-argon/less/pages/openclash.less @@ -27,7 +27,7 @@ --oc-text: var(--text-color); --oc-text-muted: var(--plugin-text-muted); --oc-neutral: var(--plugin-neutral); - --oc-scrollbar: var(--plugin-scrollbar); + --oc-scrollbar: var(--oc-accent); @supports (color: color-mix(in srgb, red, blue)) { --oc-accent-hover: color-mix(in srgb, var(--oc-accent) 88%, black); @@ -72,8 +72,8 @@ --primary-a60: var(--oc-accent-a60) !important; --radio-hover-bg: var(--oc-accent-a10) !important; --tab-hover-dark-bg: var(--oc-accent-a10) !important; - --scrollbar-thumb: var(--oc-neutral) !important; - --scrollbar-thumb-hover: var(--oc-text-muted) !important; + --scrollbar-thumb: var(--oc-scrollbar) !important; + --scrollbar-thumb-hover: var(--oc-accent-hover) !important; border-radius: var(--radius-card); .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 .cbi-button.cbi-button-add, .cbi-button.cbi-button-apply, @@ -287,6 +304,7 @@ } // Legacy help text is primary emphasis; success and error remain semantic. + b.oc-txt-good, b[style*="color:green"], b[style*="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-subscribe"], &[data-page^="admin-services-openclash-custom-dns-edit"], @@ -492,6 +530,45 @@ 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-section-table-cell:last-child { min-width: 7.25rem; diff --git a/luci-theme-argon/less/pages/package-manager.less b/luci-theme-argon/less/pages/package-manager.less index 40c2c4c95..0d48db2e8 100644 --- a/luci-theme-argon/less/pages/package-manager.less +++ b/luci-theme-argon/less/pages/package-manager.less @@ -3,6 +3,10 @@ // Native package-manager controls inject their own flex rules after the theme. [data-page="admin-system-package-manager"], [data-page="admin-system-opkg"] { + #view > #packages { + .native-table-card(); + } + .controls { min-width: 0; width: 100%; diff --git a/luci-theme-argon/less/pages/power.less b/luci-theme-argon/less/pages/power.less index a2c7230d2..3a8bc8e45 100644 --- a/luci-theme-argon/less/pages/power.less +++ b/luci-theme-argon/less/pages/power.less @@ -12,6 +12,11 @@ left: 1rem; } + // The view's section gap already separates the standalone page action. + #view > hr { + display: none; + } + .cbi-button { background: var(--warning-action-bg) !important; border-color: var(--warning-action-bg) !important; diff --git a/luci-theme-argon/less/pages/processes.less b/luci-theme-argon/less/pages/processes.less index 42d9fb9df..8f41d7939 100644 --- a/luci-theme-argon/less/pages/processes.less +++ b/luci-theme-argon/less/pages/processes.less @@ -1,6 +1,10 @@ // out: false [data-page="admin-status-processes"] { + #view > .table { + .native-table-card(); + } + .th:nth-child(3), .td:nth-child(3) { text-align: start; diff --git a/luci-theme-argon/less/pages/repokeys.less b/luci-theme-argon/less/pages/repokeys.less index 666ce98be..ffce2b281 100644 --- a/luci-theme-argon/less/pages/repokeys.less +++ b/luci-theme-argon/less/pages/repokeys.less @@ -1,6 +1,29 @@ // out: false [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 { width: 100%; min-width: 0; diff --git a/luci-theme-argon/less/pages/sshkeys.less b/luci-theme-argon/less/pages/sshkeys.less index d8d06763c..e608f3af2 100644 --- a/luci-theme-argon/less/pages/sshkeys.less +++ b/luci-theme-argon/less/pages/sshkeys.less @@ -1,10 +1,19 @@ // out: false [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 { width: 100%; - margin-inline: var(--section-inset); - max-width: ~"min(48rem, calc(100% - 2 * var(--section-inset)))"; + margin: 0; + max-width: ~"min(48rem, 100%)"; gap: var(--field-gap); > .item { diff --git a/luci-theme-argon/less/pages/wol.less b/luci-theme-argon/less/pages/wol.less new file mode 100644 index 000000000..d9f7c6993 --- /dev/null +++ b/luci-theme-argon/less/pages/wol.less @@ -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; + } +} diff --git a/qt6base/Makefile b/qt6base/Makefile index f971f1de6..6b3dedca4 100644 --- a/qt6base/Makefile +++ b/qt6base/Makefile @@ -11,7 +11,7 @@ PKG_NAME:=qt6base PKG_BASE:=6.12 PKG_BUGFIX:=0 PKG_VERSION:=$(PKG_BASE).$(PKG_BUGFIX) -PKG_RELEASE:=3 +PKG_RELEASE:=4 PKG_SOURCE:=qtbase-everywhere-src-$(PKG_VERSION).tar.xz PKG_SOURCE_URL:=https://mirrors.tencent.com/qt/archive/qt/$(PKG_BASE)/$(PKG_VERSION)/submodules/ \ diff --git a/qt6base/patches/021-fix-build-without-accessibility.patch b/qt6base/patches/021-fix-build-without-accessibility.patch new file mode 100644 index 000000000..b9fd3c41b --- /dev/null +++ b/qt6base/patches/021-fix-build-without-accessibility.patch @@ -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));