// app/actions/permission-group-assignment-actions.ts "use server"; import db from "@/db/db"; import { eq, and, inArray, sql, ne, notInArray, or } from "drizzle-orm"; import { permissionGroups, permissionGroupMembers, permissions, rolePermissions, userPermissions, roles, users, userRoles, permissionAuditLogs } from "@/db/schema"; import { checkUserPermission } from "./service"; import { getServerSession } from "next-auth/next"; import { authOptions } from "@/app/api/auth/[...nextauth]/route"; // 권한 그룹 할당 정보 조회 export async function getPermissionGroupAssignments(groupId?: number) { try { if (!groupId) { // 모든 그룹 목록 반환 const groups = await db .select({ id: permissionGroups.id, groupKey: permissionGroups.groupKey, name: permissionGroups.name, description: permissionGroups.description, domain: permissionGroups.domain, isActive: permissionGroups.isActive, permissionCount: sql`count(distinct ${permissionGroupMembers.permissionId})`.mapWith(Number), }) .from(permissionGroups) .leftJoin(permissionGroupMembers, eq(permissionGroupMembers.groupId, permissionGroups.id)) .where(eq(permissionGroups.isActive, true)) .groupBy(permissionGroups.id) .orderBy(permissionGroups.name); return { groups }; } // 특정 그룹의 할당 정보 조회 // 그룹에 속한 모든 권한 ID 조회 const groupPermissionIds = await db .select({ permissionId: permissionGroupMembers.permissionId }) .from(permissionGroupMembers) .where(eq(permissionGroupMembers.groupId, groupId)); const permissionIds = groupPermissionIds.map(p => p.permissionId); if (permissionIds.length === 0) { return { roles: [], users: [] }; } // 해당 그룹의 권한들이 할당된 역할 조회 const assignedRoles = await db .selectDistinct({ id: roles.id, name: roles.name, domain: roles.domain, grantedAt: rolePermissions.grantedAt, grantedBy: rolePermissions.grantedBy, }) .from(rolePermissions) .innerJoin(roles, eq(roles.id, rolePermissions.roleId)) .where( and( eq(rolePermissions.permissionGroupId, groupId), eq(rolePermissions.isActive, true) ) ); // 역할별 사용자 수 조회 const rolesWithUserCount = await Promise.all( assignedRoles.map(async (role) => { const userCount = await db .select({ count: sql`count(*)`.mapWith(Number) }) .from(userRoles) .where(eq(userRoles.roleId, role.id)); // grantedBy가 userId인 경우 사용자 정보 조회 let assignedBy = 'system'; if (role.grantedBy) { const [user] = await db .select({ name: users.name }) .from(users) .where(eq(users.id, role.grantedBy)); if (user) assignedBy = user.name; } return { ...role, userCount: userCount[0]?.count || 0, assignedAt: role.grantedAt, assignedBy }; }) ); // 해당 그룹의 권한들이 직접 할당된 사용자 조회 const assignedUsers = await db .selectDistinct({ id: users.id, name: users.name, email: users.email, imageUrl: users.imageUrl, domain: users.domain, grantedAt: userPermissions.grantedAt, grantedBy: userPermissions.grantedBy, }) .from(userPermissions) .innerJoin(users, eq(users.id, userPermissions.userId)) .where( and( eq(userPermissions.permissionGroupId, groupId), eq(userPermissions.isActive, true), eq(userPermissions.isGrant, true) ) ); // 사용자별 회사 정보 및 할당자 정보 추가 const usersWithDetails = await Promise.all( assignedUsers.map(async (user) => { // 회사 정보는 companyId로 조회 (vendors 테이블 필요) let assignedBy = 'system'; if (user.grantedBy) { const [grantUser] = await db .select({ name: users.name }) .from(users) .where(eq(users.id, user.grantedBy)); if (grantUser) assignedBy = grantUser.name; } return { ...user, companyName: null, // 필요시 vendors 테이블 조인 assignedAt: user.grantedAt, assignedBy }; }) ); return { roles: rolesWithUserCount, users: usersWithDetails }; } catch (error) { console.error('Failed to get permission group assignments:', error); throw new Error('권한 그룹 할당 정보 조회에 실패했습니다.'); } } // 역할 검색 (그룹에 아직 할당되지 않은) export async function searchRoles(groupId: number) { try { // 이미 해당 그룹이 할당된 역할 ID 조회 const assignedRoleIds = await db .selectDistinct({ roleId: rolePermissions.roleId }) .from(rolePermissions) .where( and( eq(rolePermissions.permissionGroupId, groupId), eq(rolePermissions.isActive, true) ) ); const assignedIds = assignedRoleIds.map(r => r.roleId); // 할당되지 않은 역할 조회 const availableRoles = await db .select({ id: roles.id, name: roles.name, domain: roles.domain, }) .from(roles) .where( assignedIds.length > 0 ? notInArray(roles.id, assignedIds) : undefined ); // 역할별 사용자 수 추가 const rolesWithUserCount = await Promise.all( availableRoles.map(async (role) => { const userCount = await db .select({ count: sql`count(*)`.mapWith(Number) }) .from(userRoles) .where(eq(userRoles.roleId, role.id)); return { ...role, userCount: userCount[0]?.count || 0 }; }) ); return rolesWithUserCount; } catch (error) { console.error('Failed to search roles:', error); throw new Error('역할 검색에 실패했습니다.'); } } // 사용자 검색 (그룹에 아직 할당되지 않은) export async function searchUsers(query: string, groupId: number) { try { // 이미 해당 그룹이 할당된 사용자 ID 조회 const assignedUserIds = await db .selectDistinct({ userId: userPermissions.userId }) .from(userPermissions) .where( and( eq(userPermissions.permissionGroupId, groupId), eq(userPermissions.isActive, true), eq(userPermissions.isGrant, true) ) ); const assignedIds = assignedUserIds.map(u => u.userId); // 할당되지 않은 사용자 검색 const availableUsers = await db .select({ id: users.id, name: users.name, email: users.email, imageUrl: users.imageUrl, domain: users.domain, }) .from(users) .where( and( or( sql`${users.name} ILIKE ${`%${query}%`}`, sql`${users.email} ILIKE ${`%${query}%`}` ), eq(users.isActive, true), assignedIds.length > 0 ? notInArray(users.id, assignedIds) : undefined ) ) .limit(20); return availableUsers.map(user => ({ ...user, companyName: null // 필요시 vendors 테이블 조인 })); } catch (error) { console.error('Failed to search users:', error); throw new Error('사용자 검색에 실패했습니다.'); } } // 그룹을 역할에 할당 export async function assignGroupToRoles(groupId: number, roleIds: number[]) { const session = await getServerSession(authOptions); if (!session?.user?.id) { throw new Error("인증이 필요합니다."); } const currentUserId = Number(session.user.id); if (!await checkUserPermission(currentUserId, "admin.permissions.manage")) { throw new Error("권한 관리 권한이 없습니다."); } try { // 그룹에 속한 모든 권한 ID 조회 const groupPermissions = await db .select({ permissionId: permissionGroupMembers.permissionId }) .from(permissionGroupMembers) .where(eq(permissionGroupMembers.groupId, groupId)); if (groupPermissions.length === 0) { throw new Error("그룹에 권한이 없습니다."); } await db.transaction(async (tx) => { for (const roleId of roleIds) { // 각 역할에 대해 그룹의 모든 권한 할당 for (const { permissionId } of groupPermissions) { // 기존 할당 확인 (중복 방지) const existing = await tx .select() .from(rolePermissions) .where( and( eq(rolePermissions.roleId, roleId), eq(rolePermissions.permissionId, permissionId), eq(rolePermissions.permissionGroupId, groupId) ) ) .limit(1); if (existing.length === 0) { await tx.insert(rolePermissions).values({ roleId, permissionId, permissionGroupId: groupId, grantedBy: currentUserId, grantedAt: new Date(), isActive: true }); } } // 감사 로그 추가 await tx.insert(permissionAuditLogs).values({ targetType: 'role', targetId: roleId, permissionGroupId: groupId, action: 'grant', performedBy: currentUserId, reason: `권한 그룹 ${groupId} 할당` }); } }); return { success: true, count: roleIds.length }; } catch (error) { console.error('Failed to assign group to roles:', error); throw new Error('역할에 권한 그룹 할당에 실패했습니다.'); } } // 그룹을 사용자에 할당 export async function assignGroupToUsers(groupId: number, userIds: number[]) { const session = await getServerSession(authOptions); if (!session?.user?.id) { throw new Error("인증이 필요합니다."); } const currentUserId = Number(session.user.id); if (!await checkUserPermission(currentUserId, "admin.permissions.manage")) { throw new Error("권한 관리 권한이 없습니다."); } try { // 그룹에 속한 모든 권한 ID 조회 const groupPermissions = await db .select({ permissionId: permissionGroupMembers.permissionId }) .from(permissionGroupMembers) .where(eq(permissionGroupMembers.groupId, groupId)); if (groupPermissions.length === 0) { throw new Error("그룹에 권한이 없습니다."); } await db.transaction(async (tx) => { for (const userId of userIds) { // 각 사용자에 대해 그룹의 모든 권한 할당 for (const { permissionId } of groupPermissions) { // 기존 할당 확인 (중복 방지) const existing = await tx .select() .from(userPermissions) .where( and( eq(userPermissions.userId, userId), eq(userPermissions.permissionId, permissionId) ) ) .limit(1); if (existing.length === 0) { await tx.insert(userPermissions).values({ userId, permissionId, permissionGroupId: groupId, isGrant: true, grantedBy: currentUserId, grantedAt: new Date(), isActive: true }); } else if (existing[0].permissionGroupId !== groupId) { // 다른 그룹으로 할당되어 있다면 업데이트 await tx.update(userPermissions) .set({ permissionGroupId: groupId, grantedBy: currentUserId, grantedAt: new Date() }) .where( and( eq(userPermissions.userId, userId), eq(userPermissions.permissionId, permissionId) ) ); } } // 감사 로그 추가 await tx.insert(permissionAuditLogs).values({ targetType: 'user', targetId: userId, permissionGroupId: groupId, action: 'grant', performedBy: currentUserId, reason: `권한 그룹 ${groupId} 할당` }); } }); return { success: true, count: userIds.length }; } catch (error) { console.error('Failed to assign group to users:', error); throw new Error('사용자에게 권한 그룹 할당에 실패했습니다.'); } } // 역할에서 그룹 제거 export async function removeGroupFromRole(groupId: number, roleId: number) { const session = await getServerSession(authOptions); if (!session?.user?.id) { throw new Error("인증이 필요합니다."); } const currentUserId = Number(session.user.id); if (!await checkUserPermission(currentUserId, "admin.permissions.manage")) { throw new Error("권한 관리 권한이 없습니다."); } try { await db.transaction(async (tx) => { // 해당 그룹으로 할당된 모든 권한 제거 await tx.delete(rolePermissions) .where( and( eq(rolePermissions.roleId, roleId), eq(rolePermissions.permissionGroupId, groupId) ) ); // 감사 로그 추가 await tx.insert(permissionAuditLogs).values({ targetType: 'role', targetId: roleId, permissionGroupId: groupId, action: 'revoke', performedBy: currentUserId, reason: `권한 그룹 ${groupId} 제거` }); }); return { success: true }; } catch (error) { console.error('Failed to remove group from role:', error); throw new Error('역할에서 권한 그룹 제거에 실패했습니다.'); } } // 사용자에서 그룹 제거 export async function removeGroupFromUser(groupId: number, userId: number) { const session = await getServerSession(authOptions); if (!session?.user?.id) { throw new Error("인증이 필요합니다."); } const currentUserId = Number(session.user.id); if (!await checkUserPermission(currentUserId, "admin.permissions.manage")) { throw new Error("권한 관리 권한이 없습니다."); } try { await db.transaction(async (tx) => { // 해당 그룹으로 할당된 모든 권한 제거 await tx.delete(userPermissions) .where( and( eq(userPermissions.userId, userId), eq(userPermissions.permissionGroupId, groupId) ) ); // 감사 로그 추가 await tx.insert(permissionAuditLogs).values({ targetType: 'user', targetId: userId, permissionGroupId: groupId, action: 'revoke', performedBy: currentUserId, reason: `권한 그룹 ${groupId} 제거` }); }); return { success: true }; } catch (error) { console.error('Failed to remove group from user:', error); throw new Error('사용자에서 권한 그룹 제거에 실패했습니다.'); } }