update 2026-07-27 14:57:54

This commit is contained in:
action 2026-07-27 14:57:54 +08:00
parent 585206e508
commit 54ea97798d
22 changed files with 482 additions and 34 deletions

View File

@ -10,7 +10,7 @@ PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_PROTO:=git
PKG_SOURCE_VERSION:=9bb42373173a609e5aa253ffceae81b982fbf36a
PKG_SOURCE_VERSION:=64dd36796f74660421d30bfa7b935797391ca428
PKG_SOURCE_URL:=https://github.com/olicesx/dae.git
PKG_MIRROR_HASH:=skip

View File

@ -16,7 +16,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-amlogic
PKG_VERSION:=3.1.319
PKG_VERSION:=3.1.320
PKG_RELEASE:=2
PKG_LICENSE:=GPL-2.0 License

View File

@ -57,22 +57,24 @@ return view.extend({
handleSaveApply: null,
handleReset: null,
// On load, call sync_menu to update sidebar menu flags from runtime platform detection. If the install menu becomes
// visible but is not in the current nav, reload once to pick up the new menu (guarded by #menu-synced hash).
load: function () {
// Call sync_menu; if the install menu is now visible on the server but
// absent in the browser nav, reload once (guarded by #menu-synced hash).
// On load, call sync_menu to update sidebar menu flags from runtime platform detection. If the sidebar nav is out of
// sync with the server-side menu flags, reload once to pick up the new menu (guarded by #menu-synced hash).
load: function () {
// Call sync_menu; reload once when the sidebar nav disagrees with the
// server-side menu flags in either direction (guarded by #menu-synced hash).
const alreadyReloaded = window.location.hash === '#menu-synced';
const syncMenuPromise = callSyncMenu().then(function (res) {
if (alreadyReloaded) return;
if (res && res.show_install === 'yes') {
// Check if the sidebar navigation already contains the install link.
const installLink = document.querySelector('a[href*="amlogic/install"]');
if (!installLink) {
// Server-side index cache updated; reload to pick up new nav.
window.location.replace(window.location.pathname + '#menu-synced');
window.location.reload();
}
if (alreadyReloaded || !res) return;
// Check if the sidebar navigation already contains the install link.
const installLink = document.querySelector('a[href*="amlogic/install"]');
// Menu became visible on the server but is absent in the browser nav.
const needShow = (res.show_install === 'yes' && !installLink);
// Menu became hidden on the server but is still present in the browser nav.
const needHide = (res.show_install === 'no' && installLink);
if (needShow || needHide) {
// Server-side index cache updated; reload to pick up new nav.
window.location.replace(window.location.pathname + '#menu-synced');
window.location.reload();
}
}).catch(function () {});
return Promise.all([callAuthor(), syncMenuPromise]).then(function (r) { return r[0]; });

View File

@ -12,10 +12,28 @@ PLATFORM=$(grep -E '^PLATFORM=' /etc/flippy-openwrt-release 2>/dev/null | \
SHOW_INSTALL=$(grep -E '^SHOW_INSTALL_MENU=' /etc/flippy-openwrt-release 2>/dev/null | \
awk -F= '{print $2}' | tr -d '"' | xargs)
case "$PLATFORM" in
*amlogic*) MENU_INSTALL=yes ;;
*) [ "$SHOW_INSTALL" = "yes" ] && MENU_INSTALL=yes || MENU_INSTALL=no ;;
# Detect whether the root fs is already on eMMC (mmcblk*p* with device/type == "MMC").
# Mirrors root_on_internal_storage() in luci.amlogic: all other boot media
# (TF/SD, NVMe, SATA, USB) keep the install menu visible.
ROOT_PT=$(df / | tail -n1 | awk '{print $1}' | awk -F '/' '{print $3}')
IS_INSTALLED=0
case "$ROOT_PT" in
mmcblk*p*)
BASE_DEV="${ROOT_PT%p*}"
[ "$(cat /sys/block/${BASE_DEV}/device/type 2>/dev/null | xargs)" = "MMC" ] && IS_INSTALLED=1
;;
esac
case "$PLATFORM" in
*amlogic*|*allwinner*) CAN_INSTALL=yes ;;
*) [ "$SHOW_INSTALL" = "yes" ] && CAN_INSTALL=yes || CAN_INSTALL=no ;;
esac
if [ "$CAN_INSTALL" = "yes" ] && [ "$IS_INSTALLED" = "0" ]; then
MENU_INSTALL=yes
else
MENU_INSTALL=no
fi
case "$PLATFORM" in
*qemu*) MENU_ARMCPU=no ;;
*) MENU_ARMCPU=yes ;;

