/** * 文件库 HTTP 门禁(契约 C4 的入驻适配)。 * * 身份链:hub session(飞书 OAuth / dev bypass)→ silo org membership。 * 网站管理员 = org 的 OWNER/ADMIN(D19:仅 root 创建与 force_adjust 特权, * 不给内容读旁路);普通成员 = 任何活跃 membership;非成员 = 403。 */ import type { FastifyReply, FastifyRequest } from "fastify"; import type { OrganizationMemberRole, PrismaClient } from "@prisma/client"; import { requireSession, sendError } from "../../admin/auth/guards.js"; import type { FileLibActor } from "./treeService.js"; export interface FileLibGuardDeps { readonly prisma: PrismaClient; readonly sessionSecret: string; /** 文件库归属的 silo org(ADR-0020/0025)。 */ readonly organizationId: string; } const WEBSITE_ADMIN_ROLES: readonly OrganizationMemberRole[] = ["OWNER", "ADMIN"]; /** 每个 /database/api/* 端点第一行调它;返回 null 时响应已发出,fail closed。 */ export async function requireFileLibActor( request: FastifyRequest, reply: FastifyReply, deps: FileLibGuardDeps, ): Promise { const auth = await requireSession(request, reply, { prisma: deps.prisma, sessionSecret: deps.sessionSecret, }); if (auth === null) return null; const membership = await deps.prisma.organizationMembership.findFirst({ where: { organizationId: deps.organizationId, userId: auth.user.id, revokedAt: null }, select: { role: true }, }); if (membership === null) { await sendError(reply, 403, "forbidden", "not a member of this organization"); return null; } return { userId: auth.user.id, isWebsiteAdmin: WEBSITE_ADMIN_ROLES.includes(membership.role), }; }