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 kSpMaxUrl = 2048;
|
||||
static constexpr size_t kSpMaxCode = 512 * 1024;
|
||||
static constexpr size_t kSpMaxData = 3500 * 1024;
|
||||
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;
|
||||
// 以 {"_sp" 开头但 JSON 非法视为格式错误(抛异常)。
|
||||
@@ -279,15 +278,7 @@ static void validate_sp(const json::object& sp) {
|
||||
if (sp.contains("persist") && !sp.at("persist").is_bool())
|
||||
throw DnfsExp("persist 必须是布尔值", 400);
|
||||
|
||||
if (t == "link") {
|
||||
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 (t == "js") {
|
||||
if (!sp.contains("code") || !sp.at("code").is_string())
|
||||
throw DnfsExp("前端代码不能为空", 400);
|
||||
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());
|
||||
if (t == "link") {
|
||||
if (sp.contains("url") && sp.at("url").is_string())
|
||||
parts.push_back(std::string(sp.at("url").as_string()));
|
||||
} else if (t == "js") {
|
||||
if (t == "js") {
|
||||
if (sp.contains("code") && sp.at("code").is_string()) {
|
||||
std::string code = std::string(sp.at("code").as_string());
|
||||
if (code.size() > kSpReviewCodePrefix) code = code.substr(0, kSpReviewCodePrefix);
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
<label>节点类型</label>
|
||||
<select id="sp-type">
|
||||
<option value="text">普通文本</option>
|
||||
<option value="link">链接(自动显示站点图标)</option>
|
||||
<option value="js">前端代码(可审计/可运行)</option>
|
||||
<option value="file">文件(可下载)</option>
|
||||
<option value="style">界面样式(CSS 注入,控制整个界面)</option>
|
||||
|
||||
+8
-40
@@ -430,7 +430,7 @@ function renderMarkdown(element, content) {
|
||||
}
|
||||
|
||||
/* ===== by qing — 特殊节点 =====
|
||||
* 信封:content 为 {"_sp":"link|js|file|style|theme", ...} JSON,普通内容走 Markdown。
|
||||
* 信封:content 为 {"_sp":"js|file|style|theme", ...} JSON,普通内容走 Markdown。
|
||||
* 发布权限由后端 dnfs 强制(节点拥有者或管理员),前端仅做展示与表单切换。 */
|
||||
|
||||
/* 与后端 base64 上限(3.5M 字符)对齐:原始文件约 2.5MB */
|
||||
@@ -445,7 +445,7 @@ function parseSp(content) {
|
||||
} catch (e) {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -464,34 +464,11 @@ function renderContent(container, item) {
|
||||
container.appendChild(note);
|
||||
}
|
||||
}
|
||||
if (sp._sp === 'link') renderSpLink(container, sp, appendNote);
|
||||
else if (sp._sp === 'js') renderSpJs(container, sp, appendNote);
|
||||
if (sp._sp === 'js') renderSpJs(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);
|
||||
}
|
||||
|
||||
/* 链接节点:服务端代理拉取的 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 内,无法访问父页面 DOM/JS,任何报错、死循环、变量覆盖
|
||||
@@ -806,8 +783,7 @@ sp_type.addEventListener('change', function() {
|
||||
sp_file.value = '';
|
||||
sp_edit_original = null;
|
||||
}
|
||||
if (t === 'link') input_note_content.placeholder = '请输入链接 URL(https://…)';
|
||||
else if (t === 'js') input_note_content.placeholder = '请输入完整前端代码(可含 HTML/CSS/JS)';
|
||||
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 === 'theme') input_note_content.placeholder = '每行一个变量,如 --accent: #e8853a';
|
||||
else input_note_content.placeholder = '';
|
||||
@@ -848,11 +824,6 @@ function parseThemeVars(text) {
|
||||
async function buildSubmitContent() {
|
||||
var t = (current_user && current_user.login) ? sp_type.value : 'text';
|
||||
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') {
|
||||
var code = input_note_content.value;
|
||||
if (!code.trim()) { alert('请输入前端代码'); return null; }
|
||||
@@ -936,16 +907,16 @@ delete_note.addEventListener('click', (event) => {
|
||||
})
|
||||
|
||||
/* by qing — 点击卡片"编辑":把节点标题/内容载入下方表单,进入编辑模式。
|
||||
* 特殊节点:link/js/style/theme 回填对应文本到文本域;file 记住原信封(未选新文件则原样提交) */
|
||||
* 特殊节点:js/style/theme 回填对应文本到文本域;file 记住原信封(未选新文件则原样提交) */
|
||||
function startEdit(item) {
|
||||
editing_id = item.id;
|
||||
input_note_title.value = item.title;
|
||||
var sp = parseSp(item.content);
|
||||
sp_edit_original = null;
|
||||
if (sp && (sp._sp === 'link' || sp._sp === 'js')) {
|
||||
sp_type.value = sp._sp;
|
||||
if (sp && sp._sp === 'js') {
|
||||
sp_type.value = 'js';
|
||||
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') {
|
||||
sp_type.value = 'style';
|
||||
sp_type.dispatchEvent(new Event('change'));
|
||||
@@ -1123,9 +1094,6 @@ function itemSearchText(item) {
|
||||
var sp = parseSp(c);
|
||||
if (!sp) {
|
||||
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') {
|
||||
if (sp.code) parts.push(sp.code);
|
||||
if (sp.note) parts.push(sp.note);
|
||||
|
||||
Reference in new issue
Block a user