feat(filelib): 项目级授权恒生效,移除独立权限开关(ADR-0030)

- permission.ts: effectiveRole 删除 D11 冻结分支,输入不再含开关字段
- treeService: 停止读 FileLibProjectSettings;建项目不再写默认行
- grantService/routes: 删 setIndependentPermission 与 PUT 路由;节点详情 DTO 去掉 independentPermission
- filelib-web: 概览 tab 移除开关;NodeDetail 类型同步
- 测试: 单测/集成改为断言恒生效语义;ADR-0030 废除契约 D11/P5
- FileLibProjectSettings 表保留(存量行忽略,不再读写),审计词表保留历史读取
This commit is contained in:
ymy
2026-07-30 22:27:45 +08:00
parent c72f8c7050
commit 39a2be6347
11 changed files with 83 additions and 155 deletions
+1 -32
View File
@@ -94,7 +94,7 @@ async function loadVisibleChain(
return { node, ancestors: ordered };
}
/** 数据获取层:把 chain、grants、groups、toggle 装配成纯 reducer 的输入。 */
/** 数据获取层:把 chain、grants、groups 装配成纯 reducer 的输入。 */
async function resolveRole(
tx: Tx,
deps: AccessDeps,
@@ -106,20 +106,11 @@ async function resolveRole(
where: { organizationId: deps.organizationId, revokedAt: null, nodeId: { in: chainIds } },
select: { nodeId: true, principalType: true, principalId: true, role: true, isCreatorGrant: true },
});
let independentPermissionsEnabled = false;
if (chain.node.kind === "PROJECT") {
const settings = await tx.fileLibProjectSettings.findUnique({
where: { nodeId: chain.node.id },
select: { independentPermissionsEnabled: true },
});
independentPermissionsEnabled = settings?.independentPermissionsEnabled ?? false;
}
const groupIds = await deps.groupResolver.resolveMemberGroupIds(actor.userId);
return effectiveRole({
nodeId: chain.node.id,
nodeKind: chain.node.kind,
ancestorIds: chain.ancestors.map((a) => a.id),
independentPermissionsEnabled,
userId: actor.userId,
groupIds,
grants,
@@ -278,11 +269,6 @@ export async function createNode(
},
});
}
if (input.kind === "PROJECT") {
await tx.fileLibProjectSettings.create({
data: { nodeId: id, independentPermissionsEnabled: false },
});
}
await writeFileLibAudit(tx, {
action: nodeAction(input.kind, "Create"),
@@ -482,20 +468,12 @@ export async function breadcrumb(
where: { organizationId: deps.organizationId, revokedAt: null, nodeId: { in: chainIds } },
select: { nodeId: true, principalType: true, principalId: true, role: true, isCreatorGrant: true },
});
const settings = chain.node.kind === "PROJECT"
? await tx.fileLibProjectSettings.findUnique({
where: { nodeId: chain.node.id },
select: { independentPermissionsEnabled: true },
})
: null;
return chainNodes.map((current, depth) => {
const role = effectiveRole({
nodeId: current.id,
nodeKind: current.kind,
ancestorIds: chainNodes.slice(0, depth).map((n) => n.id),
independentPermissionsEnabled:
current.id === chain.node.id ? settings?.independentPermissionsEnabled ?? false : false,
userId: actor.userId,
groupIds,
grants: allGrants,
@@ -545,14 +523,6 @@ export async function listChildren(
where: { organizationId: deps.organizationId, revokedAt: null, nodeId: { in: idsToFetch } },
select: { nodeId: true, principalType: true, principalId: true, role: true, isCreatorGrant: true },
});
const projectIds = children.filter((c) => c.kind === "PROJECT").map((c) => c.id);
const settingsRows = projectIds.length === 0
? []
: await tx.fileLibProjectSettings.findMany({
where: { nodeId: { in: projectIds } },
select: { nodeId: true, independentPermissionsEnabled: true },
});
const toggleByNode = new Map(settingsRows.map((s) => [s.nodeId, s.independentPermissionsEnabled]));
const groupIds = await deps.groupResolver.resolveMemberGroupIds(actor.userId);
const out: ChildNodeDto[] = [];
@@ -561,7 +531,6 @@ export async function listChildren(
nodeId: child.id,
nodeKind: child.kind,
ancestorIds: parentAncestorIds,
independentPermissionsEnabled: toggleByNode.get(child.id) ?? false,
userId: actor.userId,
groupIds,
grants: allGrants,