dnfs2: 特殊节点移除 link 类型,精简为 js/file/style/theme 四类
This commit is contained in:
1 parent
dfa8d0406a
commit
75a45313c4
3 files changed
+11
-56
No files matched your search
+3
-15
@@ -56,7 +56,6 @@ using namespace pmc::net;
|
|||||||
static constexpr size_t kMaxBodySize = 10 * 1024 * 1024;
|
static constexpr size_t kMaxBodySize = 10 * 1024 * 1024;
|
||||||
|
|
||||||
// 特殊节点
|
// 特殊节点
|
||||||
static constexpr size_t kSpMaxUrl = 2048;
|
|
||||||
static constexpr size_t kSpMaxCode = 512 * 1024;
|
static constexpr size_t kSpMaxCode = 512 * 1024;
|
||||||
static constexpr size_t kSpMaxData = 3500 * 1024;
|
static constexpr size_t kSpMaxData = 3500 * 1024;
|
||||||
static constexpr size_t kSpReviewCodePrefix = 4000;
|
static constexpr size_t kSpReviewCodePrefix = 4000;
|
||||||
@@ -243,7 +242,7 @@ private:
|
|||||||
// 特殊节点校验
|
// 特殊节点校验
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
static const std::vector<std::string> kSpTypes = {"link", "js", "file", "style", "theme"};
|
static const std::vector<std::string> kSpTypes = {"js", "file", "style", "theme"};
|
||||||
|
|
||||||
// 识别特殊节点信封:合法信封返回 object;普通内容返回 nullptr;
|
// 识别特殊节点信封:合法信封返回 object;普通内容返回 nullptr;
|
||||||
// 以 {"_sp" 开头但 JSON 非法视为格式错误(抛异常)。
|
// 以 {"_sp" 开头但 JSON 非法视为格式错误(抛异常)。
|
||||||
@@ -279,15 +278,7 @@ static void validate_sp(const json::object& sp) {
|
|||||||
if (sp.contains("persist") && !sp.at("persist").is_bool())
|
if (sp.contains("persist") && !sp.at("persist").is_bool())
|
||||||
throw DnfsExp("persist 必须是布尔值", 400);
|
throw DnfsExp("persist 必须是布尔值", 400);
|
||||||
|
|
||||||
if (t == "link") {
|
if (t == "js") {
|
||||||
if (!sp.contains("url") || !sp.at("url").is_string())
|
|
||||||
throw DnfsExp("链接节点缺少 url", 400);
|
|
||||||
std::string url = std::string(sp.at("url").as_string());
|
|
||||||
if (url.rfind("http://", 0) != 0 && url.rfind("https://", 0) != 0)
|
|
||||||
throw DnfsExp("链接节点必须是 http/https URL", 400);
|
|
||||||
if (url.size() > kSpMaxUrl)
|
|
||||||
throw DnfsExp("链接过长", 400);
|
|
||||||
} else if (t == "js") {
|
|
||||||
if (!sp.contains("code") || !sp.at("code").is_string())
|
if (!sp.contains("code") || !sp.at("code").is_string())
|
||||||
throw DnfsExp("前端代码不能为空", 400);
|
throw DnfsExp("前端代码不能为空", 400);
|
||||||
std::string code = std::string(sp.at("code").as_string());
|
std::string code = std::string(sp.at("code").as_string());
|
||||||
@@ -364,10 +355,7 @@ static std::string sp_review_text(const std::string& title, const json::object&
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string t = std::string(sp.at("_sp").as_string());
|
std::string t = std::string(sp.at("_sp").as_string());
|
||||||
if (t == "link") {
|
if (t == "js") {
|
||||||
if (sp.contains("url") && sp.at("url").is_string())
|
|
||||||
parts.push_back(std::string(sp.at("url").as_string()));
|
|
||||||
} else if (t == "js") {
|
|
||||||
if (sp.contains("code") && sp.at("code").is_string()) {
|
if (sp.contains("code") && sp.at("code").is_string()) {
|
||||||
std::string code = std::string(sp.at("code").as_string());
|
std::string code = std::string(sp.at("code").as_string());
|
||||||
if (code.size() > kSpReviewCodePrefix) code = code.substr(0, kSpReviewCodePrefix);
|
if (code.size() > kSpReviewCodePrefix) code = code.substr(0, kSpReviewCodePrefix);
|
||||||
|
|||||||
@@ -31,7 +31,6 @@
|
|||||||
<label>节点类型</label>
|
<label>节点类型</label>
|
||||||
<select id="sp-type">
|
<select id="sp-type">
|
||||||
<option value="text">普通文本</option>
|
<option value="text">普通文本</option>
|
||||||
<option value="link">链接(自动显示站点图标)</option>
|
|
||||||
<option value="js">前端代码(可审计/可运行)</option>
|
<option value="js">前端代码(可审计/可运行)</option>
|
||||||
<option value="file">文件(可下载)</option>
|
<option value="file">文件(可下载)</option>
|
||||||
<option value="style">界面样式(CSS 注入,控制整个界面)</option>
|
<option value="style">界面样式(CSS 注入,控制整个界面)</option>
|
||||||
|
|||||||
+8
-40
@@ -430,7 +430,7 @@ function renderMarkdown(element, content) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ===== by qing — 特殊节点 =====
|
/* ===== by qing — 特殊节点 =====
|
||||||
* 信封:content 为 {"_sp":"link|js|file|style|theme", ...} JSON,普通内容走 Markdown。
|
* 信封:content 为 {"_sp":"js|file|style|theme", ...} JSON,普通内容走 Markdown。
|
||||||
* 发布权限由后端 dnfs 强制(节点拥有者或管理员),前端仅做展示与表单切换。 */
|
* 发布权限由后端 dnfs 强制(节点拥有者或管理员),前端仅做展示与表单切换。 */
|
||||||
|
|
||||||
/* 与后端 base64 上限(3.5M 字符)对齐:原始文件约 2.5MB */
|
/* 与后端 base64 上限(3.5M 字符)对齐:原始文件约 2.5MB */
|
||||||
@@ -445,7 +445,7 @@ function parseSp(content) {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
return null; // 非法信封按普通文本展示(后端发布时已拦截,这里是兜底)
|
return null; // 非法信封按普通文本展示(后端发布时已拦截,这里是兜底)
|
||||||
}
|
}
|
||||||
if (obj && typeof obj === 'object' && ['link', 'js', 'file', 'style', 'theme'].indexOf(obj._sp) !== -1) return obj;
|
if (obj && typeof obj === 'object' && ['js', 'file', 'style', 'theme'].indexOf(obj._sp) !== -1) return obj;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -464,34 +464,11 @@ function renderContent(container, item) {
|
|||||||
container.appendChild(note);
|
container.appendChild(note);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sp._sp === 'link') renderSpLink(container, sp, appendNote);
|
if (sp._sp === 'js') renderSpJs(container, sp, appendNote);
|
||||||
else if (sp._sp === 'js') renderSpJs(container, sp, appendNote);
|
|
||||||
else if (sp._sp === 'file') renderSpFile(container, sp, appendNote);
|
else if (sp._sp === 'file') renderSpFile(container, sp, appendNote);
|
||||||
else if (sp._sp === 'style' || sp._sp === 'theme') renderSpCss(container, sp, appendNote, item);
|
else if (sp._sp === 'style' || sp._sp === 'theme') renderSpCss(container, sp, appendNote, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 链接节点:服务端代理拉取的 favicon + 可点击链接(卡片内交互需 stopPropagation) */
|
|
||||||
function renderSpLink(container, sp, appendNote) {
|
|
||||||
var box = document.createElement('div');
|
|
||||||
box.className = 'sp-link';
|
|
||||||
var icon = document.createElement('img');
|
|
||||||
icon.className = 'sp-favicon';
|
|
||||||
icon.alt = '';
|
|
||||||
icon.src = '/api/favicon?url=' + encodeURIComponent(sp.url);
|
|
||||||
icon.addEventListener('error', function() { icon.style.display = 'none'; });
|
|
||||||
var anchor = document.createElement('a');
|
|
||||||
anchor.className = 'sp-anchor';
|
|
||||||
anchor.href = sp.url;
|
|
||||||
anchor.target = '_blank';
|
|
||||||
anchor.rel = 'noopener noreferrer';
|
|
||||||
anchor.textContent = sp.url;
|
|
||||||
anchor.addEventListener('click', function(e) { e.stopPropagation(); });
|
|
||||||
box.appendChild(icon);
|
|
||||||
box.appendChild(anchor);
|
|
||||||
container.appendChild(box);
|
|
||||||
appendNote();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 前端代码节点:含前端内容的节点在渲染到当前页面时自动执行(隔离 iframe 沙箱)。
|
/* 前端代码节点:含前端内容的节点在渲染到当前页面时自动执行(隔离 iframe 沙箱)。
|
||||||
* 采用 iframe 沙箱而非注入父页面全局作用域,原因:
|
* 采用 iframe 沙箱而非注入父页面全局作用域,原因:
|
||||||
* - 特效脚本运行在独立 iframe 内,无法访问父页面 DOM/JS,任何报错、死循环、变量覆盖
|
* - 特效脚本运行在独立 iframe 内,无法访问父页面 DOM/JS,任何报错、死循环、变量覆盖
|
||||||
@@ -806,8 +783,7 @@ sp_type.addEventListener('change', function() {
|
|||||||
sp_file.value = '';
|
sp_file.value = '';
|
||||||
sp_edit_original = null;
|
sp_edit_original = null;
|
||||||
}
|
}
|
||||||
if (t === 'link') input_note_content.placeholder = '请输入链接 URL(https://…)';
|
if (t === 'js') input_note_content.placeholder = '请输入完整前端代码(可含 HTML/CSS/JS)';
|
||||||
else if (t === 'js') input_note_content.placeholder = '请输入完整前端代码(可含 HTML/CSS/JS)';
|
|
||||||
else if (t === 'style') input_note_content.placeholder = '请输入 CSS(如 .card { border-radius: 16px }),发布后全界面生效';
|
else if (t === 'style') input_note_content.placeholder = '请输入 CSS(如 .card { border-radius: 16px }),发布后全界面生效';
|
||||||
else if (t === 'theme') input_note_content.placeholder = '每行一个变量,如 --accent: #e8853a';
|
else if (t === 'theme') input_note_content.placeholder = '每行一个变量,如 --accent: #e8853a';
|
||||||
else input_note_content.placeholder = '';
|
else input_note_content.placeholder = '';
|
||||||
@@ -848,11 +824,6 @@ function parseThemeVars(text) {
|
|||||||
async function buildSubmitContent() {
|
async function buildSubmitContent() {
|
||||||
var t = (current_user && current_user.login) ? sp_type.value : 'text';
|
var t = (current_user && current_user.login) ? sp_type.value : 'text';
|
||||||
if (t === 'text') return input_note_content.value;
|
if (t === 'text') return input_note_content.value;
|
||||||
if (t === 'link') {
|
|
||||||
var url = input_note_content.value.trim();
|
|
||||||
if (!/^https?:\/\//i.test(url)) { alert('链接必须以 http:// 或 https:// 开头'); return null; }
|
|
||||||
return JSON.stringify({ _sp: 'link', url: url });
|
|
||||||
}
|
|
||||||
if (t === 'js') {
|
if (t === 'js') {
|
||||||
var code = input_note_content.value;
|
var code = input_note_content.value;
|
||||||
if (!code.trim()) { alert('请输入前端代码'); return null; }
|
if (!code.trim()) { alert('请输入前端代码'); return null; }
|
||||||
@@ -936,16 +907,16 @@ delete_note.addEventListener('click', (event) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
/* by qing — 点击卡片"编辑":把节点标题/内容载入下方表单,进入编辑模式。
|
/* by qing — 点击卡片"编辑":把节点标题/内容载入下方表单,进入编辑模式。
|
||||||
* 特殊节点:link/js/style/theme 回填对应文本到文本域;file 记住原信封(未选新文件则原样提交) */
|
* 特殊节点:js/style/theme 回填对应文本到文本域;file 记住原信封(未选新文件则原样提交) */
|
||||||
function startEdit(item) {
|
function startEdit(item) {
|
||||||
editing_id = item.id;
|
editing_id = item.id;
|
||||||
input_note_title.value = item.title;
|
input_note_title.value = item.title;
|
||||||
var sp = parseSp(item.content);
|
var sp = parseSp(item.content);
|
||||||
sp_edit_original = null;
|
sp_edit_original = null;
|
||||||
if (sp && (sp._sp === 'link' || sp._sp === 'js')) {
|
if (sp && sp._sp === 'js') {
|
||||||
sp_type.value = sp._sp;
|
sp_type.value = 'js';
|
||||||
sp_type.dispatchEvent(new Event('change'));
|
sp_type.dispatchEvent(new Event('change'));
|
||||||
input_note_content.value = (sp._sp === 'link') ? (sp.url || '') : (sp.code || '');
|
input_note_content.value = sp.code || '';
|
||||||
} else if (sp && sp._sp === 'style') {
|
} else if (sp && sp._sp === 'style') {
|
||||||
sp_type.value = 'style';
|
sp_type.value = 'style';
|
||||||
sp_type.dispatchEvent(new Event('change'));
|
sp_type.dispatchEvent(new Event('change'));
|
||||||
@@ -1123,9 +1094,6 @@ function itemSearchText(item) {
|
|||||||
var sp = parseSp(c);
|
var sp = parseSp(c);
|
||||||
if (!sp) {
|
if (!sp) {
|
||||||
parts.push(c);
|
parts.push(c);
|
||||||
} else if (sp._sp === 'link') {
|
|
||||||
if (sp.url) parts.push(sp.url);
|
|
||||||
if (sp.note) parts.push(sp.note);
|
|
||||||
} else if (sp._sp === 'js') {
|
} else if (sp._sp === 'js') {
|
||||||
if (sp.code) parts.push(sp.code);
|
if (sp.code) parts.push(sp.code);
|
||||||
if (sp.note) parts.push(sp.note);
|
if (sp.note) parts.push(sp.note);
|
||||||
|
|||||||
Reference in new issue
Block a user