diff --git a/baidudrive/Makefile b/baidudrive/Makefile
index 5c45a308..e08e4075 100644
--- a/baidudrive/Makefile
+++ b/baidudrive/Makefile
@@ -10,10 +10,10 @@ include $(TOPDIR)/rules.mk
PKG_ARCH_BAIDUDRIVE:=$(ARCH)
PKG_NAME:=baidudrive
-PKG_VERSION:=1.0.2
-PKG_RELEASE:=4
+PKG_VERSION:=baidudrive-runtime-v1.0.4
+PKG_RELEASE:=5
PKG_SOURCE:=$(PKG_NAME)-binary-$(PKG_VERSION).tar.gz
-PKG_SOURCE_URL:=https://github.com/linkease/istore-packages/releases/download/prebuilt/
+PKG_SOURCE_URL:=https://github.com/istoreos/istoreos-app-hub/releases/download/baidudrive-runtime-v$(PKG_VERSION)/
PKG_HASH:=skip
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-binary-$(PKG_VERSION)
diff --git a/baidudrive/files/baidudrive.init b/baidudrive/files/baidudrive.init
index da77e032..13a333ac 100644
--- a/baidudrive/files/baidudrive.init
+++ b/baidudrive/files/baidudrive.init
@@ -115,6 +115,8 @@ start_service() {
logger -t baidudrive "Starting Baidu NAS SDK"
procd_open_instance baiduNas
+ procd_set_param env \
+ BAIDU_NAS_GLIBC_DIR="/opt/baidunas-glibc"
procd_set_param command "$sdk_dir/baiduNas" \
-ip "$sdk_host" \
-port "$sdk_port" \
diff --git a/kaiplus/Makefile b/kaiplus/Makefile
index 645aff3f..4c45191c 100644
--- a/kaiplus/Makefile
+++ b/kaiplus/Makefile
@@ -10,7 +10,7 @@ PKG_ARCH_kaiplus:=$(ARCH)
PKG_NAME:=kaiplus
PKG_VERSION:=1.0.5
-PKG_RELEASE:=3
+PKG_RELEASE:=4
PKG_SOURCE:=$(PKG_NAME)-binary-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://github.com/linkease/istore-packages/releases/download/kaiplus/
PKG_HASH:=skip
diff --git a/kaiplus/files/kaiplus.config b/kaiplus/files/kaiplus.config
index d8169114..b9ddd2bf 100644
--- a/kaiplus/files/kaiplus.config
+++ b/kaiplus/files/kaiplus.config
@@ -1,5 +1,6 @@
config kaiplus
option 'enabled' '0'
option 'data_dir' ''
- option 'port' '8198'
+ option 'port' '8189'
+ option 'base_path' '/apps/kaiplus/'
option 'system_role' 'istoreos'
diff --git a/kaiplus/files/kaiplus.init b/kaiplus/files/kaiplus.init
index 22200d2e..96c05683 100644
--- a/kaiplus/files/kaiplus.init
+++ b/kaiplus/files/kaiplus.init
@@ -6,7 +6,8 @@ USE_PROCD=1
get_config() {
config_get_bool enabled "$1" enabled 1
config_get data_dir "$1" data_dir ""
- config_get port "$1" port "8198"
+ config_get port "$1" port "8189"
+ config_get base_path "$1" base_path "/apps/kaiplus/"
config_get system_role "$1" system_role "istoreos"
}
@@ -61,6 +62,7 @@ start_service() {
procd_append_param command --data-dir "$data_dir"
procd_append_param command --static-dir /usr/share/kaiplus/www
procd_append_param command --defaults-dir /usr/share/kaiplus/defaults
+ procd_append_param command --base-path "$base_path"
procd_append_param command --system-role "$system_role"
procd_set_param stdout 1
procd_set_param stderr 1
diff --git a/linkmount/Makefile b/linkease-common-bin/Makefile
similarity index 52%
rename from linkmount/Makefile
rename to linkease-common-bin/Makefile
index a6a3ec9c..ed5e2204 100644
--- a/linkmount/Makefile
+++ b/linkease-common-bin/Makefile
@@ -1,16 +1,18 @@
#
-# This is free software, licensed under the GNU General Public License v2.
-# See /LICENSE for more information.
+# 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:=linkmount
-# use PKG_SOURCE_DATE instead of PKG_VERSION for compitable
-PKG_SOURCE_DATE:=1.0.3
-PKG_RELEASE:=3
+PKG_NAME:=linkease-common-bin
+PKG_SOURCE_DATE:=1.7.5
+PKG_RELEASE:=1
+
ARCH_HEXCODE:=
ifeq ($(ARCH),x86_64)
ARCH_HEXCODE=8664
@@ -21,12 +23,12 @@ ARCH_HEXCODE=aa32
else ifeq ($(ARCH),mipsel)
ARCH_HEXCODE=1b0c
endif
-PKG_SOURCE_VERSION:=$(ARCH_HEXCODE)
-PKG_SOURCE:=$(PKG_NAME)-binary-$(PKG_SOURCE_DATE).tar.gz
-PKG_SOURCE_URL:=https://dl.istoreos.com/binary/LinkEase/AutoUpgrade/linkmount/
-PKG_HASH:=skip
-PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-binary-$(PKG_SOURCE_DATE)
+PKG_SOURCE_VERSION:=$(ARCH_HEXCODE)
+PKG_SOURCE:=linkease-binary-$(PKG_SOURCE_DATE).tar.gz
+PKG_SOURCE_URL:=https://dl.istoreos.com/binary/LinkEase/LinuxStorage/
+PKG_HASH:=skip
+PKG_BUILD_DIR:=$(BUILD_DIR)/linkease-binary-$(PKG_SOURCE_DATE)
PKG_BUILD_PARALLEL:=1
PKG_USE_MIPS16:=0
@@ -37,14 +39,14 @@ define Package/$(PKG_NAME)
SECTION:=net
CATEGORY:=Network
SUBMENU:=Web Servers/Proxies
- TITLE:=LinkMount - the file cloud
+ TITLE:=LinkEase common binary tools
DEPENDS:=@(arm||x86_64||aarch64)
PKGARCH:=all
URL:=https://www.linkease.com/
endef
define Package/$(PKG_NAME)/description
- LinkMount is used by LinkEase
+ Common LinkEase binary tools shared by LinkEase editions.
endef
define Build/Configure
@@ -54,10 +56,10 @@ define Build/Compile
endef
define Package/$(PKG_NAME)/install
- $(INSTALL_DIR) $(1)/usr/sbin/linkmount
- $(INSTALL_BIN) $(PKG_BUILD_DIR)/$(PKG_ARCH_LINKEASE)/bin/linkmount_bin $(1)/usr/sbin/linkmount/linkmount_bin
- $(CP) $(PKG_BUILD_DIR)/$(PKG_ARCH_LINKEASE)/bin/lib $(1)/usr/sbin/linkmount/lib
+ $(INSTALL_DIR) $(1)/usr/sbin
+ $(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
endef
$(eval $(call BuildPackage,$(PKG_NAME)))
-
diff --git a/linkease-common-bin/files/linkease-config.sh b/linkease-common-bin/files/linkease-config.sh
new file mode 100644
index 00000000..a915966a
--- /dev/null
+++ b/linkease-common-bin/files/linkease-config.sh
@@ -0,0 +1,112 @@
+#!/bin/sh
+
+. /lib/functions.sh
+
+ensure_linkease_config() {
+ if ! uci -q get linkease.@linkease[0] >/dev/null; then
+ uci -q add linkease linkease >/dev/null
+ uci -q set linkease.@linkease[0].enabled='0'
+ uci -q set linkease.@linkease[0].port='8897'
+ uci -q set linkease.@linkease[0].allowPublic='0'
+ uci -q commit linkease
+ fi
+}
+
+sync_linkeasefull_local_home() {
+ if uci -q get linkeasefull.@linkeasefull[0] >/dev/null; then
+ uci -q set "linkeasefull.@linkeasefull[0].data_root_parent=$1"
+ uci -q commit linkeasefull
+ fi
+}
+
+case "$1" in
+ save)
+ ensure_linkease_config
+ if [ ! -z "$2" ]; then
+ uci set "linkease.@linkease[0].preconfig=$2"
+ uci commit linkease
+ fi
+ ;;
+
+ load)
+ ensure_linkease_config
+ if [ -f "/usr/sbin/preconfig.data" ]; then
+ data="`cat /usr/sbin/preconfig.data`"
+ uci set "linkease.@linkease[0].preconfig=${data}"
+ uci commit linkease
+ 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)
+ ensure_linkease_config
+ if [ ! -z "$2" ]; then
+ uci set "linkease.@linkease[0].local_home=$2"
+ uci commit linkease
+ sync_linkeasefull_local_home "$2"
+ 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 ""
+ 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 quickstart
+ fi
+ fi
+ ;;
+
+ local_load)
+ ensure_linkease_config
+ 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
+ data="`uci -q get linkeasefull.@linkeasefull[0].data_root_parent`"
+ fi
+
+ if [ -z "${data}" ]; then
+ echo "nil"
+ else
+ echo "${data}"
+ fi
+
+ ;;
+
+ status)
+ echo "TODO"
+ ;;
+
+ *)
+ echo "Usage: $0 {save|load|local_save|local_load|status}"
+ exit 1
+esac
diff --git a/linkease/Makefile b/linkease/Makefile
index dbbb0346..5edd4f5a 100644
--- a/linkease/Makefile
+++ b/linkease/Makefile
@@ -10,9 +10,9 @@ include $(TOPDIR)/rules.mk
PKG_ARCH_LINKEASE:=$(ARCH)
PKG_NAME:=linkease
-# use PKG_SOURCE_DATE instead of PKG_VERSION for compitable
PKG_SOURCE_DATE:=1.7.5
-PKG_RELEASE:=3
+PKG_RELEASE:=4
+
ARCH_HEXCODE:=
ifeq ($(ARCH),x86_64)
ARCH_HEXCODE=8664
@@ -23,11 +23,11 @@ ARCH_HEXCODE=aa32
else ifeq ($(ARCH),mipsel)
ARCH_HEXCODE=1b0c
endif
-PKG_SOURCE_VERSION:=$(ARCH_HEXCODE)
-PKG_SOURCE:=$(PKG_NAME)-binary-$(PKG_SOURCE_DATE).tar.gz
-PKG_SOURCE_URL:=http://dl.istoreos.com/binary/LinkEase/LinuxStorage/
-PKG_HASH:=skip
+PKG_SOURCE_VERSION:=$(ARCH_HEXCODE)
+PKG_SOURCE:=linkease-binary-$(PKG_SOURCE_DATE).tar.gz
+PKG_SOURCE_URL:=https://dl.istoreos.com/binary/LinkEase/LinuxStorage/
+PKG_HASH:=skip
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-binary-$(PKG_SOURCE_DATE)
PKG_BUILD_PARALLEL:=1
@@ -40,7 +40,7 @@ define Package/$(PKG_NAME)
CATEGORY:=Network
SUBMENU:=Web Servers/Proxies
TITLE:=LinkEase - the file cloud
- DEPENDS:=@(arm||x86_64||aarch64) +linkmount
+ DEPENDS:=@(arm||x86_64||aarch64) +linkease-common-bin
PKGARCH:=all
URL:=https://www.linkease.com/
endef
@@ -70,9 +70,6 @@ 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
diff --git a/linkease/files/linkease-config.sh b/linkease/files/linkease-config.sh
old mode 100755
new mode 100644
index 5427faa4..62015a85
--- a/linkease/files/linkease-config.sh
+++ b/linkease/files/linkease-config.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-source /lib/functions.sh
+. /lib/functions.sh
case "$1" in
save)
@@ -40,7 +40,6 @@ case "$1" in
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
@@ -83,7 +82,6 @@ case "$1" in
;;
*)
- echo "Usage: $0 {save|load|status}"
+ echo "Usage: $0 {save|load|local_save|local_load|status}"
exit 1
esac
-
diff --git a/linkease/files/linkease.config b/linkease/files/linkease.config
index 5ece093b..ee982437 100644
--- a/linkease/files/linkease.config
+++ b/linkease/files/linkease.config
@@ -1,3 +1,4 @@
config linkease
option port '8897'
option enabled '1'
+ option allowPublic '0'
diff --git a/linkease/files/linkease.init b/linkease/files/linkease.init
old mode 100755
new mode 100644
index 0c51d6cf..a811349d
--- a/linkease/files/linkease.init
+++ b/linkease/files/linkease.init
@@ -3,24 +3,37 @@
START=99
USE_PROCD=1
+PROG=/usr/sbin/linkease
+LOCAL_API=/var/run/linkease.sock
+
get_config() {
- config_get_bool enabled $1 enabled 1
- config_get_bool logger $1 logger
- config_get_bool allowPublic $1 allowPublic
- config_get port $1 port 8897
+ config_get_bool enabled "$1" enabled 1
+ config_get_bool logger "$1" logger 0
+ config_get_bool allowPublic "$1" allowPublic 0
+ config_get port "$1" port 8897
+}
+
+linkeasefull_enabled() {
+ [ "$(uci -q get linkeasefull.@linkeasefull[0].enabled 2>/dev/null)" = "1" ]
}
start_service() {
config_load linkease
config_foreach get_config linkease
- [ $enabled != 1 ] && return 1
+ [ "$enabled" != "1" ] && return 1
+ [ -x "$PROG" ] || return 1
+ if linkeasefull_enabled; then
+ echo "linkease: linkeasefull is enabled; please disable linkeasefull before starting linkease" >&2
+ return 1
+ fi
+ rm -f "$LOCAL_API"
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
- [ "$allowPublic" == 1 ] && procd_append_param command --allowPublic
- [ "$logger" == 1 ] && procd_set_param stderr 1
+ procd_set_param command "$PROG"
+ procd_append_param command --deviceAddr ":$port" --localApi "$LOCAL_API"
+ [ "$allowPublic" = "1" ] && procd_append_param command --allowPublic
+ [ "$logger" = "1" ] && procd_set_param stderr 1
procd_set_param respawn
procd_close_instance
}
diff --git a/linkease/files/linkease.uci-default b/linkease/files/linkease.uci-default
index 873545db..225dd09e 100644
--- a/linkease/files/linkease.uci-default
+++ b/linkease/files/linkease.uci-default
@@ -8,23 +8,52 @@
EEOF
}
+if ! uci -q get linkease.@linkease[0] >/dev/null; then
+ uci -q add linkease linkease >/dev/null
+fi
+
+set_default() {
+ key="$1"
+ value="$2"
+ current="$(uci -q get "linkease.@linkease[0].$key" 2>/dev/null || true)"
+ [ -n "$current" ] && return 0
+ uci -q set "linkease.@linkease[0].$key=$value"
+}
+
+set_default enabled 1
+set_default port 8897
+set_default allowPublic 0
+uci -q commit linkease
+port="$(uci -q get linkease.@linkease[0].port 2>/dev/null || true)"
+[ -n "$port" ] || port=8897
+allow_public="$(uci -q get linkease.@linkease[0].allowPublic 2>/dev/null || true)"
+
uci -q batch <<-EOF >/dev/null
delete ucitrack.@linkease[-1]
add ucitrack linkease
set ucitrack.@linkease[-1].init=linkease
commit ucitrack
+EOF
+if [ "$allow_public" = "1" ]; then
+ uci -q batch <<-EOF >/dev/null
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"
+ set firewall.linkease.dest_port="$port"
commit firewall
EOF
+else
+ uci -q batch <<-EOF >/dev/null
+ delete firewall.linkease
+ commit firewall
+EOF
+fi
/etc/init.d/linkease enable
-/etc/init.d/linkease start
+/etc/init.d/linkease restart
-exit 0
\ No newline at end of file
+exit 0
diff --git a/linkeasefull/Makefile b/linkeasefull/Makefile
new file mode 100644
index 00000000..1bd12ab1
--- /dev/null
+++ b/linkeasefull/Makefile
@@ -0,0 +1,74 @@
+#
+# Copyright (C) 2026 iStoreOS
+#
+# This is free software, licensed under the GNU General Public License v3.
+#
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=linkeasefull
+PKG_SOURCE_DATE:=3.0.4
+PKG_RELEASE:=3
+
+ifeq ($(ARCH),x86_64)
+LINKEASE_RUNTIME_ARCH:=amd64
+PKG_HASH:=skip
+else ifeq ($(ARCH),aarch64)
+LINKEASE_RUNTIME_ARCH:=arm64
+PKG_HASH:=skip
+endif
+
+PKG_SOURCE:=linkease-runtime-$(PKG_SOURCE_DATE)-linux-$(LINKEASE_RUNTIME_ARCH).tar.gz
+PKG_SOURCE_URL:=https://github.com/istoreos/istoreos-app-hub/releases/download/linkeasefull-runtime-v$(PKG_SOURCE_DATE)/
+PKG_BUILD_DIR:=$(BUILD_DIR)/linkease-runtime-$(PKG_SOURCE_DATE)-linux-$(LINKEASE_RUNTIME_ARCH)
+
+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 Full - the local web desktop
+ DEPENDS:=@(x86_64||aarch64) +linkease-common-bin +ca-bundle
+ URL:=https://www.linkease.com/
+endef
+
+define Package/$(PKG_NAME)/description
+ LinkEase Full provides the local web desktop entry for LinkEase and reuses the shared LinkEase file entry.
+endef
+
+define Package/$(PKG_NAME)/conffiles
+/etc/config/linkeasefull
+endef
+
+define Package/$(PKG_NAME)/postinst
+#!/bin/sh
+if [ -z "$${IPKG_INSTROOT}" ]; then
+ [ -f /etc/uci-defaults/linkeasefull ] && /etc/uci-defaults/linkeasefull && rm -f /etc/uci-defaults/linkeasefull
+ exit 0
+fi
+endef
+
+define Build/Configure
+endef
+
+define Build/Compile
+endef
+
+define Package/$(PKG_NAME)/install
+ $(INSTALL_DIR) $(1)/usr/bin $(1)/usr/libexec/linkeasefull/linkmount_bin $(1)/etc/config $(1)/etc/init.d $(1)/etc/uci-defaults
+ $(INSTALL_BIN) $(PKG_BUILD_DIR)/bin/linkease-full $(1)/usr/bin/linkease-full
+ $(INSTALL_BIN) $(PKG_BUILD_DIR)/bin/linkremote-agent $(1)/usr/bin/linkremote-agent
+ $(INSTALL_BIN) $(PKG_BUILD_DIR)/bin/hostlink $(1)/usr/bin/hostlink
+ $(INSTALL_BIN) $(PKG_BUILD_DIR)/linkmount_bin/linkmount_bin $(1)/usr/libexec/linkeasefull/linkmount_bin/linkmount_bin
+ $(CP) $(PKG_BUILD_DIR)/linkmount_bin/lib $(1)/usr/libexec/linkeasefull/linkmount_bin/lib
+ $(CP) $(PKG_BUILD_DIR)/linkmount_bin/ver.json $(1)/usr/libexec/linkeasefull/linkmount_bin/ver.json
+ $(INSTALL_CONF) ./files/linkeasefull.config $(1)/etc/config/linkeasefull
+ $(INSTALL_BIN) ./files/linkeasefull.init $(1)/etc/init.d/linkeasefull
+ $(INSTALL_BIN) ./files/linkeasefull.uci-default $(1)/etc/uci-defaults/linkeasefull
+endef
+
+$(eval $(call BuildPackage,$(PKG_NAME)))
diff --git a/linkeasefull/files/linkeasefull.config b/linkeasefull/files/linkeasefull.config
new file mode 100644
index 00000000..b8fe9b57
--- /dev/null
+++ b/linkeasefull/files/linkeasefull.config
@@ -0,0 +1,3 @@
+config linkeasefull
+ option enabled '1'
+ option data_root_parent ''
diff --git a/linkeasefull/files/linkeasefull.init b/linkeasefull/files/linkeasefull.init
new file mode 100644
index 00000000..daf90272
--- /dev/null
+++ b/linkeasefull/files/linkeasefull.init
@@ -0,0 +1,149 @@
+#!/bin/sh /etc/rc.common
+
+START=100
+STOP=10
+USE_PROCD=1
+
+PROG=/usr/bin/linkease-full
+LINKEASE_LIBEXEC=/usr/libexec/linkeasefull
+LINKEASE_FULL_PORT=19290
+LINKEASE_BASE_PATH=/apps/
+LINKEASE_LEGACY_ADDR=0.0.0.0:8897
+LOCAL_API=/var/run/linkease.sock
+
+is_persistent_data_root_parent() {
+ [ -n "$1" ] || return 1
+ case "$1" in
+ /tmp|/tmp/*|/var|/var/*|/run|/run/*|/dev|/dev/*|/proc|/proc/*|/sys|/sys/*|/rom|/rom/*|/overlay|/overlay/*) return 1 ;;
+ esac
+ [ -d "$1" ] || return 1
+ return 0
+}
+
+resolve_data_root_parent() {
+ if [ -n "$data_root_parent" ]; then
+ is_persistent_data_root_parent "$data_root_parent"
+ return $?
+ fi
+
+ local_home="$(uci -q get linkease.@linkease[0].local_home || true)"
+ if is_persistent_data_root_parent "$local_home"; then
+ data_root_parent="$local_home"
+ return 0
+ fi
+
+ if [ -f /etc/config/quickstart ]; then
+ quickstart_main_dir="$(uci -q get quickstart.main.main_dir || true)"
+ if [ -n "$quickstart_main_dir" ]; then
+ if is_persistent_data_root_parent "$quickstart_main_dir"; then
+ data_root_parent="$quickstart_main_dir"
+ return 0
+ fi
+ fi
+ fi
+
+ return 1
+}
+
+sync_shared_local_home() {
+ [ -x /usr/sbin/linkease-config.sh ] || return 0
+ /usr/sbin/linkease-config.sh local_save "$data_root_parent" >/dev/null 2>&1 || true
+}
+
+linkease_enabled() {
+ [ "$(uci -q get linkease.@linkease[0].enabled 2>/dev/null)" = "1" ]
+}
+
+read_config() {
+ section="$1"
+ config_get_bool enabled "$section" enabled 1
+ config_get data_root_parent "$section" data_root_parent ""
+
+ if ! resolve_data_root_parent; then
+ data_root_parent=""
+ return 1
+ fi
+ data_root="$data_root_parent/.linkease_data"
+ recycle_root="$data_root_parent/.linkease_recycle"
+ apptunnel_config_dir="/tmp/linkeasefull/apptunnel-config"
+}
+
+start_service() {
+ config_load linkeasefull
+ config_foreach read_config linkeasefull
+ [ "$enabled" = "1" ] || return 1
+ [ -x "$PROG" ] || return 1
+ if linkease_enabled; then
+ echo "linkeasefull: linkease is enabled; please disable linkease before starting linkeasefull" >&2
+ return 1
+ fi
+ if [ -z "$data_root_parent" ]; then
+ echo "linkeasefull: please choose a persistent disk storage path in LuCI" >&2
+ return 1
+ fi
+ sync_shared_local_home
+
+ rm -f "$LOCAL_API"
+ mkdir -p "$data_root/users/admin" "$data_root/system" "$data_root/tmp" "$recycle_root"
+ mkdir -p "$data_root/apptunnel" "$data_root/mountremote" "$data_root/logs"
+ mkdir -p "$apptunnel_config_dir"
+
+ # Do not set LINKEASE_CONFIG on OpenWrt. That variable belongs to
+ # Docker/file-backend deployments where users explicitly mount
+ # LINKEASE_CONFIG/preconfig.data. OpenWrt binding state is authoritative in
+ # UCI and must be accessed through /usr/sbin/linkease-config.sh. This is
+ # intentional: the user's data disk may be replaced, and the device must
+ # still be able to recover its binding from flash-backed OpenWrt config.
+ #
+ # Do not export LINKEASE_APPTUNNEL_PRECONFIG_SCRIPT or
+ # LINKEASE_APPTUNNEL_DEFAULT_ROOT_DIR here. Different devices have different
+ # storage roots, and hard-coded env wiring can pin apptunnel to a removed
+ # disk. The embedded clientruntime must discover linkease-config.sh and call
+ # local_load/local_save-compatible business APIs for the current root.
+
+ procd_open_instance linkeasefull
+ procd_set_param limits nofile="65535 65535"
+ procd_set_param command "$PROG"
+ procd_set_param env \
+ "SERVER_HOST=0.0.0.0" \
+ "SERVER_PORT=$LINKEASE_FULL_PORT" \
+ "SERVER_MODE=release" \
+ "SERVER_BASE_PATH=$LINKEASE_BASE_PATH" \
+ "LINKEASE_AUTH_PROVIDER=openwrt_luci" \
+ "LINKEASE_EDITION=nas-full" \
+ "KAIPLUS_ENABLED=0" \
+ "LINKEASE_DATA_ROOT=$data_root" \
+ "LINKEASE_RECYCLE_ROOT=$recycle_root" \
+ "LINKEASE_APPTUNNEL_INTERNAL_ADDR=127.0.0.1:19810" \
+ "LINKEASE_APPTUNNEL_LEGACY_ADDR=$LINKEASE_LEGACY_ADDR" \
+ "LINKEASE_APPTUNNEL_LOCAL_API=$LOCAL_API" \
+ "LINKEASE_APPTUNNEL_CONFIG_DIR=$apptunnel_config_dir" \
+ "LINKEASE_APPTUNNEL_DATA_DIR=$data_root/apptunnel" \
+ "LINKEASE_APPTUNNEL_CACHE_DIR=$data_root/apptunnel/cache" \
+ "LINKEASE_APPTUNNEL_RUNTIME_DIR=$data_root/apptunnel/runtime" \
+ "LINKEASE_APPTUNNEL_LOG_DIR=$data_root/logs" \
+ "APPTUNNEL_MOUNTREMOTE_MODE=real" \
+ "APPTUNNEL_MOUNTREMOTE_LINKREMOTE_AGENT_BINARY=/usr/bin/linkremote-agent" \
+ "APPTUNNEL_MOUNTREMOTE_SMBD_BINARY=$LINKEASE_LIBEXEC/linkmount_bin/linkmount_bin" \
+ "APPTUNNEL_MOUNTREMOTE_WORK_DIR=$data_root/mountremote" \
+ "APPTUNNEL_MOUNTREMOTE_SOCKET_DIR=$data_root/mountremote" \
+ "APPTUNNEL_MOUNTREMOTE_SAMBA_DIR=$data_root/mountremote/samba" \
+ "APPTUNNEL_MOUNTREMOTE_STORE_PATH=$data_root/mountremote/mounts.json" \
+ "APPTUNNEL_MOUNTREMOTE_STARTUP_RECONCILE=true" \
+ "APPTUNNEL_MOUNTREMOTE_ROOT_WATCH_DIR=$data_root/mountremote/root-watch" \
+ "APPTUNNEL_MOUNTREMOTE_ALLOWED_MOUNT_PREFIX=$data_root_parent" \
+ "MOUNTREMOTE_ALLOWED_MOUNT_PREFIX=$data_root_parent" \
+ "LINKEASE_LINKMOUNT_BIN=$LINKEASE_LIBEXEC/linkmount_bin/linkmount_bin" \
+ "LINKEASE_LINKMOUNT_LIB_DIR=$LINKEASE_LIBEXEC/linkmount_bin/lib" \
+ "USER_DATA_PATH=$data_root/users/admin" \
+ "SYSTEM_DATA_PATH=$data_root/system" \
+ "TEMP_PATH=$data_root/tmp"
+ procd_set_param respawn
+ procd_set_param stdout 1
+ procd_set_param stderr 1
+ procd_close_instance
+}
+
+service_triggers() {
+ procd_add_reload_trigger "linkeasefull"
+}
diff --git a/linkeasefull/files/linkeasefull.uci-default b/linkeasefull/files/linkeasefull.uci-default
new file mode 100644
index 00000000..e178bf1e
--- /dev/null
+++ b/linkeasefull/files/linkeasefull.uci-default
@@ -0,0 +1,36 @@
+#!/bin/sh
+[ ! -f "/usr/share/ucitrack/luci-app-linkeasefull.json" ] && {
+ cat > /usr/share/ucitrack/luci-app-linkeasefull.json << EEOF
+{
+ "config": "linkeasefull",
+ "init": "linkeasefull"
+}
+EEOF
+}
+
+if ! uci -q get linkeasefull.@linkeasefull[0] >/dev/null; then
+ uci -q add linkeasefull linkeasefull >/dev/null
+fi
+
+set_default() {
+ key="$1"
+ value="$2"
+ current="$(uci -q get "linkeasefull.@linkeasefull[0].$key" 2>/dev/null || true)"
+ [ -n "$current" ] && return 0
+ uci -q set "linkeasefull.@linkeasefull[0].$key=$value"
+}
+
+set_default enabled 1
+uci -q commit linkeasefull
+
+uci -q batch <<-EOF >/dev/null
+ delete ucitrack.@linkeasefull[-1]
+ add ucitrack linkeasefull
+ set ucitrack.@linkeasefull[-1].init=linkeasefull
+ commit ucitrack
+EOF
+
+/etc/init.d/linkeasefull enable
+/etc/init.d/linkeasefull restart
+
+exit 0
diff --git a/linkeaselite/Makefile b/linkeaselite/Makefile
new file mode 100644
index 00000000..f9e4457e
--- /dev/null
+++ b/linkeaselite/Makefile
@@ -0,0 +1,75 @@
+#
+# Copyright (C) 2015-2016 OpenWrt.org
+# Copyright (C) 2026 LinkEaseLite Contributors
+#
+# This is free software, licensed under the GNU General Public License v3.
+#
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=linkeaselite
+PKG_SOURCE_DATE:=3.0.0
+PKG_RELEASE:=3
+
+PKG_SOURCE:=linkeaselite-binary-$(PKG_SOURCE_DATE).tar.gz
+PKG_SOURCE_URL:=https://github.com/linkease/istore-packages/releases/download/linkeaselite-v$(PKG_SOURCE_DATE)/
+PKG_HASH:=skip
+PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-binary-$(PKG_SOURCE_DATE)
+
+PKG_BUILD_PARALLEL:=1
+PKG_USE_MIPS16:=0
+TAR_CMD=$(HOST_TAR) -C $(PKG_BUILD_DIR) $(TAR_OPTIONS)
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/$(PKG_NAME)
+ SECTION:=net
+ CATEGORY:=Network
+ SUBMENU:=Web Servers/Proxies
+ TITLE:=LinkEaseLite - the file cloud
+ DEPENDS:=@(arm||x86_64||aarch64) +ca-bundle
+ URL:=https://www.linkease.com/
+endef
+
+define Package/$(PKG_NAME)/description
+ LinkEaseLite is a lightweight LinkEase runtime for small-memory OpenWrt devices.
+endef
+
+define Package/$(PKG_NAME)/conffiles
+/etc/config/linkeaselite
+endef
+
+define Package/$(PKG_NAME)/postinst
+#!/bin/sh
+if [ -z "$${IPKG_INSTROOT}" ]; then
+ [ -f /etc/uci-defaults/linkeaselite ] && /etc/uci-defaults/linkeaselite && rm -f /etc/uci-defaults/linkeaselite
+ exit 0
+fi
+endef
+
+define Package/$(PKG_NAME)/postrm
+#!/bin/sh
+if [ -z "$${IPKG_INSTROOT}" ]; then
+ uci -q delete firewall.linkeaselite
+ uci -q commit firewall
+ /etc/init.d/firewall reload
+fi
+exit 0
+endef
+
+define Build/Configure
+endef
+
+define Build/Compile
+endef
+
+define Package/$(PKG_NAME)/install
+ $(INSTALL_DIR) $(1)/usr/sbin $(1)/etc/config $(1)/etc/init.d $(1)/etc/uci-defaults
+ $(INSTALL_BIN) $(PKG_BUILD_DIR)/linkease-lite.$(ARCH) $(1)/usr/sbin/linkease-lite
+ $(INSTALL_BIN) ./files/linkeaselite-config.sh $(1)/usr/sbin/linkeaselite-config.sh
+ $(INSTALL_CONF) ./files/linkeaselite.config $(1)/etc/config/linkeaselite
+ $(INSTALL_BIN) ./files/linkeaselite.init $(1)/etc/init.d/linkeaselite
+ $(INSTALL_BIN) ./files/linkeaselite.uci-default $(1)/etc/uci-defaults/linkeaselite
+endef
+
+$(eval $(call BuildPackage,$(PKG_NAME)))
diff --git a/linkeaselite/files/linkeaselite-config.sh b/linkeaselite/files/linkeaselite-config.sh
new file mode 100755
index 00000000..7dc6c7ae
--- /dev/null
+++ b/linkeaselite/files/linkeaselite-config.sh
@@ -0,0 +1,78 @@
+#!/bin/sh
+
+. /lib/functions.sh
+
+case "$1" in
+ save)
+ if [ -n "$2" ]; then
+ uci set "linkeaselite.@linkeaselite[0].preconfig=$2"
+ uci commit linkeaselite
+ fi
+ ;;
+
+ load)
+ data="`uci -q get linkeaselite.@linkeaselite[0].preconfig`"
+ if [ -z "${data}" ]; then
+ echo "nil"
+ else
+ echo "${data}"
+ fi
+ ;;
+
+ local_save)
+ if [ -n "$2" ]; then
+ uci set "linkeaselite.@linkeaselite[0].local_home=$2"
+ uci commit linkeaselite
+ 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 ""
+ [ "$ROOT_DIR" = "$MAIN_DIR" ] && exit 0
+ uci set "quickstart.main.main_dir=$ROOT_DIR"
+ if [ -z "$CONF_DIR" ] || [ "$CONF_DIR" = "$MAIN_DIR/Configs" ]; then
+ uci set "quickstart.main.conf_dir=$ROOT_DIR/Configs"
+ fi
+ if [ -z "$PUB_DIR" ] || [ "$PUB_DIR" = "$MAIN_DIR/Public" ]; then
+ uci set "quickstart.main.pub_dir=$ROOT_DIR/Public"
+ fi
+ if [ -z "$DL_DIR" ] || [ "$DL_DIR" = "$MAIN_DIR/Public/Downloads" ]; then
+ uci set "quickstart.main.dl_dir=$ROOT_DIR/Public/Downloads"
+ fi
+ if [ -z "$TMP_DIR" ] || [ "$TMP_DIR" = "$MAIN_DIR/Caches" ]; then
+ uci set "quickstart.main.tmp_dir=$ROOT_DIR/Caches"
+ fi
+ uci commit quickstart
+ 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 linkeaselite.@linkeaselite[0].local_home`"
+ fi
+ if [ -z "${data}" ]; then
+ echo "nil"
+ else
+ echo "${data}"
+ fi
+ ;;
+
+ status)
+ if pidof linkease-lite >/dev/null 2>&1; then
+ echo "running"
+ else
+ echo "stopped"
+ fi
+ ;;
+
+ *)
+ echo "Usage: $0 {save|load|local_save|local_load|status}"
+ exit 1
+esac
diff --git a/linkeaselite/files/linkeaselite.config b/linkeaselite/files/linkeaselite.config
new file mode 100644
index 00000000..1e610c0f
--- /dev/null
+++ b/linkeaselite/files/linkeaselite.config
@@ -0,0 +1,4 @@
+config linkeaselite
+ option enabled '1'
+ option port '8897'
+ option allowPublic '0'
diff --git a/linkeaselite/files/linkeaselite.init b/linkeaselite/files/linkeaselite.init
new file mode 100755
index 00000000..276d6fd5
--- /dev/null
+++ b/linkeaselite/files/linkeaselite.init
@@ -0,0 +1,68 @@
+#!/bin/sh /etc/rc.common
+
+START=99
+STOP=10
+USE_PROCD=1
+
+PROG=/usr/sbin/linkease-lite
+LOCAL_API=/var/run/linkeaselite.sock
+
+read_config() {
+ section="$1"
+ config_get_bool enabled "$section" enabled 1
+ config_get port "$section" port 8897
+ config_get_bool allowPublic "$section" allowPublic 0
+}
+
+sync_firewall() {
+ uci -q delete firewall.linkeaselite
+ if [ "$allowPublic" = "1" ]; then
+ uci -q set firewall.linkeaselite=rule
+ uci -q set firewall.linkeaselite.name="linkeaselite"
+ uci -q set firewall.linkeaselite.target="ACCEPT"
+ uci -q set firewall.linkeaselite.src="wan"
+ uci -q set firewall.linkeaselite.proto="tcp"
+ uci -q set firewall.linkeaselite.dest_port="$port"
+ fi
+ uci -q commit firewall
+ /etc/init.d/firewall reload
+}
+
+stop_linkease_runtimes() {
+ [ -x /etc/init.d/linkeasefull ] && /etc/init.d/linkeasefull stop >/dev/null 2>&1
+ [ -x /etc/init.d/linkease ] && /etc/init.d/linkease stop >/dev/null 2>&1
+
+ if uci -q get linkease.@linkease[0] >/dev/null 2>&1; then
+ uci -q set linkease.@linkease[0].enabled="0"
+ uci -q commit linkease
+ fi
+ if uci -q get linkeasefull.@linkeasefull[0] >/dev/null 2>&1; then
+ uci -q set linkeasefull.@linkeasefull[0].enabled="0"
+ uci -q commit linkeasefull
+ fi
+}
+
+start_service() {
+ config_load linkeaselite
+ config_foreach read_config linkeaselite
+ sync_firewall
+ [ "$enabled" = "1" ] || return 1
+ [ -x "$PROG" ] || return 1
+
+ stop_linkease_runtimes
+ rm -f "$LOCAL_API"
+
+ procd_open_instance linkeaselite
+ procd_set_param limits nofile="65535 65535"
+ procd_set_param command "$PROG"
+ procd_append_param command --deviceAddr ":$port" --localApi "$LOCAL_API"
+ [ "$allowPublic" = "1" ] && procd_append_param command --allowPublic
+ procd_set_param respawn
+ procd_set_param stdout 1
+ procd_set_param stderr 1
+ procd_close_instance
+}
+
+service_triggers() {
+ procd_add_reload_trigger "linkeaselite"
+}
diff --git a/linkeaselite/files/linkeaselite.uci-default b/linkeaselite/files/linkeaselite.uci-default
new file mode 100644
index 00000000..6ebed20d
--- /dev/null
+++ b/linkeaselite/files/linkeaselite.uci-default
@@ -0,0 +1,60 @@
+#!/bin/sh
+[ ! -f "/usr/share/ucitrack/luci-app-linkeaselite.json" ] && {
+ cat > /usr/share/ucitrack/luci-app-linkeaselite.json << EEOF
+{
+ "config": "linkeaselite",
+ "init": "linkeaselite"
+}
+EEOF
+}
+
+if ! uci -q get linkeaselite.@linkeaselite[0] >/dev/null; then
+ uci -q add linkeaselite linkeaselite >/dev/null
+fi
+
+set_default() {
+ key="$1"
+ value="$2"
+ current="$(uci -q get "linkeaselite.@linkeaselite[0].$key" 2>/dev/null || true)"
+ [ -n "$current" ] && return 0
+ uci -q set "linkeaselite.@linkeaselite[0].$key=$value"
+}
+
+set_default enabled 1
+set_default port 8897
+set_default allowPublic 0
+uci -q commit linkeaselite
+
+port="$(uci -q get linkeaselite.@linkeaselite[0].port 2>/dev/null || true)"
+[ -n "$port" ] || port=8897
+allow_public="$(uci -q get linkeaselite.@linkeaselite[0].allowPublic 2>/dev/null || true)"
+
+uci -q batch <<-EOF >/dev/null
+ delete ucitrack.@linkeaselite[-1]
+ add ucitrack linkeaselite
+ set ucitrack.@linkeaselite[-1].init=linkeaselite
+ commit ucitrack
+EOF
+
+if [ "$allow_public" = "1" ]; then
+ uci -q batch <<-EOF >/dev/null
+ delete firewall.linkeaselite
+ set firewall.linkeaselite=rule
+ set firewall.linkeaselite.name="linkeaselite"
+ set firewall.linkeaselite.target="ACCEPT"
+ set firewall.linkeaselite.src="wan"
+ set firewall.linkeaselite.proto="tcp"
+ set firewall.linkeaselite.dest_port="$port"
+ commit firewall
+EOF
+else
+ uci -q batch <<-EOF >/dev/null
+ delete firewall.linkeaselite
+ commit firewall
+EOF
+fi
+
+/etc/init.d/linkeaselite enable
+/etc/init.d/linkeaselite restart
+
+exit 0
diff --git a/luci-app-kaiplus/Makefile b/luci-app-kaiplus/Makefile
index 23540bda..c9ac94ed 100644
--- a/luci-app-kaiplus/Makefile
+++ b/luci-app-kaiplus/Makefile
@@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk
LUCI_TITLE:=KaiPlus
PKG_VERSION:=1.0.0
-PKG_RELEASE:=3
+PKG_RELEASE:=4
LUCI_DEPENDS:=+kaiplus +luci-compat
LUCI_MINIFY_CSS:=0
LUCI_MINIFY_JS:=0
diff --git a/luci-app-kaiplus/luasrc/controller/kaiplus.lua b/luci-app-kaiplus/luasrc/controller/kaiplus.lua
index 769bd805..9a0c080b 100644
--- a/luci-app-kaiplus/luasrc/controller/kaiplus.lua
+++ b/luci-app-kaiplus/luasrc/controller/kaiplus.lua
@@ -15,10 +15,12 @@ end
function kaiplus_status()
local sys = require "luci.sys"
local uci = require "luci.model.uci".cursor()
- local port = uci:get_first("kaiplus", "kaiplus", "port", "8198")
+ local port = uci:get_first("kaiplus", "kaiplus", "port", "8189")
+ local base_path = uci:get_first("kaiplus", "kaiplus", "base_path", "/apps/kaiplus/")
local status = {
running = (sys.call("pidof kaiplus_bin >/dev/null") == 0),
- port = port
+ port = port,
+ base_path = base_path
}
luci.http.prepare_content("application/json")
luci.http.write_json(status)
diff --git a/luci-app-kaiplus/luasrc/model/cbi/kaiplus.lua b/luci-app-kaiplus/luasrc/model/cbi/kaiplus.lua
index 91de5648..15a3a29d 100644
--- a/luci-app-kaiplus/luasrc/model/cbi/kaiplus.lua
+++ b/luci-app-kaiplus/luasrc/model/cbi/kaiplus.lua
@@ -5,7 +5,10 @@ m = Map("kaiplus", translate("KaiPlus"), translate("KaiPlus is an AI workspace a
m:section(SimpleSection).template = "kaiplus/kaiplus_status"
m.on_after_commit = function(self)
- sys.call("/etc/init.d/kaiplus restart >/dev/null 2>&1 &")
+ sys.call("/etc/init.d/kaiplus restart >/dev/null 2>&1")
+ if sys.call("[ -x /etc/init.d/linkease ]") == 0 then
+ sys.call("/etc/init.d/linkease restart >/dev/null 2>&1 &")
+ end
end
s = m:section(TypedSection, "kaiplus", translate("Global settings"))
@@ -29,7 +32,7 @@ end
data_dir.default = default_path
local port = s:option(Value, "port", translate("Web port"))
-port.default = "8198"
+port.default = "8189"
port.rmempty = false
port.datatype = "port"
port.description = translate("Port for the KaiPlus web service.")
diff --git a/luci-app-kaiplus/luasrc/view/kaiplus/kaiplus_status.htm b/luci-app-kaiplus/luasrc/view/kaiplus/kaiplus_status.htm
index 147688eb..7fb4da6f 100644
--- a/luci-app-kaiplus/luasrc/view/kaiplus/kaiplus_status.htm
+++ b/luci-app-kaiplus/luasrc/view/kaiplus/kaiplus_status.htm
@@ -6,8 +6,15 @@ XHR.poll(5, '<%=url("admin/services/kaiplus_status")%>', null, function(x, st) {
el.innerHTML = '
<%:The KaiPlus service is not running.%>';
}
if (st.running) {
+ var basePath = st.base_path || "/apps/kaiplus/";
+ if (basePath.charAt(0) !== "/") {
+ basePath = "/" + basePath;
+ }
+ if (basePath.charAt(basePath.length - 1) !== "/") {
+ basePath = basePath + "/";
+ }
el.innerHTML = '
<%:The KaiPlus service is running.%>'
- + "
\" onclick=\"window.open('http://" + window.location.hostname + ":" + st.port + "/')\"/>";
+ + "
\" onclick=\"window.open('http://' + window.location.hostname + ':' + st.port + basePath)\"/>";
}
}
});
diff --git a/luci-app-linkease/Makefile b/luci-app-linkease/Makefile
index 5a62eac9..e568f7c8 100644
--- a/luci-app-linkease/Makefile
+++ b/luci-app-linkease/Makefile
@@ -6,15 +6,14 @@
include $(TOPDIR)/rules.mk
LUCI_TITLE:=LuCI support for linkease
-LUCI_DEPENDS:=+linkease
+LUCI_DEPENDS:=+linkease +luci-lib-linkeasefile
LUCI_PKGARCH:=all
PKG_VERSION:=2.1.70-r3
# PKG_RELEASE MUST be empty for luci.mk
-PKG_RELEASE:=3
+PKG_RELEASE:=4
LUCI_MINIFY_CSS:=0
LUCI_MINIFY_JS:=0
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature
-
diff --git a/luci-app-linkease/luasrc/controller/linkease.lua b/luci-app-linkease/luasrc/controller/linkease.lua
index a71b80b8..78bba9d9 100644
--- a/luci-app-linkease/luasrc/controller/linkease.lua
+++ b/luci-app-linkease/luasrc/controller/linkease.lua
@@ -8,32 +8,21 @@ function index()
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 full_enabled = (uci:get_first("linkeasefull", "linkeasefull", "enabled") == "1")
local status = {
running = (sys.call("pidof linkease >/dev/null") == 0),
- port = (port or 8897)
+ port = (port or 8897),
+ conflict = full_enabled,
+ conflict_service = full_enabled and "linkeasefull" or ""
}
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
diff --git a/luci-app-linkease/luasrc/view/linkease_status.htm b/luci-app-linkease/luasrc/view/linkease_status.htm
index 20bea068..32405fec 100644
--- a/luci-app-linkease/luasrc/view/linkease_status.htm
+++ b/luci-app-linkease/luasrc/view/linkease_status.htm
@@ -5,7 +5,11 @@
var tb = document.getElementById('linkease_status');
if (st && tb)
{
- if (st.running)
+ if (st.conflict)
+ {
+ tb.innerHTML = '
<%:LinkEase Full is enabled. Please disable LinkEase Full before starting LinkEase.%>';
+ }
+ else if (st.running)
{
tb.innerHTML = '
<%:The LinkEase service is running.%>'
+ "
\" onclick=\"window.open('http://" + window.location.hostname + ":" + st.port + "/')\"/>"
diff --git a/luci-app-linkeasefull/Makefile b/luci-app-linkeasefull/Makefile
new file mode 100644
index 00000000..5a293495
--- /dev/null
+++ b/luci-app-linkeasefull/Makefile
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2026 iStoreOS
+#
+# This is free software, licensed under the Apache License, Version 2.0 .
+#
+
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=LuCI support for linkeasefull
+LUCI_DEPENDS:=+linkeasefull +luci-lib-linkeasefile
+LUCI_PKGARCH:=all
+
+PKG_VERSION:=1.0.0
+PKG_RELEASE:=3
+
+include $(TOPDIR)/feeds/luci/luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
diff --git a/luci-app-linkeasefull/luasrc/controller/linkeasefull.lua b/luci-app-linkeasefull/luasrc/controller/linkeasefull.lua
new file mode 100644
index 00000000..e493bdc8
--- /dev/null
+++ b/luci-app-linkeasefull/luasrc/controller/linkeasefull.lua
@@ -0,0 +1,92 @@
+module("luci.controller.linkeasefull", package.seeall)
+
+function index()
+ if not nixio.fs.access("/etc/config/linkeasefull") then
+ return
+ end
+
+ entry({"admin", "services", "linkeasefull"}, cbi("linkeasefull"), _("LinkEase Full"), 21).dependent = true
+ entry({"admin", "services", "linkeasefull_status"}, call("linkeasefull_status"))
+ entry({"admin", "services", "linkeasefull", "auth"}, call("linkeasefull_auth")).leaf = true
+end
+
+local function uhttpd_has_apps_proxy_prefix()
+ local uci = require "luci.model.uci".cursor()
+ local mappings = uci:get_list("uhttpd", "main", "proxy_prefix") or {}
+
+ for _, mapping in ipairs(mappings) do
+ if mapping == "/apps=http://127.0.0.1:19290" then
+ return true
+ end
+ end
+ return false
+end
+
+function linkeasefull_status()
+ local sys = require "luci.sys"
+ local uci = require "luci.model.uci".cursor()
+ local linkease_enabled = (uci:get_first("linkease", "linkease", "enabled") == "1")
+
+ local status = {
+ running = (sys.call("pidof linkease-full >/dev/null") == 0),
+ full_port = 19290,
+ legacy_port = 8897,
+ base_path = "/apps/",
+ lan_ip = uci:get("network", "lan", "ipaddr") or "",
+ proxy_prefix_enabled = uhttpd_has_apps_proxy_prefix(),
+ conflict = linkease_enabled,
+ conflict_service = linkease_enabled and "linkease" or ""
+ }
+
+ luci.http.prepare_content("application/json")
+ luci.http.write_json(status)
+end
+
+local function retrieve_luci_session()
+ local http = require "luci.http"
+ local util = require "luci.util"
+
+ for _, key in ipairs({"sysauth_https", "sysauth_http", "sysauth"}) do
+ local sid = http.getcookie(key)
+ if sid and sid ~= "" then
+ local sdat = util.ubus("session", "get", { ubus_rpc_session = sid })
+ if sdat and type(sdat.values) == "table" then
+ return sid
+ end
+ end
+ end
+ return nil
+end
+
+local function valid_apps_return(value)
+ if not value or value == "" then
+ return false
+ end
+ if value == "/apps" then
+ return true
+ end
+ local prefix = value:sub(1, 6)
+ return prefix == "/apps/" or prefix == "/apps?" or prefix == "/apps#"
+end
+
+local function valid_cookie_value(value)
+ return value and value:match("^[A-Za-z0-9._%-_]+$") ~= nil
+end
+
+function linkeasefull_auth()
+ local http = require "luci.http"
+ local sid = retrieve_luci_session()
+
+ if not valid_cookie_value(sid) then
+ http.status(403, "Forbidden")
+ return
+ end
+
+ local target = http.formvalue("return") or "/apps/"
+ if not valid_apps_return(target) then
+ target = "/apps/"
+ end
+
+ http.header("Set-Cookie", "linkease_openwrt_sid=" .. sid .. "; Path=/apps; HttpOnly; SameSite=Lax")
+ http.redirect(target)
+end
diff --git a/luci-app-linkeasefull/luasrc/model/cbi/linkeasefull.lua b/luci-app-linkeasefull/luasrc/model/cbi/linkeasefull.lua
new file mode 100644
index 00000000..23950bc4
--- /dev/null
+++ b/luci-app-linkeasefull/luasrc/model/cbi/linkeasefull.lua
@@ -0,0 +1,86 @@
+local fs = require "nixio.fs"
+
+local m, s
+
+local function starts_with(value, prefix)
+ return value:sub(1, #prefix) == prefix
+end
+
+local function decode_mount_path(value)
+ return value:gsub("\\040", " "):gsub("\\011", "\t"):gsub("\\012", "\n"):gsub("\\134", "\\")
+end
+
+local function is_persistent_mount(path, fstype)
+ if path == "" or path == "/" then
+ return false
+ end
+ if starts_with(path, "/tmp") or starts_with(path, "/var") or starts_with(path, "/run") then
+ return false
+ end
+ if starts_with(path, "/dev") or starts_with(path, "/proc") or starts_with(path, "/sys") then
+ return false
+ end
+ if starts_with(path, "/rom") or starts_with(path, "/overlay") then
+ return false
+ end
+ if fstype == "tmpfs" or fstype == "devtmpfs" or fstype == "overlay" or fstype == "squashfs" then
+ return false
+ end
+ return true
+end
+
+local function storage_mounts()
+ local mounts = {}
+ for line in io.lines("/proc/mounts") do
+ local device, path, fstype = line:match("^(%S+)%s+(%S+)%s+(%S+)")
+ path = path and decode_mount_path(path) or ""
+ if device and fstype and is_persistent_mount(path, fstype) then
+ mounts[#mounts + 1] = {
+ path = path,
+ label = string.format("%s (%s)", path, fstype)
+ }
+ end
+ end
+ table.sort(mounts, function(a, b) return a.path < b.path end)
+ return mounts
+end
+
+m = Map("linkeasefull", translate("LinkEase Full"), translate("LinkEase Full uses fixed entries at port 19290 /apps/ and legacy port 8897."))
+
+m:section(SimpleSection).template = "linkeasefull_status"
+
+s = m:section(TypedSection, "linkeasefull", translate("Storage"))
+s.addremove = false
+s.anonymous = true
+
+s:option(Flag, "enabled", translate("Enable")).rmempty = false
+
+local data = s:option(ListValue, "data_root_parent", translate("Storage path"), translate("Choose a mounted persistent disk. /tmp is not allowed."))
+data.rmempty = false
+
+local current = m.uci:get_first("linkeasefull", "linkeasefull", "data_root_parent") or ""
+local has_current = false
+for _, mount in ipairs(storage_mounts()) do
+ data:value(mount.path, mount.label)
+ if mount.path == current then
+ has_current = true
+ end
+end
+if current ~= "" and not has_current and fs.stat(current, "type") == "dir" and is_persistent_mount(current, "") then
+ data:value(current, current)
+end
+
+function data.validate(self, value)
+ if value == nil or value == "" then
+ return nil, translate("Please choose a mounted persistent disk.")
+ end
+ if not is_persistent_mount(value, "") then
+ return nil, translate("/tmp and system paths cannot be used for LinkEase Full storage.")
+ end
+ if fs.stat(value, "type") ~= "dir" then
+ return nil, translate("The selected storage path does not exist.")
+ end
+ return value
+end
+
+return m
diff --git a/luci-app-linkeasefull/luasrc/view/linkeasefull_status.htm b/luci-app-linkeasefull/luasrc/view/linkeasefull_status.htm
new file mode 100644
index 00000000..b2348411
--- /dev/null
+++ b/luci-app-linkeasefull/luasrc/view/linkeasefull_status.htm
@@ -0,0 +1,36 @@
+
+
+