View File

@ -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)

View File

@ -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.")

View File

@ -6,8 +6,15 @@ XHR.poll(5, '<%=url("admin/services/kaiplus_status")%>', null, function(x, st) {
el.innerHTML = '<br/><em style=\"color:red\"><%:The KaiPlus service is not running.%></em>';
}
if (st.running) {
var basePath = st.base_path || "/apps/kaiplus/";
if (basePath.charAt(0) !== "/") {
basePath = "/" + basePath;
}
if (basePath.charAt(basePath.length - 1) !== "/") {
basePath = basePath + "/";
}
el.innerHTML = '<br/><em style=\"color:green\"><%:The KaiPlus service is running.%></em>'
+ "<br/><br/><input class=\"btn cbi-button cbi-button-apply\" type=\"button\" value=\" <%:Click to open KaiPlus%> \" onclick=\"window.open('http://" + window.location.hostname + ":" + st.port + "/')\"/>";
+ "<br/><br/><input class=\"btn cbi-button cbi-button-apply\" type=\"button\" value=\" <%:Click to open KaiPlus%> \" onclick=\"window.open('http://' + window.location.hostname + ':' + st.port + basePath)\"/>";
}
}
});

View File

@ -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_PKGARCH:=all
PKG_VERSION:=1.0.0
PKG_RELEASE:=1
include ../../luci.mk
# call BuildPackage - OpenWrt buildroot signature

View File

@ -0,0 +1,89 @@
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 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()
}
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

View File

@ -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

View File

@ -0,0 +1,34 @@
<script type="text/javascript">//<![CDATA[
XHR.poll(5, '<%=url("admin/services/linkeasefull_status")%>', null,
function(x, st)
{
var tb = document.getElementById('linkeasefull_status');
if (st && tb)
{
var lanHost = st.lan_ip || window.location.hostname;
var fullUrl = st.proxy_prefix_enabled
? window.location.protocol + "//" + window.location.host + "/apps/"
: "http://" + lanHost + ":" + (st.full_port || 19290) + "/apps/";
var legacyUrl = "http://" + lanHost + ":" + (st.legacy_port || 8897) + "/";
var overallStatus = st.running
? '<br/><em style=\"color:green\"><%:The LinkEase Full service is running.%></em>'
: '<br/><em style=\"color:red\"><%:The LinkEase Full service is not running.%></em>';
tb.innerHTML = overallStatus;
if (st.running)
{
tb.innerHTML = tb.innerHTML
+ "<br/><br/><input class=\"btn cbi-button cbi-button-apply\" type=\"button\" value=\" <%:Open Full Entry%> \" onclick=\"window.open('" + fullUrl + "')\"/>"
+ " <input class=\"btn cbi-button cbi-button-apply\" type=\"button\" value=\" <%:Open Standard Entry%> \" onclick=\"window.open('" + legacyUrl + "')\"/>";
}
}
}
);
//]]></script>
<fieldset class="cbi-section">
<legend><%:LinkEase Full Status%></legend>
<p id="linkeasefull_status">
<em><%:Collecting data...%></em>
</p>
</fieldset>

View File

@ -0,0 +1,35 @@
msgid "LinkEase Full"
msgstr "易有云完整版"
msgid "LinkEase Full provides the local web desktop entry for LinkEase."
msgstr "易有云完整版提供本地 Web 桌面入口。"
msgid "Global settings"
msgstr "全局设置"
msgid "Enable"
msgstr "启用"
msgid "Port"
msgstr "端口"
msgid "Base Path"
msgstr "基础路径"
msgid "Data Root Parent"
msgstr "数据根目录父路径"
msgid "The LinkEase Full service is running."
msgstr "易有云完整版服务已启动"
msgid "The LinkEase Full service is not running."
msgstr "易有云完整版服务未启动"
msgid "Click to open LinkEase Full"
msgstr "点击打开易有云完整版"
msgid "LinkEase Full Status"
msgstr "易有云完整版服务状态"
msgid "Collecting data..."
msgstr "收集数据..."

