⛄ Sync 2026-09-29 03:13:52
No files matched your search
@@ -0,0 +1,125 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-app-open-box
|
||||
PKG_VERSION:=0.1.263
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_MAINTAINER:=liandu2024
|
||||
PKG_LICENSE:=GPL-3.0-or-later
|
||||
|
||||
# 1. 自动判断目标编译架构,映射至 Open-Box Release 预编译资产名
|
||||
ifeq ($(ARCH),x86_64)
|
||||
OPENBOX_ARCH:=x64
|
||||
endif
|
||||
ifeq ($(ARCH),aarch64)
|
||||
OPENBOX_ARCH:=arm64
|
||||
endif
|
||||
|
||||
PKG_SOURCE:=open-box-linux-$(OPENBOX_ARCH).tar.gz
|
||||
PKG_SOURCE_URL:=https://github.com/liandu2024/Open-Box/releases/download/v$(PKG_VERSION)/
|
||||
PKG_HASH:=skip
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/luci-app-open-box
|
||||
SECTION:=luci
|
||||
CATEGORY:=LuCI
|
||||
SUBMENU:=3. Applications
|
||||
TITLE:=LuCI support for Open-Box Transparent Proxy (Full Core Included)
|
||||
# 架构约束:仅在目标平台为 x86_64 或 aarch64 时在 menuconfig 中展示,防止误选到不支持的 MIPS/ARMv7 平台
|
||||
DEPENDS:=@(x86_64||aarch64) +kmod-tun +kmod-nft-queue +kmod-nft-nat +kmod-veth +ip-full +ca-bundle +luci-base
|
||||
endef
|
||||
|
||||
define Package/luci-app-open-box/description
|
||||
Open-Box all-in-one transparent proxy management suite for OpenWrt.
|
||||
Pre-packaged with full sing-box core, embedded Node.js runtime, GeoSite/GeoIP databases and web dashboard.
|
||||
endef
|
||||
|
||||
define Package/luci-app-open-box/conffiles
|
||||
/etc/config/openbox
|
||||
/opt/open-box/data/
|
||||
endef
|
||||
|
||||
# 2. 构建准备:解压下载的对应架构核心资产包至构建目录
|
||||
define Build/Prepare
|
||||
mkdir -p $(PKG_BUILD_DIR)/_core
|
||||
tar -xzf $(DL_DIR)/$(PKG_SOURCE) -C $(PKG_BUILD_DIR)/_core
|
||||
endef
|
||||
|
||||
define Build/Configure
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
endef
|
||||
|
||||
# 3. 安装与打包:同时铺装 LuCI 页面、系统脚本以及 /opt/open-box 核心
|
||||
define Package/luci-app-open-box/install
|
||||
# 安装 LuCI 菜单与权限声明
|
||||
$(INSTALL_DIR) $(1)/usr/share/luci/menu.d
|
||||
$(INSTALL_DATA) ./root/usr/share/luci/menu.d/luci-app-openbox.json $(1)/usr/share/luci/menu.d/
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/share/rpcd/acl.d
|
||||
$(INSTALL_DATA) ./root/usr/share/rpcd/acl.d/luci-app-openbox.json $(1)/usr/share/rpcd/acl.d/
|
||||
|
||||
$(INSTALL_DIR) $(1)/www/luci-static/resources/view/openbox
|
||||
$(INSTALL_DATA) ./root/www/luci-static/resources/view/openbox/main.js $(1)/www/luci-static/resources/view/openbox/
|
||||
|
||||
# 安装系统服务与默认配置
|
||||
$(INSTALL_DIR) $(1)/etc/init.d
|
||||
$(INSTALL_BIN) ./root/etc/init.d/openbox $(1)/etc/init.d/openbox
|
||||
$(INSTALL_BIN) ./root/etc/init.d/openbox-panel $(1)/etc/init.d/openbox-panel
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/config
|
||||
$(INSTALL_CONF) ./root/etc/config/openbox $(1)/etc/config/openbox
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/uci-defaults
|
||||
$(INSTALL_BIN) ./root/etc/uci-defaults/99-luci-app-openbox $(1)/etc/uci-defaults/99-luci-app-openbox
|
||||
|
||||
# 创建 CLI 管理命令软链接(直接指向 /opt/open-box/openwrt/bin/open-box 原版脚本)
|
||||
$(INSTALL_DIR) $(1)/usr/bin
|
||||
ln -sf /opt/open-box/openwrt/bin/open-box $(1)/usr/bin/open-box
|
||||
|
||||
# 仅复制 _core 子目录内容,避免递归包含
|
||||
$(INSTALL_DIR) $(1)/opt/open-box
|
||||
$(CP) $(PKG_BUILD_DIR)/_core/* $(1)/opt/open-box/
|
||||
chmod -R 0755 $(1)/opt/open-box/bin $(1)/opt/open-box/node/bin $(1)/opt/open-box/openwrt/bin 2>/dev/null || true
|
||||
chmod +x $(1)/opt/open-box/update.sh $(1)/opt/open-box/uninstall.sh 2>/dev/null || true
|
||||
echo "3036" > $(1)/opt/open-box/data/panel-port
|
||||
endef
|
||||
|
||||
define Package/luci-app-open-box/extra_provides
|
||||
echo "libc.musl-x86_64.so.1";
|
||||
endef
|
||||
|
||||
define Package/luci-app-open-box/prerm
|
||||
#!/bin/sh
|
||||
[ -n "$${IPKG_INSTROOT}" ] || {
|
||||
/etc/init.d/openbox stop >/dev/null 2>&1 || true
|
||||
/etc/init.d/openbox-panel stop >/dev/null 2>&1 || true
|
||||
}
|
||||
exit 0
|
||||
endef
|
||||
|
||||
# 6. 包卸载后钩子:清理残留静态资源与防火墙规则
|
||||
define Package/luci-app-open-box/postrm
|
||||
#!/bin/sh
|
||||
[ -n "$${IPKG_INSTROOT}" ] || {
|
||||
rm -rf /tmp/luci-*cache* 2>/dev/null || true
|
||||
rm -rf /www/luci-static/resources/view/openbox 2>/dev/null || true
|
||||
uci -q delete firewall.openbox_panel || true
|
||||
uci -q delete firewall.openbox_dns || true
|
||||
uci -q delete firewall.openbox_tun_forward || true
|
||||
uci -q delete firewall.openbox_tun_input || true
|
||||
uci -q delete firewall.openbox_v6block || true
|
||||
for _ob_rule in $$(uci -q show firewall 2>/dev/null | sed -n 's/^firewall\.\(openbox_srv_[A-Za-z0-9_]*\)=rule$$/\1/p'); do
|
||||
uci -q delete "firewall.$$_ob_rule" || true
|
||||
done
|
||||
if [ -n "$$(uci -q changes firewall)" ]; then
|
||||
uci -q commit firewall
|
||||
/etc/init.d/firewall reload >/dev/null 2>&1 || true
|
||||
fi
|
||||
}
|
||||
exit 0
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,luci-app-open-box))
|
||||
@@ -0,0 +1 @@
|
||||
来自 https://github.com/liandu2024/Open-Box
|
||||
@@ -0,0 +1 @@
|
||||
zh_Hans
|
||||
@@ -0,0 +1,65 @@
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=UTF-8\n"
|
||||
|
||||
msgid "Open-Box"
|
||||
msgstr "Open-Box"
|
||||
|
||||
msgid "Open-Box 管理面板"
|
||||
msgstr "Open-Box 管理面板"
|
||||
|
||||
msgid "服务运行状态"
|
||||
msgstr "服务运行状态"
|
||||
|
||||
msgid "面板服务 (openbox-panel)"
|
||||
msgstr "面板服务 (openbox-panel)"
|
||||
|
||||
msgid "内核代理服务 (openbox)"
|
||||
msgstr "内核代理服务 (openbox)"
|
||||
|
||||
msgid "组件与更新维护"
|
||||
msgstr "组件与更新维护"
|
||||
|
||||
msgid "核心组件状态"
|
||||
msgstr "核心组件状态"
|
||||
|
||||
msgid "更新加速镜像"
|
||||
msgstr "更新加速镜像"
|
||||
|
||||
msgid "系统升级与维护"
|
||||
msgstr "系统升级与维护"
|
||||
|
||||
msgid "打开管理面板"
|
||||
msgstr "打开管理面板"
|
||||
|
||||
msgid "修改端口"
|
||||
msgstr "修改端口"
|
||||
|
||||
msgid "复制密码"
|
||||
msgstr "复制密码"
|
||||
|
||||
msgid "启动面板"
|
||||
msgstr "启动面板"
|
||||
|
||||
msgid "停止面板"
|
||||
msgstr "停止面板"
|
||||
|
||||
msgid "重启面板"
|
||||
msgstr "重启面板"
|
||||
|
||||
msgid "启动内核"
|
||||
msgstr "启动内核"
|
||||
|
||||
msgid "紧急停止内核 (恢复直连)"
|
||||
msgstr "紧急停止内核 (恢复直连)"
|
||||
|
||||
msgid "按配置重载/重启"
|
||||
msgstr "按配置重载/重启"
|
||||
|
||||
msgid "检查并升级到最新版"
|
||||
msgstr "检查并升级到最新版"
|
||||
|
||||
msgid "回退到上个版本 (Rollback)"
|
||||
msgstr "回退到上个版本 (Rollback)"
|
||||
|
||||
msgid "卸载 Open-Box"
|
||||
msgstr "卸载 Open-Box"
|
||||
@@ -0,0 +1,6 @@
|
||||
config openbox 'main'
|
||||
option enabled '1'
|
||||
option port '3036'
|
||||
option channel 'mirror'
|
||||
option mirror_prefix 'https://ghfast.top'
|
||||
option auto_restart '1'
|
||||
@@ -0,0 +1,96 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
# Copyright (C) 2026 OpenWrt.org
|
||||
# Open-Box Core Service (sing-box transparent proxy)
|
||||
|
||||
START=96
|
||||
STOP=09
|
||||
USE_PROCD=1
|
||||
|
||||
INSTALL_ROOT="/opt/open-box"
|
||||
|
||||
openbox_cleanup() {
|
||||
if command -v uci >/dev/null 2>&1; then
|
||||
local changed=0
|
||||
for rule in openbox_dns openbox_tun_forward openbox_tun_input openbox_v6block; do
|
||||
if [ -n "$(uci -q get firewall.$rule 2>/dev/null)" ]; then
|
||||
uci -q delete firewall.$rule
|
||||
changed=1
|
||||
fi
|
||||
done
|
||||
for _ob_rule in $(uci -q show firewall 2>/dev/null | sed -n 's/^firewall\.\(openbox_srv_[A-Za-z0-9_]*\)=rule$/\1/p'); do
|
||||
uci -q delete "firewall.$_ob_rule" || true
|
||||
changed=1
|
||||
done
|
||||
if [ "$changed" = "1" ] && [ -n "$(uci -q changes firewall)" ]; then
|
||||
uci -q commit firewall
|
||||
/etc/init.d/firewall reload >/dev/null 2>&1 || true
|
||||
fi
|
||||
fi
|
||||
|
||||
if command -v nft >/dev/null 2>&1; then
|
||||
nft delete table inet openbox 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
start_service() {
|
||||
if [ -f "$INSTALL_ROOT/openwrt/initd/openbox" ] && [ ! -L "$INSTALL_ROOT/openwrt/initd/openbox" ]; then
|
||||
"$INSTALL_ROOT/openwrt/initd/openbox" start
|
||||
return $?
|
||||
fi
|
||||
|
||||
if [ ! -d "$INSTALL_ROOT" ]; then
|
||||
echo "[openbox] 核心组件尚未安装在 $INSTALL_ROOT,请先安装或下载核心组件。" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
local deploy_cli="$INSTALL_ROOT/panel/server/cli/deploy.mjs"
|
||||
local node_bin="$INSTALL_ROOT/node/bin/node"
|
||||
local core_bin="$INSTALL_ROOT/bin/sing-box"
|
||||
|
||||
if [ -x "$node_bin" ] && [ -f "$deploy_cli" ]; then
|
||||
OPENBOX_ROOT="$INSTALL_ROOT" ZASHBOARD_DB_PATH="$INSTALL_ROOT/data/openbox.sqlite" \
|
||||
OPENSSL_CONF=/dev/null LD_LIBRARY_PATH="$INSTALL_ROOT/node/lib" \
|
||||
"$node_bin" "$deploy_cli" >/dev/null 2>&1
|
||||
return $?
|
||||
elif [ -x "$core_bin" ] && [ -f "$INSTALL_ROOT/etc/config.json" ]; then
|
||||
procd_open_instance openbox
|
||||
procd_set_param command "$core_bin" run -c "$INSTALL_ROOT/etc/config.json" -D "$INSTALL_ROOT"
|
||||
procd_set_param respawn
|
||||
procd_set_param limits core="unlimited"
|
||||
procd_close_instance
|
||||
else
|
||||
echo "[openbox] 核心服务未就绪,请在面板完成订阅或分流配置后启动。" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
stop_service() {
|
||||
if [ -f "$INSTALL_ROOT/openwrt/initd/openbox" ] && [ ! -L "$INSTALL_ROOT/openwrt/initd/openbox" ]; then
|
||||
"$INSTALL_ROOT/openwrt/initd/openbox" stop
|
||||
return $?
|
||||
fi
|
||||
|
||||
if [ -x "$INSTALL_ROOT/bin/sing-box" ]; then
|
||||
killall -9 sing-box 2>/dev/null || true
|
||||
fi
|
||||
openbox_cleanup
|
||||
}
|
||||
|
||||
restart() {
|
||||
stop
|
||||
sleep 1
|
||||
start
|
||||
}
|
||||
|
||||
status() {
|
||||
if [ -x "$INSTALL_ROOT/bin/sing-box" ] && pidof sing-box >/dev/null; then
|
||||
echo "openbox is running (pid $(pidof sing-box))"
|
||||
return 0
|
||||
fi
|
||||
echo "openbox is not running"
|
||||
return 1
|
||||
}
|
||||
|
||||
reload_service() {
|
||||
restart
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
# Copyright (C) 2026 OpenWrt.org
|
||||
# Open-Box Panel Web Service
|
||||
|
||||
START=95
|
||||
STOP=10
|
||||
USE_PROCD=1
|
||||
|
||||
INSTALL_ROOT="/opt/open-box"
|
||||
|
||||
openbox_get_port() {
|
||||
local p=""
|
||||
[ -f "$INSTALL_ROOT/data/panel-port" ] && p=$(cat "$INSTALL_ROOT/data/panel-port" 2>/dev/null | tr -dc '0-9')
|
||||
if [ -z "$p" ]; then
|
||||
config_load openbox
|
||||
config_get p main port 3036
|
||||
fi
|
||||
[ -n "$p" ] || p=3036
|
||||
echo "$p"
|
||||
}
|
||||
|
||||
ensure_firewall_rule() {
|
||||
local port="$1"
|
||||
command -v uci >/dev/null 2>&1 || return 0
|
||||
|
||||
local cur_port=$(uci -q get firewall.openbox_panel.dest_port 2>/dev/null)
|
||||
if [ "$cur_port" != "$port" ]; then
|
||||
uci -q delete firewall.openbox_panel 2>/dev/null || true
|
||||
uci -q set firewall.openbox_panel=rule
|
||||
uci -q set firewall.openbox_panel.name='Open-Box Panel'
|
||||
uci -q set firewall.openbox_panel.src='lan'
|
||||
uci -q set firewall.openbox_panel.proto='tcp'
|
||||
uci -q set firewall.openbox_panel.dest_port="$port"
|
||||
uci -q set firewall.openbox_panel.target='ACCEPT'
|
||||
uci -q commit firewall
|
||||
/etc/init.d/firewall reload >/dev/null 2>&1 || true
|
||||
fi
|
||||
}
|
||||
|
||||
start_service() {
|
||||
if [ -f "$INSTALL_ROOT/openwrt/initd/openbox-panel" ] && [ ! -L "$INSTALL_ROOT/openwrt/initd/openbox-panel" ]; then
|
||||
"$INSTALL_ROOT/openwrt/initd/openbox-panel" start
|
||||
return $?
|
||||
fi
|
||||
|
||||
if [ ! -d "$INSTALL_ROOT" ] || [ ! -x "$INSTALL_ROOT/node/bin/node" ]; then
|
||||
echo "[openbox-panel] 核心未安装在 $INSTALL_ROOT,请先执行 open-box install 或在 LuCI 页面下载安装核心。" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
local node_bin="$INSTALL_ROOT/node/bin/node"
|
||||
local panel_app="$INSTALL_ROOT/panel/server/index.mjs"
|
||||
[ -f "$panel_app" ] || panel_app="$INSTALL_ROOT/panel/server/index.js"
|
||||
|
||||
local port=$(openbox_get_port)
|
||||
mkdir -p "$INSTALL_ROOT/data" 2>/dev/null || true
|
||||
echo "$port" > "$INSTALL_ROOT/data/panel-port"
|
||||
ensure_firewall_rule "$port"
|
||||
|
||||
if [ -f "$INSTALL_ROOT/openwrt/bin/open-box" ] && [ ! -e /usr/bin/open-box ]; then
|
||||
ln -sf "$INSTALL_ROOT/openwrt/bin/open-box" /usr/bin/open-box 2>/dev/null || true
|
||||
fi
|
||||
|
||||
procd_open_instance openbox-panel
|
||||
procd_set_param command "$node_bin" "$panel_app"
|
||||
procd_set_param env OPENBOX_ROOT="$INSTALL_ROOT" OPENSSL_CONF=/dev/null LD_LIBRARY_PATH="$INSTALL_ROOT/node/lib" PORT="$port"
|
||||
procd_set_param respawn 3600 5 0
|
||||
procd_set_param stdout 1
|
||||
procd_set_param stderr 1
|
||||
procd_close_instance
|
||||
}
|
||||
|
||||
stop_service() {
|
||||
if [ -f "$INSTALL_ROOT/openwrt/initd/openbox-panel" ] && [ ! -L "$INSTALL_ROOT/openwrt/initd/openbox-panel" ]; then
|
||||
"$INSTALL_ROOT/openwrt/initd/openbox-panel" stop
|
||||
return $?
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
status() {
|
||||
if pgrep -f "$INSTALL_ROOT/panel/server" >/dev/null 2>&1; then
|
||||
local pid=$(pgrep -f "$INSTALL_ROOT/panel/server" | head -n 1)
|
||||
local port=$(openbox_get_port)
|
||||
echo "openbox-panel is running (pid $pid, port $port)"
|
||||
return 0
|
||||
fi
|
||||
echo "openbox-panel is not running"
|
||||
return 1
|
||||
}
|
||||
|
||||
reload_service() {
|
||||
restart
|
||||
}
|
||||
|
||||
service_triggers() {
|
||||
procd_add_reload_trigger "openbox"
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Set default firewall rule for openbox-panel
|
||||
uci -q get firewall.openbox_panel >/dev/null || {
|
||||
uci -q set firewall.openbox_panel=rule
|
||||
uci -q set firewall.openbox_panel.name='Open-Box Panel'
|
||||
uci -q set firewall.openbox_panel.src='lan'
|
||||
uci -q set firewall.openbox_panel.proto='tcp'
|
||||
uci -q set firewall.openbox_panel.dest_port='3036'
|
||||
uci -q set firewall.openbox_panel.target='ACCEPT'
|
||||
uci -q commit firewall
|
||||
}
|
||||
|
||||
# Create open-box CLI link if available
|
||||
[ -e /usr/bin/open-box ] || {
|
||||
if [ -f /usr/share/openbox/open-box ]; then
|
||||
ln -sf /usr/share/openbox/open-box /usr/bin/open-box
|
||||
elif [ -f /opt/open-box/openwrt/bin/open-box ]; then
|
||||
ln -sf /opt/open-box/openwrt/bin/open-box /usr/bin/open-box
|
||||
fi
|
||||
}
|
||||
|
||||
# Clean LuCI cache
|
||||
rm -rf /tmp/luci-*cache* 2>/dev/null || true
|
||||
|
||||
exit 0
|
||||
@@ -0,0 +1,208 @@
|
||||
#!/bin/sh
|
||||
# Open-Box 命令行管理工具
|
||||
INSTALL_ROOT="/opt/open-box"
|
||||
OPENBOX_PORT_FILE="$INSTALL_ROOT/data/panel-port"
|
||||
OPENBOX_PORT_FALLBACK=2026
|
||||
OPENBOX_PORT_DEFAULT=3036
|
||||
OPENBOX_RESERVED_PORTS="9095 7853 7854 7891 53 22 80 443"
|
||||
|
||||
if [ -f "$INSTALL_ROOT/openwrt/bin/open-box" ] && [ "$0" != "$INSTALL_ROOT/openwrt/bin/open-box" ] && [ ! -L "$INSTALL_ROOT/openwrt/bin/open-box" ]; then
|
||||
exec "$INSTALL_ROOT/openwrt/bin/open-box" "$@"
|
||||
fi
|
||||
|
||||
openbox_port_problem() {
|
||||
_p="$1"
|
||||
case "$_p" in
|
||||
''|*[!0-9]*) echo "端口要填 1024-65535 的数字"; return 0 ;;
|
||||
esac
|
||||
if [ "$_p" -lt 1024 ] || [ "$_p" -gt 65535 ]; then
|
||||
echo "端口要在 1024-65535 之间(1024 以下是系统保留端口)"
|
||||
return 0
|
||||
fi
|
||||
for _r in $OPENBOX_RESERVED_PORTS; do
|
||||
if [ "$_p" = "$_r" ]; then
|
||||
echo "$_p 是 Open-Box 自己或系统服务在用的端口(内核 API / DNS / SSH / HTTP 等),换一个"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
_busy=""
|
||||
if command -v ss >/dev/null 2>&1; then
|
||||
_busy=$(ss -ltn 2>/dev/null | awk 'NR>1 {print $4}' | sed 's/.*[:.]//' | grep -x "$_p" | head -n 1)
|
||||
elif command -v netstat >/dev/null 2>&1; then
|
||||
_busy=$(netstat -ltn 2>/dev/null | awk '{print $4}' | sed 's/.*[:.]//' | grep -x "$_p" | head -n 1)
|
||||
fi
|
||||
if [ -n "$_busy" ]; then
|
||||
echo "$_p 已经被别的程序占用(已在监听)"
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
openbox_current_port() {
|
||||
_cp=$(cat "$OPENBOX_PORT_FILE" 2>/dev/null | tr -dc '0-9')
|
||||
[ -n "$_cp" ] || _cp="$OPENBOX_PORT_DEFAULT"
|
||||
printf '%s\n' "$_cp"
|
||||
}
|
||||
|
||||
openbox_get_lan_ip() {
|
||||
LAN_IP=""
|
||||
command -v uci >/dev/null 2>&1 && LAN_IP=$(uci -q get network.lan.ipaddr 2>/dev/null | tr " " "\n" | head -n 1 | cut -d/ -f1)
|
||||
if [ -z "$LAN_IP" ] && command -v ip >/dev/null 2>&1; then
|
||||
LAN_IP=$(ip -4 -o addr show br-lan 2>/dev/null | awk '{ print $4 }' | cut -d/ -f1 | head -n 1)
|
||||
fi
|
||||
[ -n "$LAN_IP" ] || LAN_IP="192.168.1.1"
|
||||
printf '%s\n' "$LAN_IP"
|
||||
}
|
||||
|
||||
openbox_get_password() {
|
||||
if [ -f "$INSTALL_ROOT/data/openbox.sqlite" ] && [ -x "$INSTALL_ROOT/node/bin/node" ]; then
|
||||
_pw=$(OPENSSL_CONF=/dev/null LD_LIBRARY_PATH="$INSTALL_ROOT/node/lib" "$INSTALL_ROOT/node/bin/node" -e '
|
||||
try {
|
||||
const sqlite3 = require("sqlite3");
|
||||
const db = new sqlite3.Database("'$INSTALL_ROOT'/data/openbox.sqlite", sqlite3.OPEN_READONLY, (err) => {
|
||||
if (err) process.exit(1);
|
||||
db.get("SELECT value FROM settings WHERE key = ?", ["password"], (e, row) => {
|
||||
if (row && row.value) console.log(row.value);
|
||||
db.close();
|
||||
});
|
||||
});
|
||||
} catch (_) {}
|
||||
' 2>/dev/null)
|
||||
if [ -n "$_pw" ]; then
|
||||
printf '%s\n' "$_pw"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
if [ -f "$INSTALL_ROOT/data/password" ]; then
|
||||
cat "$INSTALL_ROOT/data/password" 2>/dev/null
|
||||
return 0
|
||||
fi
|
||||
echo "尚未设置(首次打开面板设置管理密码)"
|
||||
}
|
||||
|
||||
openbox_get_version() {
|
||||
_ver=$(sed -n 's/.*"version" *: *"\([^"]*\)".*/\1/p' "$INSTALL_ROOT/meta.json" 2>/dev/null | head -n 1)
|
||||
[ -n "$_ver" ] || _ver="未安装"
|
||||
printf '%s\n' "$_ver"
|
||||
}
|
||||
|
||||
cmd="${1:-}"
|
||||
|
||||
case "$cmd" in
|
||||
password)
|
||||
openbox_get_password
|
||||
exit 0
|
||||
;;
|
||||
port)
|
||||
if [ $# -ge 2 ]; then
|
||||
new_port="$2"
|
||||
_reason=$(openbox_port_problem "$new_port") && {
|
||||
echo "面板端口不能用:$_reason" >&2
|
||||
exit 1
|
||||
}
|
||||
mkdir -p "$INSTALL_ROOT/data"
|
||||
printf '%s\n' "$new_port" > "$OPENBOX_PORT_FILE"
|
||||
if command -v uci >/dev/null 2>&1; then
|
||||
uci -q set openbox.main.port="$new_port"
|
||||
uci -q set firewall.openbox_panel.dest_port="$new_port"
|
||||
uci -q commit openbox 2>/dev/null || true
|
||||
uci -q commit firewall 2>/dev/null || true
|
||||
/etc/init.d/firewall reload >/dev/null 2>&1 || true
|
||||
fi
|
||||
echo "面板端口已修改为: $new_port"
|
||||
/etc/init.d/openbox-panel restart >/dev/null 2>&1 || true
|
||||
exit 0
|
||||
else
|
||||
openbox_current_port
|
||||
exit 0
|
||||
fi
|
||||
;;
|
||||
check)
|
||||
if [ -f "$INSTALL_ROOT/update.sh" ]; then
|
||||
sh "$INSTALL_ROOT/update.sh" --probe direct
|
||||
elif [ -f /usr/share/openbox/update.sh ]; then
|
||||
sh /usr/share/openbox/update.sh --probe direct
|
||||
else
|
||||
echo "未找到更新脚本"
|
||||
fi
|
||||
exit 0
|
||||
;;
|
||||
update)
|
||||
shift
|
||||
if [ -f "$INSTALL_ROOT/update.sh" ]; then
|
||||
sh "$INSTALL_ROOT/update.sh" "$@"
|
||||
elif [ -f /usr/share/openbox/update.sh ]; then
|
||||
sh /usr/share/openbox/update.sh "$@"
|
||||
else
|
||||
echo "未找到更新脚本" >&2
|
||||
exit 1
|
||||
fi
|
||||
exit 0
|
||||
;;
|
||||
restart)
|
||||
/etc/init.d/openbox restart
|
||||
/etc/init.d/openbox-panel restart
|
||||
echo "已重启 Open-Box 服务与面板"
|
||||
exit 0
|
||||
;;
|
||||
uninstall)
|
||||
shift
|
||||
if [ -f "$INSTALL_ROOT/uninstall.sh" ]; then
|
||||
sh "$INSTALL_ROOT/uninstall.sh" "$@"
|
||||
elif [ -f /usr/share/openbox/uninstall.sh ]; then
|
||||
sh /usr/share/openbox/uninstall.sh "$@"
|
||||
else
|
||||
/etc/init.d/openbox stop
|
||||
/etc/init.d/openbox-panel stop
|
||||
rm -rf "$INSTALL_ROOT"
|
||||
echo "已停止并清理"
|
||||
fi
|
||||
exit 0
|
||||
;;
|
||||
install)
|
||||
shift
|
||||
if [ -f /usr/share/openbox/install.sh ]; then
|
||||
sh /usr/share/openbox/install.sh "$@"
|
||||
else
|
||||
curl -fsSL https://raw.githubusercontent.com/liandu2024/Open-Box/main/scripts/install.sh | sh -s -- "$@"
|
||||
fi
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
VERSION=$(openbox_get_version)
|
||||
echo ""
|
||||
echo "Open-Box $VERSION"
|
||||
echo " 1) 当前密码"
|
||||
echo " 2) 重新启动"
|
||||
echo " 3) 检查升级"
|
||||
echo " 4) 卸载"
|
||||
echo " 5) 退出"
|
||||
printf '请选择 [1-5]: '
|
||||
read -r choice
|
||||
case "$choice" in
|
||||
1)
|
||||
port=$(openbox_current_port)
|
||||
lan_ip=$(openbox_get_lan_ip)
|
||||
pw=$(openbox_get_password)
|
||||
echo ""
|
||||
echo " 面板地址: http://$lan_ip:$port"
|
||||
echo " 当前密码: $pw"
|
||||
echo ""
|
||||
;;
|
||||
2)
|
||||
/etc/init.d/openbox restart
|
||||
/etc/init.d/openbox-panel restart
|
||||
echo "服务已重启。"
|
||||
;;
|
||||
3)
|
||||
sh "$0" update
|
||||
;;
|
||||
4)
|
||||
sh "$0" uninstall
|
||||
;;
|
||||
5|*)
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"admin/services/openbox": {
|
||||
"title": "Open-Box",
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "openbox/main"
|
||||
},
|
||||
"depends": {
|
||||
"acl": [ "luci-app-openbox" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
#!/bin/sh
|
||||
# Open-Box 一键安装脚本
|
||||
set -eu
|
||||
umask 022
|
||||
|
||||
REPO="liandu2024/Open-Box"
|
||||
INSTALL_ROOT="/opt/open-box"
|
||||
CHANNEL="mirror"
|
||||
MIRROR_PREFIX="https://ghfast.top"
|
||||
PANEL_PORT="3036"
|
||||
|
||||
info() { echo "[open-box] $*"; }
|
||||
warn() { echo "[open-box] 警告:$*" >&2; }
|
||||
die() { echo "[open-box] 错误:$*" >&2; exit 1; }
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--mirror)
|
||||
CHANNEL="mirror"
|
||||
shift
|
||||
if [ $# -ge 1 ]; then
|
||||
case "$1" in
|
||||
--*) ;;
|
||||
*) MIRROR_PREFIX="$1"; shift ;;
|
||||
esac
|
||||
fi
|
||||
;;
|
||||
--direct)
|
||||
CHANNEL="direct"
|
||||
MIRROR_PREFIX=""
|
||||
shift
|
||||
;;
|
||||
--port)
|
||||
shift
|
||||
[ $# -ge 1 ] || die "--port 后面要跟端口号"
|
||||
PANEL_PORT="$1"
|
||||
shift
|
||||
;;
|
||||
*) shift ;;
|
||||
esac
|
||||
done
|
||||
|
||||
detect_arch() {
|
||||
RAW_ARCH=$(uname -m 2>/dev/null || true)
|
||||
case "$RAW_ARCH" in
|
||||
x86_64) ARCH="x64" ;;
|
||||
aarch64) ARCH="arm64" ;;
|
||||
*) die "不支持的 CPU 架构:${RAW_ARCH:-未知}。Open-Box 目前仅支持 x86_64 与 aarch64(arm64)。" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
detect_arch
|
||||
|
||||
if [ -f "/etc/config/openbox" ]; then
|
||||
p=$(uci -q get openbox.main.port 2>/dev/null || true)
|
||||
[ -n "$p" ] && PANEL_PORT="$p"
|
||||
fi
|
||||
|
||||
TMP_DL=$(mktemp -d /tmp/openbox-inst.XXXXXX 2>/dev/null || echo "/tmp/openbox-inst.$$")
|
||||
mkdir -p "$TMP_DL"
|
||||
trap 'rm -rf "$TMP_DL"' EXIT INT TERM
|
||||
|
||||
ASSET="open-box-linux-${ARCH}.tar.gz"
|
||||
ASSET_URL="https://github.com/$REPO/releases/latest/download/$ASSET"
|
||||
SHA_URL="$ASSET_URL.sha256"
|
||||
|
||||
build_url() {
|
||||
url="$1"
|
||||
if [ "$CHANNEL" = "mirror" ] && [ -n "$MIRROR_PREFIX" ]; then
|
||||
case "$MIRROR_PREFIX" in
|
||||
http://*|https://*) printf '%s/%s
|
||||
' "${MIRROR_PREFIX%/}" "$url" ;;
|
||||
*) printf 'https://%s/%s
|
||||
' "${MIRROR_PREFIX%/}" "$url" ;;
|
||||
esac
|
||||
else
|
||||
printf '%s
|
||||
' "$url"
|
||||
fi
|
||||
}
|
||||
|
||||
info "下载校验和与发布包: $ASSET ..."
|
||||
curl -fsSL -o "$TMP_DL/$ASSET.sha256" "$(build_url "$SHA_URL")" 2>/dev/null || wget -q -O "$TMP_DL/$ASSET.sha256" "$(build_url "$SHA_URL")" || die "下载校验文件失败"
|
||||
curl -fsSL -o "$TMP_DL/$ASSET" "$(build_url "$ASSET_URL")" 2>/dev/null || wget -q -O "$TMP_DL/$ASSET" "$(build_url "$ASSET_URL")" || die "下载发布包失败"
|
||||
|
||||
info "校验 SHA256 ..."
|
||||
( cd "$TMP_DL" && sha256sum -c "$ASSET.sha256" >/dev/null 2>&1 ) || die "安装包校验失败 (SHA256 不匹配)"
|
||||
|
||||
info "解包到 $INSTALL_ROOT ..."
|
||||
mkdir -p "$INSTALL_ROOT"
|
||||
tar -xzf "$TMP_DL/$ASSET" -C "$INSTALL_ROOT" || die "解包失败"
|
||||
chown -R 0:0 "$INSTALL_ROOT" 2>/dev/null || true
|
||||
|
||||
mkdir -p "$INSTALL_ROOT/data"
|
||||
printf '%s
|
||||
' "$PANEL_PORT" > "$INSTALL_ROOT/data/panel-port"
|
||||
if [ "$CHANNEL" = "mirror" ]; then
|
||||
printf 'mirror
|
||||
%s
|
||||
' "$MIRROR_PREFIX" > "$INSTALL_ROOT/data/channel"
|
||||
else
|
||||
printf 'direct
|
||||
' > "$INSTALL_ROOT/data/channel"
|
||||
fi
|
||||
|
||||
cp -f "$INSTALL_ROOT/openwrt/initd/openbox" /etc/init.d/openbox 2>/dev/null || true
|
||||
cp -f "$INSTALL_ROOT/openwrt/initd/openbox-panel" /etc/init.d/openbox-panel 2>/dev/null || true
|
||||
chmod +x /etc/init.d/openbox /etc/init.d/openbox-panel 2>/dev/null || true
|
||||
|
||||
if [ -f "$INSTALL_ROOT/openwrt/bin/open-box" ]; then
|
||||
chmod +x "$INSTALL_ROOT/openwrt/bin/open-box" 2>/dev/null || true
|
||||
ln -sf "$INSTALL_ROOT/openwrt/bin/open-box" /usr/bin/open-box 2>/dev/null || true
|
||||
fi
|
||||
|
||||
if [ -d "$INSTALL_ROOT/openwrt/luci/htdocs/luci-static/resources/view/openbox" ]; then
|
||||
mkdir -p /www/luci-static/resources/view/openbox
|
||||
cp -f "$INSTALL_ROOT/openwrt/luci/htdocs/luci-static/resources/view/openbox/"*.js /www/luci-static/resources/view/openbox/ 2>/dev/null || true
|
||||
fi
|
||||
|
||||
if [ -f "$INSTALL_ROOT/openwrt/luci/root/usr/share/luci/menu.d/luci-app-openbox.json" ]; then
|
||||
mkdir -p /usr/share/luci/menu.d
|
||||
cp -f "$INSTALL_ROOT/openwrt/luci/root/usr/share/luci/menu.d/luci-app-openbox.json" /usr/share/luci/menu.d/ 2>/dev/null || true
|
||||
fi
|
||||
|
||||
if [ -f "$INSTALL_ROOT/openwrt/luci/root/usr/share/rpcd/acl.d/luci-app-openbox.json" ]; then
|
||||
mkdir -p /usr/share/rpcd/acl.d
|
||||
cp -f "$INSTALL_ROOT/openwrt/luci/root/usr/share/rpcd/acl.d/luci-app-openbox.json" /usr/share/rpcd/acl.d/ 2>/dev/null || true
|
||||
fi
|
||||
|
||||
rm -rf /tmp/luci-*cache* 2>/dev/null || true
|
||||
/etc/init.d/rpcd restart >/dev/null 2>&1 || true
|
||||
|
||||
/etc/init.d/openbox-panel enable >/dev/null 2>&1 || true
|
||||
/etc/init.d/openbox-panel restart >/dev/null 2>&1 || true
|
||||
|
||||
info "Open-Box 核心组件安装完成!"
|
||||
@@ -0,0 +1,85 @@
|
||||
#!/bin/sh
|
||||
# Open-Box 卸载清理脚本
|
||||
set -eu
|
||||
|
||||
INSTALL_ROOT="/opt/open-box"
|
||||
STATUS_PATH="/tmp/openbox-uninstall.status"
|
||||
PURGE=0
|
||||
DETACH=0
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--purge) PURGE=1; shift ;;
|
||||
--detach) DETACH=1; shift ;;
|
||||
*) shift ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ "$DETACH" = "1" ]; then
|
||||
_args=""
|
||||
[ "$PURGE" = "1" ] && _args="--purge"
|
||||
nohup sh "$0" $_args >/dev/null 2>&1 &
|
||||
echo "卸载已在后台启动"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
{
|
||||
echo "pid=$$"
|
||||
echo "stage=stopping"
|
||||
} > "$STATUS_PATH" 2>/dev/null || true
|
||||
|
||||
/etc/init.d/openbox stop >/dev/null 2>&1 || true
|
||||
/etc/init.d/openbox-panel stop >/dev/null 2>&1 || true
|
||||
/etc/init.d/openbox disable >/dev/null 2>&1 || true
|
||||
/etc/init.d/openbox-panel disable >/dev/null 2>&1 || true
|
||||
|
||||
{
|
||||
echo "pid=$$"
|
||||
echo "stage=firewall"
|
||||
} > "$STATUS_PATH" 2>/dev/null || true
|
||||
|
||||
if command -v uci >/dev/null 2>&1; then
|
||||
uci -q delete firewall.openbox_panel || true
|
||||
uci -q delete firewall.openbox_dns || true
|
||||
uci -q delete firewall.openbox_tun_forward || true
|
||||
uci -q delete firewall.openbox_tun_input || true
|
||||
uci -q delete firewall.openbox_v6block || true
|
||||
for _ob_rule in $(uci -q show firewall 2>/dev/null | sed -n 's/^firewall\.\(openbox_srv_[A-Za-z0-9_]*\)=rule$/\1/p'); do
|
||||
uci -q delete "firewall.$_ob_rule" || true
|
||||
done
|
||||
if [ -n "$(uci -q changes firewall)" ]; then
|
||||
uci -q commit firewall
|
||||
/etc/init.d/firewall reload >/dev/null 2>&1 || true
|
||||
fi
|
||||
fi
|
||||
|
||||
{
|
||||
echo "pid=$$"
|
||||
echo "stage=files"
|
||||
} > "$STATUS_PATH" 2>/dev/null || true
|
||||
|
||||
rm -f /etc/init.d/openbox /etc/init.d/openbox-panel
|
||||
rm -f /www/luci-static/resources/view/openbox/main.js /www/luci-static/resources/view/openbox/status.js
|
||||
rmdir /www/luci-static/resources/view/openbox 2>/dev/null || true
|
||||
rm -f /usr/share/luci/menu.d/luci-app-openbox.json
|
||||
rm -f /usr/share/rpcd/acl.d/luci-app-openbox.json
|
||||
[ -L /usr/bin/open-box ] && rm -f /usr/bin/open-box
|
||||
rm -rf /tmp/luci-*cache* 2>/dev/null || true
|
||||
|
||||
if [ "$PURGE" = "1" ]; then
|
||||
rm -rf "$INSTALL_ROOT"
|
||||
else
|
||||
for entry in "$INSTALL_ROOT"/*; do
|
||||
[ -e "$entry" ] || continue
|
||||
base=$(basename -- "$entry")
|
||||
[ "$base" = "data" ] && continue
|
||||
rm -rf "$entry"
|
||||
done
|
||||
fi
|
||||
|
||||
{
|
||||
echo "pid=$$"
|
||||
echo "stage=done"
|
||||
} > "$STATUS_PATH" 2>/dev/null || true
|
||||
|
||||
echo "Open-Box 卸载完成"
|
||||
@@ -0,0 +1,204 @@
|
||||
#!/bin/sh
|
||||
# Open-Box 更新/回退脚本
|
||||
set -eu
|
||||
umask 022
|
||||
|
||||
REPO="liandu2024/Open-Box"
|
||||
INSTALL_ROOT="/opt/open-box"
|
||||
STATUS_PATH="/tmp/openbox-update.status"
|
||||
CANCEL_FLAG="/tmp/openbox-update.cancel"
|
||||
UPDATE_LOG="/tmp/openbox-update.log"
|
||||
STATUS_PID="$$"
|
||||
|
||||
DETACH=0
|
||||
ROLLBACK_MODE=0
|
||||
CHANNEL="direct"
|
||||
MIRROR_PREFIX=""
|
||||
PROBE_CHANNEL=""
|
||||
CANCEL_MODE=0
|
||||
|
||||
info() { echo "[open-box] $*"; }
|
||||
warn() { echo "[open-box] 警告:$*" >&2; }
|
||||
die() {
|
||||
echo "[open-box] 错误:$*" >&2
|
||||
write_status failed "" "" "$*"
|
||||
exit 1
|
||||
}
|
||||
|
||||
write_status() {
|
||||
[ -n "$STATUS_PID" ] || return 0
|
||||
_ws_tmp="$STATUS_PATH.$$.tmp"
|
||||
{
|
||||
echo "pid=$STATUS_PID"
|
||||
echo "stage=$1"
|
||||
echo "bytes=${2:-}"
|
||||
echo "total=${3:-}"
|
||||
echo "message=${4:-}"
|
||||
} > "$_ws_tmp" 2>/dev/null && mv -f "$_ws_tmp" "$STATUS_PATH" 2>/dev/null || true
|
||||
}
|
||||
|
||||
check_cancel() {
|
||||
if [ -e "$CANCEL_FLAG" ]; then
|
||||
write_status cancelled "" "" "已取消更新"
|
||||
exit 0
|
||||
fi
|
||||
}
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--detach) DETACH=1; shift ;;
|
||||
--rollback) ROLLBACK_MODE=1; shift ;;
|
||||
--cancel) CANCEL_MODE=1; shift ;;
|
||||
--direct) CHANNEL="direct"; MIRROR_PREFIX=""; shift ;;
|
||||
--mirror)
|
||||
CHANNEL="mirror"
|
||||
shift
|
||||
if [ $# -ge 1 ]; then
|
||||
case "$1" in
|
||||
--*) ;;
|
||||
*) MIRROR_PREFIX="$1"; shift ;;
|
||||
esac
|
||||
fi
|
||||
;;
|
||||
--probe)
|
||||
shift
|
||||
PROBE_CHANNEL="$1"
|
||||
shift
|
||||
;;
|
||||
*) shift ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ "$CANCEL_MODE" = "1" ]; then
|
||||
touch "$CANCEL_FLAG" 2>/dev/null || true
|
||||
echo "requested"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ -n "$PROBE_CHANNEL" ]; then
|
||||
start_time=$(date +%s%N 2>/dev/null || date +%s)
|
||||
target="https://github.com/$REPO/releases/latest/download/open-box-linux-x64.tar.gz.sha256"
|
||||
if [ "$PROBE_CHANNEL" != "direct" ]; then
|
||||
case "$PROBE_CHANNEL" in
|
||||
http://*|https://*) target="${PROBE_CHANNEL%/}/$target" ;;
|
||||
*) target="https://${PROBE_CHANNEL%/}/$target" ;;
|
||||
esac
|
||||
fi
|
||||
if curl -fsSL --connect-timeout 8 --max-time 15 -o /dev/null "$target" 2>/dev/null || wget -q --timeout=15 -O /dev/null "$target" 2>/dev/null; then
|
||||
end_time=$(date +%s%N 2>/dev/null || date +%s)
|
||||
ms=$(( (end_time - start_time) / 1000000 2>/dev/null || 100 ))
|
||||
[ "$ms" -le 0 ] && ms=80
|
||||
echo "ok $ms ms"
|
||||
else
|
||||
echo "fail 连接超时或无法访问"
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$DETACH" = "1" ]; then
|
||||
: > "$UPDATE_LOG" 2>/dev/null || true
|
||||
write_status starting "" "" ""
|
||||
rm -f "$CANCEL_FLAG" 2>/dev/null || true
|
||||
_args=""
|
||||
[ "$ROLLBACK_MODE" = "1" ] && _args="$_args --rollback"
|
||||
if [ "$CHANNEL" = "mirror" ]; then
|
||||
_args="$_args --mirror $MIRROR_PREFIX"
|
||||
else
|
||||
_args="$_args --direct"
|
||||
fi
|
||||
nohup sh "$0" $_args >"$UPDATE_LOG" 2>&1 </dev/null &
|
||||
echo "升级已在后台启动,进度见 $STATUS_PATH"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
write_status probing "" "" ""
|
||||
check_cancel
|
||||
|
||||
RAW_ARCH=$(uname -m 2>/dev/null || true)
|
||||
case "$RAW_ARCH" in
|
||||
x86_64) ARCH="x64" ;;
|
||||
aarch64) ARCH="arm64" ;;
|
||||
*) die "不支持的 CPU 架构: $RAW_ARCH" ;;
|
||||
esac
|
||||
|
||||
ASSET="open-box-linux-${ARCH}.tar.gz"
|
||||
ASSET_URL="https://github.com/$REPO/releases/latest/download/$ASSET"
|
||||
SHA_URL="$ASSET_URL.sha256"
|
||||
|
||||
build_url() {
|
||||
url="$1"
|
||||
if [ "$CHANNEL" = "mirror" ] && [ -n "$MIRROR_PREFIX" ]; then
|
||||
case "$MIRROR_PREFIX" in
|
||||
http://*|https://*) printf '%s/%s
|
||||
' "${MIRROR_PREFIX%/}" "$url" ;;
|
||||
*) printf 'https://%s/%s
|
||||
' "${MIRROR_PREFIX%/}" "$url" ;;
|
||||
esac
|
||||
else
|
||||
printf '%s
|
||||
' "$url"
|
||||
fi
|
||||
}
|
||||
|
||||
TMP_DL=$(mktemp -d /tmp/openbox-upd.XXXXXX 2>/dev/null || echo "/tmp/openbox-upd.$$")
|
||||
mkdir -p "$TMP_DL"
|
||||
trap 'rm -rf "$TMP_DL"' EXIT INT TERM
|
||||
|
||||
write_status downloading 0 100000000 "开始下载"
|
||||
check_cancel
|
||||
|
||||
curl -fsSL -o "$TMP_DL/$ASSET.sha256" "$(build_url "$SHA_URL")" 2>/dev/null || wget -q -O "$TMP_DL/$ASSET.sha256" "$(build_url "$SHA_URL")" || die "下载校验和失败"
|
||||
check_cancel
|
||||
|
||||
curl -fsSL -o "$TMP_DL/$ASSET" "$(build_url "$ASSET_URL")" 2>/dev/null || wget -q -O "$TMP_DL/$ASSET" "$(build_url "$ASSET_URL")" || die "下载升级包失败"
|
||||
check_cancel
|
||||
|
||||
write_status verifying "" "" ""
|
||||
( cd "$TMP_DL" && sha256sum -c "$ASSET.sha256" >/dev/null 2>&1 ) || die "校验失败 (SHA256 不匹配)"
|
||||
|
||||
write_status extracting "" "" ""
|
||||
check_cancel
|
||||
|
||||
STAGE_DIR="$INSTALL_ROOT/.update-stage.$$"
|
||||
rm -rf "$STAGE_DIR"
|
||||
mkdir -p "$STAGE_DIR"
|
||||
tar -xzf "$TMP_DL/$ASSET" -C "$STAGE_DIR" || die "解包失败"
|
||||
|
||||
write_status committing "" "" ""
|
||||
/etc/init.d/openbox-panel stop >/dev/null 2>&1 || true
|
||||
/etc/init.d/openbox stop >/dev/null 2>&1 || true
|
||||
|
||||
for comp in node panel bin openwrt; do
|
||||
if [ -e "$STAGE_DIR/$comp" ]; then
|
||||
rm -rf "$INSTALL_ROOT/$comp"
|
||||
mv "$STAGE_DIR/$comp" "$INSTALL_ROOT/$comp"
|
||||
fi
|
||||
done
|
||||
[ -e "$STAGE_DIR/meta.json" ] && mv "$STAGE_DIR/meta.json" "$INSTALL_ROOT/meta.json"
|
||||
rm -rf "$STAGE_DIR"
|
||||
|
||||
cp -f "$INSTALL_ROOT/openwrt/initd/openbox" /etc/init.d/openbox 2>/dev/null || true
|
||||
cp -f "$INSTALL_ROOT/openwrt/initd/openbox-panel" /etc/init.d/openbox-panel 2>/dev/null || true
|
||||
chmod +x /etc/init.d/openbox /etc/init.d/openbox-panel 2>/dev/null || true
|
||||
|
||||
if [ -f "$INSTALL_ROOT/openwrt/bin/open-box" ]; then
|
||||
ln -sf "$INSTALL_ROOT/openwrt/bin/open-box" /usr/bin/open-box 2>/dev/null || true
|
||||
fi
|
||||
if [ -d "$INSTALL_ROOT/openwrt/luci/htdocs/luci-static/resources/view/openbox" ]; then
|
||||
cp -f "$INSTALL_ROOT/openwrt/luci/htdocs/luci-static/resources/view/openbox/"*.js /www/luci-static/resources/view/openbox/ 2>/dev/null || true
|
||||
fi
|
||||
if [ -f "$INSTALL_ROOT/openwrt/luci/root/usr/share/luci/menu.d/luci-app-openbox.json" ]; then
|
||||
cp -f "$INSTALL_ROOT/openwrt/luci/root/usr/share/luci/menu.d/luci-app-openbox.json" /usr/share/luci/menu.d/ 2>/dev/null || true
|
||||
fi
|
||||
if [ -f "$INSTALL_ROOT/openwrt/luci/root/usr/share/rpcd/acl.d/luci-app-openbox.json" ]; then
|
||||
cp -f "$INSTALL_ROOT/openwrt/luci/root/usr/share/rpcd/acl.d/luci-app-openbox.json" /usr/share/rpcd/acl.d/ 2>/dev/null || true
|
||||
fi
|
||||
|
||||
rm -rf /tmp/luci-*cache* 2>/dev/null || true
|
||||
/etc/init.d/rpcd restart >/dev/null 2>&1 || true
|
||||
|
||||
/etc/init.d/openbox-panel start >/dev/null 2>&1 || true
|
||||
/etc/init.d/openbox start >/dev/null 2>&1 || true
|
||||
|
||||
write_status done "" "" "升级完成"
|
||||
info "升级完成!"
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"luci-app-openbox": {
|
||||
"description": "Grant UCI and system execution access for luci-app-openbox",
|
||||
"read": {
|
||||
"cgi-io": [ "backup" ],
|
||||
"file": {
|
||||
"/opt/open-box/meta.json": [ "read" ],
|
||||
"/opt/open-box/data/panel-port": [ "read" ],
|
||||
"/tmp/openbox-update.status": [ "read" ],
|
||||
"/tmp/openbox-update.log": [ "read" ],
|
||||
"/tmp/openbox-uninstall.status": [ "read" ],
|
||||
"/tmp/openbox-uninstall.log": [ "read" ]
|
||||
},
|
||||
"uci": [ "network", "firewall", "openbox" ],
|
||||
"ubus": {
|
||||
"service": [ "list" ]
|
||||
}
|
||||
},
|
||||
"write": {
|
||||
"file": {
|
||||
"/etc/init.d/openbox": [ "exec" ],
|
||||
"/etc/init.d/openbox-panel": [ "exec" ],
|
||||
"/usr/bin/open-box": [ "exec" ],
|
||||
"/opt/open-box/openwrt/bin/open-box": [ "exec" ],
|
||||
"/opt/open-box/update.sh": [ "exec" ],
|
||||
"/opt/open-box/uninstall.sh": [ "exec" ],
|
||||
"/usr/share/openbox/install.sh": [ "exec" ],
|
||||
"/usr/share/openbox/update.sh": [ "exec" ],
|
||||
"/usr/share/openbox/uninstall.sh": [ "exec" ]
|
||||
},
|
||||
"uci": [ "firewall", "openbox" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,456 @@
|
||||
'use strict';
|
||||
'require view';
|
||||
'require fs';
|
||||
'require ui';
|
||||
'require uci';
|
||||
'require rpc';
|
||||
'require poll';
|
||||
|
||||
var callServiceList = rpc.declare({
|
||||
object: 'service',
|
||||
method: 'list',
|
||||
params: [ 'name' ],
|
||||
expect: { '': {} }
|
||||
});
|
||||
|
||||
return view.extend({
|
||||
pollInterval: 3,
|
||||
|
||||
load: function() {
|
||||
return Promise.all([
|
||||
fs.read('/opt/open-box/meta.json').catch(function() { return null; }),
|
||||
fs.read('/opt/open-box/data/panel-port').catch(function() { return null; }),
|
||||
fs.exec_direct('/usr/bin/open-box', [ 'password' ]).catch(function() { return ''; }),
|
||||
callServiceList('openbox'),
|
||||
callServiceList('openbox-panel'),
|
||||
uci.load('network').catch(function() { return null; }),
|
||||
uci.load('openbox').catch(function() { return null; })
|
||||
]);
|
||||
},
|
||||
|
||||
render: function(data) {
|
||||
var metaRaw = data[0];
|
||||
var portRaw = data[1];
|
||||
var password = (data[2] || '').trim();
|
||||
var openboxSvc = data[3] || {};
|
||||
var panelSvc = data[4] || {};
|
||||
|
||||
var meta = null;
|
||||
if (metaRaw) {
|
||||
try { meta = JSON.parse(metaRaw); } catch(e) {}
|
||||
}
|
||||
|
||||
var port = (portRaw || '').replace(/[^0-9]/g, '');
|
||||
if (!port) {
|
||||
port = (uci.get('openbox', 'main', 'port') || '3036');
|
||||
}
|
||||
|
||||
var lanIp = uci.get('network', 'lan', 'ipaddr');
|
||||
if (Array.isArray(lanIp)) lanIp = lanIp[0];
|
||||
if (lanIp && lanIp.indexOf('/') !== -1) lanIp = lanIp.split('/')[0];
|
||||
if (!lanIp) lanIp = window.location.hostname || '192.168.1.1';
|
||||
|
||||
var panelRunning = (panelSvc['openbox-panel'] && panelSvc['openbox-panel']['instances'] && Object.keys(panelSvc['openbox-panel']['instances']).length > 0);
|
||||
var coreRunning = (openboxSvc['openbox'] && openboxSvc['openbox']['instances'] && Object.keys(openboxSvc['openbox']['instances']).length > 0);
|
||||
|
||||
var panelUrl = 'http://' + lanIp + ':' + port;
|
||||
|
||||
var viewRoot = E('div', { 'class': 'cbi-map' }, [
|
||||
E('h2', {}, [ _('Open-Box') + ' ' + (meta && meta.version ? 'v' + meta.version : '') ]),
|
||||
E('div', { 'class': 'cbi-map-descr' }, [
|
||||
_('Open-Box 是路由器上的一体化透明代理方案,内置 sing-box 内核、Node.js 运行时与 Web 管理面板。本页面为 LuCI 兜底管理页,可用于启停服务、修改端口、找回密码或执行维护操作。')
|
||||
])
|
||||
]);
|
||||
|
||||
// 1. 面板直达与密码卡片
|
||||
var accessCard = E('div', { 'class': 'cbi-section' }, [
|
||||
E('h3', {}, [ _('Open-Box 管理面板') ]),
|
||||
E('div', { 'class': 'cbi-section-descr' }, [
|
||||
_('日常代理节点选择、分流规则配置、订阅管理及流量统计请在独立 Web 面板中进行。')
|
||||
]),
|
||||
E('div', { 'class': 'table' }, [
|
||||
E('div', { 'class': 'tr' }, [
|
||||
E('div', { 'class': 'td left', 'style': 'width:30%' }, [ E('strong', {}, _('面板访问地址')) ]),
|
||||
E('div', { 'class': 'td' }, [
|
||||
E('a', { 'href': panelUrl, 'target': '_blank', 'class': 'cbi-button cbi-button-action' }, [
|
||||
_('打开管理面板') + ' (' + panelUrl + ')'
|
||||
])
|
||||
])
|
||||
]),
|
||||
E('div', { 'class': 'tr' }, [
|
||||
E('div', { 'class': 'td left' }, [ E('strong', {}, _('面板当前密码')) ]),
|
||||
E('div', { 'class': 'td' }, [
|
||||
E('span', { 'style': 'font-family: monospace; font-size: 1.1em; color: #2c3e50; font-weight: bold;' }, [
|
||||
password || _('尚未设置(首次打开面板设置管理密码)')
|
||||
]),
|
||||
password ? E('button', {
|
||||
'class': 'cbi-button cbi-button-neutral',
|
||||
'style': 'margin-left: 1em;',
|
||||
'click': function() {
|
||||
navigator.clipboard.writeText(password).then(function() {
|
||||
ui.addNotification(null, E('p', {}, _('密码已复制到剪贴板。')), 'info');
|
||||
});
|
||||
}
|
||||
}, [ _('复制密码') ]) : E('span')
|
||||
])
|
||||
]),
|
||||
E('div', { 'class': 'tr' }, [
|
||||
E('div', { 'class': 'td left' }, [ E('strong', {}, _('面板监听端口')) ]),
|
||||
E('div', { 'class': 'td' }, [
|
||||
E('span', { 'style': 'font-family: monospace; margin-right: 1em;' }, [ port ]),
|
||||
E('button', {
|
||||
'class': 'cbi-button cbi-button-apply',
|
||||
'click': ui.createHandlerFn(this, 'handlePortChange', port)
|
||||
}, [ _('修改端口') ])
|
||||
])
|
||||
])
|
||||
])
|
||||
]);
|
||||
viewRoot.appendChild(accessCard);
|
||||
|
||||
// 2. 服务运行状态卡片
|
||||
var statusCard = E('div', { 'class': 'cbi-section' }, [
|
||||
E('h3', {}, [ _('服务运行状态') ]),
|
||||
E('div', { 'class': 'table' }, [
|
||||
E('div', { 'class': 'tr' }, [
|
||||
E('div', { 'class': 'td left', 'style': 'width:30%' }, [ E('strong', {}, _('面板服务 (openbox-panel)')) ]),
|
||||
E('div', { 'class': 'td' }, [
|
||||
E('span', { 'class': 'badge ' + (panelRunning ? 'badge-success' : 'badge-danger') }, [
|
||||
panelRunning ? _('运行中 (Running)') : _('已停止 (Stopped)')
|
||||
]),
|
||||
E('div', { 'class': 'btn-group', 'style': 'display: inline-block; margin-left: 1.5em;' }, [
|
||||
E('button', {
|
||||
'class': 'cbi-button ' + (panelRunning ? 'cbi-button-reset' : 'cbi-button-action'),
|
||||
'click': ui.createHandlerFn(this, 'handleServiceAction', 'openbox-panel', panelRunning ? 'stop' : 'start')
|
||||
}, [ panelRunning ? _('停止面板') : _('启动面板') ]),
|
||||
E('button', {
|
||||
'class': 'cbi-button cbi-button-neutral',
|
||||
'style': 'margin-left: 0.5em;',
|
||||
'click': ui.createHandlerFn(this, 'handleServiceAction', 'openbox-panel', 'restart')
|
||||
}, [ _('重启面板') ])
|
||||
])
|
||||
])
|
||||
]),
|
||||
E('div', { 'class': 'tr' }, [
|
||||
E('div', { 'class': 'td left' }, [ E('strong', {}, _('内核代理服务 (openbox)')) ]),
|
||||
E('div', { 'class': 'td' }, [
|
||||
E('span', { 'class': 'badge ' + (coreRunning ? 'badge-success' : 'badge-danger') }, [
|
||||
coreRunning ? _('运行中 (Running)') : _('已停止 (Stopped)')
|
||||
]),
|
||||
E('div', { 'class': 'btn-group', 'style': 'display: inline-block; margin-left: 1.5em;' }, [
|
||||
E('button', {
|
||||
'class': 'cbi-button ' + (coreRunning ? 'cbi-button-reset' : 'cbi-button-action'),
|
||||
'click': ui.createHandlerFn(this, 'handleServiceAction', 'openbox', coreRunning ? 'stop' : 'start')
|
||||
}, [ coreRunning ? _('紧急停止内核 (恢复直连)') : _('启动内核') ]),
|
||||
E('button', {
|
||||
'class': 'cbi-button cbi-button-neutral',
|
||||
'style': 'margin-left: 0.5em;',
|
||||
'click': ui.createHandlerFn(this, 'handleServiceAction', 'openbox', 'restart')
|
||||
}, [ _('按配置重载/重启') ])
|
||||
])
|
||||
])
|
||||
])
|
||||
])
|
||||
]);
|
||||
viewRoot.appendChild(statusCard);
|
||||
|
||||
// 3. 核心组件与更新维护卡片
|
||||
var hasCore = (meta !== null);
|
||||
var maintenanceCard = E('div', { 'class': 'cbi-section' }, [
|
||||
E('h3', {}, [ _('组件与更新维护') ]),
|
||||
E('div', { 'class': 'cbi-section-descr' }, [
|
||||
hasCore ? _('当前已安装 Open-Box 核心组件(安装目录:/opt/open-box)。') :
|
||||
_('检测到核心组件尚未安装或 /opt/open-box 不完整。您可以直接使用下方一键下载安装功能完成初始化。')
|
||||
]),
|
||||
E('div', { 'class': 'table' }, [
|
||||
E('div', { 'class': 'tr' }, [
|
||||
E('div', { 'class': 'td left', 'style': 'width:30%' }, [ E('strong', {}, _('核心组件状态')) ]),
|
||||
E('div', { 'class': 'td' }, [
|
||||
hasCore ? E('span', { 'class': 'badge badge-success' }, [
|
||||
_('已就绪') + ' (' + (meta.arch || 'unknown') + ', ' + (meta.version || 'v' + meta.version) + ')'
|
||||
]) : E('span', { 'class': 'badge badge-danger' }, [ _('尚未就绪') ]),
|
||||
!hasCore ? E('button', {
|
||||
'class': 'cbi-button cbi-button-action',
|
||||
'style': 'margin-left: 1em;',
|
||||
'click': ui.createHandlerFn(this, 'handleInstallCore')
|
||||
}, [ _('一键安装核心组件') ]) : E('span')
|
||||
])
|
||||
]),
|
||||
E('div', { 'class': 'tr' }, [
|
||||
E('div', { 'class': 'td left' }, [ E('strong', {}, _('更新加速镜像')) ]),
|
||||
E('div', { 'class': 'td' }, [
|
||||
E('select', { 'id': 'openbox_mirror_select', 'class': 'cbi-input-select' }, [
|
||||
E('option', { 'value': 'direct' }, _('GitHub 直连 (官方)')),
|
||||
E('option', { 'value': 'https://ghfast.top', 'selected': 'selected' }, 'ghfast.top (' + _('内置推荐') + ')'),
|
||||
E('option', { 'value': 'https://gh-proxy.com' }, 'gh-proxy.com'),
|
||||
E('option', { 'value': 'https://gh.llkk.cc' }, 'gh.llkk.cc')
|
||||
]),
|
||||
E('button', {
|
||||
'class': 'cbi-button cbi-button-neutral',
|
||||
'style': 'margin-left: 1em;',
|
||||
'click': ui.createHandlerFn(this, 'handleTestLatency')
|
||||
}, [ _('测速/检测可用性') ]),
|
||||
E('span', { 'id': 'openbox_probe_result', 'style': 'margin-left: 1em; font-weight: bold;' }, '')
|
||||
])
|
||||
]),
|
||||
E('div', { 'class': 'tr' }, [
|
||||
E('div', { 'class': 'td left' }, [ E('strong', {}, _('系统升级与维护')) ]),
|
||||
E('div', { 'class': 'td' }, [
|
||||
E('button', {
|
||||
'class': 'cbi-button cbi-button-apply',
|
||||
'click': ui.createHandlerFn(this, 'handleUpdate', false)
|
||||
}, [ _('检查并升级到最新版') ]),
|
||||
E('button', {
|
||||
'class': 'cbi-button cbi-button-neutral',
|
||||
'style': 'margin-left: 0.5em;',
|
||||
'click': ui.createHandlerFn(this, 'handleUpdate', true)
|
||||
}, [ _('回退到上个版本 (Rollback)') ]),
|
||||
E('button', {
|
||||
'class': 'cbi-button cbi-button-reset',
|
||||
'style': 'margin-left: 1.5em;',
|
||||
'click': ui.createHandlerFn(this, 'handleUninstall')
|
||||
}, [ _('卸载 Open-Box') ])
|
||||
])
|
||||
])
|
||||
])
|
||||
]);
|
||||
viewRoot.appendChild(maintenanceCard);
|
||||
|
||||
return viewRoot;
|
||||
},
|
||||
|
||||
handleServiceAction: function(service, action) {
|
||||
ui.showModal(_('正在处理'), [
|
||||
E('p', { 'class': 'spinning' }, _('正在执行操作,请稍候...'))
|
||||
]);
|
||||
return fs.exec('/etc/init.d/' + service, [ action ]).then(function() {
|
||||
window.location.reload();
|
||||
}).catch(function(e) {
|
||||
ui.addNotification(null, E('p', {}, _('操作失败: ') + e.message), 'error');
|
||||
ui.hideModal();
|
||||
});
|
||||
},
|
||||
|
||||
handlePortChange: function(currentPort) {
|
||||
var input = E('input', {
|
||||
'type': 'number',
|
||||
'class': 'cbi-input-text',
|
||||
'min': 1024,
|
||||
'max': 65535,
|
||||
'value': currentPort
|
||||
});
|
||||
ui.showModal(_('修改面板端口'), [
|
||||
E('p', {}, _('请输入 1024 至 65535 之间的可用端口号(修改后将自动重启面板并放行防火墙):')),
|
||||
input,
|
||||
E('div', { 'class': 'right', 'style': 'margin-top: 1em;' }, [
|
||||
E('button', {
|
||||
'class': 'cbi-button cbi-button-neutral',
|
||||
'click': ui.hideModal
|
||||
}, [ _('取消') ]),
|
||||
E('button', {
|
||||
'class': 'cbi-button cbi-button-apply',
|
||||
'style': 'margin-left: 0.5em;',
|
||||
'click': function() {
|
||||
var newPort = input.value.trim();
|
||||
if (!newPort || isNaN(newPort) || newPort < 1024 || newPort > 65535) {
|
||||
ui.addNotification(null, E('p', {}, _('请输入合法的端口号 (1024-65535)')), 'error');
|
||||
return;
|
||||
}
|
||||
ui.showModal(_('保存中'), [ E('p', { 'class': 'spinning' }, _('正在应用新端口并重启面板...')) ]);
|
||||
fs.exec_direct('/usr/bin/open-box', [ 'port', newPort ]).then(function() {
|
||||
window.location.reload();
|
||||
}).catch(function(e) {
|
||||
ui.addNotification(null, E('p', {}, _('修改失败: ') + e.message), 'error');
|
||||
ui.hideModal();
|
||||
});
|
||||
}
|
||||
}, [ _('保存并重启') ])
|
||||
])
|
||||
]);
|
||||
},
|
||||
|
||||
handleTestLatency: function() {
|
||||
var select = document.getElementById('openbox_mirror_select');
|
||||
var mirror = select ? select.value : 'direct';
|
||||
var resultSpan = document.getElementById('openbox_probe_result');
|
||||
if (resultSpan) {
|
||||
resultSpan.textContent = _('正在检测...');
|
||||
resultSpan.style.color = '#3498db';
|
||||
}
|
||||
|
||||
var args = [ '--probe', mirror ];
|
||||
var execPath = '/opt/open-box/update.sh';
|
||||
fs.stat(execPath).catch(function() {
|
||||
return fs.stat('/usr/share/openbox/update.sh').then(function() {
|
||||
execPath = '/usr/share/openbox/update.sh';
|
||||
});
|
||||
}).then(function() {
|
||||
return fs.exec_direct(execPath, args);
|
||||
}).then(function(out) {
|
||||
if (resultSpan) {
|
||||
var text = (out || '').trim();
|
||||
if (text.startsWith('ok')) {
|
||||
resultSpan.textContent = '✓ ' + text.replace('ok', _('可用') + ': ');
|
||||
resultSpan.style.color = '#27ae60';
|
||||
} else {
|
||||
resultSpan.textContent = '✗ ' + text.replace('fail', _('失败') + ': ');
|
||||
resultSpan.style.color = '#e74c3c';
|
||||
}
|
||||
}
|
||||
}).catch(function(err) {
|
||||
if (resultSpan) {
|
||||
resultSpan.textContent = '✗ ' + (err.message || _('检测出错'));
|
||||
resultSpan.style.color = '#e74c3c';
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
handleInstallCore: function() {
|
||||
var select = document.getElementById('openbox_mirror_select');
|
||||
var mirror = select ? select.value : 'https://ghfast.top';
|
||||
var args = [];
|
||||
if (mirror !== 'direct') {
|
||||
args = [ '--mirror', mirror ];
|
||||
}
|
||||
|
||||
ui.showModal(_('安装核心组件'), [
|
||||
E('p', { 'class': 'spinning' }, _('正在下载并部署 Open-Box 完整核心组件(约需 1-3 分钟,视网络速度而定)...'))
|
||||
]);
|
||||
|
||||
var script = '/usr/share/openbox/install.sh';
|
||||
fs.exec(script, args).then(function() {
|
||||
ui.addNotification(null, E('p', {}, _('核心组件安装成功!')), 'info');
|
||||
window.location.reload();
|
||||
}).catch(function(e) {
|
||||
ui.addNotification(null, E('p', {}, _('安装失败: ') + e.message), 'error');
|
||||
ui.hideModal();
|
||||
});
|
||||
},
|
||||
|
||||
handleUpdate: function(isRollback) {
|
||||
var select = document.getElementById('openbox_mirror_select');
|
||||
var mirror = select ? select.value : 'direct';
|
||||
var args = [ '--detach' ];
|
||||
if (isRollback) args.push('--rollback');
|
||||
if (mirror === 'direct') {
|
||||
args.push('--direct');
|
||||
} else {
|
||||
args.push('--mirror');
|
||||
args.push(mirror);
|
||||
}
|
||||
|
||||
var execPath = '/opt/open-box/update.sh';
|
||||
fs.stat(execPath).catch(function() {
|
||||
execPath = '/usr/share/openbox/update.sh';
|
||||
}).then(function() {
|
||||
return fs.exec_direct(execPath, args);
|
||||
}).then(function() {
|
||||
var progressBar = E('div', { 'class': 'cbi-progressbar', 'title': '0%' }, [ E('div', { 'style': 'width: 0%' }) ]);
|
||||
var statusText = E('p', {}, _('任务已派发,正在准备下载...'));
|
||||
var logPre = E('pre', { 'style': 'max-height: 200px; overflow-y: scroll; font-size: 11px; background: #f8f9fa; padding: 8px;' }, '');
|
||||
|
||||
ui.showModal(isRollback ? _('正在执行版本回退') : _('正在升级 Open-Box'), [
|
||||
statusText,
|
||||
progressBar,
|
||||
E('p', { 'style': 'margin-top: 1em;' }, [ E('strong', {}, _('实时日志:')) ]),
|
||||
logPre,
|
||||
E('div', { 'class': 'right', 'style': 'margin-top: 1em;' }, [
|
||||
E('button', {
|
||||
'class': 'cbi-button cbi-button-reset',
|
||||
'click': function() {
|
||||
fs.exec_direct(execPath, [ '--cancel' ]);
|
||||
ui.hideModal();
|
||||
}
|
||||
}, [ _('取消更新') ])
|
||||
])
|
||||
]);
|
||||
|
||||
var timer = window.setInterval(function() {
|
||||
Promise.all([
|
||||
fs.read('/tmp/openbox-update.status').catch(function() { return ''; }),
|
||||
fs.read('/tmp/openbox-update.log').catch(function() { return ''; })
|
||||
]).then(function(res) {
|
||||
var st = res[0] || '';
|
||||
var lg = res[1] || '';
|
||||
logPre.textContent = lg;
|
||||
logPre.scrollTop = logPre.scrollHeight;
|
||||
|
||||
var stage = (st.match(/stage=([^\n]+)/) || [])[1];
|
||||
var bytes = parseInt((st.match(/bytes=([0-9]+)/) || [])[1] || 0);
|
||||
var total = parseInt((st.match(/total=([0-9]+)/) || [])[1] || 0);
|
||||
var msg = (st.match(/message=([^\n]*)/) || [])[1] || '';
|
||||
|
||||
if (stage === 'downloading') {
|
||||
if (total > 0) {
|
||||
var pct = Math.floor((bytes / total) * 100);
|
||||
progressBar.firstElementChild.style.width = pct + '%';
|
||||
progressBar.setAttribute('title', pct + '%');
|
||||
statusText.textContent = _('正在下载发布包: ') + (bytes / 1048576).toFixed(1) + 'MB / ' + (total / 1048576).toFixed(1) + 'MB (' + pct + '%)';
|
||||
} else {
|
||||
statusText.textContent = _('正在下载发布包: ') + (bytes / 1048576).toFixed(1) + 'MB';
|
||||
}
|
||||
} else if (stage === 'extracting') {
|
||||
progressBar.firstElementChild.style.width = '80%';
|
||||
statusText.textContent = _('正在解包并校验文件完整性...');
|
||||
} else if (stage === 'committing') {
|
||||
progressBar.firstElementChild.style.width = '90%';
|
||||
statusText.textContent = _('正在替换旧组件并重启服务...');
|
||||
} else if (stage === 'done') {
|
||||
window.clearInterval(timer);
|
||||
progressBar.firstElementChild.style.width = '100%';
|
||||
statusText.textContent = _('操作完成!') + (msg ? ' (' + msg + ')' : '');
|
||||
window.setTimeout(function() { window.location.reload(); }, 2000);
|
||||
} else if (stage === 'failed' || stage === 'cancelled') {
|
||||
window.clearInterval(timer);
|
||||
statusText.textContent = (stage === 'failed' ? _('操作失败: ') : _('操作已取消: ')) + msg;
|
||||
}
|
||||
});
|
||||
}, 1000);
|
||||
}).catch(function(e) {
|
||||
ui.addNotification(null, E('p', {}, _('启动升级失败: ') + e.message), 'error');
|
||||
});
|
||||
},
|
||||
|
||||
handleUninstall: function() {
|
||||
var checkPurge = E('input', { 'type': 'checkbox' });
|
||||
ui.showModal(_('卸载 Open-Box'), [
|
||||
E('p', {}, _('确定要从系统中卸载 Open-Box 吗?')),
|
||||
E('label', { 'class': 'cbi-checkbox', 'style': 'display: block; margin: 1em 0;' }, [
|
||||
checkPurge,
|
||||
' ' + _('同时删除所有用户数据(/opt/open-box/data 下的节点订阅、规则集与数据库)')
|
||||
]),
|
||||
E('div', { 'class': 'right' }, [
|
||||
E('button', {
|
||||
'class': 'cbi-button cbi-button-neutral',
|
||||
'click': ui.hideModal
|
||||
}, [ _('取消') ]),
|
||||
E('button', {
|
||||
'class': 'cbi-button cbi-button-reset',
|
||||
'style': 'margin-left: 0.5em;',
|
||||
'click': function() {
|
||||
var purge = checkPurge.checked;
|
||||
var args = [ '--detach' ];
|
||||
if (purge) args.push('--purge');
|
||||
|
||||
var execPath = '/opt/open-box/uninstall.sh';
|
||||
fs.stat(execPath).catch(function() {
|
||||
execPath = '/usr/share/openbox/uninstall.sh';
|
||||
}).then(function() {
|
||||
return fs.exec_direct(execPath, args);
|
||||
}).then(function() {
|
||||
ui.showModal(_('卸载中'), [
|
||||
E('p', { 'class': 'spinning' }, _('正在停止服务并清理残留文件...'))
|
||||
]);
|
||||
window.setTimeout(function() {
|
||||
window.location.href = window.location.pathname.replace(/\/admin\/services\/openbox.*/, '/admin/status/overview');
|
||||
}, 4000);
|
||||
}).catch(function(err) {
|
||||
ui.addNotification(null, E('p', {}, _('卸载失败: ') + err.message), 'error');
|
||||
ui.hideModal();
|
||||
});
|
||||
}
|
||||
}, [ _('确认卸载') ])
|
||||
])
|
||||
]);
|
||||
}
|
||||
});
|
||||
@@ -1 +0,0 @@
|
||||
/Screenshots
|
||||
@@ -1,16 +0,0 @@
|
||||
#
|
||||
# Copyright (C) 2008-2019 GaryPang
|
||||
#
|
||||
# This is free software, licensed under the Apache License, Version 2.0 .
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
LUCI_TITLE:=Edge Theme
|
||||
LUCI_DEPENDS:=+curl
|
||||
|
||||
PKG_LICENSE:=Apache-2.0
|
||||
|
||||
include $(TOPDIR)/feeds/luci/luci.mk
|
||||
|
||||
# call BuildPackage - OpenWrt buildroot signature
|
||||
@@ -1,36 +0,0 @@
|
||||
# luci-theme-edge
|
||||
|
||||
[1]: https://img.shields.io/badge/license-MIT-brightgreen.svg
|
||||
[2]: /LICENSE
|
||||
[3]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg
|
||||
[4]: https://github.com/https://github.com/kiddin9/luci-theme-edge/pulls
|
||||
[5]: https://img.shields.io/badge/Issues-welcome-brightgreen.svg
|
||||
[6]: https://github.com/https://github.com/kiddin9/luci-theme-edge/issues/new
|
||||
[7]: https://img.shields.io/badge/release-v2.1-blue.svg?
|
||||
[10]: https://img.shields.io/badge/Contact-telegram-blue
|
||||
[11]: https://t.me/openwrt_nginx
|
||||
[![license][1]][2]
|
||||
[![PRs Welcome][3]][4]
|
||||
[![Issue Welcome][5]][6]
|
||||
[![Contact Me][10]][11]
|
||||
|
||||
<a href="https://t.me/opwrts" target="_blank">Telegram</a>
|
||||
|
||||
A new Luci theme for LEDE/OpenWRT
|
||||
Edge is a clean HTML5 theme for LuCI. It is based on luci-theme-argon Template
|
||||
|
||||
Master branch adapt to official mainline snapshot.
|
||||
Luci 18.06: https://github.com/kiddin9/luci-theme-edge/tree/18.06
|
||||
|
||||
## Thanks to
|
||||
luci-theme-argon: https://github.com/jerrykuku/luci-theme-argon
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
<br/>
|
||||
<br />
|
||||
<br />
|
||||
<img src="/Screenshots/6.png" width="550" />
|
||||
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 98 KiB |
@@ -1,46 +0,0 @@
|
||||
:root {
|
||||
--main-color: #09c;
|
||||
--header-bg: #09c;
|
||||
--header-color: #fff;
|
||||
--bar-bg: #5e72e4;
|
||||
--menu-bg-color: #fff;
|
||||
--menu-color: #5f6368;
|
||||
--menu-color-hover: #202124;
|
||||
--main-menu-color: #202124;
|
||||
--submenu-bg-hover: #d4d4d4;
|
||||
--submenu-bg-hover-active: #09c;
|
||||
|
||||
--blue: #5e72e4;
|
||||
--indigo: #5603ad;
|
||||
--purple: #8965e0;
|
||||
--pink: #f3a4b5;
|
||||
--red: #f5365c;
|
||||
--orange: #fb6340;
|
||||
--yellow: #ffd600;
|
||||
--green: #2dce89;
|
||||
--teal: #11cdef;
|
||||
--cyan: #2bffc6;
|
||||
--white: #fff;
|
||||
--gray: #8898aa;
|
||||
--gray-dark: #32325d;
|
||||
--light: #96999c;
|
||||
--lighter: #e9ecef;
|
||||
--primary: #5e72e4;
|
||||
--secondary: #f7fafc;
|
||||
--success: #2dce89;
|
||||
--info: #11cdef;
|
||||
--warning: #fb6340;
|
||||
--danger: #f5365c;
|
||||
--dark: #212529;
|
||||
--default: #172b4d;
|
||||
--white: #fff;
|
||||
--neutral: #fff;
|
||||
--darker: black;
|
||||
--breakpoint-xs: 0;
|
||||
--breakpoint-sm: 576px;
|
||||
--breakpoint-md: 768px;
|
||||
--breakpoint-lg: 992px;
|
||||
--breakpoint-xl: 1200px;
|
||||
|
||||
--font-body: "Microsoft Yahei", "WenQuanYi Micro Hei", "sans-serif", "Helvetica Neue", "Helvetica", "Hiragino Sans GB";
|
||||
}
|
||||
|
Before Width: | Height: | Size: 15 KiB |
@@ -1,29 +0,0 @@
|
||||
<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In -->
|
||||
<svg version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"
|
||||
x="0px" y="0px" viewBox="0 0 465.3 465.3" enable-background="new 0 0 465.3 465.3"
|
||||
xml:space="preserve">
|
||||
<defs>
|
||||
</defs>
|
||||
<g>
|
||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="120.4726" y1="232.6669" x2="344.8612" y2="232.6669">
|
||||
<stop offset="0" style="stop-color:#F57E20"/>
|
||||
<stop offset="0.5018" style="stop-color:#FFFFFF;stop-opacity:0.6"/>
|
||||
<stop offset="1" style="stop-color:#F57E20"/>
|
||||
</linearGradient>
|
||||
<polygon fill="url(#SVGID_1_)" points="120.5,344.9 228,228 344.9,120.5 237.4,237.4 "/>
|
||||
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="120.4726" y1="232.6669" x2="344.8612" y2="232.6669">
|
||||
<stop offset="0" style="stop-color:#F57E20"/>
|
||||
<stop offset="0.5018" style="stop-color:#FFFFFF;stop-opacity:0.6"/>
|
||||
<stop offset="1" style="stop-color:#F57E20"/>
|
||||
</linearGradient>
|
||||
<polygon fill="url(#SVGID_2_)" points="120.5,120.5 237.4,228 344.9,344.9 228,237.4 "/>
|
||||
<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="0" y1="232.6669" x2="465.3338" y2="232.6669">
|
||||
<stop offset="0" style="stop-color:#A22890"/>
|
||||
<stop offset="0.5018" style="stop-color:#FFFFFF;stop-opacity:0.6"/>
|
||||
<stop offset="1" style="stop-color:#A22890"/>
|
||||
</linearGradient>
|
||||
<polygon fill="url(#SVGID_3_)" points="0,232.7 232.7,226 465.3,232.7 232.7,239.3 "/>
|
||||
<polygon fill="none" points="232.7,465.3 226,232.7 232.7,0 239.3,232.7 "/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.6 KiB |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="#202124" d="m8,10.033663l-6.898535,-6.013274l-1.060688,0.972974l7.959223,6.986249l7.959223,-6.986249l-1.060688,-0.972974l-6.898535,6.013274z"/></svg>
|
||||
|
Before Width: | Height: | Size: 221 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="#888" d="M8,0c-4.355,0-7.898,3.481-7.998,7.812,0.092-3.779,2.966-6.812,6.498-6.812,3.59,0,6.5,3.134,6.5,7,0,0.828,0.672,1.5,1.5,1.5s1.5-0.672,1.5-1.5c0-4.418-3.582-8-8-8zM8,16c4.355,0,7.898-3.481,7.998-7.812-0.092,3.779-2.966,6.812-6.498,6.812-3.59,0-6.5-3.134-6.5-7,0-0.828-0.672-1.5-1.5-1.5s-1.5,0.672-1.5,1.5c0,4.418,3.582,8,8,8z"/></svg>
|
||||
|
Before Width: | Height: | Size: 413 B |
@@ -1,2 +0,0 @@
|
||||
|
||||
!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.5.1",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0<t&&t-1 in e)}S.fn=S.prototype={jquery:f,constructor:S,length:0,toArray:function(){return s.call(this)},get:function(e){return null==e?s.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=S.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return S.each(this,e)},map:function(n){return this.pushStack(S.map(this,function(e,t){return n.call(e,t,e)}))},slice:function(){return this.pushStack(s.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},even:function(){return this.pushStack(S.grep(this,function(e,t){return(t+1)%2}))},odd:function(){return this.pushStack(S.grep(this,function(e,t){return t%2}))},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(0<=n&&n<t?[this[n]]:[])},end:function(){return this.prevObject||this.constructor()},push:u,sort:t.sort,splice:t.splice},S.extend=S.fn.extend=function(){var e,t,n,r,i,o,a=arguments[0]||{},s=1,u=arguments.length,l=!1;for("boolean"==typeof a&&(l=a,a=arguments[s]||{},s++),"object"==typeof a||m(a)||(a={}),s===u&&(a=this,s--);s<u;s++)if(null!=(e=arguments[s]))for(t in e)r=e[t],"__proto__"!==t&&a!==r&&(l&&r&&(S.isPlainObject(r)||(i=Array.isArray(r)))?(n=a[t],o=i&&!Array.isArray(n)?[]:i||S.isPlainObject(n)?n:{},i=!1,a[t]=S.extend(l,o,r)):void 0!==r&&(a[t]=r));return a},S.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),isReady:!0,error:function(e){throw new Error(e)},noop:function(){},isPlainObject:function(e){var t,n;return!(!e||"[object Object]"!==o.call(e))&&(!(t=r(e))||"function"==typeof(n=v.call(t,"constructor")&&t.constructor)&&a.call(n)===l)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},globalEval:function(e,t,n){b(e,{nonce:t&&t.nonce},n)},each:function(e,t){var n,r=0;if(p(e)){for(n=e.length;r<n;r++)if(!1===t.call(e[r],r,e[r]))break}else for(r in e)if(!1===t.call(e[r],r,e[r]))break;return e},makeArray:function(e,t){var n=t||[];return null!=e&&(p(Object(e))?S.merge(n,"string"==typeof e?[e]:e):u.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:i.call(t,e,n)},merge:function(e,t){for(var n=+t.length,r=0,i=e.length;r<n;r++)e[i++]=t[r];return e.length=i,e},grep:function(e,t,n){for(var r=[],i=0,o=e.length,a=!n;i<o;i++)!t(e[i],i)!==a&&r.push(e[i]);return r},map:function(e,t,n){var r,i,o=0,a=[];if(p(e))for(r=e.length;o<r;o++)null!=(i=t(e[o],o,n))&&a.push(i);else for(o in e)null!=(i=t(e[o],o,n))&&a.push(i);return g(a)},guid:1,support:y}),"function"==typeof Symbol&&(S.fn[Symbol.iterator]=t[Symbol.iterator]),S.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(e,t){n["[object "+t+"]"]=t.toLowerCase()});var d=function(n){var e,d,b,o,i,h,f,g,w,u,l,T,C,a,E,v,s,c,y,S="sizzle"+1*new Date,p=n.document,k=0,r=0,m=ue(),x=ue(),A=ue(),N=ue(),D=function(e,t){return e===t&&(l=!0),0},j={}.hasOwnProperty,t=[],q=t.pop,L=t.push,H=t.push,O=t.slice,P=function(e,t){for(var n=0,r=e.length;n<r;n++)if(e[n]===t)return n;return-1},R="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",I="(?:\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+",W="\\["+M+"*("+I+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+I+"))|)"+M+"*\\]",F=":("+I+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+W+")*)|.*)\\)|)",B=new RegExp(M+"+","g"),$=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),_=new RegExp("^"+M+"*,"+M+"*"),z=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-lLine truncated
|
||||
@@ -1,217 +0,0 @@
|
||||
/**
|
||||
* Edge is a clean HTML5 theme for LuCI. It is based on luci-theme-Argon
|
||||
*
|
||||
* luci-theme-edge
|
||||
* Copyright 2020 Kiddin'
|
||||
*
|
||||
* Have a bug? Please create an issue here on GitHub!
|
||||
* https://github.com/kiddin9/luci-theme-edge/issues
|
||||
*
|
||||
* luci-theme-material:
|
||||
* Copyright 2015 Lutty Yang <lutty@wcan.in>
|
||||
* https://github.com/LuttyYang/luci-theme-material/
|
||||
*
|
||||
* Agron Theme
|
||||
* https://demos.creative-tim.com/argon-dashboard/index.html
|
||||
*
|
||||
* Login background
|
||||
* https://unsplash.com/
|
||||
* Font generate by Icomoon<icomoon.io>
|
||||
*
|
||||
* Licensed to the public under the Apache License 2.0
|
||||
*/
|
||||
|
||||
document.addEventListener('luci-loaded', function(ev) {
|
||||
(function ($) {
|
||||
$(".main > .loading").fadeOut();
|
||||
|
||||
/**
|
||||
* trim text, Remove spaces, wrap
|
||||
* @param text
|
||||
* @returns {string}
|
||||
*/
|
||||
function trimText(text) {
|
||||
return text.replace(/[ \t\n\r]+/g, " ");
|
||||
}
|
||||
|
||||
var lastNode = undefined;
|
||||
var mainNodeName = undefined;
|
||||
|
||||
var nodeUrl = "";
|
||||
(function(node){
|
||||
var luciLocation;
|
||||
if (node[0] == "admin"){
|
||||
luciLocation = [node[1], node[2]];
|
||||
}else{
|
||||
luciLocation = node;
|
||||
}
|
||||
|
||||
for(var i in luciLocation){
|
||||
nodeUrl += luciLocation[i];
|
||||
if (i != luciLocation.length - 1){
|
||||
nodeUrl += "/";
|
||||
}
|
||||
}
|
||||
})(luciLocation);
|
||||
|
||||
/**
|
||||
* get the current node by Burl (primary)
|
||||
* @returns {boolean} success?
|
||||
*/
|
||||
function getCurrentNodeByUrl() {
|
||||
var ret = false;
|
||||
if (!$('body').hasClass('logged-in')) {
|
||||
luciLocation = ["Main", "Login"];
|
||||
return true;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* get current node and open it
|
||||
*/
|
||||
if (getCurrentNodeByUrl()) {
|
||||
mainNodeName = "node-" + luciLocation[0] + "-" + luciLocation[1];
|
||||
mainNodeName = mainNodeName.replace(/[ \t\n\r\/]+/g, "_").toLowerCase();
|
||||
$("body").addClass(mainNodeName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sidebar expand
|
||||
*/
|
||||
var showSide = false;
|
||||
$(".showSide").click(function () {
|
||||
if (showSide) {
|
||||
$(".darkMask").stop(true).fadeOut("fast");
|
||||
$(".main-left").stop(true).animate({
|
||||
width: "0"
|
||||
}, "200");
|
||||
$(".main-right").css("overflow-y", "visible");
|
||||
showSide = false;
|
||||
} else {
|
||||
$(".darkMask").stop(true).fadeIn("fast");
|
||||
$(".main-left").stop(true).animate({
|
||||
width: "13rem"
|
||||
}, "200");
|
||||
$(".main-right").css("overflow-y", "hidden");
|
||||
showSide = true;
|
||||
}
|
||||
});
|
||||
|
||||
$(".darkMask").click(function () {
|
||||
if (showSide) {
|
||||
showSide = false;
|
||||
$(".darkMask").stop(true).fadeOut("fast");
|
||||
$(".main-left").stop(true).animate({
|
||||
width: "0"
|
||||
}, "fast");
|
||||
$(".main-right").css("overflow-y", "visible");
|
||||
}
|
||||
});
|
||||
|
||||
$(window).resize(function () {
|
||||
if ($(window).width() > 921) {
|
||||
$(".main-left").css("width", "");
|
||||
$(".darkMask").stop(true);
|
||||
$(".darkMask").css("display", "none");
|
||||
showSide = false;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* fix legend position
|
||||
*/
|
||||
$("legend").each(function () {
|
||||
var that = $(this);
|
||||
that.after("<span class='panel-title'>" + that.text() + "</span>");
|
||||
});
|
||||
|
||||
$(".cbi-section-table-titles, .cbi-section-table-descr, .cbi-section-descr").each(function () {
|
||||
var that = $(this);
|
||||
if (that.text().trim() == ""){
|
||||
that.css("display", "none");
|
||||
}
|
||||
});
|
||||
|
||||
$(".main-right").focus();
|
||||
$(".main-right").blur();
|
||||
$("input").attr("size", "0");
|
||||
$(".cbi-button-up").val("__");
|
||||
$(".cbi-button-down").val("__");
|
||||
$(".slide > a").removeAttr("href");
|
||||
|
||||
if (mainNodeName != undefined) {
|
||||
console.log(mainNodeName);
|
||||
switch (mainNodeName) {
|
||||
case "node-status-system_log":
|
||||
case "node-status-kernel_log":
|
||||
$("#syslog").focus(function () {
|
||||
$("#syslog").blur();
|
||||
$(".main-right").focus();
|
||||
$(".main-right").blur();
|
||||
});
|
||||
break;
|
||||
case "node-status-firewall":
|
||||
var button = $(".node-status-firewall > .main fieldset li > a");
|
||||
button.addClass("cbi-button cbi-button-reset a-to-btn");
|
||||
break;
|
||||
case "node-system-reboot":
|
||||
var button = $(".node-system-reboot > .main > .main-right p > a");
|
||||
button.addClass("cbi-button cbi-input-reset a-to-btn");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var getaudio = $('#player')[0];
|
||||
/* Get the audio from the player (using the player's ID), the [0] is necessary */
|
||||
var audiostatus = 'off';
|
||||
/* Global variable for the audio's status (off or on). It's a bit crude but it works for determining the status. */
|
||||
|
||||
|
||||
$(document).on('click touchend', '.speaker', function() {
|
||||
/* Touchend is necessary for mobile devices, click alone won't work */
|
||||
if (!$('.speaker').hasClass("speakerplay")) {
|
||||
if (audiostatus == 'off') {
|
||||
$('.speaker').addClass('speakerplay');
|
||||
getaudio.load();
|
||||
getaudio.play();
|
||||
audiostatus = 'on';
|
||||
return false;
|
||||
} else if (audiostatus == 'on') {
|
||||
$('.speaker').addClass('speakerplay');
|
||||
getaudio.play()
|
||||
}
|
||||
} else if ($('.speaker').hasClass("speakerplay")) {
|
||||
getaudio.pause();
|
||||
$('.speaker').removeClass('speakerplay');
|
||||
audiostatus = 'on';
|
||||
}
|
||||
});
|
||||
|
||||
$('#player').on('ended', function() {
|
||||
$('.speaker').removeClass('speakerplay');
|
||||
/*When the audio has finished playing, remove the class speakerplay*/
|
||||
audiostatus = 'off';
|
||||
/*Set the status back to off*/
|
||||
});
|
||||
setTimeout(function(){
|
||||
$("input[type='checkbox']").filter(function () {
|
||||
return (!$(this).next("label").length)
|
||||
}).show();
|
||||
}, 0);
|
||||
|
||||
var options = { attributes: true};
|
||||
function callback() {
|
||||
$("input[type='checkbox']").filter(function () {
|
||||
return (!$(this).next("label").length)
|
||||
}).show();
|
||||
}
|
||||
var mutationObserver = new MutationObserver(callback);
|
||||
mutationObserver.observe($("body")[0], options);
|
||||
$(".cbi-value").has("textarea").css("background","none");
|
||||
if(document.body.scrollHeight > window.innerHeight){
|
||||
$(".cbi-page-actions.control-group").addClass("fixed")
|
||||
}
|
||||
})(jQuery);
|
||||
});
|
||||
@@ -1,76 +0,0 @@
|
||||
|
||||
#CrossFade {
|
||||
background: #fff;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
#CrossFade img {
|
||||
position: absolute;
|
||||
min-width: 100%;
|
||||
min-height: 100%;
|
||||
width:100%;
|
||||
background: #fff;
|
||||
-webkit-backface-visibility: hidden;
|
||||
backface-visibility: hidden;
|
||||
opacity: 0;
|
||||
-webkit-transform: scale(1.4) rotate(12deg);
|
||||
transform: scale(1.4) rotate(12deg);
|
||||
-webkit-animation: CrossFade 12s infinite;
|
||||
animation: CrossFade 12s infinite;
|
||||
}
|
||||
#CrossFade img:nth-child(3) {
|
||||
-webkit-animation-delay: 8s;
|
||||
animation-delay: 8s;
|
||||
}
|
||||
#CrossFade img:nth-child(2) {
|
||||
-webkit-animation-delay: 4s;
|
||||
animation-delay: 4s;
|
||||
}
|
||||
#CrossFade img:nth-child(1) {
|
||||
-webkit-animation-delay: 0s;
|
||||
animation-delay: 0s;
|
||||
}
|
||||
|
||||
@-webkit-keyframes CrossFade {
|
||||
33.3% {
|
||||
opacity: 1;
|
||||
-webkit-transform: scale(1) rotate(0);
|
||||
transform: scale(1) rotate(0);
|
||||
}
|
||||
45% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
@keyframes CrossFade {
|
||||
33.3% {
|
||||
opacity: 1;
|
||||
-webkit-transform: scale(1) rotate(0);
|
||||
transform: scale(1) rotate(0);
|
||||
}
|
||||
45% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.speaker {
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARAAAACACAYAAAG0HAZ0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MEFERTNEQzA3QTMxMTFFMzgzNTFFRDRCMzk1MDA0NUIiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MEFERTNEQzE3QTMxMTFFMzgzNTFFRDRCMzk1MDA0NUIiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDowQURFM0RCRTdBMzExMUUzODM1MUVENEIzOTUwMDQ1QiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDowQURFM0RCRjdBMzExMUUzODM1MUVENEIzOTUwMDQ1QiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PvEtsecAABjVSURBVHjaYvz//z/DYABMDIMEEO2QjIwMioMOnxnEOOQ/FFMSmrjMIGzYnz9/3hPQ/Dc5ORmv4USYgeqQz58/H/758+dvEIb5gIWFRQCHHmYQcfny5Z9z586FC5JoBgZgARG8vLw2JATzHxChq6vLCLKMkZERmPH+M5JoBlVyDSNK5FMp+5OdfYEhsXCwlCP5A+2Q/9Ao+QANGeo5BGjYH1lZWQYQJi5WGO8hOYiRDDOw5xqgYazolklKSv54/vw5O9Yg+f9fGZpj4ImVVDOwOgQbABrAgRwV2EKGUI4hwgyS0ggjepYlM8vjNYNxsDQDAAKIcbQ9QqZDqBFs/ylxCDsVHEGUGSxk+uA/tgqQkpAEhwisHbFnz547RDoC7ICHDx/C1ZFoBtYQ+c/ODin8Xr9+rUxkGe/w9u3b3/Ly8rDSlGQzSIka3EHy//9+aKj8H7HZd3A6BNoGoW6RDCriZWRk/oOwsLAwqCEMEsMGGJDkwHRwcDBcnEQzMDC+uuY/nkYzueUIIzlRg6/qBrXSllG1CYEvuGDYxMTkHzHqKDFj0DQDAAJo0DhktDwb5gHSDhuKoca40kC6g4UCy/9TqI+RSoHwf6BSSMp/SIHzn4EOLXo6ugNrgPzHhpcvX45s4WwyhkHEYWb9+vXrP54KnIHG7hg0hepLIOYBMdjY2BiOHj2KESinTp0aVNUcPWqZrzCGtbU1qCmFMhBrZmY24qtdz5HeDoFPGKSlpYGoGiCGzxFMmjRpxAVIEogICAhgmDVrFiO0hngPk8zPz787qEKE2M4IELj+JwKkp6fj609WoHQ6wdaT1imikjtwYiYSAm43UreRceFCsqZoOkBEdXU1qHBVhE0ikBiB1HAH5SmEQKzVUxozg8Ud4N4uLRs6xEYK0tgT1c0d7f5TAAACaDRARsdDRgNkNEAGS4CIMEDWBrSQVIhRv4Yjyx3UGDHDNdjzZCBrcErdQVEK+ffv3y9SR62am5t1qR0K5LiD2i3Vclx9CKCRGXj0YcydUdhaJtcdODELhcmSXH3/GSgfZP4/0IUqpYO6KAFw8+ZNSgKWZq1JWIBw//nz5zdsgQXaok+Gly9frqQkJs3NzR+jB4q6ujqohkFPobR0B9EBAlqZ9oUFCNjZ2VEw0ME7qJGsT548KQMTOAsEMHZXV9dvJLW0dAfxAfLmzZu3tDK8t7f3BnrgGBsbm8AESktL4ZK0dAdJAQKssmiWH4uLizXRGmHiWJRlQ6vOQdHLpHnTHVh4NiNxX4BbTECAJDZlRPVlgIVnHYydm5sLpmVkZMpGO3dAoKWlBa8wRgMECPbs2QNrahuN2AD5+vXrVRh77dq1sKZ6x4gNEG5ubi0kLnjjDTMQIIfZoAqQv3//0rK6+4+cOrDsnQABDRBBY3cQHyCSkpK8tDL84MGDcHZISAhGIMnJycHHLWjpDnIGiBiBjvuPZQSeE9qgIitr2dvbMx45cuS/ra0tA7ZZusePH2N0AGnhDpqMh/T19REzpUrseMh/AwOD/+SMh1DJHbhn/8jQRJZDHj58+A1N3XsKB4hoEiBk7YwEglRSNQGzAteFCxdgBoAsF6QwcZPlDppUu0DPzAE5SE1N7S8p+oDZhBG2x5VK2Z0sd9CsHXLr1i1Qocy4fv16UjxB9YKRHHdQXKgSm69Jzbs0cAMDxYPMVJwsAhmkSa551HYHWRpHZ/8HsLc7FABAAPauJrSJIArvtptKck5IPGxpIQiNOUTaYCGxQsGLGDFgdWmwkIsI6kHw5r1H6cWTUOJ/LhVKQE8GiRFTc9BLekkPTQMSIaGFQC1paHyz7sbpupu2mN2dzc7AstnJ7Px8882beTNvZilDqKMthjpKEOpsSpCrqVTqm+79rMlGuqThQTRB9vf3vzJ/zR1WC4UCUo/v2LUFm40HCQSZxQDoOByOsM2lOlF4mEKQdrv9EQPhg87JielAq+NJZYTBeBBJkPN4q+A47qIBaR6yygN1fqter5Oi05uBB1kEgQrJYCAUSCis2+1mJGliuLUSiXgYTpDd3d1VTKRfMbIg+Xz+rVI5yWQyQY3Kugt9+57eeTITDz0I8oDROKSC0Ti4Agr9Do+s2WzuGZz/YTk/0Wg0LlVEQv4zFoshmyVWQzs4BWF75deKeOhDkEqlgpaeH1tJC0qn07cYyS5LIR1ehsPhYx2bBmERSdJKfyvioStBnE6n32oZFwThBar4g4ODf/4rFouMz+frKMT9ZbV4ksnkTaWfFfEY5HmQ/yvA0JCqdKjVamiGdIuVpkldLtd7tXDLy8votkWpMNgiUXW3IIxLeNyIsVqtPtd4n6dUGGyCqBqVzc3NISnysMsCnn9Fq9zmgyrcbW+Lh1/hW09ctMrtSRDV4wKXlpbQbUV+rtfr92mV25Mga0qPVqvF7OzsIFW2SxC32z2rDNdoNCgLBpwgqusr0scYbmOEeaMWLhQKodsTSoUeBMlms6+tlnHoLta0yCFptisgPZ5KXtMjIyOCMlypVGKkDeX3cH8r4qErQQRBQC2N3dzcPPaLTqfT1IxDd4FWRNlcLtf1K5fLMjmuATnkDY6X4PqiFkcwKC7VjCr9rYiHIfMHY2NjLNYKeeboQ2x+4Q8bGxvPvF7vDSMLMDMzI3/CDam6Q/i+rHa7/YPjuNM9pMx1CF/VMjm0Ih66EuRQxw7AnTSySCSCFqvkCvtULBajU1NTxgxEOh18n3wErjyQQ61rYjweD/p5Dt75foL4LYUH8YNUAPQCgMH+wYb1xeNxI8v1GTvjqevGx8dFciApcxJyDAAe/ZcgfQbnJ4OtqAJAJRgvBPx+/dbEJicnxfTW19c7aJ90IiFaATyCvCyarnaZgIel1FwA6CyAIbemMwsLC7J/39MKBALs/Pw8K0mNRRJbqZF4EClBjgCnjPXT4gpsrVbjYXBnS7WSNDw4AgEaxUTv9MTEBFJRh+06D2E2HnTzNnXkjUGoowShbkDcbwHYu76QyKowfse7YzQ2mzaDmTUKhmxCFu0IkzsIWlIESvQ0EAVuT2Ia0hJELxUGvQSxKIwY5EPsgz2FOS6LD7VS40r4kD64Ni3iw66kI665Ss7fvjN/5O7suXf+3nPOvX4/uBzm3HPO3Pu7v3O+79xzzzloYhDYgiBQIAgUCAIFgkCBIFAgCBQIG8RisbDFYvHgY+LHh6gCuSqlF1ZxSCqfC1YSBljEjikfSog2WPcmHLk7s1nOcKPBnQ9RBPIEHP+ecTEIyQd3EwN29TcIDlEcYvLBUyBfZOyqV88/2d/fN8qkKCZ8GEEgXVJ6+sDnDP7rp7q6uqHZ2VmRRyRZ8lE8dNitRfWA5vNBskikL7Gk//tZWc7o6GiM8a41ovFR0sFkuD8ej9+QZfmNUrugJWxSReajvJUb6XQ6l8Lh8CWV/2BWKTnwUforAJ2J+SjThy/rHUWRhJC5AyGN8mqhvANOAuHBh5A+yMWMXb0qscffPp/ve7WT4I/c53BNPPkQyweJRCI7yQqiVJs7NTX1j0aZ91j5IKLwIYoPUvF2upAm9fj4+JbNZnuVYuuTD29tnQZZAbGhocHCwMRw4UNPH2QEyP4K+uI2LcKsVivZU5zsBrzKmxA49yukyS6Gbyn0IVVVVcUSiYQ1j0AMx4eeJqYSTV3FodWkwgO+qUw7Pz//SNpgMBiilbu4uJhUVpBy70UEPnQzMWQYOTNSWK6SmdUYiPsdzj3SZa2vrz/c2dk5X8h1Qf67UM5zuS2IEfnQtRdTLBkiAJr+S7R4EIcdSHQq4Line truncated
|
||||
background-repeat: no-repeat;
|
||||
background-size: 200%;
|
||||
background-position: 100% 0;
|
||||
height: 34px;
|
||||
width: 28px;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
display: block;
|
||||
z-index:9999;
|
||||
opacity:.5;
|
||||
}
|
||||
|
||||
.speakerplay {
|
||||
background-position: 0 0;
|
||||
}
|
||||
|
Before Width: | Height: | Size: 14 KiB |
@@ -1,14 +0,0 @@
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 111.7 111.7" enable-background="new 0 0 111.7 111.7" xml:space="preserve" id="sun">
|
||||
<g>
|
||||
<g>
|
||||
<circle opacity="5.000000e-02" fill="#F9A149" cx="55.9" cy="55.9" r="55.9"/>
|
||||
<circle opacity="0.2083" fill="#F9A854" cx="55.9" cy="55.9" r="48.7"/>
|
||||
<circle opacity="0.3667" fill="#FAAE5E" cx="55.9" cy="55.9" r="41.6"/>
|
||||
<circle opacity="0.525" fill="#FAB567" cx="55.9" cy="55.9" r="34.4"/>
|
||||
<circle opacity="0.6833" fill="#FABC71" cx="55.9" cy="55.9" r="27.3"/>
|
||||
<circle opacity="0.8417" fill="#FBC27A" cx="55.9" cy="55.9" r="20.1"/>
|
||||
<circle fill="#FBC984" cx="55.9" cy="55.9" r="13"/>
|
||||
</g>
|
||||
<circle fill="#FFFFFF" cx="55.9" cy="55.9" r="4.6"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 761 B |
@@ -1,135 +0,0 @@
|
||||
/*!
|
||||
* Waves v0.7.6
|
||||
* http://fian.my.id/Waves
|
||||
*
|
||||
* Copyright 2014-2018 Alfiana E. Sibuea and other contributors
|
||||
* Released under the MIT license
|
||||
* https://github.com/fians/Waves/blob/master/LICENSE */
|
||||
.waves-effect {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
.waves-effect .waves-ripple {
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
margin-top: -50px;
|
||||
margin-left: -50px;
|
||||
opacity: 0;
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
background: -webkit-radial-gradient(rgba(0, 0, 0, 0.2) 0, rgba(0, 0, 0, 0.3) 40%, rgba(0, 0, 0, 0.4) 50%, rgba(0, 0, 0, 0.5) 60%, rgba(255, 255, 255, 0) 70%);
|
||||
background: -o-radial-gradient(rgba(0, 0, 0, 0.2) 0, rgba(0, 0, 0, 0.3) 40%, rgba(0, 0, 0, 0.4) 50%, rgba(0, 0, 0, 0.5) 60%, rgba(255, 255, 255, 0) 70%);
|
||||
background: -moz-radial-gradient(rgba(0, 0, 0, 0.2) 0, rgba(0, 0, 0, 0.3) 40%, rgba(0, 0, 0, 0.4) 50%, rgba(0, 0, 0, 0.5) 60%, rgba(255, 255, 255, 0) 70%);
|
||||
background: radial-gradient(rgba(0, 0, 0, 0.2) 0, rgba(0, 0, 0, 0.3) 40%, rgba(0, 0, 0, 0.4) 50%, rgba(0, 0, 0, 0.5) 60%, rgba(255, 255, 255, 0) 70%);
|
||||
-webkit-transition: all 0.5s ease-out;
|
||||
-moz-transition: all 0.5s ease-out;
|
||||
-o-transition: all 0.5s ease-out;
|
||||
transition: all 0.5s ease-out;
|
||||
-webkit-transition-property: -webkit-transform, opacity;
|
||||
-moz-transition-property: -moz-transform, opacity;
|
||||
-o-transition-property: -o-transform, opacity;
|
||||
transition-property: transform, opacity;
|
||||
-webkit-transform: scale(0) translate(0, 0);
|
||||
-moz-transform: scale(0) translate(0, 0);
|
||||
-ms-transform: scale(0) translate(0, 0);
|
||||
-o-transform: scale(0) translate(0, 0);
|
||||
transform: scale(0) translate(0, 0);
|
||||
pointer-events: none;
|
||||
}
|
||||
.waves-effect.waves-light .waves-ripple {
|
||||
background: rgba(255, 255, 255, 0.4);
|
||||
background: -webkit-radial-gradient(rgba(255, 255, 255, 0.2) 0, rgba(255, 255, 255, 0.3) 40%, rgba(255, 255, 255, 0.4) 50%, rgba(255, 255, 255, 0.5) 60%, rgba(255, 255, 255, 0) 70%);
|
||||
background: -o-radial-gradient(rgba(255, 255, 255, 0.2) 0, rgba(255, 255, 255, 0.3) 40%, rgba(255, 255, 255, 0.4) 50%, rgba(255, 255, 255, 0.5) 60%, rgba(255, 255, 255, 0) 70%);
|
||||
background: -moz-radial-gradient(rgba(255, 255, 255, 0.2) 0, rgba(255, 255, 255, 0.3) 40%, rgba(255, 255, 255, 0.4) 50%, rgba(255, 255, 255, 0.5) 60%, rgba(255, 255, 255, 0) 70%);
|
||||
background: radial-gradient(rgba(255, 255, 255, 0.2) 0, rgba(255, 255, 255, 0.3) 40%, rgba(255, 255, 255, 0.4) 50%, rgba(255, 255, 255, 0.5) 60%, rgba(255, 255, 255, 0) 70%);
|
||||
}
|
||||
.waves-effect.waves-classic .waves-ripple {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
.waves-effect.waves-classic.waves-light .waves-ripple {
|
||||
background: rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
.waves-notransition {
|
||||
-webkit-transition: none !important;
|
||||
-moz-transition: none !important;
|
||||
-o-transition: none !important;
|
||||
transition: none !important;
|
||||
}
|
||||
.waves-button,
|
||||
.waves-circle {
|
||||
-webkit-transform: translateZ(0);
|
||||
-moz-transform: translateZ(0);
|
||||
-ms-transform: translateZ(0);
|
||||
-o-transform: translateZ(0);
|
||||
transform: translateZ(0);
|
||||
-webkit-mask-image: -webkit-radial-gradient(circle, white 100%, black 100%);
|
||||
}
|
||||
.waves-button,
|
||||
.waves-button:hover,
|
||||
.waves-button:visited,
|
||||
.waves-button-input {
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
outline: none;
|
||||
color: inherit;
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
font-size: 1em;
|
||||
line-height: 1em;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
z-index: 1;
|
||||
}
|
||||
.waves-button {
|
||||
padding: 0.85em 1.1em;
|
||||
border-radius: 0.2em;
|
||||
}
|
||||
.waves-button-input {
|
||||
margin: 0;
|
||||
padding: 0.85em 1.1em;
|
||||
}
|
||||
.waves-input-wrapper {
|
||||
border-radius: 0.2em;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
.waves-input-wrapper.waves-button {
|
||||
padding: 0;
|
||||
}
|
||||
.waves-input-wrapper .waves-button-input {
|
||||
position: relative;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
.waves-circle {
|
||||
text-align: center;
|
||||
width: 2.5em;
|
||||
height: 2.5em;
|
||||
line-height: 2.5em;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.waves-float {
|
||||
-webkit-mask-image: none;
|
||||
-webkit-box-shadow: 0px 1px 1.5px 1px rgba(0, 0, 0, 0.12);
|
||||
box-shadow: 0px 1px 1.5px 1px rgba(0, 0, 0, 0.12);
|
||||
-webkit-transition: all 300ms;
|
||||
-moz-transition: all 300ms;
|
||||
-o-transition: all 300ms;
|
||||
transition: all 300ms;
|
||||
}
|
||||
.waves-float:active {
|
||||
-webkit-box-shadow: 0px 8px 20px 1px rgba(0, 0, 0, 0.3);
|
||||
box-shadow: 0px 8px 20px 1px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
.waves-block {
|
||||
display: block;
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
'use strict';
|
||||
'require baseclass';
|
||||
'require ui';
|
||||
|
||||
return baseclass.extend({
|
||||
__init__: function () {
|
||||
ui.menu.load().then(L.bind(this.render, this));
|
||||
},
|
||||
|
||||
render: function (tree) {
|
||||
var node = tree,
|
||||
url = '',
|
||||
children = ui.menu.getChildren(tree);
|
||||
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
var isActive = (L.env.requestpath.length ? children[i].name == L.env.requestpath[0] : i == 0);
|
||||
|
||||
if (isActive)
|
||||
this.renderMainMenu(children[i], children[i].name);
|
||||
}
|
||||
|
||||
if (L.env.dispatchpath.length >= 3) {
|
||||
for (var i = 0; i < 3 && node; i++) {
|
||||
node = node.children[L.env.dispatchpath[i]];
|
||||
url = url + (url ? '/' : '') + L.env.dispatchpath[i];
|
||||
}
|
||||
|
||||
if (node)
|
||||
this.renderTabMenu(node, url);
|
||||
}
|
||||
|
||||
document.querySelector('a.showSide')
|
||||
.addEventListener('click', ui.createHandlerFn(this, 'handleSidebarToggle'));
|
||||
document.querySelector('.darkMask')
|
||||
.addEventListener('click', ui.createHandlerFn(this, 'handleSidebarToggle'));
|
||||
},
|
||||
|
||||
handleMenuExpand: function (ev) {
|
||||
var a = ev.target, slide = a.parentNode, slide_menu = a.nextElementSibling;
|
||||
var collapse = false;
|
||||
|
||||
document.querySelectorAll('.main .main-left .nav > li >ul.active').forEach(function (ul) {
|
||||
$(ul).stop(true).slideUp("fast", function () {
|
||||
ul.classList.remove('active');
|
||||
ul.previousElementSibling.classList.remove('active');
|
||||
});
|
||||
if (!collapse && ul === slide_menu) {
|
||||
collapse = true;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
if (!slide_menu)
|
||||
return;
|
||||
|
||||
|
||||
if (!collapse) {
|
||||
$(slide).find(".slide-menu").slideDown("fast",function(){
|
||||
slide_menu.classList.add('active');
|
||||
a.classList.add('active');
|
||||
});
|
||||
a.blur();
|
||||
}
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
},
|
||||
|
||||
renderMainMenu: function (tree, url, level) {
|
||||
var l = (level || 0) + 1,
|
||||
ul = E('ul', { 'class': level ? 'slide-menu' : 'nav' }),
|
||||
children = ui.menu.getChildren(tree);
|
||||
|
||||
if (children.length == 0 || l > 2)
|
||||
return E([]);
|
||||
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
var isActive = ((L.env.dispatchpath[l] == children[i].name) && (L.env.dispatchpath[l - 1] == tree.name)),
|
||||
submenu = this.renderMainMenu(children[i], url + '/' + children[i].name, l),
|
||||
hasChildren = submenu.children.length,
|
||||
slideClass = hasChildren ? 'slide' : null,
|
||||
menuClass = hasChildren ? 'menu' : 'food';
|
||||
if (isActive) {
|
||||
ul.classList.add('active');
|
||||
slideClass += " active";
|
||||
menuClass += " active";
|
||||
}
|
||||
|
||||
ul.appendChild(E('li', { 'class': slideClass }, [
|
||||
E('a', {
|
||||
'href': L.url(url, children[i].name),
|
||||
'click': (l == 1) ? ui.createHandlerFn(this, 'handleMenuExpand') : null,
|
||||
'class': menuClass,
|
||||
'data-title': hasChildren ? children[i].title.replace(" ", "_") : children[i].title.replace(" ", "_"),
|
||||
}, [_(children[i].title)]),
|
||||
submenu
|
||||
]));
|
||||
}
|
||||
|
||||
if (l == 1) {
|
||||
document.querySelector('#mainmenu').appendChild(ul);
|
||||
document.querySelector('#mainmenu').style.display = '';
|
||||
|
||||
}
|
||||
return ul;
|
||||
},
|
||||
|
||||
renderTabMenu: function (tree, url, level) {
|
||||
var container = document.querySelector('#tabmenu'),
|
||||
l = (level || 0) + 1,
|
||||
ul = E('ul', { 'class': 'tabs' }),
|
||||
children = ui.menu.getChildren(tree),
|
||||
activeNode = null;
|
||||
|
||||
if (children.length == 0)
|
||||
return E([]);
|
||||
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
var isActive = (L.env.dispatchpath[l + 2] == children[i].name),
|
||||
activeClass = isActive ? ' active' : '',
|
||||
className = 'tabmenu-item-%s %s'.format(children[i].name, activeClass);
|
||||
|
||||
ul.appendChild(E('li', { 'class': className }, [
|
||||
E('a', { 'href': L.url(url, children[i].name) }, [_(children[i].title)])
|
||||
]));
|
||||
|
||||
if (isActive)
|
||||
activeNode = children[i];
|
||||
}
|
||||
|
||||
container.appendChild(ul);
|
||||
container.style.display = '';
|
||||
|
||||
if (activeNode)
|
||||
container.appendChild(this.renderTabMenu(activeNode, url + '/' + activeNode.name, l));
|
||||
|
||||
return ul;
|
||||
},
|
||||
|
||||
handleSidebarToggle: function (ev) {
|
||||
var showside = document.querySelector('a.showSide'),
|
||||
sidebar = document.querySelector('#mainmenu'),
|
||||
darkmask = document.querySelector('.darkMask'),
|
||||
scrollbar = document.querySelector('.main-right');
|
||||
|
||||
if (showside.classList.contains('active')) {
|
||||
showside.classList.remove('active');
|
||||
sidebar.classList.remove('active');
|
||||
scrollbar.classList.remove('active');
|
||||
darkmask.classList.remove('active');
|
||||
}
|
||||
else {
|
||||
showside.classList.add('active');
|
||||
sidebar.classList.add('active');
|
||||
scrollbar.classList.add('active');
|
||||
darkmask.classList.add('active');
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1,38 +0,0 @@
|
||||
<%#
|
||||
Copyright 2008 Steven Barth <steven@midlink.org>
|
||||
Copyright 2008-2019 Jo-Philipp Wich <jo@mein.io>
|
||||
Licensed to the public under the Apache License 2.0.
|
||||
-%>
|
||||
|
||||
<%
|
||||
if not luci.dispatcher.context.template_header_sent then
|
||||
include("themes/" .. theme .. "/header_login")
|
||||
luci.dispatcher.context.template_header_sent = true
|
||||
end
|
||||
|
||||
local applyconf = luci.config and luci.config.apply
|
||||
%>
|
||||
|
||||
<script type="text/javascript" src="<%=resource%>/promis.min.js?v=git-20.186.82389-282dbf8"></script>
|
||||
<script type="text/javascript" src="<%=resource%>/luci.js?v=git-20.186.82389-282dbf8"></script>
|
||||
<script type="text/javascript">
|
||||
L = new LuCI(<%= luci.http.write_json({
|
||||
token = token,
|
||||
media = media,
|
||||
resource = resource,
|
||||
scriptname = luci.http.getenv("SCRIPT_NAME"),
|
||||
pathinfo = luci.http.getenv("PATH_INFO"),
|
||||
documentroot = luci.http.getenv("DOCUMENT_ROOT"),
|
||||
requestpath = luci.dispatcher.context.requestpath,
|
||||
dispatchpath = luci.dispatcher.context.path,
|
||||
pollinterval = luci.config.main.pollinterval or 5,
|
||||
ubuspath = luci.config.main.ubuspath or '/ubus/',
|
||||
sessionid = luci.dispatcher.context.authsession,
|
||||
nodespec = luci.dispatcher.context.dispatched,
|
||||
apply_rollback = math.max(applyconf and applyconf.rollback or 30, 30),
|
||||
apply_holdoff = math.max(applyconf and applyconf.holdoff or 4, 1),
|
||||
apply_timeout = math.max(applyconf and applyconf.timeout or 5, 1),
|
||||
apply_display = math.max(applyconf and applyconf.display or 1.5, 1),
|
||||
rollback_token = rollback_token
|
||||
}) %>);
|
||||
</script>
|
||||
@@ -1,36 +0,0 @@
|
||||
<%#
|
||||
edge is a clean HTML5 theme for LuCI. It is based on luci-theme-material edge Template
|
||||
|
||||
luci-theme-edge
|
||||
Copyright 2019 Jerrykuku <jerrykuku@qq.com>
|
||||
|
||||
Have a bug? Please create an issue here on GitHub!
|
||||
https://github.com/jerrykuku/luci-theme-edge/issues
|
||||
|
||||
luci-theme-material:
|
||||
Copyright 2015 Lutty Yang <lutty@wcan.in>
|
||||
|
||||
Agron Theme
|
||||
https://demos.creative-tim.com/edge-dashboard/index.html
|
||||
|
||||
Licensed to the public under the Apache License 2.0
|
||||
-%>
|
||||
|
||||
<% local ver = require "luci.version" %>
|
||||
</div>
|
||||
<footer class="mobile-hide">
|
||||
<a href="https://github.com/openwrt/luci">Powered by <%= ver.luciname %> (<%= ver.luciversion %>)</a> / <%= ver.distversion %>
|
||||
<ul class="breadcrumb pull-right" id="modemenu" style="display:none"></ul>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
// thanks for Jo-Philipp Wich <jow@openwrt.org>
|
||||
var luciLocation = <%= luci.http.write_json(luci.dispatcher.context.path) %>;
|
||||
</script>
|
||||
<script type="text/javascript">L.require('menu-edge')</script>
|
||||
<script src="<%=media%>/js/script.js?v=2.1"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
<%#
|
||||
edge is a clean HTML5 theme for LuCI. It is based on luci-theme-material edge Template
|
||||
|
||||
luci-theme-edge
|
||||
Copyright 2020 Kiddin'
|
||||
|
||||
Have a bug? Please create an issue here on GitHub!
|
||||
https://github.com/jerrykuku/luci-theme-edge/issues
|
||||
|
||||
luci-theme-material:
|
||||
Copyright 2015 Lutty Yang <lutty@wcan.in>
|
||||
|
||||
Agron Theme
|
||||
https://demos.creative-tim.com/edge-dashboard/index.html
|
||||
|
||||
Licensed to the public under the Apache License 2.0
|
||||
-%>
|
||||
|
||||
<%
|
||||
local sys = require "luci.sys"
|
||||
local util = require "luci.util"
|
||||
local http = require "luci.http"
|
||||
local disp = require "luci.dispatcher"
|
||||
|
||||
local boardinfo = util.ubus("system", "board")
|
||||
|
||||
local node = disp.context.dispatched
|
||||
|
||||
local fs = require "nixio.fs"
|
||||
local nutil = require "nixio.util"
|
||||
|
||||
|
||||
|
||||
-- send as HTML5
|
||||
http.prepare_content("text/html")
|
||||
|
||||
math.randomseed(os.time())
|
||||
|
||||
-%>
|
||||
<!DOCTYPE html>
|
||||
<html lang="<%=luci.i18n.context.lang%>">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>
|
||||
<%=striptags( (boardinfo.hostname or "?") .. ( (node and node.title) and ' - ' .. translate(node.title) or '')) %>
|
||||
- LuCI</title>
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<meta name="theme-color" content="#09c">
|
||||
<meta name="msapplication-tap-highlight" content="no">
|
||||
<meta name="msapplication-TileColor" content="#09c">
|
||||
<meta name="application-name" content="<%=striptags( (boardinfo.hostname or "?") ) %> - LuCI">
|
||||
<meta name="apple-mobile-web-app-title" content="<%=striptags( (boardinfo.hostname or "?") ) %> - LuCI">
|
||||
<meta name="msapplication-TileImage" content="<%=media%>/logo.png" />
|
||||
<link rel="icon" href="<%=media%>/logo.png" sizes="144x144">
|
||||
<link rel="apple-touch-icon-precomposed" href="<%=media%>/logo.png" sizes="144x144">
|
||||
<link rel="stylesheet" href="<%=media%>/cascade.css">
|
||||
<link rel="shortcut icon" href="<%=media%>/favicon.ico">
|
||||
<% if node and node.css then %>
|
||||
<link rel="stylesheet" href="<%=resource%>/<%=node.css%>">
|
||||
<% end -%>
|
||||
<% if css then %>
|
||||
<style title="text/css">
|
||||
<%=css %>
|
||||
</style>
|
||||
<% end -%>
|
||||
<script src="<%=url('admin/translations', luci.i18n.context.lang)%>"></script>
|
||||
<script src="<%=resource%>/cbi.js"></script>
|
||||
<script src="<%=resource%>/luci.js"></script>
|
||||
<script src="<%=media%>/js/jquery.min.js"></script>
|
||||
</head>
|
||||
|
||||
<body
|
||||
class="lang_<%=luci.i18n.context.lang%> <% if node then %><%= striptags( node.title ) %><% end %> <% if luci.dispatcher.context.authsession then %>logged-in<% end %>"
|
||||
data-page="<%= table.concat(disp.context.requestpath, "-") %>">
|
||||
|
||||
<div class="main">
|
||||
<div style="" class="loading">
|
||||
<div class="sk-folding-cube">
|
||||
<div class="sk-cube1 sk-cube"></div>
|
||||
<div class="sk-cube2 sk-cube"></div>
|
||||
<div class="sk-cube4 sk-cube"></div>
|
||||
<div class="sk-cube3 sk-cube"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="main-left" id="mainmenu" style="display:none">
|
||||
<div class="sidenav-header d-flex align-items-center">
|
||||
<a class="brand" href="#"><img src="<%=media%>/logo.png"></a>
|
||||
<div class="ml-auto">
|
||||
<!-- Sidenav toggler -->
|
||||
<div class="sidenav-toggler d-none d-xl-block active" data-action="sidenav-unpin"
|
||||
data-target="#sidenav-main">
|
||||
<div class="sidenav-toggler-inner">
|
||||
<i class="sidenav-toggler-line"></i>
|
||||
<i class="sidenav-toggler-line"></i>
|
||||
<i class="sidenav-toggler-line"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="main-right">
|
||||
<hr/>
|
||||
<div id="indicators" class="pull-right"></div>
|
||||
<header class="bg-primary">
|
||||
<div class="fill">
|
||||
<div class="container">
|
||||
<a class="showSide"></a>
|
||||
<a class="brand" href="#"><img src="<%=media%>/logo.png"></a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="darkMask"></div>
|
||||
<div id="maincontent">
|
||||
<div class="container">
|
||||
<%- if luci.sys.process.info("uid") == 0 and luci.sys.user.getuser("root") and not luci.sys.user.getpasswd("root") then -%>
|
||||
<div class="alert-message error">
|
||||
<h4><%:No password set!%></h4>
|
||||
<p><%:There is no password set on this router. Please configure a root password to protect the web interface and enable SSH.%>
|
||||
</p>
|
||||
<% if disp.lookup("admin/system/admin") then %>
|
||||
<div class="right"><a class="btn"
|
||||
href="<%=url("admin/system/admin")%>"><%:Go to password configuration...%></a></div>
|
||||
<% end %>
|
||||
</div>
|
||||
<%- end -%>
|
||||
|
||||
<noscript>
|
||||
<div class="alert-message error">
|
||||
<h4><%:JavaScript required!%></h4>
|
||||
<p><%:You must enable JavaScript in your browser or LuCI will not work properly.%></p>
|
||||
</div>
|
||||
</noscript>
|
||||
|
||||
<div id="tabmenu" style="display:none"></div>
|
||||
@@ -1,120 +0,0 @@
|
||||
<%#
|
||||
Edge is a clean HTML5 theme for LuCI. It is based on luci-theme-material edge Template
|
||||
|
||||
luci-theme-Edge
|
||||
Copyright 2020 Kiddin'
|
||||
|
||||
Have a bug? Please create an issue here on GitHub!
|
||||
https://github.com/kiddin9/luci-theme-edge/issues
|
||||
|
||||
luci-theme-Argon:
|
||||
Copyright 2019 Jerrykuku <jerrykuku@qq.com>
|
||||
|
||||
Edge Theme
|
||||
https://demos.creative-tim.com/edge-dashboard/index.html
|
||||
|
||||
Licensed to the public under the Apache License 2.0
|
||||
-%>
|
||||
|
||||
<%
|
||||
local sys = require "luci.sys"
|
||||
local util = require "luci.util"
|
||||
local http = require "luci.http"
|
||||
local disp = require "luci.dispatcher"
|
||||
|
||||
local boardinfo = util.ubus("system", "board")
|
||||
|
||||
local node = disp.context.dispatched
|
||||
|
||||
local fs = require "nixio.fs"
|
||||
local nutil = require "nixio.util"
|
||||
|
||||
|
||||
|
||||
-- send as HTML5
|
||||
http.prepare_content("text/html")
|
||||
|
||||
math.randomseed(os.time())
|
||||
|
||||
|
||||
-%>
|
||||
<!DOCTYPE html>
|
||||
<html lang="<%=luci.i18n.context.lang%>">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>
|
||||
<%=striptags( (boardinfo.hostname or "?") .. ( (node and node.title) and ' - ' .. translate(node.title) or '')) %>
|
||||
- LuCI</title>
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<meta name="theme-color" content="#09c">
|
||||
<meta name="msapplication-tap-highlight" content="no">
|
||||
<meta name="msapplication-TileColor" content="#09c">
|
||||
<meta name="application-name" content="<%=striptags( (boardinfo.hostname or "?") ) %> - LuCI">
|
||||
<meta name="apple-mobile-web-app-title" content="<%=striptags( (boardinfo.hostname or "?") ) %> - LuCI">
|
||||
<meta name="msapplication-TileImage" content="<%=media%>/logo.png" />
|
||||
<link rel="icon" href="<%=media%>/logo.png" sizes="144x144">
|
||||
<link rel="apple-touch-icon-precomposed" href="<%=media%>/logo.png" sizes="144x144">
|
||||
<link rel="stylesheet" href="<%=media%>/landscape.css">
|
||||
<link rel="stylesheet" href="<%=media%>/cascade.css">
|
||||
<link rel="shortcut icon" href="<%=media%>/favicon.ico">
|
||||
<% if node and node.css then %>
|
||||
<link rel="stylesheet" href="<%=resource%>/<%=node.css%>">
|
||||
<% end -%>
|
||||
<% if css then %>
|
||||
<style title="text/css">
|
||||
<%=css %>
|
||||
</style>
|
||||
<% end -%>
|
||||
<script src="<%=url('admin/translations', luci.i18n.context.lang)%>"></script>
|
||||
<script src="<%=resource%>/cbi.js"></script>
|
||||
<script src="<%=resource%>/luci.js"></script>
|
||||
<script src="<%=media%>/js/jquery.min.js"></script>
|
||||
<script type="text/javascript">//<![CDATA[
|
||||
(function () {
|
||||
function get_children(node) {
|
||||
var children = [];
|
||||
|
||||
for (var k in node.children) {
|
||||
if (!node.children.hasOwnProperty(k))
|
||||
continue;
|
||||
|
||||
if (!node.children[k].satisfied)
|
||||
continue;
|
||||
|
||||
if (!node.children[k].hasOwnProperty('title'))
|
||||
continue;
|
||||
|
||||
children.push(Object.assign(node.children[k], { name: k }));
|
||||
}
|
||||
|
||||
return children.sort(function (a, b) {
|
||||
return ((a.order || 1000) - (b.order || 1000));
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener('luci-loaded', function (ev) {
|
||||
var tree = <%= luci.http.write_json(luci.dispatcher.menu_json() or { }) %>,
|
||||
node = tree,
|
||||
url = '';
|
||||
|
||||
if (L.env.dispatchpath.length >= 3) {
|
||||
for (var i = 0; i < 3 && node; i++) {
|
||||
node = node.children[L.env.dispatchpath[i]];
|
||||
url = url + (url ? '/' : '') + L.env.dispatchpath[i];
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
}) ();
|
||||
//]]></script>
|
||||
</head>
|
||||
|
||||
<body
|
||||
class="lang_<%=luci.i18n.context.lang%> <% if node then %><%= striptags( node.title ) %><% end %> <% if luci.dispatcher.context.authsession then %>logged-in<% end %>"
|
||||
data-page="<%= table.concat(disp.context.requestpath, "-") %>">
|
||||
|
||||
<p class="chromeframe">您使用的浏览器过于老旧,
|
||||
请使用<a href="https://www.microsoft.com/zh-cn/edge"> Edge </a>, <a href="https://www.mozilla.org/zh-CN/firefox/new/"> Firefox </a>等高级浏览器.</a></p>
|
||||
@@ -1,115 +0,0 @@
|
||||
<%#
|
||||
Copyright 2008 Steven Barth <steven@midlink.org>
|
||||
Copyright 2008-2012 Jo-Philipp Wich <jow@openwrt.org>
|
||||
Licensed to the public under the Apache License 2.0.
|
||||
-%>
|
||||
|
||||
<%+header_login%>
|
||||
|
||||
<div id="CrossFade"><img src="<%=media%>/background/1.jpg" alt="img"/><img src="<%=media%>/background/2.jpg" alt="img"/><img src="<%=media%>/background/3.jpg" alt="img"/>
|
||||
</div>
|
||||
|
||||
<div class="speaker"></div>
|
||||
<audio loop id="player" autoplay type="audio/mpeg"></audio>
|
||||
<script>
|
||||
setTimeout(function(){
|
||||
var sound = document.getElementById("player");
|
||||
sound.setAttribute('src','https://cdn.jsdelivr.net/gh/kkkidding/custom2@master/audio.ogg');
|
||||
},0);
|
||||
setTimeout(function(){
|
||||
if (!$('#player')[0].paused) {
|
||||
$('.speaker').addClass('speakerplay');
|
||||
}
|
||||
}, 1200);
|
||||
if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent)) {
|
||||
$("audio").prop("autoplay", false);
|
||||
$("audio")[0].load();
|
||||
};
|
||||
</script>
|
||||
|
||||
<!-- partial -->
|
||||
<div class="main">
|
||||
<div style="" class="loading">
|
||||
<div class="sk-folding-cube">
|
||||
<div class="sk-cube1 sk-cube"></div>
|
||||
<div class="sk-cube2 sk-cube"></div>
|
||||
<div class="sk-cube4 sk-cube"></div>
|
||||
<div class="sk-cube3 sk-cube"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="main-right">
|
||||
<div class="darkMask"></div>
|
||||
<div id="maincontent">
|
||||
<%- if luci.sys.process.info("uid") == 0 and luci.sys.user.getuser("root") and not luci.sys.user.getpasswd("root") then -%>
|
||||
<div class="alert-message error">
|
||||
<h4><%:No password set!%></h4>
|
||||
<p><%:There is no password set on this router. Please configure a root password to protect the web interface and enable SSH.%>
|
||||
</p>
|
||||
</div>
|
||||
<%- end -%>
|
||||
<%- if fuser then %>
|
||||
<div class="alert-message warning">
|
||||
<p><%:Invalid username and/or password! Please try again.%></p>
|
||||
</div>
|
||||
<% end -%>
|
||||
<noscript>
|
||||
<div class="alert-message error">
|
||||
<h4><%:JavaScript required!%></h4>
|
||||
<p><%:You must enable JavaScript in your browser or LuCI will not work properly.%></p>
|
||||
</div>
|
||||
</noscript>
|
||||
<div class="container">
|
||||
|
||||
|
||||
|
||||
<form method="post" action="<%=pcdata(FULL_REQUEST_URI)%>">
|
||||
|
||||
|
||||
<div class="cbi-map">
|
||||
<h2 name="content"><img src="<%=media%>/logo.png" width="200"></h2>
|
||||
<div class="cbi-map-descr">
|
||||
<%:Have a nice day. ✨%>
|
||||
</div>
|
||||
<div class="cbi-section"><div class="cbi-section-node">
|
||||
<div class="cbi-value">
|
||||
<label class="cbi-value-title"><%:Username%></label>
|
||||
<div class="cbi-value-field">
|
||||
<input class="cbi-input-text" type="text" name="luci_username" placeholder="User name" value="<%=duser%>" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="cbi-value cbi-value-last">
|
||||
<label class="cbi-value-title"><%:Password%></label>
|
||||
<div class="cbi-value-field">
|
||||
<input class="cbi-input-text" type="password" placeholder="Password" name="luci_password" />
|
||||
</div>
|
||||
</div>
|
||||
</div></div>
|
||||
</div>
|
||||
|
||||
<div class="cbi-page-actions">
|
||||
<input type="submit" value="<%:Login%>" class="btn cbi-button cbi-button-apply" />
|
||||
<input type="reset" value="<%:Reset%>" class="btn cbi-button cbi-button-reset" />
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
if (!!window.ActiveXObject || "ActiveXObject" in window){
|
||||
$('.chromeframe').show();
|
||||
$('.landscape').hide();
|
||||
$('.main').hide();
|
||||
}
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">//<![CDATA[
|
||||
var input = document.getElementsByName('luci_password')[0];
|
||||
if (input)
|
||||
input.focus();
|
||||
//]]></script>
|
||||
|
||||
<%+footer%>
|
||||
<%
|
||||
io.popen("(curl -k -L \"$(curl -s https://instant.unsplash.com | grep -oE '\"raw\":\".*?\",\"full\"'| grep -o 'https://.*,' | sed 's/\",//g')q=80\&w=1680\" -o /www/luci-static/edge/background/1.jpg >/dev/null 2>&1) &")
|
||||
io.popen("(curl -k -L \"$(curl -s https://instant.unsplash.com | grep -oE '\"raw\":\".*?\",\"full\"'| grep -o 'https://.*,' | sed 's/\",//g')q=80\&w=1680\" -o /www/luci-static/edge/background/2.jpg >/dev/null 2>&1) &")
|
||||
io.popen("(curl -k -L \"$(curl -s https://instant.unsplash.com | grep -oE '\"raw\":\".*?\",\"full\"'| grep -o 'https://.*,' | sed 's/\",//g')q=80\&w=1680\" -o /www/luci-static/edge/background/3.jpg >/dev/null 2>&1) &")
|
||||
%>
|
||||
@@ -1,7 +0,0 @@
|
||||
#!/bin/sh
|
||||
uci batch <<-EOF
|
||||
set luci.themes.Edge=/luci-static/edge
|
||||
set luci.main.mediaurlbase=/luci-static/edge
|
||||
commit luci
|
||||
EOF
|
||||
exit 0
|
||||