summaryrefslogtreecommitdiff
path: root/lib/nonsap/action-utils.ts
blob: 731df1f54f54736afc92690901eb5a24883eff83 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
import { getServerSession } from "next-auth/next";
import { authOptions } from "@/app/api/auth/[...nextauth]/route";
import { verifyNonsapPermission, AuthAction } from "./auth-service";
import 'server-only'; // 클라이언트 번들에 포함되는 걸 방지하기 위함

export type ActionResponse<T> = {
  success: boolean;
  data?: T;
  error?: string;
  message?: string;
};

/**
 * 권한 체크와 세션 확인을 수행하는 고차 함수 (Action Factory)
 * 
 * 이 유틸리티는 Server Action을 감싸서 다음 기능을 자동으로 수행합니다:
 * 1. 사용자 세션 확인 (로그인 여부)
 * 2. NONSAP 권한 검증 (verifyNonsapPermission 사용)
 * 3. 에러 핸들링 (try-catch)
 * 
 * @param requiredActions - 해당 액션을 수행하기 위해 필요한 권한 목록 (예: ['ADD'], ['SEARCH', 'DOWN'])
 * @param action - 실제 비즈니스 로직을 수행하는 함수. 
 *                 첫 번째 인자로 input을, 두 번째 인자로 검증된 userId를 받습니다.
 * @param options - (Optional) 추가 옵션
 * @param options.url - 권한 체크를 위한 명시적 URL (생략 시 x-pathname 헤더 자동 감지). 
 *                      특정 화면의 권한을 강제로 적용하고 싶을 때 사용합니다.
 * @param options.logic - 여러 권한이 필요할 때의 논리 연산 ('AND' | 'OR'). 기본값은 'AND'.
 * @returns 권한 검증 로직이 포함된 새로운 비동기 함수
 */
export function withNonsapAuth<TInput, TOutput>(
  requiredActions: AuthAction[],
  action: (input: TInput, userId: number) => Promise<ActionResponse<TOutput>>,
  options?: {
    url?: string;
    logic?: 'AND' | 'OR';
  }
) {
  return async (input: TInput): Promise<ActionResponse<TOutput>> => {
    try {
      // 1. 세션 확인
      const session = await getServerSession(authOptions);
      const userId = Number(session?.user?.id);

      if (!userId || isNaN(userId)) {
        return { 
          success: false, 
          error: "로그인이 필요하거나 세션이 만료되었습니다." 
        };
      }

      // 2. 권한 검증
      // options.url이 있으면 그것을 사용하고, 없으면 headers()를 통해 자동 감지
      const authResult = await verifyNonsapPermission(
        userId, 
        requiredActions, 
        options?.url, 
        { logic: options?.logic }
      );

      if (!authResult.authorized) {
        // 환경변수 체크: CHECK_NONSAP_AUTH_HOC가 'true'일 때만 권한 체크를 강제함
        // 그 외의 경우(설정되지 않았거나 false 등)에는 경고만 남기고 실행 허용
        if (process.env.CHECK_NONSAP_AUTH_HOC === 'true') {
          return { 
            success: false, 
            error: authResult.message || "이 작업을 수행할 권한이 없습니다." 
          };
        } else {
          console.warn(`[withNonsapAuth] 권한 체크 실패했으나 실행 허용됨 (CHECK_NONSAP_AUTH_HOC != true). User: ${userId}, Actions: ${requiredActions.join(',')}`);
        }
      }

      // 3. 실제 액션 실행
      // userId를 주입하여 액션 내부에서 다시 세션을 조회할 필요가 없도록 합니다.
      return await action(input, userId);

    } catch (error) {
      console.error("[withNonsapAuth] Server Action Error:", error);
      return { 
        success: false, 
        error: "서버 내부 오류가 발생했습니다." 
      };
    }
  };
}