View 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 linkeaselite
LUCI_DEPENDS:=+linkeaselite
LUCI_PKGARCH:=all
PKG_VERSION:=1.0.0
PKG_RELEASE:=
LUCI_MINIFY_CSS:=0
LUCI_MINIFY_JS:=0
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature

View File

@ -0,0 +1,31 @@
module("luci.controller.linkeaselite", package.seeall)
function index()
if not nixio.fs.access("/etc/config/linkeaselite") then
return
end
local page = entry({"admin", "services", "linkeaselite"}, firstchild(), _("LinkEaseLite"), 20)
page.dependent = true
entry({"admin", "services", "linkeaselite", "config"}, cbi("linkeaselite"), _("Settings"), 10).leaf = true
entry({"admin", "services", "linkeaselite_status"}, call("linkeaselite_status"))
entry({"admin", "services", "linkeaselite", "file"}, call("linkeaselite_file_removed")).leaf = true
end
function linkeaselite_status()
local sys = require "luci.sys"
local uci = require "luci.model.uci".cursor()
local port = tonumber(uci:get_first("linkeaselite", "linkeaselite", "port"))
local status = {
running = (sys.call("pidof linkease-lite >/dev/null") == 0),
port = (port or 8897)
}
luci.http.prepare_content("application/json")
luci.http.write_json(status)
end
function linkeaselite_file_removed()
luci.http.status(404, "Not Found")
end

View File

@ -0,0 +1,24 @@
local m, s
local sys = require "luci.sys"
m = Map("linkeaselite", translate("LinkEaseLite"), translate("LinkEaseLite is an efficient data transfer tool for small-memory devices."))
m:section(SimpleSection).template = "linkeaselite_status"
m.on_after_commit = function(self)
sys.call("/etc/init.d/linkeaselite restart >/dev/null 2>&1")
end
s = m:section(TypedSection, "linkeaselite", translate("Global settings"))
s.addremove = false
s.anonymous = true
s:option(Flag, "enabled", translate("Enable")).rmempty = false
local port = s:option(Value, "port", translate("Port"))
port.rmempty = false
port.datatype = "port"
port.default = "8897"
s:option(Flag, "allowPublic", translate("AllowPublic"), translate("Allowing access via public IP addresses can lead to insufficient security.")).rmempty = false
return m

View File

@ -0,0 +1,42 @@
<script type="text/javascript">//<![CDATA[
XHR.poll(5, '<%=url("admin/services/linkeaselite_status")%>', null,
function(x, st)
{
var tb = document.getElementById('linkeaselite_status');
if (st && tb)
{
var legacyUrl = "http://" + window.location.hostname + ":" + (st.port || 8897) + "/";
var overallStatus = st.running
? '<br/><em style=\"color:green\"><%:The LinkEaseLite service is running.%></em>'
: '<br/><em style=\"color:red\"><%:The LinkEaseLite service is not running.%></em>';
tb.innerHTML = overallStatus;
function appendBreak()
{
tb.appendChild(document.createElement("br"));
}
function appendButton(value, action)
{
var button = document.createElement("input");
button.className = "btn cbi-button cbi-button-apply";
button.type = "button";
button.value = value;
button.onclick = action;
tb.appendChild(button);
}
appendBreak();
appendBreak();
appendButton(" <%:Click to open LinkEaseLite%> ", function() { window.open(legacyUrl); });
}
}
);
//]]></script>
<fieldset class="cbi-section">
<legend><%:LinkEaseLite Status%></legend>
<p id="linkeaselite_status">
<em><%:Collecting data...%></em>
</p>
</fieldset>

View File

@ -0,0 +1,35 @@
msgid "LinkEaseLite"
msgstr "易有云Lite"
msgid "LinkEaseLite is an efficient data transfer tool for small-memory devices."
msgstr "易有云Lite是面向小内存设备的轻量数据传输工具。"
msgid "Global settings"
msgstr "全局设置"
msgid "Enable"
msgstr "启用"
msgid "Port"
msgstr "端口"
msgid "AllowPublic"
msgstr "公网访问"
msgid "Allowing access via public IP addresses can lead to insufficient security."
msgstr "允许公网IP访问会导致不够安全。"
msgid "Click to open LinkEaseLite"
msgstr "点击打开易有云Lite"
msgid "The LinkEaseLite service is running."
msgstr "易有云Lite服务已启动"
msgid "The LinkEaseLite service is not running."
msgstr "易有云Lite服务未启动"
msgid "LinkEaseLite Status"
msgstr "易有云Lite服务状态"
msgid "Collecting data..."
msgstr "收集数据..."

