update 2026-09-12 16:17:58

This commit is contained in:
action
2026-09-12 16:17:58 +08:00
parent 639738b05d
commit 7497dff620
13 changed files with 252 additions and 142 deletions
@@ -61,14 +61,11 @@ function getOption(config, section, opt) {
const id = `cbid.${config}.${section}.${opt}`;
obj = document.getElementsByName(id)[0] || document.getElementById(id);
if (obj) {
//lua luci combobox
const combobox = document.getElementById('cbi.combobox.' + id);
if (combobox) {
obj.combobox = combobox;
}
const div = document.getElementById(id);
if (div && div.getElementsByTagName("li").length > 0) {
obj = div;
}
return obj;
} else {
return null;
@@ -26,12 +26,18 @@ local config = map.config
update_config(node_id, {
remarks: getOption("<%=config%>", "<%=sid%>", "remarks").value,
group: getOption("<%=config%>", "<%=sid%>", "group").value,
type: getOption("<%=config%>", "<%=sid%>", "type").value,
type: el.value,
//protocol: ''
}, node_config_url);
}
}
});
const fromUrlCache = JSON.parse(sessionStorage.getItem("fromUrl"));
if (fromUrlCache && fromUrlCache.savetime && (Date.now() - fromUrlCache.timestamp) < fromUrlCache.savetime) {
fromUrl(fromUrlCache.link, true)
} else {
sessionStorage.removeItem("fromUrl");
}
});
waitForElement('select[name*="<%=config%>"][name*="protocol"]', function(el) {
let o_val = el.value;
@@ -56,7 +62,7 @@ local config = map.config
update_config(node_id, {
remarks: getOption("<%=config%>", "<%=sid%>", "remarks").value,
group: getOption("<%=config%>", "<%=sid%>", "group").value,
protocol: getOption("<%=config%>", "<%=sid%>", name).value,
protocol: el.value,
}, node_config_url);
*/
}
@@ -1,8 +1,8 @@
<%+cbi/valueheader%>
<%
local api = require "luci.passwall.api"
local appname = api.appname
local map = self.map
local api = map.api
local config = map.config
local has_ss_rust = api.is_finded("sslocal")
local has_singbox = api.finded_com("sing-box")
@@ -17,7 +17,7 @@ local hysteria2_type = api.get_core("hysteria2_type", {{has_hysteria2,"hysteria2
local current_node = map:get(section)
-%>
<script src="<%=resource%>/view/<%=appname%>/qrcode.min.js"></script>
<script src="<%=resource%>/view/<%=api.appname%>/qrcode.min.js"></script>
<script type="text/javascript">//<![CDATA[
let has_singbox = "<%=has_singbox%>"
let has_xray = "<%=has_xray%>"
@@ -112,8 +112,7 @@ local current_node = map:get(section)
function buildUrl(btn, urlname, sid) {
var opt = {
base: "cbid.<%=appname%>",
client: true,
base: "cbid.<%=config%>",
get: function (opt) {
var id = this.base + "." + opt;
var obj = document.getElementsByName(id)[0] || document.getElementsByClassName(id)[0] || document.getElementById(id)
@@ -158,7 +157,6 @@ local current_node = map:get(section)
}
}
opt.base = "cbid." + urlname + "." + sid;
opt.client = urlname.indexOf("server") === -1;
var v_type = opt.get("type").value;
var v_alias = opt.get("remarks");
@@ -753,34 +751,49 @@ local current_node = map:get(section)
return server.replace(/^\[/, '').replace(/\]$/, '');
}
function fromUrl(btn, urlname, sid, cacheData) {
var opt = {
base: 'cbid.<%=appname%>',
client: true,
function tofromUrl(btn) {
let link = prompt('<%:Paste Share URL Here%>', '');
if (link === null || link === "") {
return false;
}
return fromUrl(link);
}
function tofromUrl(btn) {
let link = prompt('<%:Paste Share URL Here%>', '');
if (link === null || link === "") {
return false;
}
return fromUrl(link);
}
function fromUrl(link, from_cache) {
if (!link) {
return false;
}
const s = document.getElementById("<%=self.option%>-status");
if (!s) {
alert("Never");
return false;
}
s.innerHTML = "";
const current_type = '<%=current_node.type or ""%>';
let switch_type = null;
const opt = {
base: "cbid.<%=self.option%>.<%=self.value%>",
get: function (opt) {
var obj;
var id = this.base + '.' + opt;
obj = document.getElementsByName(id)[0] || document.getElementById(id);
if (obj) {
var combobox = document.getElementById('cbi.combobox.' + id);
if (combobox) {
obj.combobox = combobox;
}
var div = document.getElementById(id);
if (div && div.getElementsByTagName("li").length > 0) {
obj = div;
}
return obj;
} else {
return null;
}
return getOption("<%=config%>", "<%=self.value%>", opt);
},
set: function (opt, val) {
if (!switch_type && opt == "type" && current_type != val) {
switch_type = val;
return;
}
var obj;
obj = this.get(opt);
if (obj) {
var event = document.createEvent("HTMLEvents");
event.initEvent("change", true, true);
if (obj.type === 'checkbox') {
obj.checked = val;
} else {
@@ -814,17 +827,17 @@ local current_node = map:get(section)
}
}
try {
obj.dispatchEvent(event);
obj.dispatchEvent(new Event("change", { bubbles: true, cancelable: true }));
} catch (err) {}
} else {
//alert('<%:Faltal on set option, please help in debug: %>' + opt + ' = ' + val);
// 处理 DynamicList
// Processing DynamicList
var fullName = this.base + '.' + opt;
var lists = document.querySelectorAll('.cbi-dynlist');
for (var i = 0; i < lists.length; i++) {
var parent = lists[i].closest('.cbi-value');
if (!parent) continue;
// 尝试从 label for 属性中提取 fullName
// Try to extract fullName from the label's for attribute.
var label = parent.querySelector('label.cbi-value-title');
var labelFor = label?.getAttribute('for');
if (labelFor === fullName) {
@@ -838,58 +851,20 @@ local current_node = map:get(section)
}
}
}
},
setlist: function (opt, vlist) {
var id = this.base + "." + opt;
var objs = document.getElementsByName(id) || document.getElementsByClassName(id);
if (objs) {
var values = "";
for (var i = 0; i < vlist.length; i++) {
values += vlist[i] + ", ";
}
alert("Manually input the option:\n" + opt + "s:\n[" + values + "]");
} else {
//alert("<%:Faltal on set option, please help in debug: %>" + opt);
}
}
}
var s = document.getElementById(urlname + '-status');
if (!s) {
alert("Never");
return false;
}
opt.base = "cbid." + urlname + "." + sid;
opt.client = urlname.indexOf("server") === -1;
var ssrurl = null;
if (cacheData) {
ssrurl = cacheData.ssrurl
} else {
ssrurl = prompt('<%:Paste Share URL Here%>', '');
if (ssrurl === null || ssrurl === "") {
return false;
}
sessionStorage.setItem("fromUrl", JSON.stringify({
timestamp: Date.now(),
savetime: 60 * 1000,
urlname: urlname,
sid: sid,
ssrurl: ssrurl
}));
}
ssrurl = ssrurl.split(/\r\n|\r|\n/, 1)[0];
ssrurl = ssrurl.replace(/&amp;/gi, '&').replace(/\s*#\s*/, '#').trim(); //一些奇葩的链接用"&amp;"当做"&""#"前后带空格
s.innerHTML = "";
var ssu = ssrurl.split('://');
var event = document.createEvent("HTMLEvents");
event.initEvent("change", true, true);
link = link.split(/\r\n|\r|\n/, 1)[0];
link = link.replace(/&amp;/gi, '&').replace(/\s*#\s*/, '#').trim(); //一些奇葩的链接用"&amp;"当做"&""#"前后带空格
const link_split = link.split('://');
const link_type = link_split[0];
var dom_prefix = null
let dom_prefix = null;
if (ssu[0] === "ssr") {
if (link_type === "ssr") {
dom_prefix = "ssr_"
//var b64c = ssu[1].match(/([A-Za-z0-9_-]+)/);
var sstr = b64decsafe((ssu[1] || "").replace(/#.*/, "").trim());
//var b64c = link_split[1].match(/([A-Za-z0-9_-]+)/);
var sstr = b64decsafe((link_split[1] || "").replace(/#.*/, "").trim());
var ploc = sstr.indexOf("/?");
var url0 = "", param = "";
if (ploc > 0) {
@@ -924,8 +899,8 @@ local current_node = map:get(section)
if (typeof (rem) !== 'undefined' && rem !== '' && rem.length > 0)
opt.set('remarks', b64decutf8safe(rem));
}
if (ssu[0] === "ss") {
var url0 = ssu[1] || "";
if (link_type === "ss") {
var url0 = link_split[1] || "";
param = "";
var ploc = url0.indexOf("#");
@@ -1000,15 +975,15 @@ local current_node = map:get(section)
queryParam.type && queryParam.type !== "tcp" &&
queryParam.headerType && queryParam.headerType !== "none";
if (has_xray && (ss_type == "xray" || needUpgrade || queryParam.type === "xhttp")) {
dom_prefix = "xray_"
dom_prefix = "xray_";
opt.set('type', "Xray");
opt.set(dom_prefix + 'protocol', "shadowsocks");
} else if (has_singbox && (ss_type == "sing-box" || needUpgrade)) {
dom_prefix = "singbox_"
dom_prefix = "singbox_";
opt.set('type', "sing-box");
opt.set(dom_prefix + 'protocol', "shadowsocks");
} else {
dom_prefix = "ssrust_"
dom_prefix = "ssrust_";
opt.set('type', "SS-Rust");
}
@@ -1208,17 +1183,17 @@ local current_node = map:get(section)
}
}
}
if (ssu[0] === "trojan") {
if (link_type === "trojan") {
if (trojan_type == "sing-box" && has_singbox) {
dom_prefix = "singbox_"
dom_prefix = "singbox_";
opt.set('type', "sing-box");
opt.set(dom_prefix + 'protocol', "trojan");
} else if (trojan_type == "xray" && has_xray) {
dom_prefix = "xray_"
dom_prefix = "xray_";
opt.set('type', "Xray");
opt.set(dom_prefix + 'protocol', "trojan");
}
var m = parseNodeUrl(ssrurl);
var m = parseNodeUrl(link);
var password = m.passwd;
if (password === "") {
s.innerHTML = "<font color='red'><%:Invalid Share URL Format%></font>";
@@ -1346,14 +1321,14 @@ local current_node = map:get(section)
opt.set('remarks', decodeURIComponent(m.hash.substr(1)));
}
}
if (ssu[0] === "vmess") {
var sstr = b64DecodeUnicode((ssu[1] || "").replace(/#.*/, "").trim());
if (link_type === "vmess") {
var sstr = b64DecodeUnicode((link_split[1] || "").replace(/#.*/, "").trim());
var ploc = sstr.indexOf("/?");
if (vmess_type == "sing-box" && has_singbox) {
dom_prefix = "singbox_"
dom_prefix = "singbox_";
opt.set('type', "sing-box");
} else if (has_xray) {
dom_prefix = "xray_"
dom_prefix = "xray_";
opt.set('type', "Xray");
}
opt.set(dom_prefix + 'protocol', "vmess");
@@ -1447,14 +1422,14 @@ local current_node = map:get(section)
opt.set(dom_prefix + 'use_finalmask', !!ssm.fm);
opt.set(dom_prefix + 'finalmask', ssm.fm || "");
}
if (ssu[0] === "vless") {
if (link_type === "vless") {
if (vless_type == "sing-box" && has_singbox) {
dom_prefix = "singbox_";
} else if (has_xray) {
dom_prefix = "xray_";
}
var m = parseNodeUrl(ssrurl);
var m = parseNodeUrl(link);
var password = m.passwd;
if (password === "") {
s.innerHTML = "<font color='red'><%:Invalid Share URL Format%></font>";
@@ -1476,7 +1451,7 @@ local current_node = map:get(section)
if (["xhttp", "kcp", "mkcp"].includes(queryParam.type) && vless_type !== "xray" && has_xray) {
dom_prefix = "xray_";
}
opt.set("type", dom_prefix === "singbox_" ? "sing-box" : "Xray");
opt.set('type', dom_prefix == "singbox_" ? "sing-box" : "Xray");
opt.set(dom_prefix + 'protocol', "vless");
opt.set(dom_prefix + 'uuid', password);
@@ -1606,8 +1581,8 @@ local current_node = map:get(section)
opt.set('remarks', decodeURIComponent(m.hash.substr(1)));
}
}
if (ssu[0] === "hysteria2" || ssu[0] === "hy2") {
var m = parseNodeUrl(ssrurl);
if (link_type === "hysteria2" || link_type === "hy2") {
var m = parseNodeUrl(link);
var password = m.passwd;
if (password === "") {
s.innerHTML = "<font color='red'><%:Invalid Share URL Format%></font>";
@@ -1672,13 +1647,13 @@ local current_node = map:get(section)
opt.set('remarks', decodeURIComponent(m.hash.substr(1)));
}
}
if (ssu[0] === "tuic") {
if (link_type === "tuic") {
if (has_singbox) {
dom_prefix = "singbox_"
dom_prefix = "singbox_";
opt.set('type', "sing-box");
}
opt.set(dom_prefix + 'protocol', "tuic");
var _parsedUrl = new URL("http://" + ssu[1]);
var _parsedUrl = new URL("http://" + link_split[1]);
var username = _parsedUrl.username;
var password = _parsedUrl.password;
var hostname = _parsedUrl.hostname;
@@ -1721,13 +1696,13 @@ local current_node = map:get(section)
opt.set('remarks', decodeURIComponent(hash.substr(1)));
}
}
if (ssu[0] === "anytls") {
if (link_type === "anytls") {
if (has_singbox) {
dom_prefix = "singbox_"
dom_prefix = "singbox_";
opt.set('type', "sing-box");
}
opt.set(dom_prefix + 'protocol', "anytls");
var m = parseNodeUrl(ssrurl);
var m = parseNodeUrl(link);
var password = m.passwd;
if (password === "") {
s.innerHTML = "<font color='red'><%:Invalid Share URL Format%></font>";
@@ -1777,13 +1752,13 @@ local current_node = map:get(section)
opt.set('remarks', decodeURIComponent(m.hash.substr(1)));
}
}
if (ssu[0] === "naive+https" || ssu[0] === "naive+quic") {
if (link_type === "naive+https" || link_type === "naive+quic") {
if (has_singbox) {
dom_prefix = "singbox_"
dom_prefix = "singbox_";
opt.set('type', "sing-box");
}
opt.set(dom_prefix + 'protocol', "naive");
var _parsedUrl = new URL("http://" + ssu[1]);
var _parsedUrl = new URL("http://" + link_split[1]);
var username = _parsedUrl.username;
var password = _parsedUrl.password;
var hostname = _parsedUrl.hostname;
@@ -1818,7 +1793,7 @@ local current_node = map:get(section)
opt.set(dom_prefix + 'tls_serverName', queryParam.sni || '');
opt.set(dom_prefix + 'ech', !!queryParam.ech);
opt.set(dom_prefix + 'ech_config', queryParam.ech || '');
if (ssu[0] === "naive+quic") {
if (link_type === "naive+quic") {
opt.set(dom_prefix + 'naive_quic', true);
opt.set(dom_prefix + 'naive_congestion_control', queryParam.congestion_control || 'bbr');
}
@@ -1826,20 +1801,25 @@ local current_node = map:get(section)
opt.set('remarks', decodeURIComponent(hash.substr(1)));
}
}
if (dom_prefix && dom_prefix != null) {
const port = opt.get(dom_prefix + 'port');
if (!port) {
return false;
}
if (port.value) {
port.focus();
port.blur();
}
} else {
if (switch_type) {
sessionStorage.setItem("fromUrl", JSON.stringify({
timestamp: Date.now(),
savetime: 60 * 1000,
link: link
}));
opt.set("type", switch_type);
return
}
if (!dom_prefix) {
s.innerHTML = "<font color='red'><%:Invalid Share URL Format%></font>";
sessionStorage.removeItem("fromUrl");
return false;
}
if (opt.get(dom_prefix + 'port')) {
opt.get(dom_prefix + 'port').focus();
opt.get(dom_prefix + 'port').blur();
}
s.innerHTML = "<font color='green'><%:Import Finished %></font>";
sessionStorage.removeItem("fromUrl");
return true;
@@ -1849,17 +1829,6 @@ local current_node = map:get(section)
window.open('<%=api.url("gen_client_config")%>?id=' + sid, "_blank")
}
document.addEventListener("DOMContentLoaded", function () {
setTimeout(function () {
const fromUrlCache = JSON.parse(sessionStorage.getItem("fromUrl"));
if (fromUrlCache && fromUrlCache.savetime && (Date.now() - fromUrlCache.timestamp) < fromUrlCache.savetime) {
fromUrl(null, fromUrlCache.urlname, fromUrlCache.sid, fromUrlCache)
} else {
sessionStorage.removeItem("fromUrl");
}
}, 500);
})
//]]></script>
<style>
.share-toolbar {
@@ -1892,7 +1861,7 @@ local current_node = map:get(section)
<div class="share-toolbar">
<div class="toolbar-buttons">
<% if not current_node.protocol or not current_node.protocol:find("^_") then -%>
<input id="fromUrlBtn" type="button" class="btn cbi-button cbi-button-apply" value='<%:From Share URL%>' onclick="return fromUrl(this, '<%=self.option%>', '<%=self.value%>')" />
<input id="fromUrlBtn" type="button" class="btn cbi-button cbi-button-apply" value='<%:From Share URL%>' onclick="return tofromUrl()" />
<input id="exportUrlBtn" type="button" class="btn cbi-button cbi-button-apply" value='<%:Build Share URL%>' onclick="return exportUrl(this, '<%=self.option%>', '<%=self.value%>')" />
<input id="genQrcodeBtn" type="button" class="btn cbi-button cbi-button-apply" value='<%:Generate QRCode%>' onclick="return genQrcode(this, '<%=self.option%>', '<%=self.value%>')" />
<% end -%>
@@ -5,20 +5,21 @@ local appname = map.config
local is_js_luci = api.is_js_luci()
local section_tag = is_js_luci and "div" or "fieldset"
local default_node_type = ""
local shunt_rule_list = {}
local node = map:get("@global[0]", "node")
if node then
local node_type = map:get(node, "type")
local node_protocol = map:get(node, "protocol")
if (node_type == "Xray" or node_type == "sing-box") and node_protocol == "_shunt" then
default_node_type = node_protocol
if node_protocol == "_shunt" then
local node_shunt_group = map:get(node, "shunt_group")
map:foreach("shunt_rules", function(e)
if e[".name"] and e.remarks and e.group == node_shunt_group then
shunt_rule_list[#shunt_rule_list + 1] = e
end
end)
shunt_rule_list[#shunt_rule_list + 1] = {
[".name"] = "default_node",
remarks = translate("Default")
}
end
end
-%>
@@ -1671,14 +1672,13 @@ table td, .table .td {
<input class="btn cbi-button" type="button" onclick="set_node_div.set('@global[0]', 'node')" value="<%:OK%>" />
</div>
</div>
<% if default_node_type == "_shunt" then %>
<% if #shunt_rule_list > 0 then %>
<div id="set_node_shunt_div" style="margin: 0.5em 0; padding: 0.5em; background: #f7f7f7; border-radius: 4px;">
<div class="cbi-section-descr" style="text-align: left;"><a style="color:#FF8C00"><%:Set as the routing rule node:%></a></div>
<div>
<% for i, v in ipairs(shunt_rule_list) do %>
<input class="btn cbi-button" type="button" onclick="set_node_div.set('<%=node%>', '<%=v[".name"]%>')" value="<%=v.remarks%>" />
<% end %>
<input class="btn cbi-button" type="button" onclick="set_node_div.set('<%=node%>', 'default_node')" value="<%:Default%>" />
</div>
</div>
<% end %>
@@ -818,7 +818,7 @@ add_firewall_rule() {
# jump chains
# Only TCP, UDP Invalid.
nft "add rule $NFTABLE_NAME mangle_prerouting meta nfproto ipv4 meta l4proto tcp socket transparent 1 mark set ${FWMARK} counter accept"
nft "add rule $NFTABLE_NAME mangle_prerouting meta nfproto ipv4 meta l4proto tcp socket transparent 1 mark set ${FWMARK} counter accept comment PSW2_SOCKET"
nft "add rule $NFTABLE_NAME mangle_prerouting ip daddr != @$NFTSET_DIRECT ip protocol udp counter jump PSW2_MANGLE"
[ -n "${is_tproxy}" ] && nft "add rule $NFTABLE_NAME mangle_prerouting ip daddr != @$NFTSET_DIRECT ip protocol tcp counter jump PSW2_MANGLE"
@@ -889,7 +889,7 @@ add_firewall_rule() {
# jump chains
[ "$PROXY_IPV6" == "1" ] && {
# Only TCP, UDP Invalid.
nft "add rule $NFTABLE_NAME mangle_prerouting meta nfproto ipv6 meta l4proto tcp socket transparent 1 mark set ${FWMARK} counter accept"
nft "add rule $NFTABLE_NAME mangle_prerouting meta nfproto ipv6 meta l4proto tcp socket transparent 1 mark set ${FWMARK} counter accept comment PSW2_SOCKET"
nft "add rule $NFTABLE_NAME mangle_prerouting ip6 daddr != @$NFTSET_DIRECT6 meta nfproto {ipv6} counter jump PSW2_MANGLE_V6"
nft "add rule $NFTABLE_NAME mangle_output ip6 daddr != @$NFTSET_DIRECT6 meta nfproto {ipv6} counter jump PSW2_OUTPUT_MANGLE_V6 comment \"PSW2_OUTPUT_MANGLE\""
Submodule luci-app-syncthing deleted from b7a197a93c
+22
View File
@@ -0,0 +1,22 @@
# Copyright (C) 2020 Gyj1109
# 适配 OpenWrt 25.12 修改版
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-syncthing
PKG_VERSION:=1.0
PKG_RELEASE:=3
LUCI_TITLE:=LuCI support for Syncthing
LUCI_PKGARCH:=all
# 适配 25.12:确保安装了 syncthing 后,LuCI 界面能正确拉起
LUCI_DEPENDS:=+syncthing
include $(TOPDIR)/feeds/luci/luci.mk
# 明确定义配置文件的归属,这对 25.12 的 apk 管理器非常重要
define Package/$(PKG_NAME)/conffiles
/etc/config/syncthing
endef
$(eval $(call BuildPackage,$(PKG_NAME)))
+2
View File
@@ -0,0 +1,2 @@
# luci-app-syncthing
在Potat0000源码基础上进行了汉化优化,官方openwrt23.05.3编译后可使用
@@ -0,0 +1,17 @@
module("luci.controller.syncthing", package.seeall)
function index()
if not nixio.fs.access("/etc/config/syncthing") then
return
end
entry({"admin", "services", "syncthing"}, cbi("syncthing"), _("文件同步"), 10).dependent = true
entry({"admin", "services", "syncthing", "status"}, call("act_status")).leaf = true
end
function act_status()
local e = {}
e.running = luci.sys.call("pgrep syncthing >/dev/null") == 0
luci.http.prepare_content("application/json")
luci.http.write_json(e)
end
@@ -0,0 +1,48 @@
require("nixio.fs")
m = Map("syncthing", translate("Syncthing同步工具"))
m:section(SimpleSection).template = "syncthing/syncthing_status"
s = m:section(TypedSection, "syncthing")
s.anonymous = true
o = s:option(Flag, "enabled", translate("启用"))
o.default = 0
o.rmempty = false
gui_address = s:option(Value, "gui_address", translate("GUI访问地址"))
gui_address.description = translate("使用0.0.0.0以监控所有访问。")
gui_address.default = "http://0.0.0.0:8384"
gui_address.placeholder = "http://0.0.0.0:8384"
gui_address.rmempty = false
home = s:option(Value, "home", translate("配置文件目录"))
home.description = translate("只有保存在/etc/syncthing中的配置会自动备份!")
home.default = "/etc/syncthing"
home.placeholder = "/etc/syncthing"
home.rmempty = false
user = s:option(ListValue, "user", translate("用户"))
user.description = translate("默认是syncthing,但这可能会导致权限被拒绝。Syncthing官方不建议以root身份运行。")
user:value("", translate("syncthing"))
for u in luci.util.execi("cat /etc/passwd | cut -d ':' -f1") do
user:value(u)
end
macprocs = s:option(Value, "macprocs", translate("线程限制"))
macprocs.description = translate("0表示匹配CPU数量(默认),>0表示显式指定并发数。")
macprocs.default = "0"
macprocs.placeholder = "0"
macprocs.datatype = "range(0,32)"
macprocs.rmempty = false
nice = s:option(Value, "nice", translate("优先级"))
nice.description = translate("显式指定优先级值。0是最高,19是最低。(暂时不允许设置负值)")
nice.default = "19"
nice.placeholder = "19"
nice.datatype = "range(0,19)"
nice.rmempty = false
return m
@@ -0,0 +1,27 @@
<script type="text/javascript">//<![CDATA[
XHR.poll(1, '<%=url([[admin]], [[services]], [[syncthing]], [[status]])%>', null,
function (x, data) {
var tb = document.getElementById('syncthing_status');
if (data && tb) {
if (data.running) {
var links = '<em><b><font color="green">Syncthing <%:运行中%></font></b></em><input class="btn cbi-button mar-10" type="button" value="<%:打开Syncthing页面%>" onclick="openwebui();" />';
tb.innerHTML = links;
} else {
tb.innerHTML = '<em><b><font color="red">Syncthing <%:未运行%></font></b></em>';
}
}
}
);
function openwebui(){
var url = window.location.host+":<%=luci.sys.exec("uci -q get syncthing.syncthing.gui_address"):match(":[0-9]+"):gsub(":", "")%>";
window.open('http://'+url,'target','');
}
//]]>
</script>
<style>.mar-10 {margin-left: 50px; margin-right: 10px;}</style>
<fieldset class="cbi-section">
<p id="syncthing_status">
<em><%:正在收集数据...%></em>
</p>
</fieldset>
@@ -0,0 +1,12 @@
#!/bin/sh
touch /etc/config/syncthing
uci -q batch <<-EOF >/dev/null
delete ucitrack.@syncthing[-1]
add ucitrack syncthing
set ucitrack.@syncthing[-1].exec='/etc/init.d/syncthing stop && /etc/init.d/syncthing start'
commit ucitrack
EOF
# remove LuCI cache
rm -f /tmp/luci*
exit 0
@@ -0,0 +1,11 @@
{
"luci-app-syncthing": {
"description": "Grant UCI access for luci-app-syncthing",
"read": {
"uci": [ "syncthing" ]
},
"write": {
"uci": [ "syncthing" ]
}
}
}