Files
sam-react-prod/src/components/settings/PermissionManagement/actions.ts
byeongcheolryu 29e7b41615 chore(WEB): 다수 컴포넌트 개선 및 CEO 대시보드 추가
- CEO 대시보드 컴포넌트 추가
- AuthenticatedLayout 개선
- 각 모듈 actions.ts 에러 핸들링 개선
- API fetch-wrapper, refresh-token 로직 개선
- ReceivablesStatus 컴포넌트 업데이트
- globals.css 스타일 업데이트
- 기타 다수 컴포넌트 수정

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-08 17:15:42 +09:00

451 lines
13 KiB
TypeScript

'use server';
import { isRedirectError } from 'next/dist/client/components/redirect';
import { revalidatePath } from 'next/cache';
import { serverFetch } from '@/lib/api/fetch-wrapper';
import type { Role, RoleStats, PermissionMatrix, MenuTreeItem, ApiResponse, PaginatedResponse } from './types';
const API_URL = process.env.NEXT_PUBLIC_API_URL;
// ========== Role CRUD ==========
/**
* 역할 목록 조회
*/
export async function fetchRoles(params?: {
page?: number;
size?: number;
q?: string;
is_hidden?: boolean;
}): Promise<ApiResponse<PaginatedResponse<Role>>> {
try {
const searchParams = new URLSearchParams();
if (params?.page) searchParams.set('page', params.page.toString());
if (params?.size) searchParams.set('per_page', params.size.toString());
if (params?.q) searchParams.set('q', params.q);
if (params?.is_hidden !== undefined) searchParams.set('is_hidden', params.is_hidden.toString());
const url = `${API_URL}/api/v1/roles?${searchParams.toString()}`;
const { response, error } = await serverFetch(url, { method: 'GET' });
if (error) {
return { success: false, error: error.message };
}
if (!response) {
return { success: false, error: '역할 목록 조회에 실패했습니다.' };
}
const result = await response.json();
if (!response.ok || !result.success) {
return { success: false, error: result.message || '역할 목록 조회 실패' };
}
return { success: true, data: result.data };
} catch (error) {
if (isRedirectError(error)) throw error;
console.error('Failed to fetch roles:', error);
return { success: false, error: error instanceof Error ? error.message : '역할 목록 조회 실패' };
}
}
/**
* 역할 상세 조회
*/
export async function fetchRole(id: number): Promise<ApiResponse<Role>> {
try {
const { response, error } = await serverFetch(`${API_URL}/api/v1/roles/${id}`, { method: 'GET' });
if (error) {
return { success: false, error: error.message };
}
if (!response) {
return { success: false, error: '역할 조회에 실패했습니다.' };
}
const result = await response.json();
if (!response.ok || !result.success) {
return { success: false, error: result.message || '역할 조회 실패' };
}
return { success: true, data: result.data };
} catch (error) {
if (isRedirectError(error)) throw error;
console.error('Failed to fetch role:', error);
return { success: false, error: error instanceof Error ? error.message : '역할 조회 실패' };
}
}
/**
* 역할 생성
*/
export async function createRole(data: {
name: string;
description?: string;
is_hidden?: boolean;
}): Promise<ApiResponse<Role>> {
try {
const { response, error } = await serverFetch(`${API_URL}/api/v1/roles`, {
method: 'POST',
body: JSON.stringify(data),
});
if (error) {
return { success: false, error: error.message };
}
if (!response) {
return { success: false, error: '역할 생성에 실패했습니다.' };
}
const result = await response.json();
if (!response.ok || !result.success) {
return { success: false, error: result.message || '역할 생성 실패' };
}
revalidatePath('/settings/permissions');
return { success: true, data: result.data };
} catch (error) {
if (isRedirectError(error)) throw error;
console.error('Failed to create role:', error);
return { success: false, error: error instanceof Error ? error.message : '역할 생성 실패' };
}
}
/**
* 역할 수정
*/
export async function updateRole(
id: number,
data: {
name?: string;
description?: string;
is_hidden?: boolean;
}
): Promise<ApiResponse<Role>> {
try {
const { response, error } = await serverFetch(`${API_URL}/api/v1/roles/${id}`, {
method: 'PATCH',
body: JSON.stringify(data),
});
if (error) {
return { success: false, error: error.message };
}
if (!response) {
return { success: false, error: '역할 수정에 실패했습니다.' };
}
const result = await response.json();
if (!response.ok || !result.success) {
return { success: false, error: result.message || '역할 수정 실패' };
}
revalidatePath('/settings/permissions');
revalidatePath(`/settings/permissions/${id}`);
return { success: true, data: result.data };
} catch (error) {
if (isRedirectError(error)) throw error;
console.error('Failed to update role:', error);
return { success: false, error: error instanceof Error ? error.message : '역할 수정 실패' };
}
}
/**
* 역할 삭제
*/
export async function deleteRole(id: number): Promise<ApiResponse<void>> {
try {
const { response, error } = await serverFetch(`${API_URL}/api/v1/roles/${id}`, {
method: 'DELETE',
});
if (error) {
return { success: false, error: error.message };
}
if (!response) {
return { success: false, error: '역할 삭제에 실패했습니다.' };
}
const result = await response.json();
if (!response.ok || !result.success) {
return { success: false, error: result.message || '역할 삭제 실패' };
}
revalidatePath('/settings/permissions');
return { success: true };
} catch (error) {
if (isRedirectError(error)) throw error;
console.error('Failed to delete role:', error);
return { success: false, error: error instanceof Error ? error.message : '역할 삭제 실패' };
}
}
/**
* 역할 통계 조회
*/
export async function fetchRoleStats(): Promise<ApiResponse<RoleStats>> {
try {
const { response, error } = await serverFetch(`${API_URL}/api/v1/roles/stats`, { method: 'GET' });
if (error) {
return { success: false, error: error.message };
}
if (!response) {
return { success: false, error: '역할 통계 조회에 실패했습니다.' };
}
const result = await response.json();
if (!response.ok || !result.success) {
return { success: false, error: result.message || '역할 통계 조회 실패' };
}
return { success: true, data: result.data };
} catch (error) {
if (isRedirectError(error)) throw error;
console.error('Failed to fetch role stats:', error);
return { success: false, error: error instanceof Error ? error.message : '역할 통계 조회 실패' };
}
}
/**
* 활성 역할 목록 (드롭다운용)
*/
export async function fetchActiveRoles(): Promise<ApiResponse<Role[]>> {
try {
const { response, error } = await serverFetch(`${API_URL}/api/v1/roles/active`, { method: 'GET' });
if (error) {
return { success: false, error: error.message };
}
if (!response) {
return { success: false, error: '활성 역할 목록 조회에 실패했습니다.' };
}
const result = await response.json();
if (!response.ok || !result.success) {
return { success: false, error: result.message || '활성 역할 목록 조회 실패' };
}
return { success: true, data: result.data };
} catch (error) {
if (isRedirectError(error)) throw error;
console.error('Failed to fetch active roles:', error);
return { success: false, error: error instanceof Error ? error.message : '활성 역할 목록 조회 실패' };
}
}
// ========== Permission Matrix ==========
/**
* 권한 매트릭스용 메뉴 트리 조회
*/
export async function fetchPermissionMenus(): Promise<ApiResponse<{
menus: MenuTreeItem[];
permission_types: string[];
}>> {
try {
const { response, error } = await serverFetch(`${API_URL}/api/v1/role-permissions/menus`, { method: 'GET' });
if (error) {
return { success: false, error: error.message };
}
if (!response) {
return { success: false, error: '메뉴 트리 조회에 실패했습니다.' };
}
const result = await response.json();
if (!response.ok || !result.success) {
return { success: false, error: result.message || '메뉴 트리 조회 실패' };
}
return { success: true, data: result.data };
} catch (error) {
if (isRedirectError(error)) throw error;
console.error('Failed to fetch permission menus:', error);
return { success: false, error: error instanceof Error ? error.message : '메뉴 트리 조회 실패' };
}
}
/**
* 역할의 권한 매트릭스 조회
*/
export async function fetchPermissionMatrix(roleId: number): Promise<ApiResponse<PermissionMatrix>> {
try {
const { response, error } = await serverFetch(`${API_URL}/api/v1/roles/${roleId}/permissions/matrix`, { method: 'GET' });
if (error) {
return { success: false, error: error.message };
}
if (!response) {
return { success: false, error: '권한 매트릭스 조회에 실패했습니다.' };
}
const result = await response.json();
if (!response.ok || !result.success) {
return { success: false, error: result.message || '권한 매트릭스 조회 실패' };
}
return { success: true, data: result.data };
} catch (error) {
if (isRedirectError(error)) throw error;
console.error('Failed to fetch permission matrix:', error);
return { success: false, error: error instanceof Error ? error.message : '권한 매트릭스 조회 실패' };
}
}
/**
* 특정 메뉴의 특정 권한 토글
*/
export async function togglePermission(
roleId: number,
menuId: number,
permissionType: string
): Promise<ApiResponse<{
granted: boolean;
propagated_to: number[];
}>> {
try {
const { response, error } = await serverFetch(`${API_URL}/api/v1/roles/${roleId}/permissions/toggle`, {
method: 'POST',
body: JSON.stringify({
menu_id: menuId,
permission_type: permissionType,
}),
});
if (error) {
return { success: false, error: error.message };
}
if (!response) {
return { success: false, error: '권한 토글에 실패했습니다.' };
}
const result = await response.json();
if (!response.ok || !result.success) {
return { success: false, error: result.message || '권한 토글 실패' };
}
revalidatePath(`/settings/permissions/${roleId}`);
return { success: true, data: result.data };
} catch (error) {
if (isRedirectError(error)) throw error;
console.error('Failed to toggle permission:', error);
return { success: false, error: error instanceof Error ? error.message : '권한 토글 실패' };
}
}
/**
* 모든 권한 허용
*/
export async function allowAllPermissions(roleId: number): Promise<ApiResponse<{ count: number }>> {
try {
const { response, error } = await serverFetch(`${API_URL}/api/v1/roles/${roleId}/permissions/allow-all`, {
method: 'POST',
});
if (error) {
return { success: false, error: error.message };
}
if (!response) {
return { success: false, error: '전체 허용에 실패했습니다.' };
}
const result = await response.json();
if (!response.ok || !result.success) {
return { success: false, error: result.message || '전체 허용 실패' };
}
revalidatePath(`/settings/permissions/${roleId}`);
return { success: true, data: result.data };
} catch (error) {
if (isRedirectError(error)) throw error;
console.error('Failed to allow all permissions:', error);
return { success: false, error: error instanceof Error ? error.message : '전체 허용 실패' };
}
}
/**
* 모든 권한 거부
*/
export async function denyAllPermissions(roleId: number): Promise<ApiResponse<{ count: number }>> {
try {
const { response, error } = await serverFetch(`${API_URL}/api/v1/roles/${roleId}/permissions/deny-all`, {
method: 'POST',
});
if (error) {
return { success: false, error: error.message };
}
if (!response) {
return { success: false, error: '전체 거부에 실패했습니다.' };
}
const result = await response.json();
if (!response.ok || !result.success) {
return { success: false, error: result.message || '전체 거부 실패' };
}
revalidatePath(`/settings/permissions/${roleId}`);
return { success: true, data: result.data };
} catch (error) {
if (isRedirectError(error)) throw error;
console.error('Failed to deny all permissions:', error);
return { success: false, error: error instanceof Error ? error.message : '전체 거부 실패' };
}
}
/**
* 기본 권한으로 초기화 (view만 허용)
*/
export async function resetPermissions(roleId: number): Promise<ApiResponse<{ count: number }>> {
try {
const { response, error } = await serverFetch(`${API_URL}/api/v1/roles/${roleId}/permissions/reset`, {
method: 'POST',
});
if (error) {
return { success: false, error: error.message };
}
if (!response) {
return { success: false, error: '권한 초기화에 실패했습니다.' };
}
const result = await response.json();
if (!response.ok || !result.success) {
return { success: false, error: result.message || '권한 초기화 실패' };
}
revalidatePath(`/settings/permissions/${roleId}`);
return { success: true, data: result.data };
} catch (error) {
if (isRedirectError(error)) throw error;
console.error('Failed to reset permissions:', error);
return { success: false, error: error instanceof Error ? error.message : '권한 초기화 실패' };
}
}