- src/access.ts: shared product/story/task access checks via product ownership or membership - update_task_status accepts lowercase API values, converts to DB enum, rejects unknown values - update_task_plan replaces implementation_plan on a task - Both call requireWriteAccess() so demo accounts get PERMISSION_DENIED before any DB write Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
30 lines
968 B
TypeScript
30 lines
968 B
TypeScript
import { prisma } from './prisma.js'
|
|
|
|
export async function userCanAccessProduct(productId: string, userId: string): Promise<boolean> {
|
|
const hit = await prisma.product.findFirst({
|
|
where: {
|
|
id: productId,
|
|
OR: [{ user_id: userId }, { members: { some: { user_id: userId } } }],
|
|
},
|
|
select: { id: true },
|
|
})
|
|
return Boolean(hit)
|
|
}
|
|
|
|
export async function userCanAccessTask(taskId: string, userId: string): Promise<boolean> {
|
|
const task = await prisma.task.findUnique({
|
|
where: { id: taskId },
|
|
select: { story: { select: { product_id: true } } },
|
|
})
|
|
if (!task) return false
|
|
return userCanAccessProduct(task.story.product_id, userId)
|
|
}
|
|
|
|
export async function userCanAccessStory(storyId: string, userId: string): Promise<boolean> {
|
|
const story = await prisma.story.findUnique({
|
|
where: { id: storyId },
|
|
select: { product_id: true },
|
|
})
|
|
if (!story) return false
|
|
return userCanAccessProduct(story.product_id, userId)
|
|
}
|