mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-10-29 07:45:13 +08:00
🍕 Sync 2026-10-01 13:33:56
This commit is contained in:
1 parent
9057fa94b1
commit
1f3a4e8fb4
24 files changed
+731
-83
No files matched your search
@@ -90,10 +90,15 @@ local function read_cron_fields()
|
||||
return cron_enable, cron_time
|
||||
end
|
||||
|
||||
-- read subscription level rules from form, return rules table
|
||||
-- read subscription level rules from form, return rules table, err
|
||||
--
|
||||
-- 第二个返回值是**用户可见**的校验错误。目前只有重命名规则的正则需要校验:
|
||||
-- 非法 pattern 会被 node.rename_with_rules 里的 pcall 静默吞掉,用户看到的是
|
||||
-- 「规则写了但没生效」。在这里拦下来,调用方用 back_to_list(err) 回显。
|
||||
local function read_rules_fields()
|
||||
local http = require("luci.http")
|
||||
local core = require("substore.core")
|
||||
local node = require("substore.node")
|
||||
local proto_list = {}
|
||||
-- 协议清单来自 core.RULE_PROTOS,与表单模板共用一份(见该常量的说明)
|
||||
for _, p in ipairs(core.RULE_PROTOS) do
|
||||
@@ -101,13 +106,16 @@ local function read_rules_fields()
|
||||
end
|
||||
local rules_enable = fv(http, "rules_enable") or "0"
|
||||
if rules_enable ~= "1" then rules_enable = "0" end
|
||||
local rename_map = fv(http, "rename_map") or ""
|
||||
local vok, verr = node.validate_rename_map(rename_map)
|
||||
if not vok then return nil, verr end
|
||||
return {
|
||||
rules_enable = rules_enable,
|
||||
proto_filter = table.concat(proto_list, ","),
|
||||
keyword_include = fv(http, "keyword_include") or "",
|
||||
keyword_exclude = fv(http, "keyword_exclude") or "",
|
||||
dedup = fv(http, "dedup") or "0",
|
||||
rename_map = fv(http, "rename_map") or "",
|
||||
rename_map = rename_map,
|
||||
}
|
||||
end
|
||||
|
||||
@@ -124,7 +132,9 @@ function action_create()
|
||||
return back_to_list("名称和 URL 不能为空")
|
||||
end
|
||||
local cron_enable, cron_time = read_cron_fields()
|
||||
local rules = read_rules_fields()
|
||||
-- 规则校验失败(如非法正则)必须回显,否则用户看到的是「规则没生效」
|
||||
local rules, rules_err = read_rules_fields()
|
||||
if not rules then return back_to_list(rules_err or "规则无效") end
|
||||
local id, err = core.add(name, url, {
|
||||
proxy_enable = proxy_enable, proxy = proxy,
|
||||
cron_enable = cron_enable, cron_time = cron_time,
|
||||
@@ -153,7 +163,9 @@ function action_save()
|
||||
return back_to_list("名称和 URL 不能为空")
|
||||
end
|
||||
local cron_enable, cron_time = read_cron_fields()
|
||||
local rules = read_rules_fields()
|
||||
-- 规则校验失败(如非法正则)必须回显,否则用户看到的是「规则没生效」
|
||||
local rules, rules_err = read_rules_fields()
|
||||
if not rules then return back_to_list(rules_err or "规则无效") end
|
||||
local ok, err = core.save_meta(id, {
|
||||
name = name, url = url, proxy_enable = proxy_enable, proxy = proxy,
|
||||
cron_enable = cron_enable, cron_time = cron_time,
|
||||
@@ -174,7 +186,9 @@ function action_local_create()
|
||||
local name = (fv(http, "name") or ""):gsub("^%s+", ""):gsub("%s+$", "")
|
||||
local content = fv(http, "content") or ""
|
||||
local local_mode = fv(http, "local_mode") or "text"
|
||||
local rules = read_rules_fields()
|
||||
-- 规则校验失败(如非法正则)必须回显,否则用户看到的是「规则没生效」
|
||||
local rules, rules_err = read_rules_fields()
|
||||
if not rules then return back_to_list(rules_err or "规则无效") end
|
||||
-- §19:空名称 / 空内容必须明确报错,不能无声创建无效订阅
|
||||
if name == "" then return back_to_list("名称不能为空") end
|
||||
if content == "" then return back_to_list("订阅内容不能为空") end
|
||||
@@ -198,7 +212,9 @@ function action_local_save()
|
||||
local name = (fv(http, "name") or ""):gsub("^%s+", ""):gsub("%s+$", "")
|
||||
local content = fv(http, "content") or ""
|
||||
local local_mode = fv(http, "local_mode") or "text"
|
||||
local rules = read_rules_fields()
|
||||
-- 规则校验失败(如非法正则)必须回显,否则用户看到的是「规则没生效」
|
||||
local rules, rules_err = read_rules_fields()
|
||||
if not rules then return back_to_list(rules_err or "规则无效") end
|
||||
if id == "" then return back_to_list("缺少订阅 ID") end
|
||||
if name == "" then return back_to_list("名称不能为空") end
|
||||
if content == "" then return back_to_list("订阅内容不能为空") end
|
||||
@@ -370,7 +386,9 @@ function action_combo_save()
|
||||
-- §18:此前名称留空、或一个来源都没勾选时,这里整段跳过、直接跳回列表 ——
|
||||
-- 用户填了表单却什么都没发生,页面上也没有任何提示。
|
||||
if name == "" then return back_to_list("名称不能为空") end
|
||||
local rules = read_rules_fields()
|
||||
-- 规则校验失败(如非法正则)必须回显,否则用户看到的是「规则没生效」
|
||||
local rules, rules_err = read_rules_fields()
|
||||
if not rules then return back_to_list(rules_err or "规则无效") end
|
||||
local o = {
|
||||
rules_enable = rules.rules_enable, proto_filter = rules.proto_filter,
|
||||
keyword_include = rules.keyword_include, keyword_exclude = rules.keyword_exclude,
|
||||
|
||||
@@ -18,17 +18,18 @@
|
||||
local rm = (it and it.rename_map) or ""
|
||||
local local_mode = (it and it.local_mode) or "text"
|
||||
local content = (it and it.raw_content) or ""
|
||||
-- 注入 JS 的初始内容:JSON 字符串字面量,转义 "</" 防止提前闭合 script
|
||||
-- 注入 JS 的初始内容:JSON 字符串字面量,把全部 "<" 转成 <,
|
||||
-- 同时挡住 "</script" 提前闭合与 "<!--" 吞掉其后整页(详见 nodes.htm 同名注释)
|
||||
local util = require("substore.util")
|
||||
local js_content = (util.json_encode(content) or '""'):gsub("</", "<\\/")
|
||||
local js_content = (util.json_encode(content) or '""'):gsub("<", "\\u003c")
|
||||
-- local_mode 是用户可提交的字段,直接拼进 JS 字符串会被闭合引号注入;
|
||||
-- 归一为白名单取值后用 json_encode 输出,保证是合法的 JS 字符串字面量
|
||||
if local_mode ~= "form" then local_mode = "text" end
|
||||
local js_mode = util.json_encode(local_mode) or '"text"'
|
||||
-- 协议与字段清单:与 core.merge_form_node 共用 substore/node.lua 的同一份数据
|
||||
local node_mod = require("substore.node")
|
||||
local js_protos = (util.json_encode(node_mod.PROTOS) or "[]"):gsub("</", "<\\/")
|
||||
local js_proto_fields = (util.json_encode(node_mod.PROTO_FIELDS) or "{}"):gsub("</", "<\\/")
|
||||
local js_protos = (util.json_encode(node_mod.PROTOS) or "[]"):gsub("<", "\\u003c")
|
||||
local js_proto_fields = (util.json_encode(node_mod.PROTO_FIELDS) or "{}"):gsub("<", "\\u003c")
|
||||
%>
|
||||
<h2><%=is_edit and luci.i18n.translate("Edit local subscription") or luci.i18n.translate("Add local subscription")%></h2>
|
||||
|
||||
|
||||
@@ -8,12 +8,13 @@
|
||||
local meta = id ~= "" and core.get(id) or nil
|
||||
local nodes = id ~= "" and core.read_nodes(id) or {}
|
||||
local n = nodes[idx]
|
||||
-- 注入 JS 的节点对象:转义 "</" 防止提前闭合 script
|
||||
local js_node = n and ((util.json_encode(n) or "null"):gsub("</", "<\\/")) or "null"
|
||||
-- 注入 JS 的节点对象:把全部 "<" 转成 <,同时挡住 "</script" 提前闭合
|
||||
-- 与 "<!--" 让脚本块吞掉其后整页(详见 nodes.htm 同名注释)
|
||||
local js_node = n and ((util.json_encode(n) or "null"):gsub("<", "\\u003c")) or "null"
|
||||
-- 协议与字段清单:与 core.merge_form_node 共用 substore/node.lua 的同一份数据
|
||||
local node_mod = require("substore.node")
|
||||
local js_protos = (util.json_encode(node_mod.PROTOS) or "[]"):gsub("</", "<\\/")
|
||||
local js_proto_fields = (util.json_encode(node_mod.PROTO_FIELDS) or "{}"):gsub("</", "<\\/")
|
||||
local js_protos = (util.json_encode(node_mod.PROTOS) or "[]"):gsub("<", "\\u003c")
|
||||
local js_proto_fields = (util.json_encode(node_mod.PROTO_FIELDS) or "{}"):gsub("<", "\\u003c")
|
||||
local back_url = luci.dispatcher.build_url('admin/services/substore/nodes') .. "?id=" .. luci.util.urlencode(id)
|
||||
%>
|
||||
<h2><%:Edit node%> — <%=luci.util.pcdata(meta and meta.name or "")%></h2>
|
||||
|
||||
@@ -18,8 +18,11 @@
|
||||
-- 但只有 JSON 编码还不够:json_encode 不转义 "<",id 直接来自查询串,
|
||||
-- 形如 "</script><script>alert(1)</script>" 的值会在 HTML 词法阶段就闭合
|
||||
-- script 元素(与 JS 字符串上下文无关),剩下的部分被当作 HTML 执行。
|
||||
-- 转义 "</" 即可:在 JS 字符串里 "\/" 就是 "/",而 HTML 不再看到 "</script"。
|
||||
local js_id = (util.json_encode(id) or '""'):gsub("</", "<\\/")
|
||||
-- 把**全部** "<" 转成 <:在 JS/JSON 字符串里这就是 "<",而 HTML 词法
|
||||
-- 阶段再也看不到 "<",于是 "</script"(提前闭合)与 "<!--"(进入 script
|
||||
-- data escaped 状态,后面的 </script> 不再结束脚本块,整页被吞进脚本)两个
|
||||
-- 坑一并堵上。只转 "</" 挡不住 "<!--"。
|
||||
local js_id = (util.json_encode(id) or '""'):gsub("<", "\\u003c")
|
||||
|
||||
-- 标注原始数组下标(filter 保留表引用、sort 原地排序,下标可穿透筛选排序)
|
||||
for i, nn in ipairs(nodes) do nn.__idx = i end
|
||||
@@ -47,7 +50,7 @@
|
||||
if flash_err and flash_err ~= "" then
|
||||
%>
|
||||
<div class="cbi-section" style="border-left:4px solid #cc0000;padding:0.5em 0.75em;background:#fdf0f0;color:#cc0000">
|
||||
<strong><%:操作失败%>:</strong> <%=luci.util.pcdata(flash_err)%>
|
||||
<strong><%:Operation failed%>:</strong> <%=luci.util.pcdata(flash_err)%>
|
||||
</div>
|
||||
<% end %>
|
||||
<h2><%:Nodes%> — <%=luci.util.pcdata(meta and meta.name or "")%></h2>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
if flash_err and flash_err ~= "" then
|
||||
%>
|
||||
<div class="cbi-section" style="border-left:4px solid #cc0000;padding:0.5em 0.75em;background:#fdf0f0;color:#cc0000">
|
||||
<strong><%:操作失败%>:</strong> <%=luci.util.pcdata(flash_err)%>
|
||||
<strong><%:Operation failed%>:</strong> <%=luci.util.pcdata(flash_err)%>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
@@ -103,7 +103,7 @@
|
||||
onclick="var e=document.getElementById('link_<%=it.id%>');e.select();document.execCommand('copy');"><%:Copy%></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cbi-value-description"><%:选择格式后生成订阅链接,格式可随时切换%></div>
|
||||
<div class="cbi-value-description"><%:Pick a format to generate the subscription link; you can switch formats at any time%></div>
|
||||
</td>
|
||||
<td>
|
||||
<% if it["local"] then %>
|
||||
|
||||
Reference in new issue
Block a user