2026-05-19 08:07:17 +08:00
|
|
|
//! MNOTE 管理员目录授权页面组件
|
|
|
|
|
|
|
|
|
|
use crate::ssr::pages::layout::PageLayout;
|
|
|
|
|
use leptos::prelude::*;
|
|
|
|
|
|
|
|
|
|
#[component]
|
|
|
|
|
pub fn AdminAccessPolicyPage(
|
|
|
|
|
#[prop(optional)] workspace_name: Option<String>,
|
|
|
|
|
#[prop(optional)] policy_path: Option<String>,
|
2026-05-19 11:11:08 +08:00
|
|
|
#[prop(optional)] share_grants_path: Option<String>,
|
2026-05-19 08:07:17 +08:00
|
|
|
) -> impl IntoView {
|
|
|
|
|
let workspace_name = workspace_name
|
|
|
|
|
.unwrap_or_else(|| "开发用户 的空间".to_string())
|
|
|
|
|
.trim()
|
|
|
|
|
.to_string();
|
|
|
|
|
let policy_path = policy_path
|
|
|
|
|
.unwrap_or_else(|| "/mnt/Data1T/Mnote_data/control-plane/access-policy.json".to_string());
|
2026-05-19 11:11:08 +08:00
|
|
|
let share_grants_path = share_grants_path
|
|
|
|
|
.unwrap_or_else(|| "/mnt/Data1T/Mnote_data/control-plane/share-grants.json".to_string());
|
2026-05-19 08:07:17 +08:00
|
|
|
view! {
|
|
|
|
|
<PageLayout current_nav="admin" workspace_name={workspace_name.clone()} topbar_title={"目录授权".to_string()} show_admin_access_policy=true>
|
|
|
|
|
<main class="mnote-admin-policy-page" data-testid="mnote-admin-access-policy-page">
|
|
|
|
|
<header class="mnote-admin-policy-header">
|
|
|
|
|
<h1>"目录授权"</h1>
|
|
|
|
|
<p>"管理员可以查看、验证和管理本地目录授权,普通用户不会看到入口。"</p>
|
|
|
|
|
</header>
|
|
|
|
|
|
|
|
|
|
<section class="mnote-admin-policy-summary">
|
|
|
|
|
<div class="mnote-admin-policy-summary-item">
|
|
|
|
|
<span class="mnote-admin-policy-summary-label">"工作区"</span>
|
|
|
|
|
<strong data-testid="mnote-admin-workspace-name">{workspace_name.clone()}</strong>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="mnote-admin-policy-summary-item">
|
|
|
|
|
<span class="mnote-admin-policy-summary-label">"策略文件"</span>
|
|
|
|
|
<code data-testid="mnote-admin-policy-path">{policy_path.clone()}</code>
|
|
|
|
|
</div>
|
2026-05-19 11:11:08 +08:00
|
|
|
<div class="mnote-admin-policy-summary-item">
|
|
|
|
|
<span class="mnote-admin-policy-summary-label">"分享授权文件"</span>
|
|
|
|
|
<code data-testid="mnote-admin-share-grants-path">{share_grants_path.clone()}</code>
|
|
|
|
|
</div>
|
2026-05-19 08:07:17 +08:00
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
<section class="mnote-admin-policy-panel">
|
|
|
|
|
<header class="mnote-admin-policy-panel-header">
|
|
|
|
|
<h2>"当前策略"</h2>
|
|
|
|
|
<button type="button" data-testid="mnote-admin-policy-refresh" data-admin-action="refresh-policy">"刷新"</button>
|
|
|
|
|
</header>
|
|
|
|
|
<pre class="mnote-admin-policy-json" data-testid="mnote-admin-policy-json">{""}</pre>
|
|
|
|
|
<div class="mnote-admin-policy-note" data-testid="mnote-admin-policy-message"></div>
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
<section class="mnote-admin-policy-grid">
|
|
|
|
|
<form class="mnote-admin-policy-form" data-admin-form="validate-root">
|
|
|
|
|
<header><h2>"验证目录"</h2></header>
|
|
|
|
|
<label>
|
|
|
|
|
<span>"rootUri"</span>
|
|
|
|
|
<input data-testid="mnote-admin-root-uri" name="rootUri" type="text" placeholder="file:///mnt/Data1T/Mnote_data/users/..." />
|
|
|
|
|
</label>
|
|
|
|
|
<label>
|
|
|
|
|
<span>"rootPath"</span>
|
|
|
|
|
<input data-testid="mnote-admin-root-path" name="rootPath" type="text" placeholder="/mnt/Data1T/Mnote_data/..." />
|
|
|
|
|
</label>
|
|
|
|
|
<button type="submit" data-testid="mnote-admin-validate-root-submit">"验证"</button>
|
|
|
|
|
<pre class="mnote-admin-policy-json" data-testid="mnote-admin-validate-result"></pre>
|
|
|
|
|
</form>
|
|
|
|
|
|
|
|
|
|
<form class="mnote-admin-policy-form" data-admin-form="create-grant">
|
|
|
|
|
<header><h2>"新增授权"</h2></header>
|
|
|
|
|
<label>
|
|
|
|
|
<span>"grantId"</span>
|
|
|
|
|
<input data-testid="mnote-admin-grant-id" name="grantId" type="text" placeholder="可留空自动生成" />
|
|
|
|
|
</label>
|
|
|
|
|
<label>
|
|
|
|
|
<span>"userId"</span>
|
|
|
|
|
<input data-testid="mnote-admin-grant-user-id" name="userId" type="text" placeholder="user_123" required />
|
|
|
|
|
</label>
|
|
|
|
|
<label>
|
|
|
|
|
<span>"rootUri"</span>
|
|
|
|
|
<input data-testid="mnote-admin-grant-root-uri" name="rootUri" type="text" />
|
|
|
|
|
</label>
|
|
|
|
|
<label>
|
|
|
|
|
<span>"rootPath"</span>
|
|
|
|
|
<input data-testid="mnote-admin-grant-root-path" name="rootPath" type="text" />
|
|
|
|
|
</label>
|
|
|
|
|
<label>
|
|
|
|
|
<span>"permission"</span>
|
|
|
|
|
<select data-testid="mnote-admin-grant-permission" name="permission">
|
|
|
|
|
<option value="read">"read"</option>
|
|
|
|
|
<option value="write">"write"</option>
|
|
|
|
|
</select>
|
|
|
|
|
</label>
|
|
|
|
|
<label>
|
|
|
|
|
<span>"recursive"</span>
|
|
|
|
|
<input data-testid="mnote-admin-grant-recursive" name="recursive" type="checkbox" checked=true />
|
|
|
|
|
</label>
|
|
|
|
|
<label>
|
|
|
|
|
<span>"capabilities"</span>
|
|
|
|
|
<input data-testid="mnote-admin-grant-capabilities" name="capabilities" type="text" placeholder="ai,share" />
|
|
|
|
|
</label>
|
|
|
|
|
<button type="submit" data-testid="mnote-admin-create-grant-submit">"创建"</button>
|
|
|
|
|
<pre class="mnote-admin-policy-json" data-testid="mnote-admin-create-result"></pre>
|
|
|
|
|
</form>
|
|
|
|
|
|
|
|
|
|
<form class="mnote-admin-policy-form" data-admin-form="delete-grant">
|
|
|
|
|
<header><h2>"删除授权"</h2></header>
|
|
|
|
|
<label>
|
|
|
|
|
<span>"grantId"</span>
|
|
|
|
|
<input data-testid="mnote-admin-delete-grant-id" name="grantId" type="text" placeholder="grant_xxx" required />
|
|
|
|
|
</label>
|
|
|
|
|
<button type="submit" data-testid="mnote-admin-delete-grant-submit">"删除"</button>
|
|
|
|
|
<pre class="mnote-admin-policy-json" data-testid="mnote-admin-delete-result"></pre>
|
|
|
|
|
</form>
|
|
|
|
|
</section>
|
2026-05-19 11:11:08 +08:00
|
|
|
|
|
|
|
|
<section class="mnote-admin-policy-panel" data-testid="mnote-admin-share-grants-panel">
|
|
|
|
|
<header class="mnote-admin-policy-panel-header">
|
|
|
|
|
<h2>"分享授权"</h2>
|
|
|
|
|
<button type="button" data-testid="mnote-admin-share-grants-refresh" data-admin-action="refresh-share-grants">"刷新"</button>
|
|
|
|
|
</header>
|
|
|
|
|
<pre class="mnote-admin-policy-json" data-testid="mnote-admin-share-grants-json">{""}</pre>
|
|
|
|
|
<div class="mnote-admin-policy-note" data-testid="mnote-admin-share-grants-message"></div>
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
<section class="mnote-admin-policy-grid">
|
|
|
|
|
<form class="mnote-admin-policy-form" data-admin-form="create-share-grant">
|
|
|
|
|
<header><h2>"新增分享授权"</h2></header>
|
|
|
|
|
<label>
|
|
|
|
|
<span>"grantId"</span>
|
|
|
|
|
<input data-testid="mnote-admin-share-grant-id" name="shareGrantId" type="text" placeholder="可留空自动生成" />
|
|
|
|
|
</label>
|
|
|
|
|
<label>
|
|
|
|
|
<span>"shareId"</span>
|
|
|
|
|
<input data-testid="mnote-admin-share-id" name="shareId" type="text" placeholder="share_xxx" required />
|
|
|
|
|
</label>
|
|
|
|
|
<label>
|
|
|
|
|
<span>"ownerUserId"</span>
|
|
|
|
|
<input data-testid="mnote-admin-share-owner-user-id" name="ownerUserId" type="text" placeholder="owner_123" required />
|
|
|
|
|
</label>
|
|
|
|
|
<label>
|
|
|
|
|
<span>"targetUserId"</span>
|
|
|
|
|
<input data-testid="mnote-admin-share-target-user-id" name="targetUserId" type="text" placeholder="target_123" required />
|
|
|
|
|
</label>
|
|
|
|
|
<label>
|
|
|
|
|
<span>"rootUri"</span>
|
|
|
|
|
<input data-testid="mnote-admin-share-root-uri" name="shareRootUri" type="text" />
|
|
|
|
|
</label>
|
|
|
|
|
<label>
|
|
|
|
|
<span>"rootPath"</span>
|
|
|
|
|
<input data-testid="mnote-admin-share-root-path" name="shareRootPath" type="text" />
|
|
|
|
|
</label>
|
|
|
|
|
<label>
|
|
|
|
|
<span>"documentId"</span>
|
|
|
|
|
<input data-testid="mnote-admin-share-document-id" name="documentId" type="text" placeholder="doc_123" />
|
|
|
|
|
</label>
|
|
|
|
|
<label>
|
|
|
|
|
<span>"allowedResourceIds"</span>
|
|
|
|
|
<input data-testid="mnote-admin-share-resource-ids" name="allowedResourceIds" type="text" placeholder="doc_123,mindmap_456" />
|
|
|
|
|
</label>
|
|
|
|
|
<label>
|
|
|
|
|
<span>"permission"</span>
|
|
|
|
|
<select data-testid="mnote-admin-share-permission" name="sharePermission">
|
|
|
|
|
<option value="read">"read"</option>
|
|
|
|
|
<option value="write">"write"</option>
|
|
|
|
|
</select>
|
|
|
|
|
</label>
|
|
|
|
|
<label>
|
|
|
|
|
<span>"capabilities"</span>
|
|
|
|
|
<input data-testid="mnote-admin-share-capabilities" name="shareCapabilities" type="text" placeholder="ai,share" />
|
|
|
|
|
</label>
|
|
|
|
|
<button type="submit" data-testid="mnote-admin-create-share-grant-submit">"创建分享授权"</button>
|
|
|
|
|
<pre class="mnote-admin-policy-json" data-testid="mnote-admin-create-share-grant-result"></pre>
|
|
|
|
|
</form>
|
|
|
|
|
|
|
|
|
|
<form class="mnote-admin-policy-form" data-admin-form="delete-share-grant">
|
|
|
|
|
<header><h2>"撤销分享授权"</h2></header>
|
|
|
|
|
<label>
|
|
|
|
|
<span>"shareId 或 grantId"</span>
|
|
|
|
|
<input data-testid="mnote-admin-delete-share-id" name="deleteShareId" type="text" placeholder="share_xxx" required />
|
|
|
|
|
</label>
|
|
|
|
|
<button type="submit" data-testid="mnote-admin-delete-share-grant-submit">"撤销"</button>
|
|
|
|
|
<pre class="mnote-admin-policy-json" data-testid="mnote-admin-delete-share-grant-result"></pre>
|
|
|
|
|
</form>
|
|
|
|
|
</section>
|
2026-05-19 08:07:17 +08:00
|
|
|
<script>{ADMIN_POLICY_SCRIPT}</script>
|
|
|
|
|
</main>
|
|
|
|
|
</PageLayout>
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const ADMIN_POLICY_SCRIPT: &str = r#"
|
|
|
|
|
(function () {
|
|
|
|
|
var root = document.querySelector('[data-testid="mnote-admin-access-policy-page"]');
|
|
|
|
|
if (!root) return;
|
|
|
|
|
var message = root.querySelector('[data-testid="mnote-admin-policy-message"]');
|
|
|
|
|
var policyJson = root.querySelector('[data-testid="mnote-admin-policy-json"]');
|
2026-05-19 11:11:08 +08:00
|
|
|
var shareGrantsJson = root.querySelector('[data-testid="mnote-admin-share-grants-json"]');
|
2026-05-19 08:07:17 +08:00
|
|
|
var validateResult = root.querySelector('[data-testid="mnote-admin-validate-result"]');
|
|
|
|
|
var createResult = root.querySelector('[data-testid="mnote-admin-create-result"]');
|
|
|
|
|
var deleteResult = root.querySelector('[data-testid="mnote-admin-delete-result"]');
|
2026-05-19 11:11:08 +08:00
|
|
|
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"]');
|
2026-05-19 08:07:17 +08:00
|
|
|
var refreshButton = root.querySelector('[data-admin-action="refresh-policy"]');
|
2026-05-19 11:11:08 +08:00
|
|
|
var refreshShareGrantsButton = root.querySelector('[data-admin-action="refresh-share-grants"]');
|
2026-05-19 08:07:17 +08:00
|
|
|
|
|
|
|
|
function setText(node, value) {
|
|
|
|
|
if (!node) return;
|
|
|
|
|
node.textContent = typeof value === 'string' ? value : JSON.stringify(value, null, 2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function formValues(form) {
|
|
|
|
|
var data = new FormData(form);
|
|
|
|
|
var capabilities = String(data.get('capabilities') || '')
|
|
|
|
|
.split(/[,\s]+/)
|
|
|
|
|
.map(function (item) { return item.trim(); })
|
|
|
|
|
.filter(Boolean);
|
|
|
|
|
return {
|
|
|
|
|
id: String(data.get('grantId') || '').trim(),
|
|
|
|
|
userId: String(data.get('userId') || '').trim(),
|
|
|
|
|
rootUri: String(data.get('rootUri') || '').trim(),
|
|
|
|
|
rootPath: String(data.get('rootPath') || '').trim(),
|
|
|
|
|
permission: String(data.get('permission') || 'read').trim(),
|
|
|
|
|
recursive: data.get('recursive') === 'on' || data.get('recursive') === 'true',
|
|
|
|
|
capabilities: capabilities,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-19 11:11:08 +08:00
|
|
|
function splitList(value) {
|
|
|
|
|
return String(value || '')
|
|
|
|
|
.split(/[,\s]+/)
|
|
|
|
|
.map(function (item) { return item.trim(); })
|
|
|
|
|
.filter(Boolean);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function shareGrantFormValues(form) {
|
|
|
|
|
var data = new FormData(form);
|
|
|
|
|
return {
|
|
|
|
|
id: String(data.get('shareGrantId') || '').trim(),
|
|
|
|
|
shareId: String(data.get('shareId') || '').trim(),
|
|
|
|
|
ownerUserId: String(data.get('ownerUserId') || '').trim(),
|
|
|
|
|
targetUserId: String(data.get('targetUserId') || '').trim(),
|
|
|
|
|
rootUri: String(data.get('shareRootUri') || '').trim(),
|
|
|
|
|
rootPath: String(data.get('shareRootPath') || '').trim(),
|
|
|
|
|
documentId: String(data.get('documentId') || '').trim(),
|
|
|
|
|
allowedResourceIds: splitList(data.get('allowedResourceIds')),
|
|
|
|
|
permission: String(data.get('sharePermission') || 'read').trim(),
|
|
|
|
|
capabilities: splitList(data.get('shareCapabilities')),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-19 08:07:17 +08:00
|
|
|
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 refreshPolicy() {
|
|
|
|
|
var payload = await requestJson('/api/admin/access-policy', { method: 'GET', headers: {} });
|
|
|
|
|
setText(policyJson, payload);
|
|
|
|
|
setText(message, '已刷新策略');
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-19 11:11:08 +08:00
|
|
|
async function refreshShareGrants() {
|
|
|
|
|
var payload = await requestJson('/api/admin/share-grants', { method: 'GET', headers: {} });
|
|
|
|
|
setText(shareGrantsJson, payload);
|
|
|
|
|
setText(shareGrantsMessage, '已刷新分享授权');
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-19 08:07:17 +08:00
|
|
|
refreshButton && refreshButton.addEventListener('click', function () {
|
|
|
|
|
setText(message, '正在刷新策略...');
|
|
|
|
|
refreshPolicy().catch(function (error) { setText(message, error.message || '刷新失败'); });
|
|
|
|
|
});
|
|
|
|
|
|
2026-05-19 11:11:08 +08:00
|
|
|
refreshShareGrantsButton && refreshShareGrantsButton.addEventListener('click', function () {
|
|
|
|
|
setText(shareGrantsMessage, '正在刷新分享授权...');
|
|
|
|
|
refreshShareGrants().catch(function (error) { setText(shareGrantsMessage, error.message || '刷新失败'); });
|
|
|
|
|
});
|
|
|
|
|
|
2026-05-19 08:07:17 +08:00
|
|
|
root.querySelector('[data-admin-form="validate-root"]').addEventListener('submit', function (event) {
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
var values = formValues(event.currentTarget);
|
|
|
|
|
setText(validateResult, '正在验证...');
|
|
|
|
|
requestJson('/api/admin/access-policy/validate-root', {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
body: JSON.stringify({ rootUri: values.rootUri, rootPath: values.rootPath }),
|
|
|
|
|
}).then(function (payload) {
|
|
|
|
|
setText(validateResult, payload);
|
|
|
|
|
setText(message, '目录验证完成');
|
|
|
|
|
}).catch(function (error) {
|
|
|
|
|
setText(validateResult, { ok: false, error: error.message || '验证失败' });
|
|
|
|
|
setText(message, error.message || '验证失败');
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
root.querySelector('[data-admin-form="create-grant"]').addEventListener('submit', function (event) {
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
var values = formValues(event.currentTarget);
|
|
|
|
|
setText(createResult, '正在创建...');
|
|
|
|
|
requestJson('/api/admin/access-policy/grants', {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
body: JSON.stringify(values),
|
|
|
|
|
}).then(function (payload) {
|
|
|
|
|
setText(createResult, payload);
|
|
|
|
|
setText(message, '授权已创建');
|
|
|
|
|
return refreshPolicy();
|
|
|
|
|
}).catch(function (error) {
|
|
|
|
|
setText(createResult, { ok: false, error: error.message || '创建失败' });
|
|
|
|
|
setText(message, error.message || '创建失败');
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
root.querySelector('[data-admin-form="delete-grant"]').addEventListener('submit', function (event) {
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
var values = formValues(event.currentTarget);
|
|
|
|
|
var grantId = values.id;
|
|
|
|
|
setText(deleteResult, '正在删除...');
|
|
|
|
|
requestJson('/api/admin/access-policy/grants/' + encodeURIComponent(grantId), {
|
|
|
|
|
method: 'DELETE',
|
|
|
|
|
headers: {},
|
|
|
|
|
}).then(function (payload) {
|
|
|
|
|
setText(deleteResult, payload);
|
|
|
|
|
setText(message, '授权已删除');
|
|
|
|
|
return refreshPolicy();
|
|
|
|
|
}).catch(function (error) {
|
|
|
|
|
setText(deleteResult, { ok: false, error: error.message || '删除失败' });
|
|
|
|
|
setText(message, error.message || '删除失败');
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2026-05-19 11:11:08 +08:00
|
|
|
root.querySelector('[data-admin-form="create-share-grant"]').addEventListener('submit', function (event) {
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
var values = shareGrantFormValues(event.currentTarget);
|
|
|
|
|
setText(createShareGrantResult, '正在创建...');
|
|
|
|
|
requestJson('/api/admin/share-grants', {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
body: JSON.stringify(values),
|
|
|
|
|
}).then(function (payload) {
|
|
|
|
|
setText(createShareGrantResult, payload);
|
|
|
|
|
setText(shareGrantsMessage, '分享授权已创建');
|
|
|
|
|
return refreshShareGrants();
|
|
|
|
|
}).catch(function (error) {
|
|
|
|
|
setText(createShareGrantResult, { ok: false, error: error.message || '创建失败' });
|
|
|
|
|
setText(shareGrantsMessage, error.message || '创建失败');
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
root.querySelector('[data-admin-form="delete-share-grant"]').addEventListener('submit', function (event) {
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
var data = new FormData(event.currentTarget);
|
|
|
|
|
var shareId = String(data.get('deleteShareId') || '').trim();
|
|
|
|
|
setText(deleteShareGrantResult, '正在撤销...');
|
|
|
|
|
requestJson('/api/admin/share-grants/' + encodeURIComponent(shareId), {
|
|
|
|
|
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 || '撤销失败');
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2026-05-19 08:07:17 +08:00
|
|
|
refreshPolicy().catch(function (error) {
|
|
|
|
|
setText(message, error.message || '加载策略失败');
|
|
|
|
|
});
|
2026-05-19 11:11:08 +08:00
|
|
|
refreshShareGrants().catch(function (error) {
|
|
|
|
|
setText(shareGrantsMessage, error.message || '加载分享授权失败');
|
|
|
|
|
});
|
2026-05-19 08:07:17 +08:00
|
|
|
})();
|
|
|
|
|
"#;
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use super::*;
|
|
|
|
|
use crate::ssr::render_view;
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn admin_access_policy_page_renders_admin_controls() {
|
|
|
|
|
let html = render_view(view! {
|
|
|
|
|
<AdminAccessPolicyPage
|
|
|
|
|
workspace_name={"我的空间".to_string()}
|
|
|
|
|
policy_path={"/mnt/Data1T/Mnote_data/control-plane/access-policy.json".to_string()}
|
|
|
|
|
/>
|
|
|
|
|
});
|
|
|
|
|
assert!(html.contains("mnote-admin-access-policy-page"));
|
|
|
|
|
assert!(html.contains("mnote-admin-policy-json"));
|
2026-05-19 11:11:08 +08:00
|
|
|
assert!(html.contains("mnote-admin-share-grants-panel"));
|
|
|
|
|
assert!(html.contains("mnote-admin-share-grants-json"));
|
2026-05-19 08:07:17 +08:00
|
|
|
assert!(html.contains("mnote-admin-validate-root-submit"));
|
|
|
|
|
assert!(html.contains("mnote-admin-create-grant-submit"));
|
|
|
|
|
assert!(html.contains("mnote-admin-delete-grant-submit"));
|
2026-05-19 11:11:08 +08:00
|
|
|
assert!(html.contains("mnote-admin-create-share-grant-submit"));
|
|
|
|
|
assert!(html.contains("mnote-admin-delete-share-grant-submit"));
|
2026-05-19 08:07:17 +08:00
|
|
|
}
|
|
|
|
|
}
|