summaryrefslogtreecommitdiff
path: root/lib/knox-api/mail/knox-mail.ts
diff options
context:
space:
mode:
Diffstat (limited to 'lib/knox-api/mail/knox-mail.ts')
-rw-r--r--lib/knox-api/mail/knox-mail.ts312
1 files changed, 312 insertions, 0 deletions
diff --git a/lib/knox-api/mail/knox-mail.ts b/lib/knox-api/mail/knox-mail.ts
new file mode 100644
index 00000000..48ba6c16
--- /dev/null
+++ b/lib/knox-api/mail/knox-mail.ts
@@ -0,0 +1,312 @@
+'use server';
+
+// Knox Mail API TypeScript Library for Next.js Server Actions
+// Based on Knox Mail API Guide v2.0
+
+export interface KnoxMailConfig {
+ baseUrl: string;
+ systemId: string;
+ userId: string;
+}
+
+// 공통 타입 정의
+export interface KnoxMailResponse<T = unknown> {
+ result: 'success' | 'error';
+ mailId?: string;
+ data?: T;
+ error?: {
+ code: string;
+ message: string;
+ };
+}
+
+// 발신인 정보
+export interface Sender {
+ emailAddress: string;
+}
+
+// 수신인 정보
+export interface Recipient {
+ emailAddress: string;
+ recipientType: 'TO' | 'CC' | 'BCC';
+}
+
+// 일반 메일 발신 요청
+export interface SendMailRequest {
+ subject: string;
+ contents: string;
+ contentType: 'TEXT' | 'HTML' | 'MIME';
+ docSecuType: 'PERSONAL' | 'OFFICIAL';
+ sender: Sender;
+ recipients: Recipient[];
+ attachments?: File[];
+ reservedTime?: string; // yyyy-MM-dd HH:mm 형식
+}
+
+// 대외비 메일 DRM 옵션
+export interface DrmOption {
+ validDays: string;
+ useCount: string;
+ canView: string;
+ canPrint: string;
+ notifyExternalUser: string;
+ notifyInternalUser: string;
+}
+
+// 대외비 메일 발신 요청
+export interface SendSecuMailRequest extends Omit<SendMailRequest, 'docSecuType'> {
+ docSecuType: 'CONFIDENTIAL' | 'CONFIDENTIAL_STRICT';
+ drmOption: DrmOption;
+}
+
+// 메일 발신 취소 요청
+export interface CancelMailRequest {
+ recipients: string[];
+}
+
+// 메일 별 수신상태 조회 요청
+export interface DeliveryStatusCountRequest {
+ mailIds: string[];
+}
+
+// 수신상태 카운트 응답
+export interface DeliveryStatusCount {
+ mailId: string;
+ totalStatusCount: number;
+ sendingCount: number;
+ openCount: number;
+ unopenCount: number;
+ etcCount: number;
+ sendCancelCount: number;
+ sendCancelReqCount: number;
+ sendBlockCount: number;
+ sendFailCount: number;
+ drmProcessCount: number;
+ drmFailCount: number;
+ reserveCount: number;
+ reserveCancelCount: number;
+ unknownCount: number;
+}
+
+// 수신인 별 수신상태 응답
+export interface RecipientDeliveryStatus {
+ name: string;
+ position: string;
+ enName: string;
+ enPosition: string;
+ company: string;
+ department: string;
+ enDepartment: string;
+ email: string;
+ userID: string;
+ recipientType: 'TO' | 'CC' | 'BCC';
+ updateTime: {
+ month: number;
+ year: number;
+ dayOfMonth: number;
+ hourOfDay: number;
+ minute: number;
+ second: number;
+ };
+ status: string;
+ needOpenNotify: boolean;
+ isDLAddr: boolean;
+}
+
+// 내부 유틸리티 함수들
+function getHeaders(systemId: string): Record<string, string> {
+ return {
+ 'System-ID': systemId,
+ 'Content-Type': 'application/json',
+ };
+}
+
+async function makeRequest<T>(
+ config: KnoxMailConfig,
+ endpoint: string,
+ method: 'GET' | 'POST',
+ data?: unknown,
+ isMultipart = false
+): Promise<KnoxMailResponse<T>> {
+ const url = `${config.baseUrl}${endpoint}?userId=${config.userId}`;
+
+ const headers = getHeaders(config.systemId);
+ if (isMultipart) {
+ delete headers['Content-Type']; // multipart/form-data는 브라우저가 자동으로 설정
+ }
+
+ try {
+ const response = await fetch(url, {
+ method,
+ headers,
+ body: data ? (isMultipart ? data as FormData : JSON.stringify(data)) : undefined,
+ });
+
+ if (!response.ok) {
+ const errorData = await response.json().catch(() => ({}));
+ return {
+ result: 'error',
+ error: {
+ code: response.status.toString(),
+ message: errorData.message || response.statusText,
+ },
+ };
+ }
+
+ const result = await response.json();
+ return {
+ result: 'success',
+ ...result,
+ };
+ } catch (error) {
+ return {
+ result: 'error',
+ error: {
+ code: 'NETWORK_ERROR',
+ message: error instanceof Error ? error.message : 'Unknown error',
+ },
+ };
+ }
+}
+
+function createFormData(mailData: Record<string, unknown>, attachments?: File[]): FormData {
+ const formData = new FormData();
+
+ // 메일 데이터를 JSON으로 추가
+ formData.append('mail', JSON.stringify(mailData));
+
+ // 첨부파일이 있으면 추가
+ if (attachments && attachments.length > 0) {
+ attachments.forEach(file => {
+ formData.append('attachments', file);
+ });
+ }
+
+ return formData;
+}
+
+// Next.js 서버 액션 함수들
+
+/**
+ * 일반 메일 발신 서버 액션
+ * @param config Knox Mail API 설정
+ * @param request 메일 발신 요청 데이터
+ * @returns 발신 결과 (mailId 포함)
+ */
+export async function sendMail(
+ config: KnoxMailConfig,
+ request: SendMailRequest
+): Promise<KnoxMailResponse<{ mailId: string }>> {
+ const { attachments, ...mailData } = request;
+
+ if (attachments && attachments.length > 0) {
+ const formData = createFormData(mailData, attachments);
+ return makeRequest<{ mailId: string }>(config, '/mail/api/v2.0/mails/send', 'POST', formData, true);
+ } else {
+ return makeRequest<{ mailId: string }>(config, '/mail/api/v2.0/mails/send', 'POST', mailData);
+ }
+}
+
+/**
+ * 대외비 메일 발신 서버 액션
+ * @param config Knox Mail API 설정
+ * @param request 대외비 메일 발신 요청 데이터
+ * @returns 발신 결과 (mailId 포함)
+ */
+export async function sendSecuMail(
+ config: KnoxMailConfig,
+ request: SendSecuMailRequest
+): Promise<KnoxMailResponse<{ mailId: string }>> {
+ const { attachments, ...mailData } = request;
+
+ if (attachments && attachments.length > 0) {
+ const formData = createFormData(mailData, attachments);
+ return makeRequest<{ mailId: string }>(config, '/mail/api/v2.0/mails/secu-send', 'POST', formData, true);
+ } else {
+ return makeRequest<{ mailId: string }>(config, '/mail/api/v2.0/mails/secu-send', 'POST', mailData);
+ }
+}
+
+/**
+ * 메일 발신 취소 서버 액션
+ * @param config Knox Mail API 설정
+ * @param mailId 취소할 메일 ID
+ * @param request 취소 요청 데이터 (수신인 목록)
+ * @returns 취소 결과
+ */
+export async function cancelMail(
+ config: KnoxMailConfig,
+ mailId: string,
+ request: CancelMailRequest
+): Promise<KnoxMailResponse> {
+ return makeRequest(config, `/mail/api/v2.0/mails/${mailId}/cancel`, 'POST', request);
+}
+
+/**
+ * 메일 별 수신상태 조회 서버 액션
+ * @param config Knox Mail API 설정
+ * @param request 조회할 메일 ID 목록
+ * @returns 메일별 수신상태 카운트 목록
+ */
+export async function getDeliveryStatusCount(
+ config: KnoxMailConfig,
+ request: DeliveryStatusCountRequest
+): Promise<KnoxMailResponse<DeliveryStatusCount[]>> {
+ return makeRequest<DeliveryStatusCount[]>(config, '/mail/api/v2.0/mails/deliverystatus/count', 'POST', request);
+}
+
+/**
+ * 수신인 별 수신상태 조회 서버 액션
+ * @param config Knox Mail API 설정
+ * @param mailId 조회할 메일 ID
+ * @returns 수신인별 수신상태 목록
+ */
+export async function getRecipientDeliveryStatus(
+ config: KnoxMailConfig,
+ mailId: string
+): Promise<KnoxMailResponse<RecipientDeliveryStatus[]>> {
+ return makeRequest<RecipientDeliveryStatus[]>(config, `/mail/api/v2.0/mails/${mailId}/deliverystatus`, 'GET');
+}
+
+// 편의 함수들
+export async function createKnoxMailConfig(
+ baseUrl: string,
+ systemId: string,
+ userId: string
+): Promise<KnoxMailConfig> {
+ return {
+ baseUrl,
+ systemId,
+ userId,
+ };
+}
+
+// 에러 코드 상수
+export const KNOX_MAIL_ERROR_CODES = {
+ ML1001: '필수값 중 입력되지 않은 값이 있습니다.',
+ ML1002: '사용자 정보가 존재하지 않습니다.',
+ ML1003: '요청하신 데이터가 존재하지 않습니다.',
+ ML1101: '본문 (content) 값이 입력되지 않았습니다.',
+ ML1102: '첨부 파일 처리 중 에러가 발생했습니다.',
+ ML1104: '일반 메일 발신 중 docSecuType 값이 유효하지 않습니다.',
+ ML1107: '대외비/극비 보안 옵션 값이 유효하지 않습니다.',
+ ML1108: '대외비/극비 메일 contentType 값이 유효하지 않습니다.',
+ ML1109: '대외비/극비 메일 docSecuType 값이 유효하지 않습니다.',
+ ML1110: '극비 메일일 경우에는 내부 수신인에게만 메일 발신이 가능합니다.',
+ ML1111: '대외비/극비 메일에서 허용되지 않는 첨부 파일의 확장자가 있습니다.',
+ ML1112: '메일 제목 크기가 300 bytes를 초과하였습니다.',
+ ML1114: '메일 발신 API에서 허용되는 content 크기를 초과하였습니다.',
+ ML1115: '일반 메일 contentType 값이 유효하지 않습니다.',
+ ML1117: '메일 발신 API에서 발신가능한 최대 수신인을 초과하였습니다.',
+ ML1118: '수신자의 수신 타입이 잘못 설정되었습니다.',
+ CO400: '잘못된 요청입니다.',
+} as const;
+
+// 타입 가드 함수들
+export function isKnoxMailError(response: KnoxMailResponse): response is KnoxMailResponse & { error: { code: string; message: string } } {
+ return response.result === 'error' && !!response.error;
+}
+
+export function isKnoxMailSuccess<T>(response: KnoxMailResponse<T>): response is KnoxMailResponse<T> & { result: 'success' } {
+ return response.result === 'success';
+}