flnxi-Wu/lib/material-permissions.ts
2026-06-03 18:41:52 +08:00

154 lines
3.7 KiB
TypeScript

import type { Prisma } from '@prisma/client'
import { prisma } from '@/lib/prisma'
import { planDepartmentScopeWithPaidCore } from '@/lib/license'
export type MaterialPermissionActor = {
id: string
role: string
isSuperAdmin: boolean
isActive: boolean
canDownload: boolean
department?: string | null
managedDepartment?: string | null
}
export type MaterialPermissionTarget = {
userId?: string | null
createdByUserId?: string | null
ownerType?: string | null
ownerId?: string | null
user?: {
id?: string | null
department?: string | null
} | null
}
const normalizeDepartment = (department?: string | null) => (department || '').trim()
export async function getMaterialPermissionActor(session: unknown): Promise<MaterialPermissionActor | null> {
const userId = (session as any)?.user?.id
if (!userId) {
return null
}
const user = await prisma.user.findUnique({
where: { id: userId },
select: {
id: true,
role: true,
isSuperAdmin: true,
isActive: true,
canDownload: true,
department: true,
managedDepartment: true,
},
})
if (!user || (!user.isSuperAdmin && user.isActive === false)) {
return null
}
return {
id: user.id,
role: user.role,
isSuperAdmin: Boolean(user.isSuperAdmin),
isActive: user.isActive,
canDownload: user.canDownload,
department: user.department,
managedDepartment: user.managedDepartment,
}
}
export function canActorDownloadMaterial(actor: MaterialPermissionActor | null) {
if (!actor) {
return false
}
return actor.isSuperAdmin || actor.role === 'ADMIN' || actor.canDownload
}
export function isMaterialOwnedByActor(material: MaterialPermissionTarget, actor: MaterialPermissionActor | null) {
if (!actor) {
return false
}
return (
material.userId === actor.id ||
(material.ownerType === 'USER' && material.ownerId === actor.id)
)
}
export async function canManageMaterial(
material: MaterialPermissionTarget,
actor: MaterialPermissionActor | null
) {
if (!actor) {
return false
}
if (actor.isSuperAdmin) {
return true
}
if (isMaterialOwnedByActor(material, actor)) {
return true
}
if (actor.role !== 'ADMIN') {
return false
}
const scopeDepartment = normalizeDepartment(actor.managedDepartment)
const targetDepartment = normalizeDepartment(material.user?.department)
if (!scopeDepartment || !targetDepartment) {
return false
}
const departments = await prisma.department.findMany({ select: { name: true } })
const scopePlan = await planDepartmentScopeWithPaidCore({
scopeDepartment,
targetDepartment,
departments,
})
return scopePlan.valid ? scopePlan.withinScope : false
}
export async function buildMaterialManagementScopeWhere(
actor: MaterialPermissionActor | null
): Promise<Prisma.MaterialWhereInput> {
if (!actor) {
return { id: { in: [] } }
}
if (actor.isSuperAdmin) {
return {}
}
const scopeDepartment = normalizeDepartment(actor.managedDepartment)
const ownWhere: Prisma.MaterialWhereInput[] = [
{ userId: actor.id },
{ ownerType: 'USER', ownerId: actor.id },
]
if (actor.role !== 'ADMIN' || !scopeDepartment) {
return { OR: ownWhere }
}
const departments = await prisma.department.findMany({ select: { name: true } })
const scopePlan = await planDepartmentScopeWithPaidCore({
scopeDepartment,
departments,
})
if (!scopePlan.valid || scopePlan.scopedDepartments.length === 0) {
return { OR: ownWhere }
}
return {
OR: [
...ownWhere,
{ user: { department: { in: scopePlan.scopedDepartments } } },
{ user: { department: { startsWith: `${scopeDepartment}-` } } },
],
}
}