feat: enhance session management by implementing edit permissions for team admins and updating session components to reflect new access controls
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { prisma } from '@/services/database';
|
||||
import { resolveCollaborator } from '@/services/auth';
|
||||
import { getTeamMemberIdsForAdminTeams } from '@/services/teams';
|
||||
import { getTeamMemberIdsForAdminTeams, isAdminOfUser } from '@/services/teams';
|
||||
import type { ShareRole, MotivatorType } from '@prisma/client';
|
||||
|
||||
// ============================================
|
||||
@@ -104,6 +104,7 @@ export async function getTeamCollaboratorSessionsForAdmin(userId: string) {
|
||||
isOwner: false as const,
|
||||
role: 'VIEWER' as const,
|
||||
isTeamCollab: true as const,
|
||||
canEdit: true as const, // Admin has full rights on team member sessions
|
||||
}));
|
||||
|
||||
return Promise.all(
|
||||
@@ -115,8 +116,8 @@ export async function getTeamCollaboratorSessionsForAdmin(userId: string) {
|
||||
}
|
||||
|
||||
export async function getMotivatorSessionById(sessionId: string, userId: string) {
|
||||
// Check if user owns the session OR has it shared
|
||||
const session = await prisma.movingMotivatorsSession.findFirst({
|
||||
// Check if user owns the session, has it shared, or is team admin of owner
|
||||
let session = await prisma.movingMotivatorsSession.findFirst({
|
||||
where: {
|
||||
id: sessionId,
|
||||
OR: [
|
||||
@@ -137,13 +138,25 @@ export async function getMotivatorSessionById(sessionId: string, userId: string)
|
||||
},
|
||||
});
|
||||
|
||||
if (!session) return null;
|
||||
if (!session) {
|
||||
const raw = await prisma.movingMotivatorsSession.findUnique({
|
||||
where: { id: sessionId },
|
||||
include: {
|
||||
user: { select: { id: true, name: true, email: true } },
|
||||
cards: { orderBy: { orderIndex: 'asc' } },
|
||||
shares: { include: { user: { select: { id: true, name: true, email: true } } } },
|
||||
},
|
||||
});
|
||||
if (!raw || !(await isAdminOfUser(raw.userId, userId))) return null;
|
||||
session = raw;
|
||||
}
|
||||
|
||||
// Determine user's role
|
||||
const isOwner = session.userId === userId;
|
||||
const share = session.shares.find((s) => s.userId === userId);
|
||||
const isAdminOfOwner = !isOwner && !share && (await isAdminOfUser(session.userId, userId));
|
||||
const role = isOwner ? ('OWNER' as const) : share?.role || ('VIEWER' as const);
|
||||
const canEdit = isOwner || role === 'EDITOR';
|
||||
const canEdit = isOwner || role === 'EDITOR' || isAdminOfOwner;
|
||||
|
||||
// Resolve participant to user if it's an email
|
||||
const resolvedParticipant = await resolveCollaborator(session.participant);
|
||||
@@ -151,7 +164,7 @@ export async function getMotivatorSessionById(sessionId: string, userId: string)
|
||||
return { ...session, isOwner, role, canEdit, resolvedParticipant };
|
||||
}
|
||||
|
||||
// Check if user can access session (owner or shared)
|
||||
// Check if user can access session (owner, shared, or team admin of owner)
|
||||
export async function canAccessMotivatorSession(sessionId: string, userId: string) {
|
||||
const count = await prisma.movingMotivatorsSession.count({
|
||||
where: {
|
||||
@@ -159,10 +172,15 @@ export async function canAccessMotivatorSession(sessionId: string, userId: strin
|
||||
OR: [{ userId }, { shares: { some: { userId } } }],
|
||||
},
|
||||
});
|
||||
return count > 0;
|
||||
if (count > 0) return true;
|
||||
const session = await prisma.movingMotivatorsSession.findUnique({
|
||||
where: { id: sessionId },
|
||||
select: { userId: true },
|
||||
});
|
||||
return session ? isAdminOfUser(session.userId, userId) : false;
|
||||
}
|
||||
|
||||
// Check if user can edit session (owner or EDITOR role)
|
||||
// Check if user can edit session (owner, EDITOR role, or team admin of owner)
|
||||
export async function canEditMotivatorSession(sessionId: string, userId: string) {
|
||||
const count = await prisma.movingMotivatorsSession.count({
|
||||
where: {
|
||||
@@ -170,7 +188,22 @@ export async function canEditMotivatorSession(sessionId: string, userId: string)
|
||||
OR: [{ userId }, { shares: { some: { userId, role: 'EDITOR' } } }],
|
||||
},
|
||||
});
|
||||
return count > 0;
|
||||
if (count > 0) return true;
|
||||
const session = await prisma.movingMotivatorsSession.findUnique({
|
||||
where: { id: sessionId },
|
||||
select: { userId: true },
|
||||
});
|
||||
return session ? isAdminOfUser(session.userId, userId) : false;
|
||||
}
|
||||
|
||||
// Check if user can delete session (owner or team admin only - NOT EDITOR)
|
||||
export async function canDeleteMotivatorSession(sessionId: string, userId: string) {
|
||||
const session = await prisma.movingMotivatorsSession.findUnique({
|
||||
where: { id: sessionId },
|
||||
select: { userId: true },
|
||||
});
|
||||
if (!session) return false;
|
||||
return session.userId === userId || isAdminOfUser(session.userId, userId);
|
||||
}
|
||||
|
||||
const DEFAULT_MOTIVATOR_TYPES: MotivatorType[] = [
|
||||
@@ -216,15 +249,21 @@ export async function updateMotivatorSession(
|
||||
userId: string,
|
||||
data: { title?: string; participant?: string }
|
||||
) {
|
||||
if (!(await canEditMotivatorSession(sessionId, userId))) {
|
||||
return { count: 0 };
|
||||
}
|
||||
return prisma.movingMotivatorsSession.updateMany({
|
||||
where: { id: sessionId, userId },
|
||||
where: { id: sessionId },
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
export async function deleteMotivatorSession(sessionId: string, userId: string) {
|
||||
if (!(await canDeleteMotivatorSession(sessionId, userId))) {
|
||||
return { count: 0 };
|
||||
}
|
||||
return prisma.movingMotivatorsSession.deleteMany({
|
||||
where: { id: sessionId, userId },
|
||||
where: { id: sessionId },
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user