- executeServerAction 공통 유틸 도입으로 actions.ts 대폭 간소화 (50+개 파일) - sanitize 유틸 추가 (XSS 방지) - middleware CSP 헤더 추가 및 Open Redirect 방지 - 프록시 라우트 로깅 개발환경 한정으로 변경 - 프로덕션 불필요 console.log 제거 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
185 lines
4.7 KiB
TypeScript
185 lines
4.7 KiB
TypeScript
/**
|
|
* FCM 푸시 알림 공통 서버 액션
|
|
*
|
|
* 사용 예시:
|
|
* import { sendFcmNotification, sendApprovalNotification } from '@/lib/actions/fcm';
|
|
*
|
|
* // 기본 알림 발송
|
|
* const result = await sendFcmNotification({ title: '알림', body: '내용' });
|
|
*
|
|
* // 결재 알림 발송 (프리셋)
|
|
* const result = await sendApprovalNotification();
|
|
*/
|
|
|
|
'use server';
|
|
|
|
import { executeServerAction } from '@/lib/api/execute-server-action';
|
|
|
|
// ============================================
|
|
// 타입 정의
|
|
// ============================================
|
|
|
|
export interface FcmNotificationParams {
|
|
/** 알림 제목 (필수) */
|
|
title: string;
|
|
/** 알림 본문 (필수) */
|
|
body: string;
|
|
/** 특정 테넌트에게만 발송 */
|
|
tenant_id?: number;
|
|
/** 특정 사용자에게만 발송 */
|
|
user_id?: number;
|
|
/** 플랫폼 필터 (android, ios, web) */
|
|
platform?: 'android' | 'ios' | 'web';
|
|
/** 알림 채널 ID (Android) */
|
|
channel_id?: string;
|
|
/** 알림 타입 (앱에서 분기 처리용) */
|
|
type?: string;
|
|
/** 클릭 시 이동할 URL */
|
|
url?: string;
|
|
/** 알림 사운드 키 */
|
|
sound_key?: string;
|
|
}
|
|
|
|
export interface FcmResult {
|
|
success: boolean;
|
|
error?: string;
|
|
sentCount?: number;
|
|
__authError?: boolean;
|
|
}
|
|
|
|
// ============================================
|
|
// 기본 FCM 발송 함수
|
|
// ============================================
|
|
|
|
/**
|
|
* FCM 푸시 알림 발송
|
|
*/
|
|
export async function sendFcmNotification(
|
|
params: FcmNotificationParams
|
|
): Promise<FcmResult> {
|
|
interface FcmResponseData { success?: number }
|
|
const result = await executeServerAction<FcmResponseData>({
|
|
url: `${process.env.NEXT_PUBLIC_API_URL}/api/v1/admin/fcm/send`,
|
|
method: 'POST',
|
|
body: params,
|
|
errorMessage: 'FCM 발송에 실패했습니다.',
|
|
});
|
|
|
|
if (!result.success) {
|
|
return { success: false, error: result.error, __authError: result.__authError };
|
|
}
|
|
|
|
return {
|
|
success: true,
|
|
sentCount: result.data?.success || 0,
|
|
};
|
|
}
|
|
|
|
// ============================================
|
|
// 프리셋 함수들 (자주 사용하는 알림 타입)
|
|
// ============================================
|
|
|
|
/**
|
|
* 결재 알림 발송 (프리셋)
|
|
*/
|
|
export async function sendApprovalNotification(
|
|
customParams?: Partial<FcmNotificationParams>
|
|
): Promise<FcmResult> {
|
|
return sendFcmNotification({
|
|
title: '결재 알림',
|
|
body: '결재 문서가 완료되었습니다.',
|
|
type: 'approval',
|
|
channel_id: 'push_approval_request',
|
|
...customParams,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 작업지시 알림 발송 (프리셋)
|
|
*/
|
|
export async function sendWorkOrderNotification(
|
|
customParams?: Partial<FcmNotificationParams>
|
|
): Promise<FcmResult> {
|
|
return sendFcmNotification({
|
|
title: '작업지시 알림',
|
|
body: '새로운 작업지시가 있습니다.',
|
|
type: 'work_order',
|
|
channel_id: 'work_order',
|
|
...customParams,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 일반 공지 알림 발송 (프리셋)
|
|
*/
|
|
export async function sendNoticeNotification(
|
|
customParams?: Partial<FcmNotificationParams>
|
|
): Promise<FcmResult> {
|
|
return sendFcmNotification({
|
|
title: '공지사항',
|
|
body: '새로운 공지사항이 있습니다.',
|
|
type: 'notice',
|
|
channel_id: 'notice',
|
|
...customParams,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 신규업체 알림 발송 (프리셋)
|
|
*/
|
|
export async function sendNewClientNotification(
|
|
customParams?: Partial<FcmNotificationParams>
|
|
): Promise<FcmResult> {
|
|
return sendFcmNotification({
|
|
title: '신규업체 알림',
|
|
body: '새로운 업체가 등록되었습니다.',
|
|
type: 'new_client',
|
|
channel_id: 'push_vendor_register',
|
|
...customParams,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 수주 알림 발송 (프리셋)
|
|
*/
|
|
export async function sendSalesOrderNotification(
|
|
customParams?: Partial<FcmNotificationParams>
|
|
): Promise<FcmResult> {
|
|
return sendFcmNotification({
|
|
title: '수주 알림',
|
|
body: '수주가 완료되었습니다.',
|
|
type: 'sales_order',
|
|
channel_id: 'push_sales_order',
|
|
...customParams,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 계약완료 알림 발송 (프리셋)
|
|
*/
|
|
export async function sendContractCompletedNotification(
|
|
customParams?: Partial<FcmNotificationParams>
|
|
): Promise<FcmResult> {
|
|
return sendFcmNotification({
|
|
title: '계약 완료 알림',
|
|
body: '계약이 완료되었습니다.',
|
|
type: 'contract_completed',
|
|
channel_id: 'push_contract',
|
|
...customParams,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 발주완료 알림 발송 (프리셋)
|
|
*/
|
|
export async function sendPurchaseOrderNotification(
|
|
customParams?: Partial<FcmNotificationParams>
|
|
): Promise<FcmResult> {
|
|
return sendFcmNotification({
|
|
title: '발주 완료 알림',
|
|
body: '발주가 완료되었습니다.',
|
|
type: 'purchase_order',
|
|
channel_id: 'push_purchase_order',
|
|
...customParams,
|
|
});
|
|
} |