update 2026-07-28 14:21:36

This commit is contained in:
action
2026-07-28 14:21:36 +08:00
parent ce54ee1bb0
commit 9c289a7240
44 changed files with 19 additions and 474 deletions
@@ -15,12 +15,10 @@ end
function kaiplus_status() function kaiplus_status()
local sys = require "luci.sys" local sys = require "luci.sys"
local uci = require "luci.model.uci".cursor() local uci = require "luci.model.uci".cursor()
local port = uci:get_first("kaiplus", "kaiplus", "port", "8189") local port = uci:get_first("kaiplus", "kaiplus", "port", "8198")
local base_path = uci:get_first("kaiplus", "kaiplus", "base_path", "/apps/kaiplus/")
local status = { local status = {
running = (sys.call("pidof kaiplus_bin >/dev/null") == 0), 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.prepare_content("application/json")
luci.http.write_json(status) luci.http.write_json(status)
@@ -5,10 +5,7 @@ m = Map("kaiplus", translate("KaiPlus"), translate("KaiPlus is an AI workspace a
m:section(SimpleSection).template = "kaiplus/kaiplus_status" m:section(SimpleSection).template = "kaiplus/kaiplus_status"
m.on_after_commit = function(self) 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 end
s = m:section(TypedSection, "kaiplus", translate("Global settings")) s = m:section(TypedSection, "kaiplus", translate("Global settings"))
@@ -32,7 +29,7 @@ end
data_dir.default = default_path data_dir.default = default_path
local port = s:option(Value, "port", translate("Web port")) local port = s:option(Value, "port", translate("Web port"))
port.default = "8189" port.default = "8198"
port.rmempty = false port.rmempty = false
port.datatype = "port" port.datatype = "port"
port.description = translate("Port for the KaiPlus web service.") port.description = translate("Port for the KaiPlus web service.")
@@ -6,15 +6,8 @@ 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>'; el.innerHTML = '<br/><em style=\"color:red\"><%:The KaiPlus service is not running.%></em>';
} }
if (st.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 = '<br/><em style=\"color:green\"><%:The KaiPlus service is running.%></em>' el.innerHTML = '<br/><em style=\"color:green\"><%:The KaiPlus service is running.%></em>'
+ "<br/><br/><input class=\"btn cbi-button cbi-button-apply\" type=\"button\" value=\" <%:Click to open KaiPlus%> \" onclick=\"window.open('http://' + window.location.hostname + ':' + st.port + basePath)\"/>"; + "<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 + "/')\"/>";
} }
} }
}); });
+2 -2
View File
@@ -6,9 +6,9 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
LUCI_TITLE:=LuCI support for linkease LUCI_TITLE:=LuCI support for linkease
LUCI_DEPENDS:=+luci-lib-linkeasefile LUCI_DEPENDS:=+linkease
LUCI_PKGARCH:=all LUCI_PKGARCH:=all
PKG_VERSION:=2.1.70-r4 PKG_VERSION:=2.1.70-r3
# PKG_RELEASE MUST be empty for luci.mk # PKG_RELEASE MUST be empty for luci.mk
PKG_RELEASE:= PKG_RELEASE:=
LUCI_MINIFY_CSS:=0 LUCI_MINIFY_CSS:=0

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

@@ -9,6 +9,7 @@ function index()
entry({"admin", "services", "linkease_status"}, call("linkease_status")) entry({"admin", "services", "linkease_status"}, call("linkease_status"))
entry({"admin", "services", "linkease", "file"}, call("linkease_file_template")).leaf = true
end end
@@ -26,3 +27,13 @@ function linkease_status()
luci.http.write_json(status) luci.http.write_json(status)
end 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
@@ -7,27 +7,13 @@ local ltn12 = require "luci.ltn12"
local table = require "table" local table = require "table"
local util = require "luci.util" local util = require "luci.util"
module("luci.controller.linkease_file", package.seeall) module("luci.controller.linkease_backend", package.seeall)
local BLOCKSIZE = 2048 local BLOCKSIZE = 2048
local LINKEASE_UNIX = "/var/run/linkease.sock" local LINKEASE_UNIX = "/var/run/linkease.sock"
function index() function index()
entry({"linkease"}, call("linkease_backend")).leaf=true entry({"linkease"}, call("linkease_backend")).leaf=true
local e = entry({"admin", "services", "linkease", "file"}, call("linkease_file_template"))
e.leaf = true
e.dependent = false
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 end
local function sink_socket(sock, io_err) local function sink_socket(sock, io_err)
-18
View File
@@ -1,18 +0,0 @@
#
# 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.1
PKG_RELEASE:=1
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature
@@ -1,89 +0,0 @@
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
@@ -1,86 +0,0 @@
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
@@ -1,34 +0,0 @@
<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>
@@ -1,35 +0,0 @@
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 "收集数据..."
@@ -1,4 +0,0 @@
#!/bin/sh
rm -f /tmp/luci-indexcache /tmp/luci-indexcache.*
exit 0
-18
View File
@@ -1,18 +0,0 @@
# 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
@@ -1,31 +0,0 @@
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
@@ -1,24 +0,0 @@
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
@@ -1,42 +0,0 @@
<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>
@@ -1,35 +0,0 @@
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 "收集数据..."
@@ -1,4 +0,0 @@
#!/bin/sh
rm -f /tmp/luci-indexcache /tmp/luci-indexcache.*
exit 0
-16
View File
@@ -1,16 +0,0 @@
include $(TOPDIR)/rules.mk
LUCI_TITLE:=LuCI support for linkease file
LUCI_DEPENDS:=+linkease
LUCI_PKGARCH:=all
PKG_VERSION:=2.1.70-r3
# 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
@@ -1,4 +0,0 @@
#!/bin/sh
rm -f /tmp/luci-indexcache /tmp/luci-indexcache.*
exit 0