View File

@ -0,0 +1,4 @@
#!/bin/sh
rm -f /tmp/luci-indexcache /tmp/luci-indexcache.*
exit 0

View File

@ -1746,9 +1746,9 @@ acl_app() {
run_${type} flag=$tcp_node node=$tcp_node tcp_redir_port=$redir_port ${_extra_param} config_file=$config_file log_file=$log_file loglevel=$loglevel
else
config_file="acl/${tcp_node}_SOCKS_${socks_port}.json"
run_socks flag=$tcp_node node=$tcp_node bind=127.0.0.1 socks_port=$socks_port config_file=$config_file
# local log_file=$TMP_ACL_PATH/ipt2socks_${tcp_node}_${redir_port}.log
# log_file="/dev/null"
run_socks flag=$tcp_node node=$tcp_node bind=127.0.0.1 socks_port=$socks_port config_file=$config_file log_file=$log_file
# log_file=$TMP_ACL_PATH/ipt2socks_${tcp_node}_${redir_port}.log
log_file="/dev/null"
run_ipt2socks flag=acl_${tcp_node} tcp_tproxy=${is_tproxy} local_port=$redir_port socks_address=127.0.0.1 socks_port=$socks_port log_file=$log_file
fi
run_dns ${_dns_port}
@ -1820,9 +1820,9 @@ acl_app() {
run_${type} flag=$udp_node node=$udp_node udp_redir_port=$redir_port config_file=$config_file log_file=$log_file loglevel=$loglevel
else
config_file="acl/${udp_node}_SOCKS_${socks_port}.json"
run_socks flag=$udp_node node=$udp_node bind=127.0.0.1 socks_port=$socks_port config_file=$config_file
# local log_file=$TMP_ACL_PATH/ipt2socks_${udp_node}_${redir_port}.log
# log_file="/dev/null"
run_socks flag=$udp_node node=$udp_node bind=127.0.0.1 socks_port=$socks_port config_file=$config_file log_file=$log_file
# log_file=$TMP_ACL_PATH/ipt2socks_${udp_node}_${redir_port}.log
log_file="/dev/null"
run_ipt2socks flag=acl_${udp_node} local_port=$redir_port socks_address=127.0.0.1 socks_port=$socks_port log_file=$log_file
fi
fi

View File

@ -476,7 +476,7 @@ ln_run() {
fi
#echo "${file_func} $*" >&2
[ -n "${file_func}" ] || echolog " - 找不到 ${ln_name},无法启动..."
[ "${output}" != "/dev/null" ] && [ "${ln_name}" != "chinadns-ng" ] && {
[ "${output}" != "/dev/null" ] && [ -n "$(echo "${output}" | grep -E "default|SOCKS_")" ] && [ "${ln_name}" != "chinadns-ng" ] && {
local persist_log_path=$(config_t_get global persist_log_path)
local sys_log=$(config_t_get global sys_log "0")
}

View File

@ -27,7 +27,7 @@ define Download/openlist-frontend
HASH:=74f6487dd73c130ecd396a8d005dab815f6e838e0520447ed54910a0059cfbca
endef
PKG_BUILD_DEPENDS:=golang/host
PKG_BUILD_DEPENDS:=golang/host fuse
PKG_BUILD_PARALLEL:=1
PKG_USE_MIPS16:=0
PKG_BUILD_FLAGS:=no-mips16

View File

@ -30,13 +30,13 @@ define Download/geosite
HASH:=e6093b92682caca6719697981425f1fc7e50f6b8634632ad756e324070d08ff4
endef
GEOSITE_IRAN_VER:=202607130116
GEOSITE_IRAN_VER:=202607270122
GEOSITE_IRAN_FILE:=iran.dat.$(GEOSITE_IRAN_VER)
define Download/geosite-ir
URL:=https://github.com/bootmortis/iran-hosted-domains/releases/download/$(GEOSITE_IRAN_VER)/
URL_FILE:=iran.dat
FILE:=$(GEOSITE_IRAN_FILE)
HASH:=a6cc63b73db6decbc8990cfcb3f3a63e0c99378575458fd04df7517ce94a0479
HASH:=6566f24c6349bbd4cba2f6d19e37e80b1b4138c1f8e436d407416b6f8cd13856
endef
define Package/v2ray-geodata/template