mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-10-26 21:12:51 +08:00
🐶 Sync 2026-09-02 00:00:06
This commit is contained in:
1 parent
aee1f777fe
commit
fe8c6d347a
38 files changed
+667
-102
No files matched your search
+2
-2
@@ -10,8 +10,8 @@ include $(TOPDIR)/rules.mk
|
||||
PKG_ARCH_BAIDUDRIVE:=$(ARCH)
|
||||
|
||||
PKG_NAME:=baidudrive
|
||||
PKG_VERSION:=linkeasefull-runtime-v3.0.15
|
||||
PKG_RELEASE:=17
|
||||
PKG_VERSION:=linkeasefull-runtime-v3.0.16
|
||||
PKG_RELEASE:=18
|
||||
PKG_SOURCE:=$(PKG_NAME)-binary-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://github.com/istoreos/istoreos-app-hub/releases/download/baidudrive-runtime-v$(PKG_VERSION)/
|
||||
PKG_HASH:=skip
|
||||
|
||||
+2
-2
@@ -6,9 +6,9 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=dae
|
||||
PKG_VERSION:=2026.08.28
|
||||
PKG_RELEASE:=39
|
||||
PKG_RELEASE:=40
|
||||
|
||||
PKG_SOURCE:=dae-src-2026.08.28-cc57c26ba484.tar.gz
|
||||
PKG_SOURCE:=dae-src-2026.08.28-d6aca8a10b35.tar.gz
|
||||
PKG_SOURCE_URL:=https://github.com/kenzok8/openwrt-daede/releases/download/dae-src
|
||||
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
|
||||
PKG_HASH:=skip
|
||||
|
||||
+2
-2
@@ -6,9 +6,9 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=daed
|
||||
PKG_VERSION:=2026.08.28
|
||||
PKG_RELEASE:=50
|
||||
PKG_RELEASE:=51
|
||||
|
||||
PKG_SOURCE:=daed-src-2026.08.28-943c54572fb6.tar.gz
|
||||
PKG_SOURCE:=daed-src-2026.08.28-73f01b995c34.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
|
||||
|
||||
@@ -9,8 +9,8 @@ include $(TOPDIR)/rules.mk
|
||||
PKG_ARCH_DOCKERMANAGER:=$(ARCH)
|
||||
|
||||
PKG_NAME:=dockermanager
|
||||
PKG_VERSION:=linkeasefull-runtime-v3.0.15
|
||||
PKG_RELEASE:=3
|
||||
PKG_VERSION:=linkeasefull-runtime-v3.0.16
|
||||
PKG_RELEASE:=4
|
||||
PKG_SOURCE:=$(PKG_NAME)-binary-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://github.com/istoreos/istoreos-app-hub/releases/download/dockermanager-runtime-v$(PKG_VERSION)/
|
||||
PKG_HASH:=skip
|
||||
|
||||
@@ -13,15 +13,52 @@
|
||||
"entry": "index.html",
|
||||
"basePath": "/apps/dockermanager/"
|
||||
},
|
||||
"config": {
|
||||
"providerOrder": ["uci"],
|
||||
"providers": {
|
||||
"uci": {
|
||||
"type": "uci"
|
||||
}
|
||||
}
|
||||
},
|
||||
"backend": {
|
||||
"type": "http",
|
||||
"transport": "unix",
|
||||
"scheme": "http",
|
||||
"host": "127.0.0.1",
|
||||
"portFromUci": "dockermanager.@dockermanager[0].port",
|
||||
"defaultPort": 8192,
|
||||
"values": {
|
||||
"socketPath": {
|
||||
"default": "/var/run/dockermanager.sock",
|
||||
"keys": {
|
||||
"uci": "dockermanager.@dockermanager[0].socket_path"
|
||||
}
|
||||
},
|
||||
"listenMode": {
|
||||
"default": "auto",
|
||||
"keys": {
|
||||
"uci": "dockermanager.@dockermanager[0].listen_mode"
|
||||
}
|
||||
},
|
||||
"externalPortEnabled": {
|
||||
"default": false,
|
||||
"keys": {
|
||||
"uci": "dockermanager.@dockermanager[0].external_port_enabled"
|
||||
}
|
||||
},
|
||||
"port": {
|
||||
"default": 8192,
|
||||
"keys": {
|
||||
"uci": "dockermanager.@dockermanager[0].port"
|
||||
}
|
||||
}
|
||||
},
|
||||
"upstreamBasePath": "/apps/dockermanager/",
|
||||
"apiPath": "api/",
|
||||
"pathMode": "preserve"
|
||||
"pathMode": "preserve",
|
||||
"fallback": {
|
||||
"transport": "tcp",
|
||||
"scheme": "http",
|
||||
"host": "127.0.0.1"
|
||||
}
|
||||
},
|
||||
"window": {
|
||||
"width": 1180,
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
config dockermanager
|
||||
option 'enabled' '0'
|
||||
option 'data_dir' ''
|
||||
option 'listen_mode' 'auto'
|
||||
option 'external_port_enabled' '0'
|
||||
option 'socket_path' '/var/run/dockermanager.sock'
|
||||
option 'port' '8192'
|
||||
option 'bind_addr' '0.0.0.0'
|
||||
option 'base_path' '/apps/dockermanager/'
|
||||
@@ -6,6 +6,9 @@ USE_PROCD=1
|
||||
get_config() {
|
||||
config_get_bool enabled "$1" enabled 0
|
||||
config_get data_dir "$1" data_dir ""
|
||||
config_get listen_mode "$1" listen_mode "auto"
|
||||
config_get_bool external_port_enabled "$1" external_port_enabled 0
|
||||
config_get socket_path "$1" socket_path "/var/run/dockermanager.sock"
|
||||
config_get port "$1" port "8192"
|
||||
config_get bind_addr "$1" bind_addr "0.0.0.0"
|
||||
config_get base_path "$1" base_path "/apps/dockermanager/"
|
||||
@@ -33,13 +36,25 @@ start_service() {
|
||||
|
||||
logger -t dockermanager "Starting Docker Manager service"
|
||||
procd_open_instance docker-manager
|
||||
if [ "$listen_mode" = "tcp" ] || [ "$external_port_enabled" = 1 ]; then
|
||||
procd_set_param env \
|
||||
DOCKER_MANAGER_LISTEN_MODE="tcp" \
|
||||
DOCKER_MANAGER_ADDR="$bind_addr:$port" \
|
||||
DOCKER_MANAGER_BASE_PATH="$base_path" \
|
||||
DOCKER_MANAGER_DATA_DIR="$data_dir" \
|
||||
DOCKER_MANAGER_STATIC_ROOT="/usr/share/dockermanager/www" \
|
||||
DOCKER_MANAGER_PLATFORM="openwrt" \
|
||||
DOCKER_MANAGER_AUTH_MODE="openwrt_luci"
|
||||
else
|
||||
procd_set_param env \
|
||||
DOCKER_MANAGER_LISTEN_MODE="unix" \
|
||||
DOCKER_MANAGER_SOCKET_PATH="$socket_path" \
|
||||
DOCKER_MANAGER_BASE_PATH="$base_path" \
|
||||
DOCKER_MANAGER_DATA_DIR="$data_dir" \
|
||||
DOCKER_MANAGER_STATIC_ROOT="/usr/share/dockermanager/www" \
|
||||
DOCKER_MANAGER_PLATFORM="openwrt" \
|
||||
DOCKER_MANAGER_AUTH_MODE="openwrt_luci"
|
||||
fi
|
||||
procd_set_param command /usr/sbin/docker-manager
|
||||
procd_set_param stdout 1
|
||||
procd_set_param stderr 1
|
||||
|
||||
+2
-2
@@ -9,8 +9,8 @@ include $(TOPDIR)/rules.mk
|
||||
PKG_ARCH_kaiplus:=$(ARCH)
|
||||
|
||||
PKG_NAME:=kaiplus
|
||||
PKG_VERSION:=linkeasefull-runtime-v3.0.15
|
||||
PKG_RELEASE:=7
|
||||
PKG_VERSION:=linkeasefull-runtime-v3.0.16
|
||||
PKG_RELEASE:=8
|
||||
PKG_SOURCE:=$(PKG_NAME)-binary-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://github.com/istoreos/istoreos-app-hub/releases/download/kaiplus-runtime-v$(PKG_VERSION)/
|
||||
PKG_HASH:=skip
|
||||
|
||||
@@ -13,15 +13,52 @@
|
||||
"entry": "index.html",
|
||||
"basePath": "/apps/kaiplus/"
|
||||
},
|
||||
"config": {
|
||||
"providerOrder": ["uci"],
|
||||
"providers": {
|
||||
"uci": {
|
||||
"type": "uci"
|
||||
}
|
||||
}
|
||||
},
|
||||
"backend": {
|
||||
"type": "http",
|
||||
"transport": "unix",
|
||||
"scheme": "http",
|
||||
"host": "127.0.0.1",
|
||||
"portFromUci": "kaiplus.@kaiplus[0].port",
|
||||
"defaultPort": 8189,
|
||||
"values": {
|
||||
"socketPath": {
|
||||
"default": "/var/run/kaiplus.sock",
|
||||
"keys": {
|
||||
"uci": "kaiplus.@kaiplus[0].socket_path"
|
||||
}
|
||||
},
|
||||
"listenMode": {
|
||||
"default": "auto",
|
||||
"keys": {
|
||||
"uci": "kaiplus.@kaiplus[0].listen_mode"
|
||||
}
|
||||
},
|
||||
"externalPortEnabled": {
|
||||
"default": false,
|
||||
"keys": {
|
||||
"uci": "kaiplus.@kaiplus[0].external_port_enabled"
|
||||
}
|
||||
},
|
||||
"port": {
|
||||
"default": 8189,
|
||||
"keys": {
|
||||
"uci": "kaiplus.@kaiplus[0].port"
|
||||
}
|
||||
}
|
||||
},
|
||||
"upstreamBasePath": "/apps/kaiplus/",
|
||||
"apiPath": "api/",
|
||||
"pathMode": "preserve"
|
||||
"pathMode": "preserve",
|
||||
"fallback": {
|
||||
"transport": "tcp",
|
||||
"scheme": "http",
|
||||
"host": "127.0.0.1"
|
||||
}
|
||||
},
|
||||
"window": {
|
||||
"width": 1180,
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
config kaiplus
|
||||
option 'enabled' '0'
|
||||
option 'data_dir' ''
|
||||
option 'listen_mode' 'auto'
|
||||
option 'external_port_enabled' '0'
|
||||
option 'socket_path' '/var/run/kaiplus.sock'
|
||||
option 'port' '8189'
|
||||
option 'bind_addr' '0.0.0.0'
|
||||
option 'base_path' '/apps/kaiplus/'
|
||||
option 'system_role' 'istoreos'
|
||||
@@ -4,9 +4,13 @@ START=93
|
||||
USE_PROCD=1
|
||||
|
||||
get_config() {
|
||||
config_get_bool enabled "$1" enabled 1
|
||||
config_get_bool enabled "$1" enabled 0
|
||||
config_get data_dir "$1" data_dir ""
|
||||
config_get listen_mode "$1" listen_mode "auto"
|
||||
config_get_bool external_port_enabled "$1" external_port_enabled 0
|
||||
config_get socket_path "$1" socket_path "/var/run/kaiplus.sock"
|
||||
config_get port "$1" port "8189"
|
||||
config_get bind_addr "$1" bind_addr "0.0.0.0"
|
||||
config_get base_path "$1" base_path "/apps/kaiplus/"
|
||||
config_get system_role "$1" system_role "istoreos"
|
||||
}
|
||||
@@ -50,15 +54,35 @@ start_service() {
|
||||
|
||||
logger -t kaiplus "Starting KaiPlus service"
|
||||
procd_open_instance kaiplus_bin
|
||||
if [ "$listen_mode" = "tcp" ] || [ "$external_port_enabled" = 1 ]; then
|
||||
procd_set_param env \
|
||||
KAIPLUS_HOME="$data_dir" \
|
||||
KAIPLUS_SYSTEM_ROLE="$system_role" \
|
||||
KAIPLUS_LISTEN_MODE="tcp" \
|
||||
KAIPLUS_WORKSPACE_HELPER_DIR="/usr/share/kaiplus/helpers" \
|
||||
KAIPLUS_WORKSPACE_TOOL_INSTALL_DIR="/tmp/kaiplus_workspace_tool" \
|
||||
KAIPLUS_SSH_HELPER_TOKEN="$helper_token" \
|
||||
REASONIX_CREDENTIALS_STORE="file"
|
||||
else
|
||||
mkdir_p "$(dirname "$socket_path")"
|
||||
procd_set_param env \
|
||||
KAIPLUS_HOME="$data_dir" \
|
||||
KAIPLUS_SYSTEM_ROLE="$system_role" \
|
||||
KAIPLUS_LISTEN_MODE="unix" \
|
||||
KAIPLUS_SOCKET_PATH="$socket_path" \
|
||||
KAIPLUS_WORKSPACE_HELPER_DIR="/usr/share/kaiplus/helpers" \
|
||||
KAIPLUS_WORKSPACE_TOOL_INSTALL_DIR="/tmp/kaiplus_workspace_tool" \
|
||||
KAIPLUS_SSH_HELPER_TOKEN="$helper_token" \
|
||||
REASONIX_CREDENTIALS_STORE="file"
|
||||
fi
|
||||
procd_set_param command /usr/sbin/kaiplus_bin kaiplus-web
|
||||
procd_append_param command --addr "0.0.0.0:$port"
|
||||
if [ "$listen_mode" = "tcp" ] || [ "$external_port_enabled" = 1 ]; then
|
||||
procd_append_param command --listen-mode tcp
|
||||
procd_append_param command --addr "$bind_addr:$port"
|
||||
else
|
||||
procd_append_param command --listen-mode unix
|
||||
procd_append_param command --socket-path "$socket_path"
|
||||
fi
|
||||
procd_append_param command --data-dir "$data_dir"
|
||||
procd_append_param command --static-dir /usr/share/kaiplus/www
|
||||
procd_append_param command --defaults-dir /usr/share/kaiplus/defaults
|
||||
|
||||
@@ -12,7 +12,7 @@ PKG_ARCH_LINKEASE:=$(ARCH)
|
||||
PKG_NAME:=linkease-common-bin
|
||||
# use PKG_SOURCE_DATE instead of PKG_VERSION for compitable
|
||||
PKG_SOURCE_DATE:=1.7.5
|
||||
PKG_RELEASE:=19
|
||||
PKG_RELEASE:=20
|
||||
|
||||
ARCH_HEXCODE:=
|
||||
ifeq ($(ARCH),x86_64)
|
||||
@@ -28,7 +28,7 @@ else ifeq ($(ARCH),mipsel)
|
||||
ARCH_HEXCODE=1b0c
|
||||
endif
|
||||
|
||||
PKG_SOURCE_VERSION:=ff87526d94aaa464981d5111c8631ca66396bef7
|
||||
PKG_SOURCE_VERSION:=be8ef471de3f6385bf2d570bd4048328361c24c0
|
||||
PKG_SOURCE:=linkease-common-bin-$(PKG_SOURCE_DATE)-linux-$(PKG_ARCH_LINKEASE).tar.gz
|
||||
PKG_SOURCE_URL:=https://github.com/istoreos/istoreos-app-hub/releases/download/linkease-runtime-v$(PKG_SOURCE_DATE)/
|
||||
PKG_BUILD_DIR:=$(BUILD_DIR)/linkease-common-bin-$(PKG_SOURCE_DATE)-linux-$(PKG_ARCH_LINKEASE)
|
||||
|
||||
+2
-2
@@ -12,7 +12,7 @@ PKG_ARCH_LINKEASE:=$(ARCH)
|
||||
PKG_NAME:=linkease
|
||||
# use PKG_SOURCE_DATE instead of PKG_VERSION for compitable
|
||||
PKG_SOURCE_DATE:=1.7.5
|
||||
PKG_RELEASE:=22
|
||||
PKG_RELEASE:=23
|
||||
|
||||
ARCH_HEXCODE:=
|
||||
ifeq ($(ARCH),x86_64)
|
||||
@@ -28,7 +28,7 @@ else ifeq ($(ARCH),mipsel)
|
||||
ARCH_HEXCODE=1b0c
|
||||
endif
|
||||
|
||||
PKG_SOURCE_VERSION:=ff87526d94aaa464981d5111c8631ca66396bef7
|
||||
PKG_SOURCE_VERSION:=be8ef471de3f6385bf2d570bd4048328361c24c0
|
||||
PKG_SOURCE:=linkease-bin-$(PKG_SOURCE_DATE)-linux-$(PKG_ARCH_LINKEASE).tar.gz
|
||||
PKG_SOURCE_URL:=https://github.com/istoreos/istoreos-app-hub/releases/download/linkease-runtime-v$(PKG_SOURCE_DATE)/
|
||||
PKG_BUILD_DIR:=$(BUILD_DIR)/linkease-bin-$(PKG_SOURCE_DATE)-linux-$(PKG_ARCH_LINKEASE)
|
||||
|
||||
@@ -10,8 +10,8 @@ PKG_ARCH_LINKEASE:=$(ARCH)
|
||||
|
||||
PKG_NAME:=linkeasefull
|
||||
# use PKG_SOURCE_DATE instead of PKG_VERSION for compitable
|
||||
PKG_SOURCE_DATE:=3.0.13
|
||||
PKG_RELEASE:=21
|
||||
PKG_SOURCE_DATE:=3.0.16
|
||||
PKG_RELEASE:=22
|
||||
|
||||
ARCH_HEXCODE:=
|
||||
ifeq ($(ARCH),x86_64)
|
||||
@@ -24,7 +24,7 @@ LINKEASE_RUNTIME_ARCH:=arm64
|
||||
PKG_HASH:=skip
|
||||
endif
|
||||
|
||||
PKG_SOURCE_VERSION:=ff87526d94aaa464981d5111c8631ca66396bef7
|
||||
PKG_SOURCE_VERSION:=be8ef471de3f6385bf2d570bd4048328361c24c0
|
||||
PKG_SOURCE:=linkease-runtime-$(PKG_SOURCE_DATE)-linux-$(LINKEASE_RUNTIME_ARCH).tar.gz
|
||||
PKG_SOURCE_URL:=https://github.com/istoreos/istoreos-app-hub/releases/download/linkeasefull-runtime-v$(PKG_SOURCE_DATE)/
|
||||
PKG_BUILD_DIR:=$(BUILD_DIR)/linkease-runtime-$(PKG_SOURCE_DATE)-linux-$(LINKEASE_RUNTIME_ARCH)
|
||||
|
||||
@@ -110,6 +110,7 @@ start_service() {
|
||||
--localApi "$LOCAL_API" \
|
||||
--supplierCode "linkease"
|
||||
procd_set_param env \
|
||||
"LINKEASE_AUTH_PROVIDER=openwrt" \
|
||||
"KAIPLUS_ENABLED=0"
|
||||
procd_set_param respawn
|
||||
procd_set_param stdout 1
|
||||
|
||||
@@ -11,7 +11,7 @@ LUCI_DEPENDS:=+dockermanager +luci-lib-linkeaseauth
|
||||
LUCI_PKGARCH:=all
|
||||
|
||||
PKG_VERSION:=0.1.1
|
||||
PKG_RELEASE:=1
|
||||
PKG_RELEASE:=2
|
||||
|
||||
include $(TOPDIR)/feeds/luci/luci.mk
|
||||
|
||||
|
||||
@@ -38,6 +38,11 @@ local function uhttpd_apps_proxy_available()
|
||||
return uhttpd_supports_proxy_prefix() and uhttpd_has_apps_proxy_prefix()
|
||||
end
|
||||
|
||||
local function linkeasefull_running()
|
||||
local sys = require "luci.sys"
|
||||
return sys.call("[ -x /etc/init.d/linkeasefull ] && /etc/init.d/linkeasefull running >/dev/null 2>&1") == 0
|
||||
end
|
||||
|
||||
local function normalized_base_path(path)
|
||||
path = path or "/apps/dockermanager/"
|
||||
if path:sub(1, 1) ~= "/" then
|
||||
@@ -94,29 +99,79 @@ local function dockermanager_config()
|
||||
local uci = require "luci.model.uci".cursor()
|
||||
local port = uci:get_first("dockermanager", "dockermanager", "port") or "8192"
|
||||
local base_path = normalized_base_path(uci:get_first("dockermanager", "dockermanager", "base_path"))
|
||||
return port, base_path
|
||||
local external_port_enabled = uci:get_first("dockermanager", "dockermanager", "external_port_enabled") == "1"
|
||||
local listen_mode = uci:get_first("dockermanager", "dockermanager", "listen_mode") or "auto"
|
||||
local socket_path = uci:get_first("dockermanager", "dockermanager", "socket_path") or "/var/run/dockermanager.sock"
|
||||
return port, base_path, external_port_enabled, listen_mode, socket_path
|
||||
end
|
||||
|
||||
local function dockermanager_entry_url()
|
||||
local port, base_path = dockermanager_config()
|
||||
if uhttpd_apps_proxy_available() then
|
||||
local port, base_path, external_port_enabled = dockermanager_config()
|
||||
if linkeasefull_running() and uhttpd_apps_proxy_available() then
|
||||
return base_path
|
||||
end
|
||||
if not external_port_enabled then
|
||||
return nil
|
||||
end
|
||||
return "http://" .. url_authority(request_or_lan_host(), port) .. base_path
|
||||
end
|
||||
|
||||
local function enable_external_port()
|
||||
local uci = require "luci.model.uci".cursor()
|
||||
local section = nil
|
||||
uci:foreach("dockermanager", "dockermanager", function(s)
|
||||
if not section then
|
||||
section = s[".name"]
|
||||
end
|
||||
end)
|
||||
if section then
|
||||
uci:set("dockermanager", section, "external_port_enabled", "1")
|
||||
uci:set("dockermanager", section, "listen_mode", "tcp")
|
||||
uci:commit("dockermanager")
|
||||
end
|
||||
local sys = require "luci.sys"
|
||||
sys.call("/etc/init.d/dockermanager restart >/dev/null 2>&1; sleep 1")
|
||||
end
|
||||
|
||||
local function render_enable_port_confirm()
|
||||
local dispatcher = require "luci.dispatcher"
|
||||
local confirm_url = dispatcher.build_url("admin", "services", "dockermanager", "open") .. "?enable_port=1"
|
||||
luci.http.prepare_content("text/html; charset=utf-8")
|
||||
luci.http.write("<!doctype html><html><head><meta charset='utf-8'><title>Docker Manager</title></head><body>")
|
||||
luci.http.write("<h2>Docker Manager</h2>")
|
||||
luci.http.write("<p>当前系统无法通过 LinkEase 桌面代理打开 Docker Manager。</p>")
|
||||
luci.http.write("<p>可以启用局域网端口 8192 后继续打开。</p>")
|
||||
luci.http.write("<p><a class='cbi-button cbi-button-apply' href='" .. confirm_url .. "'>启用 8192 并打开</a></p>")
|
||||
luci.http.write("</body></html>")
|
||||
end
|
||||
|
||||
local function render_open_failed()
|
||||
luci.http.prepare_content("text/html; charset=utf-8")
|
||||
luci.http.write("<!doctype html><html><head><meta charset='utf-8'><title>Docker Manager</title></head><body>")
|
||||
luci.http.write("<h2>Docker Manager</h2>")
|
||||
luci.http.write("<p>Docker Manager 已安装,但当前没有可用访问入口。</p>")
|
||||
luci.http.write("<p>请启用 LinkEaseFull 的 /apps 代理,或在 Docker Manager 设置中开放 8192 端口。</p>")
|
||||
luci.http.write("</body></html>")
|
||||
end
|
||||
|
||||
function dockermanager_status()
|
||||
local sys = require "luci.sys"
|
||||
local uci = require "luci.model.uci".cursor()
|
||||
local port, base_path = dockermanager_config()
|
||||
local port, base_path, external_port_enabled, listen_mode, socket_path = dockermanager_config()
|
||||
local entry_url = dockermanager_entry_url()
|
||||
|
||||
local status = {
|
||||
running = (sys.call("pidof docker-manager >/dev/null") == 0),
|
||||
port = port,
|
||||
base_path = base_path,
|
||||
listen_mode = listen_mode,
|
||||
socket_path = socket_path,
|
||||
external_port_enabled = external_port_enabled,
|
||||
entry_url = entry_url or "",
|
||||
lan_ip = uci:get("network", "lan", "ipaddr") or "",
|
||||
proxy_prefix_supported = uhttpd_supports_proxy_prefix(),
|
||||
proxy_prefix_enabled = uhttpd_apps_proxy_available()
|
||||
proxy_prefix_enabled = uhttpd_apps_proxy_available(),
|
||||
linkeasefull_running = linkeasefull_running()
|
||||
}
|
||||
|
||||
luci.http.prepare_content("application/json")
|
||||
@@ -125,5 +180,19 @@ end
|
||||
|
||||
function dockermanager_open()
|
||||
local http = require "luci.http"
|
||||
http.redirect(linkease_auth_url("auth") .. "?return=" .. cookie_encode(dockermanager_entry_url()))
|
||||
if http.formvalue("enable_port") == "1" then
|
||||
enable_external_port()
|
||||
end
|
||||
|
||||
local entry_url = dockermanager_entry_url()
|
||||
if entry_url then
|
||||
http.redirect(linkease_auth_url("auth") .. "?return=" .. cookie_encode(entry_url))
|
||||
return
|
||||
end
|
||||
|
||||
if http.formvalue("enable_port") == "1" then
|
||||
render_open_failed()
|
||||
return
|
||||
end
|
||||
render_enable_port_confirm()
|
||||
end
|
||||
@@ -12,6 +12,22 @@ s:option(Flag, "enabled", translate("Enable")).rmempty = false
|
||||
local data_dir = s:option(Value, "data_dir", translate("Data directory"))
|
||||
data_dir.rmempty = false
|
||||
|
||||
local listen_mode = s:option(ListValue, "listen_mode", translate("Listen mode"))
|
||||
listen_mode:value("auto", translate("Auto"))
|
||||
listen_mode:value("unix", translate("Unix socket"))
|
||||
listen_mode:value("tcp", translate("TCP port"))
|
||||
listen_mode.default = "auto"
|
||||
listen_mode.rmempty = false
|
||||
|
||||
local external_port_enabled = s:option(Flag, "external_port_enabled", translate("Enable external port access"))
|
||||
external_port_enabled.default = "0"
|
||||
external_port_enabled.rmempty = false
|
||||
|
||||
local socket_path = s:option(Value, "socket_path", translate("Unix socket path"))
|
||||
socket_path.default = "/var/run/dockermanager.sock"
|
||||
socket_path.rmempty = false
|
||||
socket_path.readonly = true
|
||||
|
||||
local port = s:option(Value, "port", translate("Listen port"))
|
||||
port.default = "8192"
|
||||
port.rmempty = false
|
||||
@@ -24,5 +40,6 @@ bind_addr.rmempty = false
|
||||
local base_path = s:option(Value, "base_path", translate("Base path"))
|
||||
base_path.default = "/apps/dockermanager/"
|
||||
base_path.rmempty = false
|
||||
base_path.readonly = true
|
||||
|
||||
return m
|
||||
@@ -8,6 +8,18 @@
|
||||
var status = st.running
|
||||
? '<br/><em style=\"color:green\"><%:The Docker Manager service is running.%></em>'
|
||||
: '<br/><em style=\"color:red\"><%:The Docker Manager service is not running.%></em>';
|
||||
if (st.proxy_prefix_enabled && st.linkeasefull_running)
|
||||
{
|
||||
status = status + '<br/><em><%:Access mode: LinkEase Desktop proxy.%></em>';
|
||||
}
|
||||
else if (st.external_port_enabled)
|
||||
{
|
||||
status = status + '<br/><em><%:Access mode: external port.%></em>';
|
||||
}
|
||||
else
|
||||
{
|
||||
status = status + '<br/><em><%:External port access is disabled.%></em>';
|
||||
}
|
||||
tb.innerHTML = status;
|
||||
|
||||
if (st.running)
|
||||
|
||||
@@ -12,7 +12,7 @@ LUCI_DEPENDS:=+luci-base +logd +jsonfilter
|
||||
LUCI_PKGARCH:=all
|
||||
|
||||
PKG_VERSION:=0.1.37
|
||||
PKG_RELEASE:=3
|
||||
PKG_RELEASE:=4
|
||||
|
||||
# Reproducible build: honor SOURCE_DATE_EPOCH from the build environment (set by docker-sdk.sh / CI).
|
||||
ifdef SOURCE_DATE_EPOCH
|
||||
|
||||
@@ -503,7 +503,6 @@ return view.extend({
|
||||
this.lastRulesError = 'rules_unavailable';
|
||||
}
|
||||
this.updateBackendUi();
|
||||
this.updateStatus();
|
||||
},
|
||||
|
||||
backendDisplayLabel() {
|
||||
@@ -520,8 +519,20 @@ return view.extend({
|
||||
map.setAttribute('data-backend', this.firewallBackend || 'unknown');
|
||||
|
||||
const label = document.getElementById('fwlive-backend');
|
||||
if (label)
|
||||
label.textContent = this.backendDisplayLabel();
|
||||
if (label) {
|
||||
let text = this.backendDisplayLabel();
|
||||
if (this.lastRulesError) {
|
||||
let err = '';
|
||||
if (this.lastRulesError === 'rules_truncated')
|
||||
err = _('Rule labels incomplete — map truncated');
|
||||
else if (this.lastRulesError === 'mktemp_failed')
|
||||
err = _('Rule labels unavailable — temp file failed');
|
||||
else
|
||||
err = _('Rule labels unavailable');
|
||||
text = text ? text + ' \u00b7 ' + err : err;
|
||||
}
|
||||
label.textContent = text;
|
||||
}
|
||||
|
||||
this.updateEmptyStateUi();
|
||||
},
|
||||
@@ -928,17 +939,6 @@ return view.extend({
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.lastRulesError) {
|
||||
status.className = 'fwlive-status fwlive-status-error';
|
||||
if (this.lastRulesError === 'rules_truncated')
|
||||
status.textContent = _('Rule labels incomplete — map truncated') + suffix;
|
||||
else if (this.lastRulesError === 'mktemp_failed')
|
||||
status.textContent = _('Rule labels unavailable — temp file failed') + suffix;
|
||||
else
|
||||
status.textContent = _('Rule labels unavailable') + suffix;
|
||||
return;
|
||||
}
|
||||
|
||||
status.className = this.paused
|
||||
? 'fwlive-status fwlive-status-paused'
|
||||
: 'fwlive-status';
|
||||
|
||||
@@ -382,6 +382,8 @@ collect_logging_blockers() {
|
||||
[ -n "$zone" ] || logging_blockers_append 'no_wan_zone'
|
||||
check_nf_log_ipv4 || logging_blockers_append 'nf_log_ipv4_missing'
|
||||
check_nf_log_ipv6 || logging_blockers_append 'nf_log_ipv6_missing'
|
||||
# rpcd/fwlive run_with_timeout fail-closes to 127 without timeout (#229): surface as blocker.
|
||||
command -v timeout >/dev/null 2>&1 || logging_blockers_append 'timeout_missing'
|
||||
|
||||
[ -n "$LOGGING_BLOCKERS" ] || return 0
|
||||
return 1
|
||||
|
||||
@@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
LUCI_TITLE:=KaiPlus
|
||||
PKG_VERSION:=1.0.1
|
||||
PKG_RELEASE:=5
|
||||
PKG_RELEASE:=6
|
||||
LUCI_DEPENDS:=+kaiplus +luci-compat
|
||||
LUCI_MINIFY_CSS:=0
|
||||
LUCI_MINIFY_JS:=0
|
||||
|
||||
@@ -1,27 +1,197 @@
|
||||
local http = require "luci.http"
|
||||
|
||||
module("luci.controller.kaiplus", package.seeall)
|
||||
|
||||
local APPS_PROXY_PREFIX = "/apps=http://127.0.0.1:19290"
|
||||
|
||||
function index()
|
||||
entry({"admin", "services", "kaiplus_status"}, call("kaiplus_status"))
|
||||
local open = entry({"admin", "services", "kaiplus", "open"}, call("kaiplus_open"))
|
||||
open.leaf = true
|
||||
open.dependent = false
|
||||
open.sysauth = "root"
|
||||
open.sysauth_authenticator = "htmlauth"
|
||||
|
||||
if not nixio.fs.access("/etc/config/kaiplus") then
|
||||
return
|
||||
end
|
||||
local page
|
||||
page = entry({"admin", "services", "kaiplus"}, cbi("kaiplus"), _("KaiPlus"), 100)
|
||||
page.dependent = true
|
||||
entry({"admin", "services", "kaiplus_status"}, call("kaiplus_status"))
|
||||
end
|
||||
|
||||
local function uhttpd_has_apps_proxy_prefix()
|
||||
local uci = require "luci.model.uci".cursor()
|
||||
local mappings = uci:get_list("uhttpd", "main", "proxy_prefix") or {}
|
||||
|
||||
for _, mapping in ipairs(mappings) do
|
||||
if mapping == APPS_PROXY_PREFIX then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function uhttpd_supports_proxy_prefix()
|
||||
local sys = require "luci.sys"
|
||||
return sys.call("grep -qr 'proxy_prefix' /etc/init.d/uhttpd /lib/functions /usr/share/uhttpd 2>/dev/null") == 0
|
||||
end
|
||||
|
||||
local function uhttpd_apps_proxy_available()
|
||||
return uhttpd_supports_proxy_prefix() and uhttpd_has_apps_proxy_prefix()
|
||||
end
|
||||
|
||||
local function linkeasefull_running()
|
||||
local sys = require "luci.sys"
|
||||
return sys.call("[ -x /etc/init.d/linkeasefull ] && /etc/init.d/linkeasefull running >/dev/null 2>&1") == 0
|
||||
end
|
||||
|
||||
local function normalized_base_path(path)
|
||||
path = path or "/apps/kaiplus/"
|
||||
if path:sub(1, 1) ~= "/" then
|
||||
path = "/" .. path
|
||||
end
|
||||
if path:sub(-1) ~= "/" then
|
||||
path = path .. "/"
|
||||
end
|
||||
return path
|
||||
end
|
||||
|
||||
local function cookie_encode(value)
|
||||
return tostring(value or ""):gsub("([^A-Za-z0-9._~-])", function(char)
|
||||
return string.format("%%%02X", char:byte())
|
||||
end)
|
||||
end
|
||||
|
||||
local function authority_host(authority)
|
||||
if not authority or authority == "" then
|
||||
return ""
|
||||
end
|
||||
if authority:sub(1, 1) == "[" then
|
||||
return authority:match("^%[([^%]]+)%]") or ""
|
||||
end
|
||||
return authority:match("^([^:]+)") or authority
|
||||
end
|
||||
|
||||
local function url_authority(host, port)
|
||||
if not host or host == "" then
|
||||
host = "127.0.0.1"
|
||||
end
|
||||
if host:find(":") and host:sub(1, 1) ~= "[" then
|
||||
host = "[" .. host .. "]"
|
||||
end
|
||||
return host .. ":" .. tostring(port)
|
||||
end
|
||||
|
||||
local function request_or_lan_host()
|
||||
local http = require "luci.http"
|
||||
local uci = require "luci.model.uci".cursor()
|
||||
local host = authority_host(http.getenv("HTTP_HOST") or "")
|
||||
if host ~= "" then
|
||||
return host
|
||||
end
|
||||
return uci:get("network", "lan", "ipaddr") or "127.0.0.1"
|
||||
end
|
||||
|
||||
local function linkease_auth_url(name)
|
||||
local dispatcher = require "luci.dispatcher"
|
||||
return dispatcher.build_url("admin", "services", "linkease_auth", name)
|
||||
end
|
||||
|
||||
local function kaiplus_config()
|
||||
local uci = require "luci.model.uci".cursor()
|
||||
local port = uci:get_first("kaiplus", "kaiplus", "port", "8189")
|
||||
local base_path = normalized_base_path(uci:get_first("kaiplus", "kaiplus", "base_path"))
|
||||
local external_port_enabled = uci:get_first("kaiplus", "kaiplus", "external_port_enabled") == "1"
|
||||
local listen_mode = uci:get_first("kaiplus", "kaiplus", "listen_mode") or "auto"
|
||||
local socket_path = uci:get_first("kaiplus", "kaiplus", "socket_path") or "/var/run/kaiplus.sock"
|
||||
return port, base_path, external_port_enabled, listen_mode, socket_path
|
||||
end
|
||||
|
||||
local function kaiplus_entry_url()
|
||||
local port, base_path, external_port_enabled = kaiplus_config()
|
||||
if linkeasefull_running() and uhttpd_apps_proxy_available() then
|
||||
return base_path
|
||||
end
|
||||
if not external_port_enabled then
|
||||
return nil
|
||||
end
|
||||
return "http://" .. url_authority(request_or_lan_host(), port) .. base_path
|
||||
end
|
||||
|
||||
local function enable_external_port()
|
||||
local uci = require "luci.model.uci".cursor()
|
||||
local section = nil
|
||||
uci:foreach("kaiplus", "kaiplus", function(s)
|
||||
if not section then
|
||||
section = s[".name"]
|
||||
end
|
||||
end)
|
||||
if section then
|
||||
uci:set("kaiplus", section, "external_port_enabled", "1")
|
||||
uci:set("kaiplus", section, "listen_mode", "tcp")
|
||||
uci:commit("kaiplus")
|
||||
end
|
||||
local sys = require "luci.sys"
|
||||
sys.call("/etc/init.d/kaiplus restart >/dev/null 2>&1; sleep 1")
|
||||
end
|
||||
|
||||
local function render_enable_port_confirm()
|
||||
local dispatcher = require "luci.dispatcher"
|
||||
local confirm_url = dispatcher.build_url("admin", "services", "kaiplus", "open") .. "?enable_port=1"
|
||||
luci.http.prepare_content("text/html; charset=utf-8")
|
||||
luci.http.write("<!doctype html><html><head><meta charset='utf-8'><title>KaiPlus</title></head><body>")
|
||||
luci.http.write("<h2>KaiPlus</h2>")
|
||||
luci.http.write("<p>当前系统无法通过 LinkEase 桌面代理打开 KaiPlus。</p>")
|
||||
luci.http.write("<p>可以启用局域网端口 8189 后继续打开。</p>")
|
||||
luci.http.write("<p><a class='cbi-button cbi-button-apply' href='" .. confirm_url .. "'>启用 8189 并打开</a></p>")
|
||||
luci.http.write("</body></html>")
|
||||
end
|
||||
|
||||
local function render_open_failed()
|
||||
luci.http.prepare_content("text/html; charset=utf-8")
|
||||
luci.http.write("<!doctype html><html><head><meta charset='utf-8'><title>KaiPlus</title></head><body>")
|
||||
luci.http.write("<h2>KaiPlus</h2>")
|
||||
luci.http.write("<p>KaiPlus 已安装,但当前没有可用访问入口。</p>")
|
||||
luci.http.write("<p>请启用 LinkEaseFull 的 /apps 代理,或在 KaiPlus 设置中开放 8189 端口。</p>")
|
||||
luci.http.write("</body></html>")
|
||||
end
|
||||
|
||||
function kaiplus_status()
|
||||
local sys = require "luci.sys"
|
||||
local uci = require "luci.model.uci".cursor()
|
||||
local port = uci:get_first("kaiplus", "kaiplus", "port", "8189")
|
||||
local base_path = uci:get_first("kaiplus", "kaiplus", "base_path", "/apps/kaiplus/")
|
||||
local port, base_path, external_port_enabled, listen_mode, socket_path = kaiplus_config()
|
||||
local entry_url = kaiplus_entry_url()
|
||||
local status = {
|
||||
running = (sys.call("pidof kaiplus_bin >/dev/null") == 0),
|
||||
port = port,
|
||||
base_path = base_path
|
||||
base_path = base_path,
|
||||
listen_mode = listen_mode,
|
||||
socket_path = socket_path,
|
||||
external_port_enabled = external_port_enabled,
|
||||
entry_url = entry_url or "",
|
||||
lan_ip = uci:get("network", "lan", "ipaddr") or "",
|
||||
proxy_prefix_supported = uhttpd_supports_proxy_prefix(),
|
||||
proxy_prefix_enabled = uhttpd_apps_proxy_available(),
|
||||
linkeasefull_running = linkeasefull_running()
|
||||
}
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json(status)
|
||||
end
|
||||
|
||||
function kaiplus_open()
|
||||
local http = require "luci.http"
|
||||
if http.formvalue("enable_port") == "1" then
|
||||
enable_external_port()
|
||||
end
|
||||
|
||||
local entry_url = kaiplus_entry_url()
|
||||
if entry_url then
|
||||
http.redirect(linkease_auth_url("auth") .. "?return=" .. cookie_encode(entry_url))
|
||||
return
|
||||
end
|
||||
|
||||
if http.formvalue("enable_port") == "1" then
|
||||
render_open_failed()
|
||||
return
|
||||
end
|
||||
render_enable_port_confirm()
|
||||
end
|
||||
@@ -31,10 +31,35 @@ for _, val in pairs(paths) do
|
||||
end
|
||||
data_dir.default = default_path
|
||||
|
||||
local listen_mode = s:option(ListValue, "listen_mode", translate("Listen mode"))
|
||||
listen_mode:value("auto", translate("Auto"))
|
||||
listen_mode:value("unix", translate("Unix socket"))
|
||||
listen_mode:value("tcp", translate("TCP port"))
|
||||
listen_mode.default = "auto"
|
||||
listen_mode.rmempty = false
|
||||
|
||||
local external_port_enabled = s:option(Flag, "external_port_enabled", translate("Enable external port access"))
|
||||
external_port_enabled.default = "0"
|
||||
external_port_enabled.rmempty = false
|
||||
|
||||
local socket_path = s:option(Value, "socket_path", translate("Unix socket path"))
|
||||
socket_path.default = "/var/run/kaiplus.sock"
|
||||
socket_path.rmempty = false
|
||||
socket_path.readonly = true
|
||||
|
||||
local port = s:option(Value, "port", translate("Web port"))
|
||||
port.default = "8189"
|
||||
port.rmempty = false
|
||||
port.datatype = "port"
|
||||
port.description = translate("Port for the KaiPlus web service.")
|
||||
|
||||
local bind_addr = s:option(Value, "bind_addr", translate("Bind address"))
|
||||
bind_addr.default = "0.0.0.0"
|
||||
bind_addr.rmempty = false
|
||||
|
||||
local base_path = s:option(Value, "base_path", translate("Base path"))
|
||||
base_path.default = "/apps/kaiplus/"
|
||||
base_path.rmempty = false
|
||||
base_path.readonly = true
|
||||
|
||||
return m
|
||||
@@ -2,19 +2,22 @@
|
||||
XHR.poll(5, '<%=url("admin/services/kaiplus_status")%>', null, function(x, st) {
|
||||
var el = document.getElementById('kaiplus_status');
|
||||
if (st && el) {
|
||||
if (!st.running) {
|
||||
el.innerHTML = '<br/><em style=\"color:red\"><%:The KaiPlus service is not running.%></em>';
|
||||
var status = st.running
|
||||
? '<br/><em style=\"color:green\"><%:The KaiPlus service is running.%></em>'
|
||||
: '<br/><em style=\"color:red\"><%:The KaiPlus service is not running.%></em>';
|
||||
if (st.proxy_prefix_enabled && st.linkeasefull_running) {
|
||||
status = status + '<br/><em><%:Access mode: LinkEase Desktop proxy.%></em>';
|
||||
}
|
||||
else if (st.external_port_enabled) {
|
||||
status = status + '<br/><em><%:Access mode: external port.%></em>';
|
||||
}
|
||||
else {
|
||||
status = status + '<br/><em><%:External port access is disabled.%></em>';
|
||||
}
|
||||
el.innerHTML = status;
|
||||
if (st.running) {
|
||||
var basePath = st.base_path || "/apps/kaiplus/";
|
||||
if (basePath.charAt(0) !== "/") {
|
||||
basePath = "/" + basePath;
|
||||
}
|
||||
if (basePath.charAt(basePath.length - 1) !== "/") {
|
||||
basePath = basePath + "/";
|
||||
}
|
||||
el.innerHTML = '<br/><em style=\"color:green\"><%:The KaiPlus service is running.%></em>'
|
||||
+ "<br/><br/><input class=\"btn cbi-button cbi-button-apply\" type=\"button\" value=\" <%:Click to open KaiPlus%> \" onclick=\"window.open('http://' + window.location.hostname + ':' + st.port + basePath)\"/>";
|
||||
el.innerHTML = el.innerHTML
|
||||
+ "<br/><br/><input class=\"btn cbi-button cbi-button-apply\" type=\"button\" value=\" <%:Click to open KaiPlus%> \" onclick=\"window.open('<%=url("admin/services/kaiplus/open")%>')\"/>";
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -9,7 +9,7 @@ LUCI_TITLE:=LuCI support for quickstart
|
||||
LUCI_DEPENDS:=+quickstart +luci-app-ttyd +luci-i18n-ttyd-zh-cn
|
||||
LUCI_PKGARCH:=all
|
||||
|
||||
PKG_VERSION:=0.12.8-r1
|
||||
PKG_VERSION:=0.12.10-r1
|
||||
# PKG_RELEASE MUST be empty for luci.mk
|
||||
PKG_RELEASE:=
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
var go=Object.defineProperty,vo=Object.defineProperties;var bo=Object.getOwnPropertyDescriptors;var xa=Object.getOwnPropertySymbols;var ho=Object.prototype.hasOwnProperty,_o=Object.prototype.propertyIsEnumerable;var We=(o,n,a)=>n in o?go(o,n,{enumerable:!0,configurable:!0,writable:!0,value:a}):o[n]=a,st=(o,n)=>{for(var a in n||(n={}))ho.call(n,a)&&We(o,a,n[a]);if(xa)for(var a of xa(n))_o.call(n,a)&&We(o,a,n[a]);return o},vt=(o,n)=>vo(o,bo(n));var xo=(o,n)=>()=>(n||o((n={exports:{}}).exports,n),n.exports);var ve=(o,n,a)=>(We(o,typeof n!="symbol"?n+"":n,a),a);var L=(o,n,a)=>new Promise((l,u)=>{var d=g=>{try{k(a.next(g))}catch(p){u(p)}},c=g=>{try{k(a.throw(g))}catch(p){u(p)}},k=g=>g.done?l(g.value):Promise.resolve(g.value).then(d,c);k((a=a.apply(o,n)).next())});import{a as wo,c as ko,d as we,b as I,e as Q,u as e,o as r,f as s,g as t,t as i,n as ht,h as D,i as Y,r as _t,j as Yt,w as O,v as Rt,k as Ia,l as Z,m as V,p as E,q as $t,s as Pt,x as pt,F as U,y as tt,z as at,A as jt,B as ut,C as ct,D as ke,E as ee,T as St,G as Nt,H as lt,I as ft,J as xt,K as dt,L as gt,M as Bt,N as Xe,O as Ma,P as La,Q as Oa,R as ta,S as ea,U as Na,V as aa,W as Pe,X as yo,Y as oa,Z as ye,_ as Fo,$ as Eo,a0 as na,a1 as $o,a2 as Te,a3 as Co,a4 as Ie,a5 as Do,a6 as Bo,a7 as Yo,a8 as Ao,a9 as So,aa as zo,ab as Po,ac as To}from"./vendor.js?v=91daea62";var sT=xo(ae=>{const Io=function(){const n=document.createElement("link").relList;if(n&&n.supports&&n.supports("modulepreload"))return;for(const u of document.querySelectorAll('link[rel="modulepreload"]'))l(u);new MutationObserver(u=>{for(const d of u)if(d.type==="childList")for(const c of d.addedNodes)c.tagName==="LINK"&&c.rel==="modulepreload"&&l(c)}).observe(document,{childList:!0,subtree:!0});function a(u){const d={};return u.integrity&&(d.integrity=u.integrity),u.referrerpolicy&&(d.referrerPolicy=u.referrerpolicy),u.crossorigin==="use-credentials"?d.credentials="include":u.crossorigin==="anonymous"?d.credentials="omit":d.credentials="same-origin",d}function l(u){if(u.ep)return;u.ep=!0;const d=a(u);fetch(u.href,d)}};Io();const Gt={language:void 0,numberFormat:new Intl.NumberFormat("en",{notation:"compact"})},Mo=o=>L(ae,null,function*(){const n=window.vue_lang;new Date().getTime();let a={};try{const d=yield wo({url:window.vue_lang_data,method:"GET"});d.data&&(a=d.data)}catch(d){console.log(d)}const l=ko({defaultLanguage:n,mutedLanguages:["zh-cn"],translations:a,setGlobalProperties:!1,provideDirective:!1,provideComponent:!1});o.use(l),Gt.language=l;const{$gettext:u}=l;window.$i18n=u;try{Gt.numberFormat=new Intl.NumberFormat(n,{notation:"compact"})}catch(d){console.error("Intl.NumberFormat unsupported lang",n,d)}}),J=()=>{if(Gt.language)return Gt.language;throw new Error("I18N Uninitialized!")},Me=()=>({$gettext:(o,n,a)=>{if(Gt.language)return Gt.language.$gettext(o,n,a);throw new Error("I18N Uninitialized!")},$ngettext:(o,n,a,l,u)=>{if(Gt.language)return Gt.language.$ngettext(o,n,a,l,u);throw new Error("I18N Uninitialized!")}}),De=o=>typeof o=="number"?Gt.numberFormat.format(o):"?",{$gettext:Lo,$ngettext:uT}=Me(),Va=(o,n)=>L(ae,null,function*(){return new Promise((a,l)=>L(ae,null,function*(){try{const u=yield fetch(o,n);if(Math.floor(u.status/100)!=2)throw u.status+" "+u.statusText;const d=st({},u);d.data=yield u.json(),a(d)}catch(u){const d=u;l(Lo("\u7F51\u7EDC\u5F02\u5E38\uFF1A")+((d==null?void 0:d.message)||u))}}))});class ia{constructor(n){ve(this,"config",{baseURL:"",headers:{}});ve(this,"useRequest",n=>n);ve(this,"useResponse",n=>n);ve(this,"useError",n=>n);n.baseURL&&(this.config.baseURL=n.baseURL),n.headers&&(this.config.headers=n.headers)}static create(n){return new ia(n)}Do(n,a){return L(this,null,function*(){return new Promise((l,u)=>L(this,null,function*(){try{const d=this.useRequest({baseURL:this.config.baseURL,headers:this.config.headers});n=`${d.baseURL||""}${n}`,a.headers==null&&(a.headers={}),d.headers&&(a.headers=st({},d.headers));const k=yield fetch(n,a),g=st({},k);g.data=yield k.json(),l(this.useResponse(g))}catch(d){this.useError(d),u(d)}}))})}TEXT(n,a){return L(this,null,function*(){return new Promise((l,u)=>L(this,null,function*(){try{const d=this.useRequest({baseURL:this.config.baseURL,headers:this.config.headers});n=`${d.baseURL||""}${n}`,a.headers==null&&(a.headers={}),d.headers&&(a.headers=st({},d.headers));const k=yield fetch(n,a),g=st({},k);g.data=yield k.text(),l(g)}catch(d){this.useError(d),u(d)}}))})}interceptors(){const n=this;return{requset:{use(a){n.useRequest=a}},response:{use(a,l){n.useResponse=a,l&&(n.useError=l)}}}}}const Ga=ia.create({});Ga.interceptors().requset.use(o=>o);Ga.interceptors().response.use(o=>(o.data&&o.data.success==null&&o.data.success==0,o));const{$gettext:ja,$ngettext:cT}=Me(),Oo="/cgi-bin/luci/istore",No="/cgi-bin/luci/admin";let Ae=!1;const W=(o,n)=>(o.indexOf("//")==-1&&(o=`${Oo}${o}`),Va(o,n).then(a=>(a!=null&&a.data&&a.data.success==-1001&&a.data.error=="Forbidden"&&(Ae||(Ae=!0,alert(ja("\u767B\u5F55\u8FC7\u671F\uFF0C\u8BF7\u91CLine truncated
|
||||
var go=Object.defineProperty,vo=Object.defineProperties;var bo=Object.getOwnPropertyDescriptors;var xa=Object.getOwnPropertySymbols;var ho=Object.prototype.hasOwnProperty,_o=Object.prototype.propertyIsEnumerable;var We=(o,n,a)=>n in o?go(o,n,{enumerable:!0,configurable:!0,writable:!0,value:a}):o[n]=a,st=(o,n)=>{for(var a in n||(n={}))ho.call(n,a)&&We(o,a,n[a]);if(xa)for(var a of xa(n))_o.call(n,a)&&We(o,a,n[a]);return o},vt=(o,n)=>vo(o,bo(n));var xo=(o,n)=>()=>(n||o((n={exports:{}}).exports,n),n.exports);var ve=(o,n,a)=>(We(o,typeof n!="symbol"?n+"":n,a),a);var L=(o,n,a)=>new Promise((l,u)=>{var d=g=>{try{k(a.next(g))}catch(p){u(p)}},c=g=>{try{k(a.throw(g))}catch(p){u(p)}},k=g=>g.done?l(g.value):Promise.resolve(g.value).then(d,c);k((a=a.apply(o,n)).next())});import{a as wo,c as ko,d as we,b as I,e as Q,u as e,o as r,f as s,g as t,t as i,n as ht,h as D,i as Y,r as _t,j as Yt,w as O,v as Rt,k as Ia,l as Z,m as V,p as E,q as $t,s as Pt,x as pt,F as U,y as tt,z as at,A as jt,B as ut,C as ct,D as ke,E as ee,T as St,G as Nt,H as lt,I as ft,J as xt,K as dt,L as gt,M as Bt,N as Xe,O as Ma,P as La,Q as Oa,R as ta,S as ea,U as Na,V as aa,W as Pe,X as yo,Y as oa,Z as ye,_ as Fo,$ as Eo,a0 as na,a1 as $o,a2 as Te,a3 as Co,a4 as Ie,a5 as Do,a6 as Bo,a7 as Yo,a8 as Ao,a9 as So,aa as zo,ab as Po,ac as To}from"./vendor.js?v=91daea62";var sT=xo(ae=>{const Io=function(){const n=document.createElement("link").relList;if(n&&n.supports&&n.supports("modulepreload"))return;for(const u of document.querySelectorAll('link[rel="modulepreload"]'))l(u);new MutationObserver(u=>{for(const d of u)if(d.type==="childList")for(const c of d.addedNodes)c.tagName==="LINK"&&c.rel==="modulepreload"&&l(c)}).observe(document,{childList:!0,subtree:!0});function a(u){const d={};return u.integrity&&(d.integrity=u.integrity),u.referrerpolicy&&(d.referrerPolicy=u.referrerpolicy),u.crossorigin==="use-credentials"?d.credentials="include":u.crossorigin==="anonymous"?d.credentials="omit":d.credentials="same-origin",d}function l(u){if(u.ep)return;u.ep=!0;const d=a(u);fetch(u.href,d)}};Io();const Gt={language:void 0,numberFormat:new Intl.NumberFormat("en",{notation:"compact"})},Mo=o=>L(ae,null,function*(){const n=window.vue_lang;new Date().getTime();let a={};try{const d=yield wo({url:window.vue_lang_data,method:"GET"});d.data&&(a=d.data)}catch(d){console.log(d)}const l=ko({defaultLanguage:n,mutedLanguages:["zh-cn"],translations:a,setGlobalProperties:!1,provideDirective:!1,provideComponent:!1});o.use(l),Gt.language=l;const{$gettext:u}=l;window.$i18n=u;try{Gt.numberFormat=new Intl.NumberFormat(n,{notation:"compact"})}catch(d){console.error("Intl.NumberFormat unsupported lang",n,d)}}),J=()=>{if(Gt.language)return Gt.language;throw new Error("I18N Uninitialized!")},Me=()=>({$gettext:(o,n,a)=>{if(Gt.language)return Gt.language.$gettext(o,n,a);throw new Error("I18N Uninitialized!")},$ngettext:(o,n,a,l,u)=>{if(Gt.language)return Gt.language.$ngettext(o,n,a,l,u);throw new Error("I18N Uninitialized!")}}),De=o=>typeof o=="number"?Gt.numberFormat.format(o):"?",{$gettext:Lo,$ngettext:uT}=Me(),Va=(o,n)=>L(ae,null,function*(){return new Promise((a,l)=>L(ae,null,function*(){try{const u=yield fetch(o,n);if(Math.floor(u.status/100)!=2)throw u.status+" "+u.statusText;const d=st({},u);d.data=yield u.json(),a(d)}catch(u){const d=u;l(Lo("\u7F51\u7EDC\u5F02\u5E38\uFF1A")+((d==null?void 0:d.message)||u))}}))});class ia{constructor(n){ve(this,"config",{baseURL:"",headers:{}});ve(this,"useRequest",n=>n);ve(this,"useResponse",n=>n);ve(this,"useError",n=>n);n.baseURL&&(this.config.baseURL=n.baseURL),n.headers&&(this.config.headers=n.headers)}static create(n){return new ia(n)}Do(n,a){return L(this,null,function*(){return new Promise((l,u)=>L(this,null,function*(){try{const d=this.useRequest({baseURL:this.config.baseURL,headers:this.config.headers});n=`${d.baseURL||""}${n}`,a.headers==null&&(a.headers={}),d.headers&&(a.headers=st({},d.headers));const k=yield fetch(n,a),g=st({},k);g.data=yield k.json(),l(this.useResponse(g))}catch(d){this.useError(d),u(d)}}))})}TEXT(n,a){return L(this,null,function*(){return new Promise((l,u)=>L(this,null,function*(){try{const d=this.useRequest({baseURL:this.config.baseURL,headers:this.config.headers});n=`${d.baseURL||""}${n}`,a.headers==null&&(a.headers={}),d.headers&&(a.headers=st({},d.headers));const k=yield fetch(n,a),g=st({},k);g.data=yield k.text(),l(g)}catch(d){this.useError(d),u(d)}}))})}interceptors(){const n=this;return{requset:{use(a){n.useRequest=a}},response:{use(a,l){n.useResponse=a,l&&(n.useError=l)}}}}}const Ga=ia.create({});Ga.interceptors().requset.use(o=>o);Ga.interceptors().response.use(o=>(o.data&&o.data.success==null&&o.data.success==0,o));const{$gettext:ja,$ngettext:cT}=Me(),Oo="/cgi-bin/luci/istore",No="/cgi-bin/luci/admin";let Ae=!1;const W=(o,n)=>(o.indexOf("//")==-1&&(o=`${Oo}${o}`),Va(o,n).then(a=>(a!=null&&a.data&&a.data.success==-1001&&a.data.error=="Forbidden"&&(Ae||(Ae=!0,alert(ja("\u767B\u5F55\u8FC7\u671F\uFF0C\u8BF7\u91CLine truncated
|
||||
`+k.value+`
|
||||
`+d.value,disabled:""},null,8,RF)]),t("div",WF,[t("div",{class:"close",onClick:m,disabled:u.value},i(e(a)("\u5173\u95ED")),9,HF),u.value?D("",!0):(r(),s("div",{key:0,class:"next",onClick:v,disabled:u.value},i(e(a)("\u8FD0\u884C")),9,JF))])])]),_:1}))}});var KF=N(ZF,[["__scopeId","data-v-70c3aae0"]]);const QF={class:"actioner-container"},XF={class:"actioner-container_header"},tE={class:"tabs"},eE={class:"actioner-container_body"},aE={key:0,class:"table"},oE={class:"tr"},nE={class:"td left"},iE={class:"td left"},rE={class:"tr"},sE={class:"td left"},dE={class:"td left"},lE={class:"tr"},uE={class:"td left"},cE={class:"td left"},pE=["value"],fE=["value"],mE=["value"],gE={class:"actioner-container_footer"},vE=["disabled"],bE=I({props:{close:{type:Function,required:!0},disk:{type:Object,required:!0}},setup(o){const n=o,{$gettext:a,$ngettext:l}=J(),u=E(!1),d=E("info"),c=b=>{switch(d.value=b,b){case"info":break;case"attribute":m();break;case"log":p();break;case"extend":v();break}},k=()=>{u.value=!0,n.close()},g=gt({log:"",attribute:"",extend:""}),p=()=>L(this,null,function*(){try{const b=yield G.Smart.Test.Result.POST({type:"selftest",devicePath:n.disk.path||""});if(b.data){const{result:y,error:f}=b.data;y&&y.result&&(g.log=y.result),f&&(g.log=f)}}catch(b){g.log=b}}),m=()=>L(this,null,function*(){try{const b=yield G.Smart.Attribute.Result.POST({devicePath:n.disk.path||""});if(b.data){const{result:y,error:f}=b.data;y&&y.result&&(g.attribute=y.result),f&&(g.attribute=f)}}catch(b){g.attribute=b}}),v=()=>L(this,null,function*(){try{const b=yield G.Smart.Extend.Result.POST({devicePath:n.disk.path||""});if(b.data){const{result:y,error:f}=b.data;y&&y.result&&(g.extend=y.result),f&&(g.extend=f)}}catch(b){g.extend=b}});return(b,y)=>(r(),Z(je,null,{default:V(()=>[t("div",QF,[t("div",XF,[t("ul",tE,[t("li",{class:lt({"active cbi-tab":d.value=="info"}),onClick:y[0]||(y[0]=f=>c("info"))},[t("a",null,i(e(a)("\u8BBE\u5907\u4FE1\u606F")),1)],2),t("li",{class:lt({"active cbi-tab":d.value=="attribute"}),onClick:y[1]||(y[1]=f=>c("attribute"))},[t("a",null,i(e(a)("\u5C5E\u6027")),1)],2),t("li",{class:lt({"active cbi-tab":d.value=="log"}),onClick:y[2]||(y[2]=f=>c("log"))},[t("a",null,i(e(a)("\u81EA\u68C0\u65E5\u5FD7")),1)],2),t("li",{class:lt({"active cbi-tab":d.value=="extend"}),onClick:y[3]||(y[3]=f=>c("extend"))},[t("a",null,i(e(a)("\u6269\u5C55\u4FE1\u606F")),1)],2)])]),t("div",eE,[d.value=="info"?(r(),s("table",aE,[t("tr",oE,[t("td",nE,i(e(a)("\u8BBE\u5907")),1),t("td",iE,i(o.disk.path),1)]),t("tr",rE,[t("td",sE,i(e(a)("\u578B\u53F7")),1),t("td",dE,i(o.disk.model),1)]),t("tr",lE,[t("td",uE,i(e(a)("\u5E8F\u53F7")),1),t("td",cE,i(o.disk.serial),1)])])):d.value=="attribute"?(r(),s("textarea",{key:1,disabled:"",value:e(g).attribute},null,8,pE)):d.value=="log"?(r(),s("textarea",{key:2,disabled:"",value:e(g).log},null,8,fE)):d.value=="extend"?(r(),s("textarea",{key:3,disabled:"",value:e(g).extend},null,8,mE)):D("",!0)]),t("div",gE,[t("div",{class:"close",onClick:k,disabled:u.value},i(e(a)("\u5173\u95ED")),9,vE)])])]),_:1}))}});var hE=N(bE,[["__scopeId","data-v-313197ee"]]);const _E=o=>{const n=document.createElement("div");document.body.appendChild(n);const a=Y(tF,vt(st({},o),{close:()=>{l()}})),l=()=>{n.remove()};Ie(a,n)},xE=o=>{const n=document.createElement("div");document.body.appendChild(n);const a=Y(GF,vt(st({},o),{close:()=>{l()}})),l=()=>{n.remove()};Ie(a,n)},wE=o=>{const n=document.createElement("div");document.body.appendChild(n);const a=Y(KF,vt(st({},o),{close:()=>{l()}})),l=()=>{n.remove()};Ie(a,n)},kE=o=>{const n=document.createElement("div");document.body.appendChild(n);const a=Y(hE,vt(st({},o),{close:()=>{l()}})),l=()=>{n.remove()};Ie(a,n)},yE={class:"cbi-section"},FE={class:"cbi-value"},EE={class:"cbi-value-title"},$E={class:"cbi-value-field"},CE={class:"cbi-checkbox"},DE=["value"],BE={class:"cbi-value"},YE={class:"cbi-value-title"},AE={class:"cbi-value-field"},SE={class:"cbi-checkbox"},zE={value:"never"},PE={value:"sleep"},TE={value:"standby"},IE={value:"idle"},ME={class:"cbi-value-description"},LE=t("br",null,null,-1),OE=t("br",null,null,-1),NE=t("br",null,null,-1),VE=t("br",null,null,-1),GE={class:"cbi-value"},jE={class:"cbi-value-title"},UE={class:"cbi-value-field"},qE={class:"cbi-checkbox"},RE={value:0},WE=["value"],HE={class:"cbi-value-description"},JE={class:"cbi-value"},ZE={class:"cbi-value-title"},KE={class:"cbi-value-field"},QE={class:"cbi-checkbox"},XE={value:0},t$=["value"],e$={class:"cbi-value-description"},a$={class:"cbi-section cbi-tblsection",id:"cbi-nfs-mount"},o$={class:"table cbi-section-table"},n$={class:"tr cbi-section-table-titles anonymous"},i$={class:"th cbi-section-table-cell","data-widget":"value"},r$={class:"th cbi-section-table-cell","data-widget":"value"},s$={class:"th cbi-section-table-cell","data-widget":"value"},d$={class:"th cbi-section-table-cell","data-widget":"value"},l$={class:"th cbi-section-table-cell","data-widget":"value"},u$={class:"th cbi-section-table-cell","data-widget":"value"
Line truncated
|
||||
@@ -1,2 +1,2 @@
|
||||
@keyframes bganimation-0698ba82{0%{background-position:0% 50%}50%{background-position:100% 50%}to{background-position:0% 50%}}@keyframes rotateEnter-0698ba82{0%{transform:rotateY(180deg)}10%{transform:rotateY(198deg)}20%{transform:rotateY(216deg)}30%{transform:rotateY(234deg)}40%{transform:rotateY(252deg)}50%{transform:rotateY(270deg)}60%{transform:rotateY(288deg)}70%{transform:rotateY(306deg)}80%{transform:rotateY(324deg)}90%{transform:rotateY(342deg)}to{transform:rotateY(360deg)}}@keyframes turns-0698ba82{0%{-webkit-transform:rotate(0deg)}25%{-webkit-transform:rotate(90deg)}50%{-webkit-transform:rotate(180deg)}75%{-webkit-transform:rotate(270deg)}to{-webkit-transform:rotate(360deg)}}.rotate-enter-active[data-v-0698ba82]{animation:rotateEnter-0698ba82 .7s;position:relative}.rotate-leave-active[data-v-0698ba82]{opacity:0;display:none;position:relative;z-index:-999}.app-container[data-v-0698ba82]{width:100%;background-color:var(--card-bg-color);box-shadow:var(--card-box-shadow);padding:10px 30px;border-radius:6px;position:relative}.disk-item-tooltip[data-v-0698ba82]{position:fixed;background:rgba(0,0,0,.7);z-index:10111;color:#fff;padding:.5rem 1rem;font-size:1em;min-width:200px;line-height:24px}.disk-item-tooltip[data-v-0698ba82]:after{content:"";position:absolute;bottom:-6px;border-color:#4c4c4c rgba(0,0,0,0) rgba(0,0,0,0);left:0;right:0;text-align:center;width:0;margin:0 auto;border-width:6px 8px 0;border-style:solid}#main .app-btn,#actioner .app-btn{min-height:36px}@keyframes bganimation-9bc295c2{0%{background-position:0% 50%}50%{background-position:100% 50%}to{background-position:0% 50%}}@keyframes rotateEnter-9bc295c2{0%{transform:rotateY(180deg)}10%{transform:rotateY(198deg)}20%{transform:rotateY(216deg)}30%{transform:rotateY(234deg)}40%{transform:rotateY(252deg)}50%{transform:rotateY(270deg)}60%{transform:rotateY(288deg)}70%{transform:rotateY(306deg)}80%{transform:rotateY(324deg)}90%{transform:rotateY(342deg)}to{transform:rotateY(360deg)}}@keyframes turns-9bc295c2{0%{-webkit-transform:rotate(0deg)}25%{-webkit-transform:rotate(90deg)}50%{-webkit-transform:rotate(180deg)}75%{-webkit-transform:rotate(270deg)}to{-webkit-transform:rotate(360deg)}}.rotate-enter-active[data-v-9bc295c2]{animation:rotateEnter-9bc295c2 .7s;position:relative}.rotate-leave-active[data-v-9bc295c2]{opacity:0;display:none;position:relative;z-index:-999}.app-container[data-v-9bc295c2]{width:100%;background-color:var(--card-bg-color);box-shadow:var(--card-box-shadow);padding:10px 30px;border-radius:6px;position:relative}#main[data-v-9bc295c2]{width:100%}#main[data-v-9bc295c2],#main[data-v-9bc295c2] *{-webkit-box-sizing:border-box;-webkit-tap-highlight-color:transparent;box-sizing:border-box;word-wrap:break-word;outline:none}@keyframes bganimation{0%{background-position:0% 50%}50%{background-position:100% 50%}to{background-position:0% 50%}}@keyframes rotateEnter{0%{transform:rotateY(180deg)}10%{transform:rotateY(198deg)}20%{transform:rotateY(216deg)}30%{transform:rotateY(234deg)}40%{transform:rotateY(252deg)}50%{transform:rotateY(270deg)}60%{transform:rotateY(288deg)}70%{transform:rotateY(306deg)}80%{transform:rotateY(324deg)}90%{transform:rotateY(342deg)}to{transform:rotateY(360deg)}}@keyframes turns{0%{-webkit-transform:rotate(0deg)}25%{-webkit-transform:rotate(90deg)}50%{-webkit-transform:rotate(180deg)}75%{-webkit-transform:rotate(270deg)}to{-webkit-transform:rotate(360deg)}}.rotate-enter-active{animation:rotateEnter .7s;position:relative}.rotate-leave-active{opacity:0;display:none;position:relative;z-index:-999}.app-container{width:100%;background-color:var(--card-bg-color);box-shadow:var(--card-box-shadow);padding:10px 30px;border-radius:6px;position:relative}:root{--flow-bg-color: #fff;--flow-span-color: rgba(0, 0, 0, .6);--card-bg-color: #fff;--card-box-shadow: 0 0 10px 1px #bfbfbf24;--app-container_title-color: #1e1e1e;--app-container_status-label_block: black;--item-label_key-span-color: #333;--item-label_value-span-color: #333;--app-container_status-label_bg: #f3f3f3;--item_btn-border: 1px solid #553afb;--item_btn-color: #553afb;--tit-color: #1e1e1e;--popup-bg-color: #fff;--tag-bg-color: #f5f5f5;--gradient-bg-color:linear-gradient(90deg, rgba(255,255,255,0) 0%, rgba(255,255,255,.8) 62%, rgba(255,255,255,1) 100%);--breadcrumbs-tit-color:#d6dbf8;--breadcrumbs-tit-color1:#f1f2f9;--border-color: #e5e5e5;--SystemUpdateBanner-bg-color: #eff6ff;--SystemUpdateBanner-border-color: #bedbff;--btn-border-color: #e5e5e5;--card-txt-color: #32325d;--SystemInfo-bg-color:#f9fafd}@media (prefers-color-scheme: light){:root{--flow-bg-color: #fff;--flow-span-color: rgba(0, 0, 0, .6);--card-bg-color: #fff;--card-box-shadow: 0 0 10px 1px #bfbfbf24;--app-container_title-color: #1e1e1e;--app-container_status-label_block: black;--item-label_key-span-color: #333;--item-label_value-span-color: #333;--app-container_status-label_bg: #f3f3f3;--item_btn-border: 1px solid #553afb;--item_btn-color: #553afb;--tit-color: #1e1e1e;--popup-bg-color: #fff;--tag-Line truncated
|
||||
@keyframes bganimation-0698ba82{0%{background-position:0% 50%}50%{background-position:100% 50%}to{background-position:0% 50%}}@keyframes rotateEnter-0698ba82{0%{transform:rotateY(180deg)}10%{transform:rotateY(198deg)}20%{transform:rotateY(216deg)}30%{transform:rotateY(234deg)}40%{transform:rotateY(252deg)}50%{transform:rotateY(270deg)}60%{transform:rotateY(288deg)}70%{transform:rotateY(306deg)}80%{transform:rotateY(324deg)}90%{transform:rotateY(342deg)}to{transform:rotateY(360deg)}}@keyframes turns-0698ba82{0%{-webkit-transform:rotate(0deg)}25%{-webkit-transform:rotate(90deg)}50%{-webkit-transform:rotate(180deg)}75%{-webkit-transform:rotate(270deg)}to{-webkit-transform:rotate(360deg)}}.rotate-enter-active[data-v-0698ba82]{animation:rotateEnter-0698ba82 .7s;position:relative}.rotate-leave-active[data-v-0698ba82]{opacity:0;display:none;position:relative;z-index:-999}.app-container[data-v-0698ba82]{width:100%;background-color:var(--card-bg-color);box-shadow:var(--card-box-shadow);padding:10px 30px;border-radius:6px;position:relative}.disk-item-tooltip[data-v-0698ba82]{position:fixed;background:rgba(0,0,0,.7);z-index:10111;color:#fff;padding:.5rem 1rem;font-size:1em;min-width:200px;line-height:24px}.disk-item-tooltip[data-v-0698ba82]:after{content:"";position:absolute;bottom:-6px;border-color:#4c4c4c rgba(0,0,0,0) rgba(0,0,0,0);left:0;right:0;text-align:center;width:0;margin:0 auto;border-width:6px 8px 0;border-style:solid}#main .app-btn,#actioner .app-btn{min-height:36px}@keyframes bganimation-9bc295c2{0%{background-position:0% 50%}50%{background-position:100% 50%}to{background-position:0% 50%}}@keyframes rotateEnter-9bc295c2{0%{transform:rotateY(180deg)}10%{transform:rotateY(198deg)}20%{transform:rotateY(216deg)}30%{transform:rotateY(234deg)}40%{transform:rotateY(252deg)}50%{transform:rotateY(270deg)}60%{transform:rotateY(288deg)}70%{transform:rotateY(306deg)}80%{transform:rotateY(324deg)}90%{transform:rotateY(342deg)}to{transform:rotateY(360deg)}}@keyframes turns-9bc295c2{0%{-webkit-transform:rotate(0deg)}25%{-webkit-transform:rotate(90deg)}50%{-webkit-transform:rotate(180deg)}75%{-webkit-transform:rotate(270deg)}to{-webkit-transform:rotate(360deg)}}.rotate-enter-active[data-v-9bc295c2]{animation:rotateEnter-9bc295c2 .7s;position:relative}.rotate-leave-active[data-v-9bc295c2]{opacity:0;display:none;position:relative;z-index:-999}.app-container[data-v-9bc295c2]{width:100%;background-color:var(--card-bg-color);box-shadow:var(--card-box-shadow);padding:10px 30px;border-radius:6px;position:relative}#main[data-v-9bc295c2]{width:100%}#main[data-v-9bc295c2],#main[data-v-9bc295c2] *{-webkit-box-sizing:border-box;-webkit-tap-highlight-color:transparent;box-sizing:border-box;word-wrap:break-word;outline:none}@keyframes bganimation{0%{background-position:0% 50%}50%{background-position:100% 50%}to{background-position:0% 50%}}@keyframes rotateEnter{0%{transform:rotateY(180deg)}10%{transform:rotateY(198deg)}20%{transform:rotateY(216deg)}30%{transform:rotateY(234deg)}40%{transform:rotateY(252deg)}50%{transform:rotateY(270deg)}60%{transform:rotateY(288deg)}70%{transform:rotateY(306deg)}80%{transform:rotateY(324deg)}90%{transform:rotateY(342deg)}to{transform:rotateY(360deg)}}@keyframes turns{0%{-webkit-transform:rotate(0deg)}25%{-webkit-transform:rotate(90deg)}50%{-webkit-transform:rotate(180deg)}75%{-webkit-transform:rotate(270deg)}to{-webkit-transform:rotate(360deg)}}.rotate-enter-active{animation:rotateEnter .7s;position:relative}.rotate-leave-active{opacity:0;display:none;position:relative;z-index:-999}.app-container{width:100%;background-color:var(--card-bg-color);box-shadow:var(--card-box-shadow);padding:10px 30px;border-radius:6px;position:relative}:root{--flow-bg-color: #fff;--flow-span-color: rgba(0, 0, 0, .6);--card-bg-color: #fff;--card-box-shadow: 0 0 10px 1px #bfbfbf24;--app-container_title-color: #1e1e1e;--app-container_status-label_block: black;--item-label_key-span-color: #333;--item-label_value-span-color: #333;--app-container_status-label_bg: #f3f3f3;--item_btn-border: 1px solid #553afb;--item_btn-color: #553afb;--tit-color: #1e1e1e;--popup-bg-color: #fff;--tag-bg-color: #f5f5f5;--gradient-bg-color:linear-gradient(90deg, rgba(255,255,255,0) 0%, rgba(255,255,255,.8) 62%, rgba(255,255,255,1) 100%);--breadcrumbs-tit-color:#d6dbf8;--breadcrumbs-tit-color1:#f1f2f9;--border-color: #e5e5e5;--SystemUpdateBanner-bg-color: #eff6ff;--SystemUpdateBanner-border-color: #bedbff;--btn-border-color: #e5e5e5;--card-txt-color: #32325d;--SystemInfo-bg-color:#f9fafd}@media (prefers-color-scheme: light){:root{--flow-bg-color: #fff;--flow-span-color: rgba(0, 0, 0, .6);--card-bg-color: #fff;--card-box-shadow: 0 0 10px 1px #bfbfbf24;--app-container_title-color: #1e1e1e;--app-container_status-label_block: black;--item-label_key-span-color: #333;--item-label_value-span-color: #333;--app-container_status-label_bg: #f3f3f3;--item_btn-border: 1px solid #553afb;--item_btn-color: #553afb;--tit-color: #1e1e1e;--popup-bg-color: #fff;--tag-Line truncated
|
||||
.page-container>div>div>a:first-child,.info_content>.status_box{display:none !important}
|
||||
@@ -1,4 +1,5 @@
|
||||
local http = require "luci.http"
|
||||
local string = require "string"
|
||||
|
||||
module("luci.controller.quickstart", package.seeall)
|
||||
|
||||
@@ -25,6 +26,9 @@ function index()
|
||||
else
|
||||
entry({"admin", "quickstart"}, call("redirect_fallback")).leaf = true
|
||||
end
|
||||
if not nixio.fs.access("/usr/share/luci/menu.d/luci-app-dockerman.json") then
|
||||
entry({"admin", "services", "dockerman"}, call("redirect_dockerman")).leaf = true
|
||||
end
|
||||
end
|
||||
|
||||
function networkguide_index()
|
||||
@@ -39,6 +43,24 @@ function redirect_fallback()
|
||||
luci.http.redirect(luci.dispatcher.build_url("admin", "status"))
|
||||
end
|
||||
|
||||
local function starts_with(str, start)
|
||||
return string.sub(str, 1, #start) == start
|
||||
end
|
||||
|
||||
local function replace_first(str, pattern, replacement)
|
||||
-- The third argument (1) limits the replacement to the first match
|
||||
local result = str:gsub(pattern, replacement, 1)
|
||||
return result
|
||||
end
|
||||
|
||||
function redirect_dockerman(path, ...)
|
||||
local newargs = {}
|
||||
if path ~= nil then
|
||||
newargs = {path == "configuration" and "config" or path, ...}
|
||||
end
|
||||
luci.http.redirect(luci.dispatcher.build_url("admin", "docker", unpack(newargs)))
|
||||
end
|
||||
|
||||
local function vue_lang()
|
||||
local i18n = require("luci.i18n")
|
||||
local lang = i18n.translate("quickstart_vue_lang")
|
||||
|
||||
@@ -17,7 +17,7 @@ LUCI_NAME:=luci-theme-footstrap
|
||||
FOOTSTRAP_VERSION?=
|
||||
ifneq ($(FOOTSTRAP_VERSION),)
|
||||
PKG_VERSION:=$(FOOTSTRAP_VERSION)
|
||||
PKG_RELEASE:=50
|
||||
PKG_RELEASE:=51
|
||||
endif
|
||||
|
||||
LUCI_TITLE:=Footstrap Theme
|
||||
|
||||
@@ -110,6 +110,13 @@ function run() {
|
||||
* question from which box can carry a floor. */
|
||||
const SHRINKS = '.cbi-section > div, .table';
|
||||
|
||||
/* A box qualifies for a floor through its CHILDREN, so emptying it takes it out of `SHRINKS` — and
|
||||
* a floor nothing sweeps any more is a floor nothing can take off: measured on Network ->
|
||||
* Interfaces, a section emptied of its table kept 1299px of `min-height` for the life of the page.
|
||||
* Every floor this writes is marked, so the sweep finds its own work again whatever became of the
|
||||
* markup underneath. The attribute is in the theme's own namespace and says nothing to CSS. */
|
||||
const FLOORED = '[data-fs-floor]';
|
||||
|
||||
/* What the container would stand at with no floor under it, asked of the CONTENT rather than by
|
||||
* taking the floor off and re-measuring.
|
||||
*
|
||||
@@ -144,22 +151,27 @@ function naturalHeight(el) {
|
||||
if (!box.height) return 0;
|
||||
const end = last.getBoundingClientRect();
|
||||
if (!end.height && !end.width) return 0; /* a last child out of the flow says nothing */
|
||||
let bottom = end.bottom;
|
||||
/* AND THE TEXT AFTER IT. A box whose content ends in text ends below its last ELEMENT, and
|
||||
* measuring to that element writes a floor shorter than the box: `.cbi-section-descr` on
|
||||
* /admin/network/dhcp measured 115px against the 156px it stands at, and a floor 41px short lets
|
||||
* the document shrink under the reader during the very tick the floor is there to hold.
|
||||
*
|
||||
* Only the tail, not `selectNodeContents(el)`: a Range over the whole box takes in whatever is
|
||||
* out of the flow inside it and writes a floor TALLER than the box instead — 205px against 107
|
||||
* on the Overview, which is the same blank page seen from the other side. */
|
||||
if (last.nextSibling) {
|
||||
const tail = document.createRange();
|
||||
tail.setStartAfter(last);
|
||||
tail.setEnd(el, el.childNodes.length);
|
||||
const box2 = tail.getBoundingClientRect();
|
||||
if (box2.height || box2.width) bottom = Math.max(bottom, box2.bottom);
|
||||
}
|
||||
const cs = window.getComputedStyle(el);
|
||||
const below = parseFloat(cs.paddingBottom) + parseFloat(cs.borderBottomWidth);
|
||||
return Math.round(end.bottom - box.top + (below || 0));
|
||||
return Math.round(bottom - box.top + (below || 0));
|
||||
}
|
||||
|
||||
/* The floor is the height the next tick may not go below, one per container. Read in one pass and
|
||||
* written in another, so the sweep costs a single forced layout rather than one per element.
|
||||
*
|
||||
* WRITTEN ONLY WHERE THE VALUE CHANGES, which is the difference between a floor and a page the
|
||||
* engine refuses to anchor: see naturalHeight() above. Measured over 25 s of real polling on the
|
||||
* Overview at 390px, the clear-and-rewrite shape wrote style 1550 times on 25.12 and 170 times on
|
||||
* ImmortalWrt 24.10, of which 75 and 62 carried a value that had actually moved — the rest were
|
||||
* suppression bought for nothing.
|
||||
*
|
||||
* Not while the reader scrolls: a rect read is a forced layout, and a floor staying where it was is
|
||||
* still a floor. */
|
||||
/* The floor is the height the next tick may not go below, one per box. Read in one pass and written
|
||||
* in another, so the sweep costs a single forced layout rather than one per element.
|
||||
*
|
||||
@@ -179,26 +191,85 @@ function naturalHeight(el) {
|
||||
*
|
||||
* Not while the reader scrolls: a rect read is a forced layout, and a floor staying where it was is
|
||||
* still a floor. */
|
||||
/* Boxes that measured empty on the LAST pass, so a floor that is holding nothing up can be told
|
||||
* from one that is holding the page still while `dom.content()` refills. WeakSet: a box the router
|
||||
* has replaced is garbage, and this must not be what keeps it alive. */
|
||||
const emptied = new WeakSet();
|
||||
|
||||
/* Off in one place, so the mark and the style can never disagree about who wears a floor. */
|
||||
function dropFloor(box) {
|
||||
if (box.style.minHeight) box.style.minHeight = '';
|
||||
box.removeAttribute('data-fs-floor');
|
||||
}
|
||||
|
||||
/* THE SECOND LOOK HAS TO BE SCHEDULED, not waited for. Every other pass here is driven by the
|
||||
* MutationObserver on `#view`, and a container that empties and stays empty produces no further
|
||||
* mutation — so "clear it if it is still empty next pass" never gets a next pass, and the floor
|
||||
* stands for the life of the page (measured: 1299px on Network -> Interfaces).
|
||||
*
|
||||
* One poll interval, because that is how long a container that is genuinely refilling may take: the
|
||||
* router's own `pollinterval` when the page will say it, and its shipped default of 5 s otherwise.
|
||||
* Shorter risks taking the floor away from a tick still in flight, which is the 568px clamp this
|
||||
* whole mechanism exists to stop. */
|
||||
let _emptyCheck = null;
|
||||
|
||||
function checkEmptyLater() {
|
||||
if (_emptyCheck) return;
|
||||
let secs = 5;
|
||||
try { secs = (window.L && L.env && L.env.pollinterval) || 5; } catch (e) { /* not on a LuCI page */ }
|
||||
_emptyCheck = setTimeout(() => { _emptyCheck = null; run(); }, secs * 1000);
|
||||
}
|
||||
|
||||
function holdFloor() {
|
||||
if (scrolling()) return;
|
||||
const host = document.getElementById('view');
|
||||
if (!host) return; /* the login page has no view */
|
||||
const boxes = [], hs = [];
|
||||
host.querySelectorAll(SHRINKS).forEach((el) => {
|
||||
let box = el;
|
||||
while (box && box !== host && window.getComputedStyle(box).display.startsWith('table'))
|
||||
const boxes = [], hs = [], gone = [];
|
||||
host.querySelectorAll(SHRINKS + ', ' + FLOORED).forEach((el) => {
|
||||
let box = el, cs = window.getComputedStyle(el);
|
||||
while (box && box !== host && cs.display.startsWith('table')) {
|
||||
box = box.parentElement;
|
||||
if (box) cs = window.getComputedStyle(box);
|
||||
}
|
||||
/* several tables in one section climb to the same box; it needs one floor, not one each */
|
||||
if (!box || box === host || boxes.indexOf(box) !== -1) return;
|
||||
boxes.push(box);
|
||||
hs.push(naturalHeight(box));
|
||||
/* `visibility` inherits, so this is equally true of every box inside a collapsed pane */
|
||||
const hidden = cs.visibility === 'hidden';
|
||||
gone.push(hidden);
|
||||
hs.push(hidden ? 0 : naturalHeight(box));
|
||||
});
|
||||
boxes.forEach((box, i) => {
|
||||
/* zero is an empty box, and the floor it already wears is what holds it up — the moment this
|
||||
* whole mechanism exists for */
|
||||
if (hs[i] <= 0) return;
|
||||
/* A BOX THE READER CANNOT SEE GIVES ITS FLOOR BACK. `min-height` beats the `height: 0` an
|
||||
* inactive tab pane is collapsed with (theme/30-tables.css), so a floor written while the pane
|
||||
* was open pins the collapse open once it closes: on Network -> Interfaces the `interface`
|
||||
* pane held its 1265px after the reader left it, and the tab they were looking at started
|
||||
* that far down the page (issue #41). The clear-and-remeasure shape this replaced in 0.14.4
|
||||
* cleared every floor each tick, so a pane going inactive lost its own on the next one; only
|
||||
* a box that cannot hold anything up is cleared here, and it is re-measured the tick after it
|
||||
* comes back. A collapsed pane whose floor is still standing also measures a height of its
|
||||
* own, which is why refusing to write is not enough. */
|
||||
if (gone[i]) { dropFloor(box); return; }
|
||||
/* ZERO IS AN EMPTY BOX, and the floor it already wears is what holds it up — the moment this
|
||||
* whole mechanism exists for, since `dom.content()` empties before it refills.
|
||||
*
|
||||
* But only for the one pass. A box that is still empty on the next one is not refilling, and
|
||||
* its floor is then blank page that nothing takes back: measured on Network -> Interfaces,
|
||||
* a section emptied and left alone keeps 1299px of `min-height` for the life of the page,
|
||||
* with the document standing at 1720px around no content at all. That is what 0.14.4 changed
|
||||
* by replacing clear-and-remeasure, which read the collapsed height and wrote nothing. */
|
||||
if (hs[i] <= 0) {
|
||||
if (!box.style.minHeight) return;
|
||||
if (emptied.has(box)) { dropFloor(box); emptied.delete(box); }
|
||||
else { emptied.add(box); checkEmptyLater(); }
|
||||
return;
|
||||
}
|
||||
emptied.delete(box);
|
||||
const px = hs[i] + 'px';
|
||||
if (box.style.minHeight !== px) box.style.minHeight = px;
|
||||
if (box.style.minHeight !== px) {
|
||||
box.style.minHeight = px;
|
||||
box.setAttribute('data-fs-floor', '');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -932,6 +1003,21 @@ return baseclass.extend({
|
||||
const room = this.roomFor(el);
|
||||
const grown = el.getBoundingClientRect().width;
|
||||
return Math.max(el.scrollWidth, grown) > room + 1; /* +1: sub-pixel rounding */
|
||||
},
|
||||
|
||||
/* IS SOMEBODY ELSE ALREADY SCROLLING THIS? An app that puts its table in a box of its own with
|
||||
* `overflow-x: auto` has answered the overflow question itself, and the theme re-laying that
|
||||
* table overrules a decision that was not its to take: luci-app-filemanager parks its listing in
|
||||
* a 598px `div.resizeable` and the whole table came out as cards on a 1280px screen, where the
|
||||
* page had 1224px of room and the reader had asked for none of it.
|
||||
*
|
||||
* The walk stops at the content root, so the theme's own scrollers are not this test's business:
|
||||
* `#modal_overlay` is the dialog's scroller (base/60-modal.css) and the scroll fallback the theme
|
||||
* gives a foreign table is on the TABLE itself (theme/30-tables.css), not on an ancestor. */
|
||||
inScroller(el) {
|
||||
for (let p = el.parentElement; p && p.id !== 'view' && p.id !== 'modal_overlay'; p = p.parentElement)
|
||||
if ((/(auto|scroll)/).test(window.getComputedStyle(p).overflowX)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
});
|
||||
@@ -492,6 +492,21 @@ function fitTables() {
|
||||
return;
|
||||
}
|
||||
|
||||
/* SOMEBODY ELSE'S SCROLLER, SOMEBODY ELSE'S DECISION. An app that puts its table in a box of
|
||||
* its own with `overflow-x: auto` has already answered the question this file asks, and
|
||||
* every answer here would overrule it: inside a 598px box a wide table overflows by
|
||||
* definition, so the ladder ends in a card stack however much room the page has.
|
||||
* luci-app-filemanager parks its listing in a `div.resizeable` of exactly that width and
|
||||
* came out as cards on a 1280px screen, with 1224px of column beside it (docs/third-party-
|
||||
* apps.md: what the theme may do to a foreign table). Asked before the slot's answer is
|
||||
* applied, so a table that was carded before this rule existed is un-carded rather than
|
||||
* re-carded. */
|
||||
if (fit.inScroller(t)) {
|
||||
t.classList.add('fs-fitted');
|
||||
t.classList.remove('fs-stacked', 'fs-drop-xs');
|
||||
return;
|
||||
}
|
||||
|
||||
/* the column count, read from the tree: asking forces no layout */
|
||||
const shape = shapeOf(t);
|
||||
|
||||
|
||||
@@ -187,8 +187,31 @@
|
||||
input.focus();
|
||||
|
||||
/* if the router also serves HTTPS, hop over to it before the password goes over this
|
||||
* connection; the <img> probe is upstream's trick for proving the TLS port answers first */
|
||||
if (document.location.protocol === 'https:') return;
|
||||
* connection; the <img> probe is upstream's trick for proving the TLS port answers first
|
||||
*
|
||||
* THE HOP CAN ARRIVE WITHOUT THE SESSION. On Chromium the jump from http to https is
|
||||
* cross-site — schemeful same-site puts the two schemes in different sites — so the
|
||||
* browser withholds LuCI's `SameSite=strict` cookie (dispatcher.uc) on exactly that
|
||||
* navigation and a reader already logged in over https lands back on this form: 403 to
|
||||
* the hop against 200 to curl carrying the cookie, measured on a stand serving both
|
||||
* schemes. Firefox sends it and answers 200 straight away.
|
||||
*
|
||||
* So where this form is what the hop landed on, it is asked again once, over the same
|
||||
* scheme: that navigation is same-site everywhere and carries the cookie. The retry is
|
||||
* bounded by the REFERRER rather than by a marker in the URL — after it, the referrer is
|
||||
* this https page instead of the http one, so a reader who genuinely has no session sees
|
||||
* this form once and stays on it. A `#…` marker was tried and is not what ships: on
|
||||
* Firefox the retry never runs, so nothing takes the fragment back off the address. */
|
||||
if (document.location.protocol === 'https:') {
|
||||
/* the host is compared as a PARSED origin, not as a prefix of the string:
|
||||
* `http://192.168.1.1.example.com` starts with `http://192.168.1.1` */
|
||||
const from = document.referrer ? new URL(document.referrer) : null;
|
||||
|
||||
if (from && from.protocol === 'http:' && from.hostname === window.location.hostname)
|
||||
window.location.replace(window.location.href);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const { hostname, pathname } = window.location;
|
||||
|
||||
|
||||
+2
-2
@@ -14,8 +14,8 @@ MISE_HASH_x86_64:=3832f39c325e343f81fe3d92b2447c5d1a5eea1bc85092bb7b6c2580622264
|
||||
MISE_HASH_aarch64:=06186cfbfe947049b21d58575fb0ea800cc26ed1375f20f4b678cb3a9d679437
|
||||
|
||||
PKG_NAME:=mise
|
||||
PKG_VERSION:=2026.8.16
|
||||
PKG_RELEASE:=3
|
||||
PKG_VERSION:=2026.9.0
|
||||
PKG_RELEASE:=4
|
||||
PKG_SOURCE:=$(PKG_NAME)-v$(PKG_VERSION)-linux-$(MISE_ARCH)-musl.tar.gz
|
||||
PKG_SOURCE_URL:=https://github.com/jdx/mise/releases/download/v$(PKG_VERSION)/
|
||||
PKG_HASH:=skip
|
||||
|
||||
+2
-2
@@ -7,9 +7,9 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=openlist2
|
||||
PKG_VERSION:=4.2.5
|
||||
PKG_VERSION:=4.2.6
|
||||
PKG_WEB_VERSION:=4.2.5
|
||||
PKG_RELEASE:=13
|
||||
PKG_RELEASE:=14
|
||||
|
||||
PKG_SOURCE:=openlist-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://codeload.github.com/OpenListTeam/OpenList/tar.gz/v$(PKG_VERSION)?
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=fastfetch
|
||||
PKG_VERSION:=2.67.1
|
||||
PKG_RELEASE:=14
|
||||
PKG_VERSION:=2.68.1
|
||||
PKG_RELEASE:=15
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://codeload.github.com/fastfetch-cli/fastfetch/tar.gz/$(PKG_VERSION)?
|
||||
|
||||
+2
-2
@@ -43,11 +43,11 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=wwand
|
||||
PKG_RELEASE:=16
|
||||
PKG_RELEASE:=17
|
||||
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_URL:=https://github.com/ddimension/wwand.git
|
||||
PKG_SOURCE_VERSION:=68a3f844a310fe9d7216d8febc496856ee5e0054
|
||||
PKG_SOURCE_VERSION:=3e226ad39268a18b6f5a9a699b766aa601b2d904
|
||||
PKG_SOURCE_DATE:=2026-08-31
|
||||
PKG_MIRROR_HASH:=skip
|
||||
|
||||
|
||||
Reference in new issue
Block a user