update 2025-02-17 23:37:02
47
floatip/Makefile
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#
|
||||||
|
# Copyright (C) 2024 jjm2473 <jjm2473@gmail.com>
|
||||||
|
#
|
||||||
|
# This is free software, licensed under the MIT License.
|
||||||
|
#
|
||||||
|
|
||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
PKG_NAME:=floatip
|
||||||
|
PKG_VERSION:=1.0.8
|
||||||
|
PKG_RELEASE:=1
|
||||||
|
PKG_MAINTAINER:=jjm2473 <jjm2473@gmail.com>
|
||||||
|
|
||||||
|
include $(INCLUDE_DIR)/package.mk
|
||||||
|
|
||||||
|
define Package/$(PKG_NAME)
|
||||||
|
SECTION:=net
|
||||||
|
CATEGORY:=Network
|
||||||
|
SUBMENU:=IP Addresses and Names
|
||||||
|
TITLE:=Float IP
|
||||||
|
DEPENDS:=+curl
|
||||||
|
PKGARCH:=all
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/$(PKG_NAME)/description
|
||||||
|
Auto setup an IP if some host down
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/$(PKG_NAME)/conffiles
|
||||||
|
/etc/config/floatip
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Build/Configure
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Build/Compile
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/$(PKG_NAME)/install
|
||||||
|
$(INSTALL_DIR) $(1)/usr/libexec $(1)/etc/init.d $(1)/etc/config $(1)/etc/uci-defaults
|
||||||
|
$(INSTALL_BIN) ./files/floatip.sh $(1)/usr/libexec/floatip.sh
|
||||||
|
$(INSTALL_BIN) ./files/floatip.init $(1)/etc/init.d/floatip
|
||||||
|
$(INSTALL_CONF) ./files/floatip.config $(1)/etc/config/floatip
|
||||||
|
$(INSTALL_BIN) ./files/floatip.uci-default $(1)/etc/uci-defaults/floatip
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(eval $(call BuildPackage,$(PKG_NAME)))
|
17
floatip/files/floatip.config
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
config floatip 'main'
|
||||||
|
# 启动时,enabled != 1 ,或者原 lan 口配置网段不包括 set_ip,清除自身的 set_ip,然后退出进程。
|
||||||
|
option enabled '0'
|
||||||
|
# fallback 表示后备
|
||||||
|
option role 'fallback'
|
||||||
|
# option role 'main'
|
||||||
|
# 对于 fallback 节点,检查到 check_ip 都不在线超过一定时间(例如30秒),就设置自身的 set_ip,然后检查 check_ip 中任一 IP 在线就清除自身的 set_ip,重复上述流程。
|
||||||
|
# 对于 main 节点,启动后不断检查 set_ip 和 check_url,直到 set_ip 不在线且 check_url 没有失败,就设置自身的 set_ip 并允许 LAN 口 ping,否则清除自身的 set_ip 并禁止 LAN 口 ping,重复上述流程。
|
||||||
|
# set_ip 可以不提供前缀长度,将会按 lan 口配置的网段的长度
|
||||||
|
option set_ip '192.168.100.3/24'
|
||||||
|
# option set_ip '192.168.100.3'
|
||||||
|
# check_ip 仅 fallback 有效,并且检查时只检查跟 set_ip 同一网段的
|
||||||
|
list check_ip '192.168.100.2'
|
||||||
|
# list check_ip '192.168.100.4'
|
||||||
|
# check_url 仅 main 有效
|
||||||
|
# list check_url 'https://www.google.com/generate_204'
|
||||||
|
# option check_url_timeout '5'
|
67
floatip/files/floatip.init
Executable file
@ -0,0 +1,67 @@
|
|||||||
|
#!/bin/sh /etc/rc.common
|
||||||
|
|
||||||
|
START=98
|
||||||
|
USE_PROCD=1
|
||||||
|
|
||||||
|
enable_lan_ping() {
|
||||||
|
uci -q set firewall.floatip_lan_offline.enabled=0 || return 0
|
||||||
|
uci changes | grep -Fq 'firewall.floatip_lan_offline.enabled' || return 0
|
||||||
|
uci commit firewall
|
||||||
|
/etc/init.d/firewall reload
|
||||||
|
}
|
||||||
|
|
||||||
|
start_service() {
|
||||||
|
config_load floatip
|
||||||
|
config_get_bool enabled "main" enabled 0
|
||||||
|
ifdown floatip
|
||||||
|
[[ "$enabled" = 1 ]] || {
|
||||||
|
enable_lan_ping
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
[[ "`uci -q get network.lan.proto`" = "static" ]] || return 0
|
||||||
|
local set_ip set_prefix
|
||||||
|
config_get set_ip "main" set_ip
|
||||||
|
[[ -n "$set_ip" ]] || return 0
|
||||||
|
if [[ "$set_ip" = "*/*" ]]; then
|
||||||
|
eval "$(ipcalc.sh "$set_ip" )";set_prefix=$PREFIX;set_ip=$IP
|
||||||
|
else
|
||||||
|
set_prefix=32
|
||||||
|
fi
|
||||||
|
local lan_ip="`uci -q get network.lan.ipaddr`"
|
||||||
|
[[ -n "$lan_ip" ]] || return 0
|
||||||
|
local lan_net lan_prefix set_net ip
|
||||||
|
local in_range=0
|
||||||
|
local lan_netmask="`uci -q get network.lan.netmask`"
|
||||||
|
for ip in $lan_ip; do
|
||||||
|
if [[ "$ip" = "*/*" ]]; then
|
||||||
|
eval "$(ipcalc.sh $ip )";lan_net=$NETWORK;lan_prefix=$PREFIX
|
||||||
|
else
|
||||||
|
# prefix=32 if not present
|
||||||
|
[[ -n "$lan_netmask" ]] || continue
|
||||||
|
eval "$(ipcalc.sh $ip $lan_netmask )";lan_net=$NETWORK;lan_prefix=$PREFIX
|
||||||
|
fi
|
||||||
|
[[ "$set_prefix" -ge "$lan_prefix" ]] || continue
|
||||||
|
eval "$(ipcalc.sh $set_ip/$lan_prefix )";set_net=$NETWORK
|
||||||
|
[[ "$set_net" = "$lan_net" ]] && {
|
||||||
|
[[ "$set_prefix" = 32 ]] && set_prefix=$lan_prefix
|
||||||
|
in_range=1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
done
|
||||||
|
|
||||||
|
[[ $in_range = 1 ]] || return 0
|
||||||
|
procd_open_instance
|
||||||
|
procd_set_param command /usr/libexec/floatip.sh "$set_prefix"
|
||||||
|
procd_set_param stderr 1
|
||||||
|
procd_set_param file /etc/config/floatip
|
||||||
|
procd_close_instance
|
||||||
|
}
|
||||||
|
|
||||||
|
stop_service() {
|
||||||
|
enable_lan_ping
|
||||||
|
ifdown floatip
|
||||||
|
}
|
||||||
|
|
||||||
|
service_triggers() {
|
||||||
|
procd_add_reload_trigger "network" "floatip"
|
||||||
|
}
|
223
floatip/files/floatip.sh
Executable file
@ -0,0 +1,223 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
DEFAULT_PREFIX=24
|
||||||
|
|
||||||
|
# random number 0-255
|
||||||
|
random() {
|
||||||
|
local num=$(dd if=/dev/urandom bs=1 count=1 2>/dev/null | hexdump -ve '1/1 "%u"')
|
||||||
|
if [[ -z "$num" ]]; then
|
||||||
|
num=$(($(grep -om1 '[0-9][0-9]$' /proc/uptime) * 255 / 100))
|
||||||
|
fi
|
||||||
|
echo ${num:-1}
|
||||||
|
}
|
||||||
|
|
||||||
|
# check host alive, timeout in 2 seconds
|
||||||
|
host_alive() {
|
||||||
|
ping -4 -c 2 -A -t 1 -W 1 -q "$1" >/dev/null
|
||||||
|
# arping -f -q -b -c 2 -w 2 -i 1 -I br-lan "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
set_up() {
|
||||||
|
local ipaddr="$1"
|
||||||
|
echo "set my floatip to $ipaddr" >&2
|
||||||
|
if ! uci -q get network.floatip.ipaddr | grep -Fwq $ipaddr; then
|
||||||
|
if [[ "x$(uci -q get network.floatip)" = xinterface ]]; then
|
||||||
|
uci -q batch <<-EOF >/dev/null
|
||||||
|
delete network.floatip.ipaddr
|
||||||
|
add_list network.floatip.ipaddr=$ipaddr
|
||||||
|
EOF
|
||||||
|
else
|
||||||
|
uci -q batch <<-EOF >/dev/null
|
||||||
|
set network.floatip=interface
|
||||||
|
set network.floatip.proto=static
|
||||||
|
add_list network.floatip.ipaddr=$ipaddr
|
||||||
|
set network.floatip.device=br-lan
|
||||||
|
set network.floatip.auto=0
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
uci commit network
|
||||||
|
fi
|
||||||
|
ifup floatip
|
||||||
|
}
|
||||||
|
|
||||||
|
set_lan_ping() {
|
||||||
|
if [[ "$1" = 0 ]]; then
|
||||||
|
if [[ "x$(uci -q get firewall.floatip_lan_offline)" = xrule ]]; then
|
||||||
|
uci -q delete firewall.floatip_lan_offline.enabled
|
||||||
|
uci changes | grep -Fq 'firewall.floatip_lan_offline.enabled' || return 0
|
||||||
|
else
|
||||||
|
uci -q batch <<-EOF >/dev/null
|
||||||
|
set firewall.floatip_lan_offline=rule
|
||||||
|
set firewall.floatip_lan_offline.name=FloatIP-LAN-Offline
|
||||||
|
set firewall.floatip_lan_offline.src=lan
|
||||||
|
set firewall.floatip_lan_offline.proto=icmp
|
||||||
|
set firewall.floatip_lan_offline.icmp_type=echo-request
|
||||||
|
set firewall.floatip_lan_offline.family=ipv4
|
||||||
|
set firewall.floatip_lan_offline.target=DROP
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
uci -q set firewall.floatip_lan_offline.enabled=0 || return 0
|
||||||
|
uci changes | grep -Fq 'firewall.floatip_lan_offline.enabled' || return 0
|
||||||
|
fi
|
||||||
|
uci commit firewall
|
||||||
|
/etc/init.d/firewall reload 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
safe_sleep() {
|
||||||
|
local sec="$1"
|
||||||
|
[[ "$sec" -lt 1 ]] && sec=1
|
||||||
|
sleep $sec
|
||||||
|
}
|
||||||
|
|
||||||
|
. /lib/functions.sh
|
||||||
|
|
||||||
|
fallback_loop() {
|
||||||
|
local set_ip check_ip set_net set_prefix
|
||||||
|
config_get set_ip "main" set_ip
|
||||||
|
[[ -n "$set_ip" ]] || return 1
|
||||||
|
[[ "$set_ip" = "*/*" ]] || set_ip="$set_ip/$DEFAULT_PREFIX"
|
||||||
|
eval "$(ipcalc.sh "$set_ip" )";set_net=$NETWORK;set_prefix=$PREFIX;set_ip=$IP
|
||||||
|
local ipaddr="$set_ip/$set_prefix"
|
||||||
|
echo "ipaddr=$ipaddr"
|
||||||
|
|
||||||
|
local valid_check_ip cip
|
||||||
|
config_get check_ip "main" check_ip
|
||||||
|
for cip in $check_ip; do
|
||||||
|
eval "$(ipcalc.sh $cip/$set_prefix )"
|
||||||
|
[[ "$NETWORK" = "$set_net" ]] && valid_check_ip="$valid_check_ip $cip"
|
||||||
|
done
|
||||||
|
valid_check_ip="$valid_check_ip "
|
||||||
|
|
||||||
|
local order_check_ip="$valid_check_ip"
|
||||||
|
local found_alive consume_time
|
||||||
|
local dead_counter=0 floatip_up=0
|
||||||
|
while :; do
|
||||||
|
found_alive=0
|
||||||
|
consume_time=0
|
||||||
|
echo "checking host(s) $order_check_ip alive"
|
||||||
|
for cip in $order_check_ip; do
|
||||||
|
if host_alive $cip; then
|
||||||
|
echo "host $cip alive"
|
||||||
|
found_alive=1
|
||||||
|
# reorder to reduce check time
|
||||||
|
order_check_ip=" ${cip}${valid_check_ip// $cip / }"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
consume_time=$(($consume_time + 2))
|
||||||
|
done
|
||||||
|
if [[ $found_alive = 1 ]]; then
|
||||||
|
if [[ $floatip_up = 1 ]]; then
|
||||||
|
echo "set down floatip" >&2
|
||||||
|
ifdown floatip
|
||||||
|
floatip_up=0
|
||||||
|
else
|
||||||
|
dead_counter=0
|
||||||
|
fi
|
||||||
|
safe_sleep $((10 - $consume_time))
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
if [[ $floatip_up = 1 ]]; then
|
||||||
|
safe_sleep $((5 - $consume_time))
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
dead_counter=$(($dead_counter + 1))
|
||||||
|
if [[ $dead_counter -lt 3 ]]; then
|
||||||
|
safe_sleep $((10 - $consume_time))
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
echo "no host alive, set up floatip $ipaddr" >&2
|
||||||
|
set_up "$ipaddr"
|
||||||
|
floatip_up=1
|
||||||
|
sleep 5
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
main_loop() {
|
||||||
|
local set_ip set_prefix
|
||||||
|
config_get set_ip "main" set_ip
|
||||||
|
[[ -n "$set_ip" ]] || return 1
|
||||||
|
[[ "$set_ip" = "*/*" ]] || set_ip="$set_ip/$DEFAULT_PREFIX"
|
||||||
|
eval "$(ipcalc.sh "$set_ip" )";set_prefix=$PREFIX;set_ip=$IP
|
||||||
|
local ipaddr="$set_ip/$set_prefix"
|
||||||
|
echo "ipaddr=$ipaddr"
|
||||||
|
|
||||||
|
local check_urls check_url_timeout
|
||||||
|
config_get check_urls "main" check_url
|
||||||
|
config_get check_url_timeout "main" check_url_timeout '5'
|
||||||
|
local dead_counter=0 floatip_up=0 url_pass check_url curl_code consume_time found_alive
|
||||||
|
# sleep 2-6s
|
||||||
|
sleep $(( $(random) / 60 + 2))
|
||||||
|
while :; do
|
||||||
|
consume_time=0
|
||||||
|
if [[ $floatip_up = 0 ]]; then
|
||||||
|
found_alive=0
|
||||||
|
echo "checking host $set_ip alive"
|
||||||
|
if host_alive $set_ip; then
|
||||||
|
echo "host $set_ip alive"
|
||||||
|
found_alive=1
|
||||||
|
else
|
||||||
|
consume_time=$(($consume_time + 2))
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
url_pass=1
|
||||||
|
for check_url in $check_urls ; do
|
||||||
|
curl -L --fail --show-error --no-progress-meter -o /dev/null \
|
||||||
|
--connect-timeout "$check_url_timeout" --max-time "$check_url_timeout" \
|
||||||
|
-I "$check_url" 2>&1
|
||||||
|
curl_code=$?
|
||||||
|
[[ $curl_code = 0 ]] && continue
|
||||||
|
[[ $curl_code = 6 || $curl_code = 7 || $curl_code = 28 ]] && \
|
||||||
|
consume_time=$(($consume_time + $check_url_timeout))
|
||||||
|
echo "check_url $check_url fail, code $curl_code"
|
||||||
|
url_pass=0
|
||||||
|
break
|
||||||
|
done
|
||||||
|
if [[ $floatip_up = 0 ]]; then
|
||||||
|
if [[ $url_pass = 1 ]]; then
|
||||||
|
# notify fallback node to offline
|
||||||
|
set_lan_ping
|
||||||
|
if [[ $found_alive = 0 ]]; then
|
||||||
|
echo "no host alive, and url passed, set up floatip $ipaddr" >&2
|
||||||
|
set_up "$ipaddr"
|
||||||
|
floatip_up=1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
set_lan_ping 0
|
||||||
|
fi
|
||||||
|
safe_sleep $((5 - $consume_time))
|
||||||
|
continue
|
||||||
|
else
|
||||||
|
if [[ $url_pass = 0 ]]; then
|
||||||
|
dead_counter=$(($dead_counter + 1))
|
||||||
|
if [[ $dead_counter -lt 3 ]]; then
|
||||||
|
safe_sleep $((5 - $consume_time))
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
echo "set down floatip, and disable ping" >&2
|
||||||
|
ifdown floatip
|
||||||
|
set_lan_ping 0
|
||||||
|
floatip_up=0
|
||||||
|
fi
|
||||||
|
dead_counter=0
|
||||||
|
fi
|
||||||
|
sleep 20
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
local role
|
||||||
|
config_load floatip
|
||||||
|
config_get role "main" role
|
||||||
|
if [[ "$role" = "main" ]]; then
|
||||||
|
main_loop
|
||||||
|
elif [[ "$role" = "fallback" ]]; then
|
||||||
|
fallback_loop
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ -n "$1" ]]; then
|
||||||
|
[[ "$1" -ge 0 && "$1" -lt 32 ]] && DEFAULT_PREFIX=$1
|
||||||
|
fi
|
||||||
|
|
||||||
|
main
|
14
floatip/files/floatip.uci-default
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
|
||||||
|
uci -q batch <<-EOF >/dev/null
|
||||||
|
delete ucitrack.@floatip[-1]
|
||||||
|
add ucitrack floatip
|
||||||
|
set ucitrack.@floatip[-1].init=floatip
|
||||||
|
commit ucitrack
|
||||||
|
EOF
|
||||||
|
|
||||||
|
[[ "`uci -q get network.lan.proto`" = "static" && -n "`uci -q get network.lan.gateway`" ]] || exit 0
|
||||||
|
|
||||||
|
uci -q batch <<-EOF >/dev/null
|
||||||
|
set floatip.main.role=main
|
||||||
|
commit floatip
|
||||||
|
EOF
|
69
linkease/Makefile
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
#
|
||||||
|
# Copyright (C) 2015-2016 OpenWrt.org
|
||||||
|
# Copyright (C) 2020 jjm2473@gmail.com
|
||||||
|
#
|
||||||
|
# This is free software, licensed under the GNU General Public License v3.
|
||||||
|
#
|
||||||
|
|
||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
PKG_ARCH_LINKEASE:=$(ARCH)
|
||||||
|
|
||||||
|
PKG_NAME:=linkease
|
||||||
|
PKG_VERSION:=1.6.9
|
||||||
|
PKG_RELEASE:=$(PKG_ARCH_LINKEASE)-2
|
||||||
|
PKG_SOURCE:=$(PKG_NAME)-binary-$(PKG_VERSION).tar.gz
|
||||||
|
PKG_SOURCE_URL:=http://fw0.koolcenter.com/binary/LinkEase/LinuxStorage/
|
||||||
|
PKG_HASH:=09d17d4c41e27c4f3e4b3a722e589c78e09b2124ea377c44a3d7af06005e8012
|
||||||
|
|
||||||
|
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-binary-$(PKG_VERSION)
|
||||||
|
|
||||||
|
PKG_BUILD_PARALLEL:=1
|
||||||
|
PKG_USE_MIPS16:=0
|
||||||
|
|
||||||
|
include $(INCLUDE_DIR)/package.mk
|
||||||
|
|
||||||
|
define Package/$(PKG_NAME)
|
||||||
|
SECTION:=net
|
||||||
|
CATEGORY:=Network
|
||||||
|
SUBMENU:=Web Servers/Proxies
|
||||||
|
TITLE:=LinkEase - the file cloud
|
||||||
|
DEPENDS:=@(arm||x86_64||aarch64) +linkmount
|
||||||
|
PKGARCH:=all
|
||||||
|
URL:=https://www.linkease.com/
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/$(PKG_NAME)/description
|
||||||
|
LinkEase is a file cloud
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/$(PKG_NAME)/conffiles
|
||||||
|
/etc/config/linkease
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/$(PKG_NAME)/postinst
|
||||||
|
#!/bin/sh
|
||||||
|
if [ -z "$${IPKG_INSTROOT}" ]; then
|
||||||
|
[ -f /etc/uci-defaults/linkease ] && /etc/uci-defaults/linkease && rm -f /etc/uci-defaults/linkease
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Build/Configure
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Build/Compile
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/$(PKG_NAME)/install
|
||||||
|
$(INSTALL_DIR) $(1)/usr/sbin/linkease-plugins $(1)/etc/config $(1)/etc/init.d $(1)/etc/uci-defaults
|
||||||
|
$(INSTALL_BIN) $(PKG_BUILD_DIR)/linkease.$(PKG_ARCH_LINKEASE) $(1)/usr/sbin/linkease
|
||||||
|
$(INSTALL_BIN) $(PKG_BUILD_DIR)/heif-converter.$(PKG_ARCH_LINKEASE) $(1)/usr/sbin/heif-converter
|
||||||
|
$(INSTALL_BIN) $(PKG_BUILD_DIR)/linkease-media.$(PKG_ARCH_LINKEASE) $(1)/usr/sbin/linkease-media
|
||||||
|
$(INSTALL_BIN) ./files/linkease-config.sh $(1)/usr/sbin/linkease-config.sh
|
||||||
|
$(INSTALL_CONF) ./files/linkease.config $(1)/etc/config/linkease
|
||||||
|
$(INSTALL_BIN) ./files/linkease.init $(1)/etc/init.d/linkease
|
||||||
|
$(INSTALL_BIN) ./files/linkease.uci-default $(1)/etc/uci-defaults/linkease
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(eval $(call BuildPackage,$(PKG_NAME)))
|
134
linkease/files/aria2.sh
Executable file
@ -0,0 +1,134 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
sh_ver="1.0.0"
|
||||||
|
export PATH=/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/sbin:/bin
|
||||||
|
aria2_conf_dir=/var/etc/aria2/
|
||||||
|
#替换成你设备aria2.conf路径
|
||||||
|
aria2_conf=${aria2_conf_dir}/aria2.conf.main
|
||||||
|
#替换成你设备的aria2c路径
|
||||||
|
aria2c=/usr/bin/aria2c
|
||||||
|
Green_font_prefix="\033[32m"
|
||||||
|
Red_font_prefix="\033[31m"
|
||||||
|
Green_background_prefix="\033[42;37m"
|
||||||
|
Red_background_prefix="\033[41;37m"
|
||||||
|
Font_color_suffix="\033[0m"
|
||||||
|
Info="[${Green_font_prefix}信息${Font_color_suffix}]"
|
||||||
|
Error="[${Red_font_prefix}错误${Font_color_suffix}]"
|
||||||
|
Tip="[${Green_font_prefix}注意${Font_color_suffix}]"
|
||||||
|
error_code=11
|
||||||
|
success_code=0
|
||||||
|
|
||||||
|
return_error(){
|
||||||
|
echo 'Content-Type:application/json;charset=utf-8'
|
||||||
|
echo
|
||||||
|
echo "{
|
||||||
|
"\"success\"":$error_code,
|
||||||
|
"\"error\"":"\"$1\"",
|
||||||
|
"\"result"\":null
|
||||||
|
}"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
return_ok(){
|
||||||
|
echo 'Content-Type:application/json;charset=utf-8'
|
||||||
|
echo
|
||||||
|
echo "{
|
||||||
|
"\"success\"":$success_code,
|
||||||
|
"\"error\"":"\"$1\"",
|
||||||
|
"\"result"\":null
|
||||||
|
}"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
return_result(){
|
||||||
|
echo 'Content-Type:application/json;charset=utf-8'
|
||||||
|
echo
|
||||||
|
echo "{
|
||||||
|
"\"success\"":$success_code,
|
||||||
|
"\"error\"":"\"\"",
|
||||||
|
"\"result"\":$1
|
||||||
|
}"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#进程中是否运行aria2
|
||||||
|
check_pid() {
|
||||||
|
PID=$(ps -ef | grep "aria2c" | grep -v grep | grep -v "aria2.sh" | grep -v "init.d" | grep -v "service" | awk '{print $2}')
|
||||||
|
}
|
||||||
|
|
||||||
|
#aria2是否正在运行
|
||||||
|
aria2_work_status(){
|
||||||
|
check_pid
|
||||||
|
# [[ ! -z ${PID} ]] && echo -e "${Error} Aria2 正在运行,请检查 !" && exit 1
|
||||||
|
[[ ! -z ${PID} ]] && return_ok "Aria2正在运行"
|
||||||
|
return_error "Aria2未运行"
|
||||||
|
}
|
||||||
|
|
||||||
|
#检测设备是否安装aria2
|
||||||
|
check_installed_status() {
|
||||||
|
[[ ! -e ${aria2c} ]] && return_error "Aria2 没有安装,请检查 !"
|
||||||
|
[[ ! -e ${aria2_conf} ]] && return_error "Aria2 配置文件不存在,请检查 !"
|
||||||
|
# return_ok "Aria2已安装"
|
||||||
|
}
|
||||||
|
#读取aria2配置信息
|
||||||
|
read_config() {
|
||||||
|
check_installed_status
|
||||||
|
if [[ ! -e ${aria2_conf} ]]; then
|
||||||
|
return_error "Aria2 配置文件不存在,请检查 !"
|
||||||
|
else
|
||||||
|
conf_text=$(cat ${aria2_conf} | grep -v '#')
|
||||||
|
aria2_dir=$(echo -e "${conf_text}" | grep "^dir=" | awk -F "=" '{print $NF}')
|
||||||
|
aria2_port=$(echo -e "${conf_text}" | grep "^rpc-listen-port=" | awk -F "=" '{print $NF}')
|
||||||
|
aria2_passwd=$(echo -e "${conf_text}" | grep "^rpc-secret=" | awk -F "=" '{print $NF}')
|
||||||
|
aria2_bt_port=$(echo -e "${conf_text}" | grep "^listen-port=" | awk -F "=" '{print $NF}')
|
||||||
|
aria2_dht_port=$(echo -e "${conf_text}" | grep "^dht-listen-port=" | awk -F "=" '{print $NF}')
|
||||||
|
|
||||||
|
return_result "{
|
||||||
|
"\"dir"\":"\"$aria2_dir"\",
|
||||||
|
"\"rpc-listen-port"\":"\"$aria2_port"\",
|
||||||
|
"\"rpc-secret"\":"\"$aria2_passwd"\",
|
||||||
|
"\"listen-port"\":"\"$aria2_bt_port"\",
|
||||||
|
"\"dht-listen-port"\":"\"$aria2_dht_port"\"}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#"Content-Type:text/html;charset=utf-8"
|
||||||
|
#echo
|
||||||
|
|
||||||
|
#SERVER_SOFTWARE = $SERVER_SOFTWARE #服务器软件
|
||||||
|
#SERVER_NAME = $SERVER_NAME #服务器主机名
|
||||||
|
#GATEWAY_INTERFACE = $GATEWAY_INTERFACE #CGI版本
|
||||||
|
#SERVER_PROTOCOL = $SERVER_PROTOCOL #通信使用的协议
|
||||||
|
#SERVER_PORT = $SERVER_PORT #服务器的端口号
|
||||||
|
#REQUEST_METHOD = $REQUEST_METHOD #请求方法(GET/POST/PUT/DELETE..)
|
||||||
|
#HTTP_ACCEPT = $HTTP_ACCEPT #HTTP定义的浏览器能够接受的数据类型
|
||||||
|
#SCRIPT_NAME = $SCRIPT_NAME #当前运行的脚本名称(包含路径)
|
||||||
|
#QUERY_STRING = $QUERY_STRING #地址栏中传的数据(get方式)
|
||||||
|
#REMOTE_ADDR = $REMOTE_ADDR #客户端的ip
|
||||||
|
|
||||||
|
#根据url QUERY调不同方法
|
||||||
|
query(){
|
||||||
|
aria2Query=${QUERY_STRING}
|
||||||
|
parse(){
|
||||||
|
echo $1 | sed 's/.*'$2'=\([[:alnum:]]*\).*/\1/'
|
||||||
|
}
|
||||||
|
value=$(parse $aria2Query "action")
|
||||||
|
|
||||||
|
if [ ! -z = "$value" ]
|
||||||
|
then
|
||||||
|
if [ "$value" = "status" ]
|
||||||
|
then
|
||||||
|
check_installed_status
|
||||||
|
elif [ "$value" = "readConfig" ]
|
||||||
|
then
|
||||||
|
read_config
|
||||||
|
elif [ "$value" = "workStatus" ]
|
||||||
|
then
|
||||||
|
aria2_work_status
|
||||||
|
else
|
||||||
|
echo
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
return_error "action不能为空"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
query
|
89
linkease/files/linkease-config.sh
Executable file
@ -0,0 +1,89 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
source /lib/functions.sh
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
save)
|
||||||
|
if [ ! -z "$2" ]; then
|
||||||
|
uci set "linkease.@linkease[0].preconfig=$2"
|
||||||
|
uci commit
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
load)
|
||||||
|
if [ -f "/usr/sbin/preconfig.data" ]; then
|
||||||
|
data="`cat /usr/sbin/preconfig.data`"
|
||||||
|
uci set "linkease.@linkease[0].preconfig=${data}"
|
||||||
|
uci commit
|
||||||
|
rm /usr/sbin/preconfig.data
|
||||||
|
else
|
||||||
|
data="`uci -q get linkease.@linkease[0].preconfig`"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "${data}" ]; then
|
||||||
|
echo "nil"
|
||||||
|
else
|
||||||
|
echo "${data}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
;;
|
||||||
|
|
||||||
|
local_save)
|
||||||
|
if [ ! -z "$2" ]; then
|
||||||
|
uci set "linkease.@linkease[0].local_home=$2"
|
||||||
|
uci commit
|
||||||
|
ROOT_DIR="$2"
|
||||||
|
if [ -f "/etc/config/quickstart" ]; then
|
||||||
|
config_load quickstart
|
||||||
|
config_get MAIN_DIR main main_dir ""
|
||||||
|
config_get CONF_DIR main conf_dir ""
|
||||||
|
config_get PUB_DIR main pub_dir ""
|
||||||
|
config_get DL_DIR main dl_dir ""
|
||||||
|
config_get TMP_DIR main tmp_dir ""
|
||||||
|
# echo "$MAIN_DIR $CONF_DIR $PUB_DIR $DL_DIR $TMP_DIR"
|
||||||
|
if [ "$ROOT_DIR" = "$MAIN_DIR" ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
uci set "quickstart.main.main_dir=$ROOT_DIR"
|
||||||
|
if [ -z "$CONF_DIR" -o "$CONF_DIR" = "$MAIN_DIR/Configs" ]; then
|
||||||
|
uci set "quickstart.main.conf_dir=$ROOT_DIR/Configs"
|
||||||
|
fi
|
||||||
|
if [ -z "$PUB_DIR" -o "$PUB_DIR" = "$MAIN_DIR/Public" ]; then
|
||||||
|
uci set "quickstart.main.pub_dir=$ROOT_DIR/Public"
|
||||||
|
fi
|
||||||
|
if [ -z "$DL_DIR" -o "$DL_DIR" = "$MAIN_DIR/Public/Downloads" ]; then
|
||||||
|
uci set "quickstart.main.dl_dir=$ROOT_DIR/Public/Downloads"
|
||||||
|
fi
|
||||||
|
if [ -z "$TMP_DIR" -o "$TMP_DIR" = "$MAIN_DIR/Caches" ]; then
|
||||||
|
uci set "quickstart.main.tmp_dir=$ROOT_DIR/Caches"
|
||||||
|
fi
|
||||||
|
uci commit
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
local_load)
|
||||||
|
if [ -f "/etc/config/quickstart" ]; then
|
||||||
|
data="`uci -q get quickstart.main.main_dir`"
|
||||||
|
fi
|
||||||
|
if [ -z "$data" ]; then
|
||||||
|
data="`uci -q get linkease.@linkease[0].local_home`"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "${data}" ]; then
|
||||||
|
echo "nil"
|
||||||
|
else
|
||||||
|
echo "${data}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
;;
|
||||||
|
|
||||||
|
status)
|
||||||
|
echo "TODO"
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
echo "Usage: $0 {save|load|status}"
|
||||||
|
exit 1
|
||||||
|
esac
|
||||||
|
|
3
linkease/files/linkease.config
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
config linkease
|
||||||
|
option port '8897'
|
||||||
|
option enabled '1'
|
28
linkease/files/linkease.init
Executable file
@ -0,0 +1,28 @@
|
|||||||
|
#!/bin/sh /etc/rc.common
|
||||||
|
|
||||||
|
START=99
|
||||||
|
USE_PROCD=1
|
||||||
|
|
||||||
|
get_config() {
|
||||||
|
config_get_bool enabled $1 enabled 1
|
||||||
|
config_get_bool logger $1 logger
|
||||||
|
config_get port $1 port 8897
|
||||||
|
}
|
||||||
|
|
||||||
|
start_service() {
|
||||||
|
config_load linkease
|
||||||
|
config_foreach get_config linkease
|
||||||
|
[ $enabled != 1 ] && return 1
|
||||||
|
|
||||||
|
procd_open_instance
|
||||||
|
procd_set_param limits nofile="65535 65535"
|
||||||
|
procd_set_param command /usr/sbin/linkease
|
||||||
|
[ -n "$port" ] && procd_append_param command --deviceAddr ":$port" --localApi /var/run/linkease.sock
|
||||||
|
[ "$logger" == 1 ] && procd_set_param stderr 1
|
||||||
|
procd_set_param respawn
|
||||||
|
procd_close_instance
|
||||||
|
}
|
||||||
|
|
||||||
|
service_triggers() {
|
||||||
|
procd_add_reload_trigger "linkease"
|
||||||
|
}
|
22
linkease/files/linkease.uci-default
Executable file
@ -0,0 +1,22 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
uci -q batch <<-EOF >/dev/null
|
||||||
|
delete ucitrack.@linkease[-1]
|
||||||
|
add ucitrack linkease
|
||||||
|
set ucitrack.@linkease[-1].init=linkease
|
||||||
|
commit ucitrack
|
||||||
|
|
||||||
|
delete firewall.linkease
|
||||||
|
set firewall.linkease=rule
|
||||||
|
set firewall.linkease.name="linkease"
|
||||||
|
set firewall.linkease.target="ACCEPT"
|
||||||
|
set firewall.linkease.src="wan"
|
||||||
|
set firewall.linkease.proto="tcp"
|
||||||
|
set firewall.linkease.dest_port="8897"
|
||||||
|
commit firewall
|
||||||
|
EOF
|
||||||
|
|
||||||
|
/etc/init.d/linkease enable
|
||||||
|
/etc/init.d/linkease start
|
||||||
|
|
||||||
|
exit 0
|
18
luci-app-floatip/Makefile
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Copyright (C) 2016 Openwrt.org
|
||||||
|
#
|
||||||
|
# This is free software, licensed under the Apache License, Version 2.0 .
|
||||||
|
#
|
||||||
|
|
||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
LUCI_TITLE:=LuCI support for floatip
|
||||||
|
LUCI_DEPENDS:=+floatip
|
||||||
|
LUCI_PKGARCH:=all
|
||||||
|
PKG_VERSION:=0.1.2-1
|
||||||
|
# PKG_RELEASE MUST be empty for luci.mk
|
||||||
|
PKG_RELEASE:=
|
||||||
|
|
||||||
|
include $(TOPDIR)/feeds/luci/luci.mk
|
||||||
|
|
||||||
|
# call BuildPackage - OpenWrt buildroot signature
|
||||||
|
|
24
luci-app-floatip/luasrc/controller/floatip.lua
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
module("luci.controller.floatip", package.seeall)
|
||||||
|
|
||||||
|
function index()
|
||||||
|
if not nixio.fs.access("/etc/config/floatip") then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
entry({"admin", "services", "floatip"}, cbi("floatip"), _("FloatingGateway"), 20).dependent = true
|
||||||
|
|
||||||
|
entry({"admin", "services", "floatip_status"}, call("floatip_status"))
|
||||||
|
end
|
||||||
|
|
||||||
|
function floatip_status()
|
||||||
|
local sys = require "luci.sys"
|
||||||
|
local uci = require "luci.model.uci".cursor()
|
||||||
|
|
||||||
|
local status = {
|
||||||
|
running = (sys.call("pidof floatip.sh >/dev/null") == 0),
|
||||||
|
}
|
||||||
|
|
||||||
|
luci.http.prepare_content("application/json")
|
||||||
|
luci.http.write_json(status)
|
||||||
|
end
|
||||||
|
|
34
luci-app-floatip/luasrc/model/cbi/floatip.lua
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
local m, s, o
|
||||||
|
|
||||||
|
m = Map("floatip", translate("FloatingGateway"), translate("FloatingGateway allows two gateway within one lan which can switch between each other in case of a failure."))
|
||||||
|
|
||||||
|
m:section(SimpleSection).template = "floatip_status"
|
||||||
|
|
||||||
|
s=m:section(NamedSection, "main", translate("Global settings"))
|
||||||
|
s.anonymous=true
|
||||||
|
|
||||||
|
o = s:option(Flag, "enabled", translate("Enable"))
|
||||||
|
o.rmempty = false
|
||||||
|
|
||||||
|
o = s:option(ListValue, "role", translate("Node Role"))
|
||||||
|
o.rmempty = false
|
||||||
|
o.widget = "select"
|
||||||
|
o:value("main", translate("Preempt Node"))
|
||||||
|
o:value("fallback", translate("Fallback Node"))
|
||||||
|
|
||||||
|
o = s:option(Value, "set_ip", translate("Floating Gateway IP"))
|
||||||
|
o.rmempty = false
|
||||||
|
o.datatype = "or(ip4addr,cidr4)"
|
||||||
|
|
||||||
|
o = s:option(Value, "check_ip", translate("Preempt Node IP"))
|
||||||
|
o.datatype = "ip4addr"
|
||||||
|
o:depends("role", "fallback")
|
||||||
|
|
||||||
|
o = s:option(Value, "check_url", translate("Check URL"), translate("If status code of the URL is not 2xx, then release the floating IP and disable LAN port pinging"))
|
||||||
|
o:depends("role", "main")
|
||||||
|
|
||||||
|
o = s:option(Value, "check_url_timeout", translate("Check URL Timeout (s)"), translate("Default is 5 seconds if not set"))
|
||||||
|
o.datatype = "uinteger"
|
||||||
|
o:depends("role", "main")
|
||||||
|
|
||||||
|
return m
|
26
luci-app-floatip/luasrc/view/floatip_status.htm
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<script type="text/javascript">//<![CDATA[
|
||||||
|
XHR.poll(5, '<%=url("admin/services/floatip_status")%>', null,
|
||||||
|
function(x, st)
|
||||||
|
{
|
||||||
|
var tb = document.getElementById('floatip_status');
|
||||||
|
if (st && tb)
|
||||||
|
{
|
||||||
|
if (st.running)
|
||||||
|
{
|
||||||
|
tb.innerHTML = '<br/><em style=\"color:green\"><%:The FloatingGateway service is running.%></em>';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tb.innerHTML = '<br/><em style=\"color:red\"><%:The FloatingGateway service is not running.%></em>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
//]]></script>
|
||||||
|
|
||||||
|
<fieldset class="cbi-section">
|
||||||
|
<legend><%:FloatingGateway Status%></legend>
|
||||||
|
<p id="floatip_status">
|
||||||
|
<em><%:Collecting data...%></em>
|
||||||
|
</p>
|
||||||
|
</fieldset>
|
47
luci-app-floatip/po/zh-cn/floatip.po
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
msgid "FloatingGateway"
|
||||||
|
msgstr "浮动网关"
|
||||||
|
|
||||||
|
msgid "Running state"
|
||||||
|
msgstr "运行状态"
|
||||||
|
|
||||||
|
msgid "FloatingGateway allows two gateway within one lan which can switch between each other in case of a failure."
|
||||||
|
msgstr "浮动网关可以让你在内网有两个相互备份的网关,出现问题会相互切换。"
|
||||||
|
|
||||||
|
msgid "The FloatingGateway service is running."
|
||||||
|
msgstr "服务已启动"
|
||||||
|
|
||||||
|
msgid "The FloatingGateway service is not running."
|
||||||
|
msgstr "服务未启动"
|
||||||
|
|
||||||
|
msgid "FloatingGateway Status"
|
||||||
|
msgstr "服务状态"
|
||||||
|
|
||||||
|
msgid "Collecting data..."
|
||||||
|
msgstr "收集数据..."
|
||||||
|
|
||||||
|
msgid "Node Role"
|
||||||
|
msgstr "节点角色"
|
||||||
|
|
||||||
|
msgid "Preempt Node"
|
||||||
|
msgstr "旁路由"
|
||||||
|
|
||||||
|
msgid "Fallback Node"
|
||||||
|
msgstr "主路由"
|
||||||
|
|
||||||
|
msgid "Floating Gateway IP"
|
||||||
|
msgstr "浮动网关 IP"
|
||||||
|
|
||||||
|
msgid "Preempt Node IP"
|
||||||
|
msgstr "旁路由 IP"
|
||||||
|
|
||||||
|
msgid "Check URL"
|
||||||
|
msgstr "检查 URL"
|
||||||
|
|
||||||
|
msgid "If status code of the URL is not 2xx, then release the floating IP and disable LAN port pinging"
|
||||||
|
msgstr "如果检查URL状态码不是2xx,则释放浮动IP,并禁止LAN口ping"
|
||||||
|
|
||||||
|
msgid "Check URL Timeout (s)"
|
||||||
|
msgstr "检查 URL 超时(秒)"
|
||||||
|
|
||||||
|
msgid "Default is 5 seconds if not set"
|
||||||
|
msgstr "默认5秒"
|
1
luci-app-floatip/po/zh_Hans
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
zh-cn
|
20
luci-app-linkease/Makefile
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# Copyright (C) 2016 Openwrt.org
|
||||||
|
#
|
||||||
|
# This is free software, licensed under the Apache License, Version 2.0 .
|
||||||
|
#
|
||||||
|
|
||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
LUCI_TITLE:=LuCI support for linkease
|
||||||
|
LUCI_DEPENDS:=+linkease
|
||||||
|
LUCI_PKGARCH:=all
|
||||||
|
PKG_VERSION:=2.1.12-2
|
||||||
|
# PKG_RELEASE MUST be empty for luci.mk
|
||||||
|
PKG_RELEASE:=
|
||||||
|
LUCI_MINIFY_CSS:=0
|
||||||
|
LUCI_MINIFY_JS:=0
|
||||||
|
|
||||||
|
include $(TOPDIR)/feeds/luci/luci.mk
|
||||||
|
|
||||||
|
# call BuildPackage - OpenWrt buildroot signature
|
||||||
|
|
@ -0,0 +1 @@
|
|||||||
|
import{ab as L,i as v,ac as M}from"./index.js";var N=/\s/;function $(n){for(var r=n.length;r--&&N.test(n.charAt(r)););return r}var B=/^\s+/;function R(n){return n&&n.slice(0,$(n)+1).replace(B,"")}var k=0/0,F=/^[-+]0x[0-9a-f]+$/i,_=/^0b[01]+$/i,j=/^0o[0-7]+$/i,D=parseInt;function S(n){if(typeof n=="number")return n;if(L(n))return k;if(v(n)){var r=typeof n.valueOf=="function"?n.valueOf():n;n=v(r)?r+"":r}if(typeof n!="string")return n===0?n:+n;n=R(n);var t=_.test(n);return t||j.test(n)?D(n.slice(2),t?2:8):F.test(n)?k:+n}var H=function(){return M.Date.now()};const h=H;var P="Expected a function",U=Math.max,X=Math.min;function z(n,r,t){var u,c,l,s,i,f,o=0,b=!1,d=!1,T=!0;if(typeof n!="function")throw new TypeError(P);r=S(r)||0,v(t)&&(b=!!t.leading,d="maxWait"in t,l=d?U(S(t.maxWait)||0,r):l,T="trailing"in t?!!t.trailing:T);function x(e){var a=u,m=c;return u=c=void 0,o=e,s=n.apply(m,a),s}function W(e){return o=e,i=setTimeout(g,r),b?x(e):s}function O(e){var a=e-f,m=e-o,E=r-a;return d?X(E,l-m):E}function p(e){var a=e-f,m=e-o;return f===void 0||a>=r||a<0||d&&m>=l}function g(){var e=h();if(p(e))return y(e);i=setTimeout(g,O(e))}function y(e){return i=void 0,T&&u?x(e):(u=c=void 0,s)}function A(){i!==void 0&&clearTimeout(i),o=0,u=f=c=i=void 0}function C(){return i===void 0?s:y(h())}function I(){var e=h(),a=p(e);if(u=arguments,c=this,f=e,a){if(i===void 0)return W(f);if(d)return clearTimeout(i),i=setTimeout(g,r),x(f)}return i===void 0&&(i=setTimeout(g,r)),s}return I.cancel=A,I.flush=C,I}export{z as d};
|
@ -0,0 +1 @@
|
|||||||
|
import{e as t,r as a,F as _,y as s,S as o,q as n,a7 as c}from"./index.js";const r={class:"layout-conatiner"},d={class:"layout-f"},l={class:"layout-r"},i=t({__name:"index",setup(u){return a(!0),(e,p)=>(n(),_("div",r,[s("div",d,[o(e.$slots,"f",{},void 0,!0)]),s("div",l,[o(e.$slots,"r",{},void 0,!0)])]))}});const v=c(i,[["__scopeId","data-v-cbf72d2b"]]);export{v as l};
|
@ -0,0 +1 @@
|
|||||||
|
var g=(e,t,s)=>new Promise((a,l)=>{var c=i=>{try{n(s.next(i))}catch(d){l(d)}},u=i=>{try{n(s.throw(i))}catch(d){l(d)}},n=i=>i.done?a(i.value):Promise.resolve(i.value).then(c,u);n((s=s.apply(e,t)).next())});import{a8 as h,a9 as y,aa as f}from"./index.js";const o=e=>["linkease-file_web",e].join("/"),r={model:{get(){return localStorage.getItem(o("model"))=="true"},set(e){localStorage.setItem(o("model"),e)}},order:{get(){const e=localStorage.getItem(o("order"));return e||"date"},set(e){localStorage.setItem(o("order"),e)}},orderBy:{get(){return localStorage.getItem(o("orderBy"))||"asc"},set(e){localStorage.setItem(o("orderBy"),e)}},filter:{get(){return localStorage.getItem(o("filter"))||"default"},set(e){localStorage.setItem(o("filter"),e)}},copy:{get(){let e={type:"copy",values:[]};try{const t=localStorage.getItem(o("copy"));t&&(e=JSON.parse(t))}catch(t){}return e},set(e){const t=JSON.stringify(e);localStorage.setItem(o("copy"),t)}}},m=h("app",{state:()=>({keys:0,config:{model:r.model.get(),order:r.order.get(),orderBy:r.orderBy.get(),filter:r.filter.get()},menus:[],entries:[],checknoxEntries:[],previewImage:{enable:!1,values:[]},copy:r.copy.get(),task:0}),getters:{isCopy(){var e,t;return((t=(e=this.copy)==null?void 0:e.values)==null?void 0:t.length)>0}},actions:{reload(){this.keys++},taskIncr(e){e===void 0?this.task=0:this.task+=e},getEntries(){return g(this,null,function*(){try{const e=yield y.File.Basic.List.POST({path:"/local"});if(e.data){const{success:t,result:s,error:a}=e.data;t==0&&(this.menus=s.entries||[]),a&&f.Warning(a)}}catch(e){console.log(e),f.Error(`${e}`)}})},setConfigModel(){this.config.model=!this.config.model,r.model.set(`${this.config.model}`)},setConfigOrder(e){this.config.order=e,r.order.set(e)},setConfigOrderBy(e){this.config.orderBy=e,r.orderBy.set(e)},setConfigFilter(e){this.config.filter=e,r.filter.set(e)},setCopy(e){this.copy.type=e.type,this.copy.values=e.values||[],r.copy.set(e)}}}),S=h("editor",{state:()=>({entries:[],editableTabs:[],editableTabsValue:""}),actions:{addTab(e){for(let t=0;t<this.editableTabs.length;t++)if(this.editableTabs[t].rootPath===e.rootPath){this.editableTabsValue=e.rootPath||"";return}this.editableTabs.push(e),this.editableTabsValue=e.rootPath||""},removeTab(e){const t=this.editableTabs;let s=this.editableTabsValue;s===e&&t.forEach((a,l)=>{if(a.rootPath===e){const c=t[l+1]||t[l-1];c&&(s=c.rootPath||"")}}),this.editableTabsValue=s,this.editableTabs=t.filter(a=>a.rootPath!==e)}}});export{S as a,r as s,m as u};
|
@ -0,0 +1 @@
|
|||||||
|
var u=(c,d,n)=>new Promise((p,r)=>{var s=a=>{try{i(n.next(a))}catch(e){r(e)}},m=a=>{try{i(n.throw(a))}catch(e){r(e)}},i=a=>a.done?p(a.value):Promise.resolve(a.value).then(s,m);i((n=n.apply(c,d)).next())});import{e as S,r as x,aA as D,az as g,t as B,x as y,A as l,a2 as w,q as _,y as o,P as F,bH as I,F as f,O as C,I as b,v as V,a3 as k,bI as q,aM as A,B as N,a9 as P,a4 as T,C as L,a5 as M,a6 as O,aB as h,a7 as z}from"./index.js";import{u as G}from"./chunk.6424c2fc.js";const v=c=>(M("data-v-61900a83"),c=c(),O(),c),H=["onSubmit"],U={class:"form-item"},W=v(()=>o("div",{class:"form-item_label form-required"}," \u6570\u636E\u76EE\u5F55 ",-1)),$={class:"form-item_content"},j=v(()=>o("option",{value:"",selected:""},"\u9009\u62E9\u6570\u636E\u76EE\u5F55",-1)),J=["value"],K={class:"form-btns"},Q=v(()=>o("div",{class:"auto"},null,-1)),R=S({__name:"index",setup(c){return u(this,null,function*(){let d,n;const p=G(),r=x(!1),s=D({dataPath:""}),m=()=>u(this,null,function*(){try{const a=yield P.LocalDevice.Data.GET();if(a.data){const{success:e,result:t}=a.data;t!=null&&t.dataPath&&(s.dataPath=t.dataPath)}}catch(a){}});[d,n]=g(()=>m()),yield d,n();const i=()=>u(this,null,function*(){if(!(s.dataPath==""||s.dataPath==null)){r.value=!0;try{const a={dataPath:s.dataPath},e=yield P.LocalDevice.Data.POST(a);if(e.data){const{success:t,error:E}=e.data;E&&h.Warning(E),t===0&&h.Success("\u4FDD\u5B58\u6210\u529F")}}catch(a){h.Error(`${a}`)}finally{r.value=!1}}});return(a,e)=>(_(),B(l(w),{id:"page"},{default:y(()=>[o("form",{onSubmit:N(i,["prevent"])},[o("div",U,[W,o("div",$,[F(o("select",{placeholder:"\u9009\u62E9\u6570\u636E\u76EE\u5F55","onUpdate:modelValue":e[0]||(e[0]=t=>s.dataPath=t),required:""},[j,(_(!0),f(b,null,C(l(p).menus,t=>(_(),f(b,null,[t.name!="root"?(_(),f("option",{key:0,value:"/local/"+t.name},T(t.name),9,J)):L("",!0)],64))),256))],512),[[I,s.dataPath,void 0,{trim:!0}]])])]),o("div",K,[Q,V(l(A),{type:"primary","native-type":"submit",icon:l(q),loading:r.value,disabled:r.value},{default:y(()=>[k(" \u4FDD\u5B58 ")]),_:1},8,["icon","loading","disabled"])])],40,H)]),_:1}))})}});const aa=z(R,[["__scopeId","data-v-61900a83"]]);export{aa as default};
|
BIN
luci-app-linkease/htdocs/luci-static/linkeasefile/codicon.ttf
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
luci-app-linkease/htdocs/luci-static/linkeasefile/icons/dir.png
Normal file
After Width: | Height: | Size: 8.1 KiB |
BIN
luci-app-linkease/htdocs/luci-static/linkeasefile/icons/doc.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 8.2 KiB |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 4.7 KiB |
20
luci-app-linkease/htdocs/luci-static/linkeasefile/index.js
Normal file
39
luci-app-linkease/luasrc/controller/linkease.lua
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
module("luci.controller.linkease", package.seeall)
|
||||||
|
|
||||||
|
function index()
|
||||||
|
if not nixio.fs.access("/etc/config/linkease") then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
entry({"admin", "services", "linkease"}, cbi("linkease"), _("LinkEase"), 20).dependent = true
|
||||||
|
|
||||||
|
entry({"admin", "services", "linkease_status"}, call("linkease_status"))
|
||||||
|
|
||||||
|
entry({"admin", "services", "linkease", "file"}, call("linkease_file_template")).leaf = true
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function linkease_status()
|
||||||
|
local sys = require "luci.sys"
|
||||||
|
local uci = require "luci.model.uci".cursor()
|
||||||
|
local port = tonumber(uci:get_first("linkease", "linkease", "port"))
|
||||||
|
|
||||||
|
local status = {
|
||||||
|
running = (sys.call("pidof linkease >/dev/null") == 0),
|
||||||
|
port = (port or 8897)
|
||||||
|
}
|
||||||
|
|
||||||
|
luci.http.prepare_content("application/json")
|
||||||
|
luci.http.write_json(status)
|
||||||
|
end
|
||||||
|
|
||||||
|
function get_params(name)
|
||||||
|
local data = {
|
||||||
|
prefix=luci.dispatcher.build_url(unpack({"admin", "services", "linkease", name})),
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
end
|
||||||
|
|
||||||
|
function linkease_file_template()
|
||||||
|
luci.template.render("linkease/file", get_params("file"))
|
||||||
|
end
|
191
luci-app-linkease/luasrc/controller/linkease_backend.lua
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
-- Copyright 2022 xiaobao <xiaobao@linkease.com>
|
||||||
|
-- Licensed to the public under the MIT License
|
||||||
|
|
||||||
|
local http = require "luci.http"
|
||||||
|
local nixio = require "nixio"
|
||||||
|
local ltn12 = require "luci.ltn12"
|
||||||
|
local table = require "table"
|
||||||
|
local util = require "luci.util"
|
||||||
|
|
||||||
|
module("luci.controller.linkease_backend", package.seeall)
|
||||||
|
|
||||||
|
local BLOCKSIZE = 2048
|
||||||
|
local LINKEASE_UNIX = "/var/run/linkease.sock"
|
||||||
|
|
||||||
|
function index()
|
||||||
|
entry({"linkease"}, call("linkease_backend")).leaf=true
|
||||||
|
end
|
||||||
|
|
||||||
|
local function sink_socket(sock, io_err)
|
||||||
|
if sock then
|
||||||
|
return function(chunk, err)
|
||||||
|
if not chunk then
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
return sock:send(chunk)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return ltn12.sink.error(io_err or "unable to send socket")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function session_retrieve(sid, allowed_users)
|
||||||
|
local sdat = util.ubus("session", "get", { ubus_rpc_session = sid })
|
||||||
|
if type(sdat) == "table" and
|
||||||
|
type(sdat.values) == "table" and
|
||||||
|
type(sdat.values.token) == "string" and
|
||||||
|
(not allowed_users or
|
||||||
|
util.contains(allowed_users, sdat.values.username))
|
||||||
|
then
|
||||||
|
return sid, sdat.values
|
||||||
|
end
|
||||||
|
return nil, nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local function get_session()
|
||||||
|
local sid
|
||||||
|
local key
|
||||||
|
local sdat
|
||||||
|
for _, key in ipairs({"sysauth_https", "sysauth_http", "sysauth"}) do
|
||||||
|
sid = http.getcookie(key)
|
||||||
|
if sid then
|
||||||
|
sid, sdat = session_retrieve(sid, nil)
|
||||||
|
if sid and sdat then
|
||||||
|
return sid, sdat
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil, nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local function chunksource(sock, buffer)
|
||||||
|
buffer = buffer or ""
|
||||||
|
return function()
|
||||||
|
local output
|
||||||
|
local _, endp, count = buffer:find("^([0-9a-fA-F]+);?.-\r\n")
|
||||||
|
while not count and #buffer <= 1024 do
|
||||||
|
local newblock, code = sock:recv(1024 - #buffer)
|
||||||
|
if not newblock then
|
||||||
|
return nil, code
|
||||||
|
end
|
||||||
|
buffer = buffer .. newblock
|
||||||
|
_, endp, count = buffer:find("^([0-9a-fA-F]+);?.-\r\n")
|
||||||
|
end
|
||||||
|
count = tonumber(count, 16)
|
||||||
|
if not count then
|
||||||
|
return nil, -1, "invalid encoding"
|
||||||
|
elseif count == 0 then
|
||||||
|
return nil
|
||||||
|
elseif count + 2 <= #buffer - endp then
|
||||||
|
output = buffer:sub(endp+1, endp+count)
|
||||||
|
buffer = buffer:sub(endp+count+3)
|
||||||
|
return output
|
||||||
|
else
|
||||||
|
output = buffer:sub(endp+1, endp+count)
|
||||||
|
buffer = ""
|
||||||
|
if count - #output > 0 then
|
||||||
|
local remain, code = sock:recvall(count-#output)
|
||||||
|
if not remain then
|
||||||
|
return nil, code
|
||||||
|
end
|
||||||
|
output = output .. remain
|
||||||
|
count, code = sock:recvall(2)
|
||||||
|
else
|
||||||
|
count, code = sock:recvall(count+2-#buffer+endp)
|
||||||
|
end
|
||||||
|
if not count then
|
||||||
|
return nil, code
|
||||||
|
end
|
||||||
|
return output
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function linkease_backend()
|
||||||
|
local sock = nixio.socket("unix", "stream")
|
||||||
|
if sock:connect(LINKEASE_UNIX) ~= true then
|
||||||
|
http.status(500, "connect failed")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local input = {}
|
||||||
|
input[#input+1] = http.getenv("REQUEST_METHOD") .. " " .. http.getenv("REQUEST_URI") .. " HTTP/1.1"
|
||||||
|
local req = http.context.request
|
||||||
|
local start = "HTTP_"
|
||||||
|
local start_len = string.len(start)
|
||||||
|
local ctype = http.getenv("CONTENT_TYPE")
|
||||||
|
if ctype then
|
||||||
|
input[#input+1] = "Content-Type: " .. ctype
|
||||||
|
end
|
||||||
|
for k, v in pairs(req.message.env) do
|
||||||
|
if string.sub(k, 1, start_len) == start and not string.find(k, "FORWARDED") then
|
||||||
|
input[#input+1] = string.sub(k, start_len+1, string.len(k)) .. ": " .. v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local sid, sdat = get_session()
|
||||||
|
if sdat ~= nil then
|
||||||
|
input[#input+1] = "X-Forwarded-Sid: " .. sid
|
||||||
|
input[#input+1] = "X-Forwarded-Token: " .. sdat.token
|
||||||
|
end
|
||||||
|
-- input[#input+1] = "X-Forwarded-For: " .. http.getenv("REMOTE_HOST") ..":".. http.getenv("REMOTE_PORT")
|
||||||
|
local num = tonumber(http.getenv("CONTENT_LENGTH")) or 0
|
||||||
|
input[#input+1] = "Content-Length: " .. tostring(num)
|
||||||
|
input[#input+1] = "\r\n"
|
||||||
|
local source = ltn12.source.cat(ltn12.source.string(table.concat(input, "\r\n")), http.source())
|
||||||
|
local ret = ltn12.pump.all(source, sink_socket(sock, "write sock error"))
|
||||||
|
if ret ~= 1 then
|
||||||
|
sock:close()
|
||||||
|
http.status(500, "proxy error")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local linesrc = sock:linesource()
|
||||||
|
local line, code, error = linesrc()
|
||||||
|
if not line then
|
||||||
|
sock:close()
|
||||||
|
http.status(500, "response parse failed")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local protocol, status, msg = line:match("^([%w./]+) ([0-9]+) (.*)")
|
||||||
|
if not protocol then
|
||||||
|
sock:close()
|
||||||
|
http.status(500, "response protocol error")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
num = tonumber(status) or 0
|
||||||
|
http.status(num, msg)
|
||||||
|
|
||||||
|
local allow_ranges = http.getenv("SERVER_SOFTWARE") ~= "uhttpd"
|
||||||
|
|
||||||
|
local chunked = 0
|
||||||
|
line = linesrc()
|
||||||
|
while line and line ~= "" do
|
||||||
|
local key, val = line:match("^([%w-]+)%s?:%s?(.*)")
|
||||||
|
if key and key ~= "Status" then
|
||||||
|
if key == "Transfer-Encoding" and val == "chunked" then
|
||||||
|
chunked = 1
|
||||||
|
end
|
||||||
|
if key ~= "Connection" and key ~= "Transfer-Encoding" and ( allow_ranges or (key ~= "Content-Length" and key ~= "Accept-Ranges") ) then
|
||||||
|
http.header(key, val)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
line = linesrc()
|
||||||
|
end
|
||||||
|
if not line then
|
||||||
|
sock:close()
|
||||||
|
http.status(500, "parse header failed")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local body_buffer = linesrc(true)
|
||||||
|
if chunked == 1 then
|
||||||
|
ltn12.pump.all(chunksource(sock, body_buffer), http.write)
|
||||||
|
else
|
||||||
|
local body_source = ltn12.source.cat(ltn12.source.string(body_buffer), sock:blocksource())
|
||||||
|
ltn12.pump.all(body_source, http.write)
|
||||||
|
end
|
||||||
|
|
||||||
|
sock:close()
|
||||||
|
end
|
||||||
|
|
20
luci-app-linkease/luasrc/model/cbi/linkease.lua
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
--wulishui <wulishui@gmail.com> ,20200911
|
||||||
|
--jjm2473 <jjm2473@gmail.com> ,20210127
|
||||||
|
|
||||||
|
local m, s
|
||||||
|
|
||||||
|
m = Map("linkease", translate("LinkEase"), translate("LinkEase is an efficient data transfer tool."))
|
||||||
|
|
||||||
|
m:section(SimpleSection).template = "linkease_status"
|
||||||
|
|
||||||
|
s=m:section(TypedSection, "linkease", translate("Global settings"))
|
||||||
|
s.addremove=false
|
||||||
|
s.anonymous=true
|
||||||
|
|
||||||
|
s:option(Flag, "enabled", translate("Enable")).rmempty=false
|
||||||
|
|
||||||
|
s:option(Value, "port", translate("Port")).rmempty=false
|
||||||
|
|
||||||
|
return m
|
||||||
|
|
||||||
|
|
@ -0,0 +1 @@
|
|||||||
|
<%+linkease_status%>
|
25
luci-app-linkease/luasrc/view/linkease/file.htm
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<!-- <meta name="viewport" content="width=device-width, initial-scale=1.0" /> -->
|
||||||
|
<meta name="viewport" content="width=1380" />
|
||||||
|
<title>易有云文件管理</title>
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
var pathe_prefix = "<%=prefix%>"
|
||||||
|
window.path_base = pathe_prefix
|
||||||
|
window.ver = "<%# PKG_VERSION %>"
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
<script type="module" crossorigin src="/luci-static/linkeasefile/index.js<%# ?v=PKG_VERSION %>"></script>
|
||||||
|
<link rel="stylesheet" href="/luci-static/linkeasefile/style.css<%# ?v=PKG_VERSION %>">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
28
luci-app-linkease/luasrc/view/linkease_status.htm
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<script type="text/javascript">//<![CDATA[
|
||||||
|
XHR.poll(5, '<%=url("admin/services/linkease_status")%>', null,
|
||||||
|
function(x, st)
|
||||||
|
{
|
||||||
|
var tb = document.getElementById('linkease_status');
|
||||||
|
if (st && tb)
|
||||||
|
{
|
||||||
|
if (st.running)
|
||||||
|
{
|
||||||
|
tb.innerHTML = '<br/><em style=\"color:green\"><%:The LinkEase service is running.%></em>'
|
||||||
|
+ "<br/><br/><input class=\"btn cbi-button cbi-button-apply\" type=\"button\" value=\" <%:Click to open LinkEase%> \" onclick=\"window.open('http://" + window.location.hostname + ":" + st.port + "/')\"/>"
|
||||||
|
+ "<br/><input class=\"btn cbi-button cbi-button-apply\" type=\"button\" value=\" <%:Click to open Files%> \" onclick=\"window.open('/cgi-bin/luci/admin/services/linkease/file/')\"/>";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tb.innerHTML = '<br/><em style=\"color:red\"><%:The LinkEase service is not running.%></em>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
//]]></script>
|
||||||
|
|
||||||
|
<fieldset class="cbi-section">
|
||||||
|
<legend><%:LinkEase Status%></legend>
|
||||||
|
<p id="linkease_status">
|
||||||
|
<em><%:Collecting data...%></em>
|
||||||
|
</p>
|
||||||
|
</fieldset>
|
30
luci-app-linkease/po/zh-cn/linkease.po
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
msgid "LinkEase"
|
||||||
|
msgstr "易有云文件管理器"
|
||||||
|
|
||||||
|
msgid "Running state"
|
||||||
|
msgstr "运行状态"
|
||||||
|
|
||||||
|
msgid "Click to open LinkEase"
|
||||||
|
msgstr "点击打开易有云"
|
||||||
|
|
||||||
|
msgid "Click to open Files"
|
||||||
|
msgstr "本地文件管理"
|
||||||
|
|
||||||
|
msgid "LinkEase is an efficient data transfer tool."
|
||||||
|
msgstr "易有云是一个微型家庭数据服务中心,主要用于文件的集中存放、读取、备份及日常管理。释放用户终端设备空间,实现个人、家庭文件长久留存;支持家庭相册、视频文件随时随地多终端查看、播放,满足家庭文件的日常管理。"
|
||||||
|
|
||||||
|
msgid "Port"
|
||||||
|
msgstr "端口"
|
||||||
|
|
||||||
|
msgid "The LinkEase service is running."
|
||||||
|
msgstr "易有云服务已启动"
|
||||||
|
|
||||||
|
msgid "The LinkEase service is not running."
|
||||||
|
msgstr "易有云服务未启动"
|
||||||
|
|
||||||
|
msgid "LinkEase Status"
|
||||||
|
msgstr "易有云服务状态"
|
||||||
|
|
||||||
|
msgid "Collecting data..."
|
||||||
|
msgstr "收集数据..."
|
||||||
|
|
1
luci-app-linkease/po/zh_Hans
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
zh-cn
|
4
luci-app-linkease/root/etc/uci-defaults/50_luci-linkease
Executable file
@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
rm -f /tmp/luci-indexcache
|
||||||
|
exit 0
|