//! MNOTE 文件夹授权管理组件 use crate::routes::control_plane_status_display; use leptos::prelude::*; #[component] pub fn AdminAccessPolicyPanel( #[prop(optional)] workspace_name: Option, #[prop(optional)] share_grants_path: Option, #[prop(optional, default = true)] is_admin: bool, #[prop(optional, default = true)] boot_script: bool, ) -> impl IntoView { let workspace_name = workspace_name .unwrap_or_else(|| "开发用户 的空间".to_string()) .trim() .to_string(); let share_grants_path = share_grants_path.unwrap_or_else(control_plane_status_display); let scope_text = if is_admin { "管理员可以授权任意本地文件夹。" } else { "普通用户只能授权自己空间下的文件夹。" }; view! {
"授权管理"

"文件夹授权"

{scope_text}

"工作区" {workspace_name.clone()}
"控制面" {share_grants_path.clone()}

"新增文件夹授权"


                

"授权列表"

"查看当前可管理的文件夹授权,并撤销不再需要的授权。"

"正在读取授权..."
"查看授权 JSON"
{""}
{if boot_script { view! { }.into_any() } else { view! {}.into_any() }}
} } pub(crate) const ADMIN_POLICY_SCRIPT: &str = r#" (function () { function initAccessPolicyPanel(scope) { var root = (scope || document).querySelector('[data-testid="mnote-admin-access-policy-page"]'); if (!root) return; if (root.getAttribute('data-access-policy-ready') === 'true') return; root.setAttribute('data-access-policy-ready', 'true'); var shareGrantsJson = root.querySelector('[data-testid="mnote-admin-share-grants-json"]'); var shareGrantsMessage = root.querySelector('[data-testid="mnote-admin-share-grants-message"]'); var createShareGrantResult = root.querySelector('[data-testid="mnote-admin-create-share-grant-result"]'); var deleteShareGrantResult = root.querySelector('[data-testid="mnote-admin-delete-share-grant-result"]'); var refreshShareGrantsButton = root.querySelector('[data-admin-action="refresh-share-grants"]'); var shareGrantsList = root.querySelector('[data-testid="mnote-admin-share-grants-list"]'); var currentActorId = document.body && document.body.getAttribute ? String(document.body.getAttribute('data-mnote-actor-id') || '').trim() : ''; var pageConfig = (function () { var node = root.querySelector('#__MNOTE_ACCESS_POLICY_PAGE__'); try { return JSON.parse(node ? node.textContent || '{}' : '{}'); } catch (_) { return {}; } })(); var isAdmin = pageConfig.isAdmin === true; function setText(node, value) { if (!node) return; node.textContent = typeof value === 'string' ? value : JSON.stringify(value, null, 2); } function escapeHtml(value) { return String(value == null ? '' : value) .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } function renderBadge(value) { var text = String(value || '').trim() || 'read'; return '' + escapeHtml(text) + ''; } function readDirectoryGrants(payload) { if (payload && Array.isArray(payload.grants)) return payload.grants; if (payload && payload.policy && Array.isArray(payload.policy.grants)) return payload.policy.grants; return []; } function accessPolicyUrl(suffix) { var base = isAdmin ? '/api/admin/access-policy' : '/api/user/access-policy'; return suffix ? base + suffix : base; } function pathToFileRootUri(value) { var trimmed = String(value || '').trim(); if (!trimmed) return ''; if (/^file:\/\//i.test(trimmed)) return trimmed; if (trimmed.charAt(0) !== '/') return ''; return 'file://' + trimmed.split('/').map(function(part, index) { return index === 0 ? '' : encodeURIComponent(part); }).join('/'); } function localFolderOpenHref(grant) { var rootUri = String(grant.rootUri || '').trim(); if (rootUri && !/^file:\/\//i.test(rootUri)) rootUri = ''; if (!rootUri) { var rootPath = String(grant.rootPath || '').trim(); if (rootPath) rootUri = pathToFileRootUri(rootPath); } if (!rootUri) { rootUri = pathToFileRootUri(String(grant.rootUri || '').trim()); } if (!rootUri) return ''; var url = new URL('/', window.location.origin); url.searchParams.set('treeView', 'filetree'); url.searchParams.set('sourceKind', 'local_folder'); url.searchParams.set('rootUri', rootUri); return url.pathname + url.search; } function isDefaultWorkspaceAutoGrant(grant) { var source = String(grant && grant.source || '').trim(); var workspaceId = String(grant && grant.workspaceId || '').trim(); var rootUri = String(grant && grant.rootUri || '').trim(); var permission = String(grant && grant.permission || '').trim(); var createdBy = String(grant && (grant.createdBy || grant.ownerUserId) || '').trim(); var targetUser = String(grant && (grant.userId || grant.targetUserId) || '').trim(); return source === 'auto' && workspaceId && permission === 'write' && createdBy === targetUser && /^local:\/\/users\/.+\/workspaces\/my-space$/.test(rootUri); } function renderShareGrants(payload) { if (!shareGrantsList) return; var grants = readDirectoryGrants(payload); if (!grants.length) { shareGrantsList.innerHTML = '
暂无文件夹授权
'; return; } shareGrantsList.innerHTML = grants.map(function(grant) { var active = grant.active === false || grant.status === 'revoked' ? '已撤销' : '有效'; var openHref = localFolderOpenHref(grant); var rootLabel = escapeHtml(grant.rootPath || grant.rootUri || ''); var createdBy = String(grant.createdBy || grant.ownerUserId || '').trim(); var systemOwned = isDefaultWorkspaceAutoGrant(grant); var canRevoke = !systemOwned && (isAdmin || (createdBy && currentActorId && createdBy === currentActorId)); var rootNode = openHref ? '' + rootLabel + '' : '' + rootLabel + ''; var revokeButton = grant.active === false || !canRevoke ? '' : ''; return '
' + '
' + rootNode + '授权用户 ' + escapeHtml(grant.targetUserId || grant.userId || '') + '
' + '
' + renderBadge(grant.permission) + renderBadge(active) + '
' + '
创建者 ' + escapeHtml(createdBy) + '授权 ID ' + escapeHtml(grant.id || '') + '' + revokeButton + '
' + '
'; }).join(''); } function shareGrantFormValues(form) { var data = new FormData(form); return { userId: String(data.get('targetUserId') || '').trim(), targetUserId: String(data.get('targetUserId') || '').trim(), rootPath: String(data.get('rootPath') || '').trim(), permission: String(data.get('sharePermission') || 'read').trim(), }; } async function requestJson(url, options) { var response = await fetch(url, { credentials: 'include', headers: { 'content-type': 'application/json' }, ...options, }); var payload = await response.json().catch(function () { return {}; }); if (!response.ok || payload.ok === false) { throw new Error((payload && payload.message) || ('请求失败: ' + response.status)); } return payload; } async function refreshShareGrants() { var payload = await requestJson(accessPolicyUrl(''), { method: 'GET', headers: {} }); setText(shareGrantsJson, payload); renderShareGrants(payload); setText(shareGrantsMessage, '已刷新授权列表'); } refreshShareGrantsButton && refreshShareGrantsButton.addEventListener('click', function () { setText(shareGrantsMessage, '正在刷新授权列表...'); refreshShareGrants().catch(function (error) { setText(shareGrantsMessage, error.message || '刷新失败'); }); }); root.querySelector('[data-admin-form="create-share-grant"]').addEventListener('submit', function (event) { event.preventDefault(); var form = event.currentTarget; var values = shareGrantFormValues(form); setText(createShareGrantResult, '正在创建...'); requestJson(accessPolicyUrl('/grants'), { method: 'POST', body: JSON.stringify(values), }).then(function (payload) { setText(createShareGrantResult, payload); setText(shareGrantsMessage, '文件夹授权已创建'); form.reset(); return refreshShareGrants(); }).catch(function (error) { setText(createShareGrantResult, { ok: false, error: error.message || '创建失败' }); setText(shareGrantsMessage, error.message || '创建失败'); }); }); shareGrantsList && shareGrantsList.addEventListener('click', function(event) { var button = event.target && event.target.closest ? event.target.closest('[data-admin-action="revoke-access-grant"]') : null; if (!button) return; event.preventDefault(); var grantId = String(button.getAttribute('data-grant-id') || '').trim(); if (!grantId) return; setText(deleteShareGrantResult, '正在撤销...'); requestJson(accessPolicyUrl('/grants/') + encodeURIComponent(grantId), { method: 'DELETE', headers: {}, }).then(function (payload) { setText(deleteShareGrantResult, payload); setText(shareGrantsMessage, '文件夹授权已撤销'); return refreshShareGrants(); }).catch(function (error) { setText(deleteShareGrantResult, { ok: false, error: error.message || '撤销失败' }); setText(shareGrantsMessage, error.message || '撤销失败'); }); }); refreshShareGrants().catch(function (error) { setText(shareGrantsMessage, error.message || '加载授权失败'); renderShareGrants(null); }); } window.MNOTEInitAccessPolicyPanel = initAccessPolicyPanel; var shell = document.body && document.body.getAttribute('data-mnote-shell'); if (shell === 'admin' || shell === 'user-access-policy') { initAccessPolicyPanel(document); } })(); "#; #[cfg(test)] mod tests { use super::*; use crate::ssr::render_view; #[test] fn admin_access_policy_panel_renders_minimal_folder_grant_controls() { let html = render_view(view! { }); assert!(html.contains("mnote-admin-access-policy-page")); assert!(html.contains("文件夹授权")); assert!(html.contains("文件夹地址")); assert!(html.contains("授权用户 ID")); assert!(html.contains("read")); assert!(html.contains("write")); assert!(html.contains("mnote-admin-share-grants-panel")); assert!(html.contains("mnote-admin-share-grants-json")); assert!(html.contains("mnote-admin-create-share-grant-submit")); assert!(html.contains("/api/admin/access-policy")); assert!(html.contains("/api/user/access-policy")); assert!(html.contains("data-mnote-shell")); assert!(!html.contains("/api/admin/share-grants")); assert!(!html.contains("/api/user/share-grants")); assert!( html.contains("data-admin-action="revoke-access-grant"") || html.contains("revoke-access-grant") ); assert!(!html.contains("name=\"grantId\"")); assert!(!html.contains("name=\"shareId\"")); assert!(!html.contains("share_xxx")); assert!(!html.contains("name=\"ownerUserId\"")); assert!(!html.contains("documentId")); assert!(!html.contains("allowedResourceIds")); assert!(!html.contains("capabilities")); assert!(!html.contains("mnote-admin-validate-root-submit")); } #[test] fn admin_policy_script_keeps_form_reference_for_async_reset() { assert!(ADMIN_POLICY_SCRIPT.contains("var form = event.currentTarget;")); assert!(ADMIN_POLICY_SCRIPT.contains("form.reset();")); assert!(!ADMIN_POLICY_SCRIPT.contains("event.currentTarget.reset();")); } #[test] fn admin_policy_script_links_grants_to_local_folder_entry() { assert!(ADMIN_POLICY_SCRIPT.contains("function localFolderOpenHref(grant)")); assert!(ADMIN_POLICY_SCRIPT.contains("sourceKind', 'local_folder'")); assert!(ADMIN_POLICY_SCRIPT.contains("data-testid=\"mnote-admin-open-granted-root\"")); assert!(ADMIN_POLICY_SCRIPT.contains("function isDefaultWorkspaceAutoGrant(grant)")); assert!(ADMIN_POLICY_SCRIPT.contains("var canRevoke = !systemOwned &&")); } }