import { MembershipRole, type Site, type Page } from "@prisma/client" import type { AuthUser } from "./auth.server" import { PageVisibilityEnum } from "./types" type Action = | { type: "can-delete-page" /** The site id this page belongs to */ siteId: string } | { type: "can-list-page" visibility: PageVisibilityEnum siteId: string } | { type: "can-create-page" siteId: string } | { type: "can-read-page" page: Page } | { type: "can-update-site" site: Site } export const createGate = ({ user, requireAuth, }: { user: AuthUser | null | undefined requireAuth?: TRequiredAuth }) => { if (requireAuth && !user) { throw new Error("require auth") } const isSiteMember = (siteId: string, roles: MembershipRole[]) => { if (!user) return false return user.memberships.some( (m) => m.siteId === siteId && roles.includes(m.role) ) } return { user: user as TRequiredAuth extends true ? AuthUser : AuthUser | null | undefined, allows(action: Action): boolean { if (action.type === "can-delete-page") { return isSiteMember(action.siteId, [ MembershipRole.ADMIN, MembershipRole.OWNER, ]) } if (action.type === "can-list-page") { if (action.visibility === PageVisibilityEnum.Published) { return true } return isSiteMember(action.siteId, [ MembershipRole.ADMIN, MembershipRole.OWNER, ]) } if (action.type === "can-create-page") { return isSiteMember(action.siteId, [ MembershipRole.ADMIN, MembershipRole.OWNER, ]) } if (action.type === "can-read-page") { const isPublished = action.page.published && action.page.publishedAt && action.page.publishedAt <= new Date() return isPublished ? !action.page.deletedAt : !action.page.deletedAt && isSiteMember(action.page.siteId, [ MembershipRole.ADMIN, MembershipRole.OWNER, ]) } if (action.type === "can-update-site") { return isSiteMember(action.site.id, [ MembershipRole.ADMIN, MembershipRole.OWNER, ]) } return false }, permissionError(message = "not allowed") { return new Error(message) }, } } export type Gate = ReturnType