xLog/app/lib/gate.server.ts

106 lines
2.5 KiB
TypeScript

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 = <TRequiredAuth extends boolean | undefined>({
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<typeof createGate>