feat(dnfs): get_notes 按当前用户注入 can_operate,前端按权显隐编辑/删除按钮

dnfs get_notes 可选解析 Authorization(畸形 Token try 兜底),为每个节点
附带 can_operate 标记(is_admin 或 owner,口径与 delete/update 一致);
dnfs.js 卡片编辑/删除按钮仅 can_operate === true 时渲染,未登录或无权隐藏。
This commit is contained in:
qing committed 2026-07-30 09:17:28 +08:00
1 parent 46c3a197be
commit b4e1cf462a
2 files changed
+40 -17

No files matched your search

+17
View File
@@ -252,6 +252,23 @@ def get_notes():
result = notes['data']
# 解析当前用户(可选):为每个节点附带操作权标记 can_operate,
# 前端据此显隐编辑/删除按钮。口径与 delete_note / update_note 一致:
# 管理员(is_admin)或节点 owner 有权。
# get_notes 保持公开:未携带 Token 或 Token 解析失败时,所有节点一律无权。
info = None
auth: str = request.headers.get('Authorization', '')
if auth.startswith('Bearer '):
try:
info = decode_token(auth[7:])
except Exception:
info = None
for note in result:
note['can_operate'] = bool(
info and (info.get('is_admin') or note.get('owner') == info.get('user'))
)
logs.info('dnfs', f'get_notes 查询 pid={pid} 返回{len(result)}条', extra={'ip': request.remote_addr})
return jsonify(result), 200
+23 -17
View File
@@ -560,16 +560,21 @@ function load_notes(main_node) {
sts_div.appendChild(makeStatItem(ICON_EYE, item.visit_count));
sts_div.appendChild(makeStatItem(ICON_HEART, item.like_count));
sts_div.appendChild(makeStatItem(ICON_SHARE, item.share_count));
// 卡片操作按钮:编辑 / 删除(stopPropagation 防止触发卡片自身的点入跳转)
// 卡片操作按钮:编辑 / 移动 / 删除(stopPropagation 防止触发卡片自身的点入跳转)
// 编辑与删除按 can_operate 显隐:操作权由后端 get_notes 按 owner / is_admin 判定,
// 与 delete_note / update_note 鉴权口径一致;未登录或无权时隐藏
let act_div = document.createElement('div');
act_div.className = "card-actions";
let edit_btn = document.createElement('button');
edit_btn.className = "act-btn act-edit";
edit_btn.innerHTML = ICON_EDIT + "编辑";
edit_btn.addEventListener('click', function(event) {
event.stopPropagation();
startEdit(item);
});
if (item.can_operate === true) {
let edit_btn = document.createElement('button');
edit_btn.className = "act-btn act-edit";
edit_btn.innerHTML = ICON_EDIT + "编辑";
edit_btn.addEventListener('click', function(event) {
event.stopPropagation();
startEdit(item);
});
act_div.appendChild(edit_btn);
}
let move_btn = document.createElement('button');
move_btn.className = "act-btn act-move";
move_btn.innerHTML = ICON_MOVE + "移动";
@@ -577,16 +582,17 @@ function load_notes(main_node) {
event.stopPropagation();
startMove(item);
});
let del_btn = document.createElement('button');
del_btn.className = "act-btn act-del";
del_btn.innerHTML = ICON_TRASH + "删除";
del_btn.addEventListener('click', function(event) {
event.stopPropagation();
deleteChild(item.id);
});
act_div.appendChild(edit_btn);
act_div.appendChild(move_btn);
act_div.appendChild(del_btn);
if (item.can_operate === true) {
let del_btn = document.createElement('button');
del_btn.className = "act-btn act-del";
del_btn.innerHTML = ICON_TRASH + "删除";
del_btn.addEventListener('click', function(event) {
event.stopPropagation();
deleteChild(item.id);
});
act_div.appendChild(del_btn);
}
ttl.appendChild(wrt_div);
ttl.appendChild(sts_div);
let ctt = document.createElement('p');