"use server" import { z } from "zod" // 타입 정의 const ColorSchema = z.object({ r: z.number().min(0).max(255), g: z.number().min(0).max(255), b: z.number().min(0).max(255), }) const TextSchema = z.object({ content: z.string().min(1), contentglobal: z.string().optional(), size: z.number().min(0).max(1024).optional(), pos: z.number().min(1).max(3), exColorVO: ColorSchema.optional(), style: z.string().optional(), }) const LinkSchema = z.object({ rel: z.string().max(10).optional(), href: z.string().url().optional(), args: z.string().optional(), }) const VisualSchema = z.object({ template: z.string().max(10).optional(), skin: z.string().max(10).optional(), global: z.enum(["Y", "N"]).optional(), logo: z.enum(["Y", "N"]).optional(), logourl: z.string().url().optional(), exTextVOList: z.array(TextSchema), }) const ActionsSchema = z.object({ popup: z.enum(["Y", "N"]).optional(), snooze: z.enum(["Y", "N"]).optional(), clickable: z.enum(["Y", "N"]).optional(), hint: z.string().optional(), exLinksVOList: z.array(LinkSchema).optional(), }) const NotificationRequestSchema = z.object({ targetAddress: z.array(z.string().email()).max(100), ntype: z.enum(["NEW", "RECALL"]), messageid: z.string().optional(), systemname: z.string().min(1), from: z.string().min(1), fromGlobal: z.string().min(1), exVisualVO: VisualSchema, exActionsVO: ActionsSchema, }) export type NotificationRequest = z.infer export type NotificationColor = z.infer export type NotificationText = z.infer export type NotificationLink = z.infer export type NotificationVisual = z.infer export type NotificationActions = z.infer // 응답 타입 export interface NotificationResponse { result: string errorCode: string | null message: string } // 에러 타입 export interface NotificationError { httpStatus: number errorCode: string message: string } // 환경 변수 검증 const getApiConfig = () => { const baseUrl = process.env.KNOX_API_BASE_URL const systemId = process.env.KNOX_SYSTEM_ID if (!baseUrl || !systemId) { throw new Error("Knox API configuration missing: KNOX_API_BASE_URL and KNOX_SYSTEM_ID are required") } return { baseUrl, systemId } } /** * Knox Suite 실시간 토스트 알림 전송 */ export async function sendNotification( request: NotificationRequest ): Promise { try { // 요청 데이터 검증 const validatedRequest = NotificationRequestSchema.parse(request) const { baseUrl, systemId } = getApiConfig() const response = await fetch(`${baseUrl}/notification/api/v2.0/sendnotification`, { method: "POST", headers: { "Content-Type": "application/json", "System-ID": systemId, "hint": validatedRequest.exActionsVO.hint || "multibrowser", }, body: JSON.stringify(validatedRequest), }) if (!response.ok) { const errorData = await response.json() throw new Error(`API Error: ${errorData.message || response.statusText}`) } const result: NotificationResponse = await response.json() return result } catch (error) { console.error("Knox notification error:", error) throw error } } /** * 간단한 토스트 알림 전송 (기본 설정 사용) */ export async function sendSimpleNotification( targetEmails: string[], title: string, titleGlobal: string, systemName: string, link?: string ): Promise { const notification: NotificationRequest = { targetAddress: targetEmails, ntype: "NEW", systemname: systemName, from: title, fromGlobal: titleGlobal, exVisualVO: { template: "content", skin: "White", global: "N", logo: "Y", logourl: "", exTextVOList: [ { content: title, contentglobal: titleGlobal, size: 14, pos: 1, exColorVO: { r: 0, g: 0, b: 0, }, style: "BOLD", }, ], }, exActionsVO: { popup: "Y", clickable: "Y", hint: "multibrowser", exLinksVOList: link ? [ { rel: "popup", href: link, args: "", }, ] : [], }, } return await sendNotification(notification) } /** * 알림 취소 (RECALL) */ export async function recallNotification( targetEmails: string[], systemName: string, messageId: string ): Promise { const notification: NotificationRequest = { targetAddress: targetEmails, ntype: "RECALL", messageid: messageId, systemname: systemName, from: "알림 취소", fromGlobal: "Notification Recall", exVisualVO: { template: "content", skin: "White", global: "N", logo: "Y", logourl: "", exTextVOList: [ { content: "알림이 취소되었습니다", contentglobal: "Notification has been recalled", size: 14, pos: 1, exColorVO: { r: 255, g: 0, b: 0, }, style: "BOLD", }, ], }, exActionsVO: { popup: "N", clickable: "N", hint: "multibrowser", }, } return await sendNotification(notification) } /** * 사용자 정의 스타일 토스트 알림 */ export async function sendCustomNotification( targetEmails: string[], systemName: string, title: string, titleGlobal: string, options: { template?: string skin?: string logoUrl?: string textColor?: NotificationColor textSize?: number textStyle?: string link?: string linkRel?: string } = {} ): Promise { const notification: NotificationRequest = { targetAddress: targetEmails, ntype: "NEW", systemname: systemName, from: title, fromGlobal: titleGlobal, exVisualVO: { template: options.template || "content", skin: options.skin || "White", global: "N", logo: options.logoUrl ? "Y" : "Y", logourl: options.logoUrl || "", exTextVOList: [ { content: title, contentglobal: titleGlobal, size: options.textSize || 14, pos: 1, exColorVO: options.textColor || { r: 0, g: 0, b: 0, }, style: options.textStyle || "BOLD", }, ], }, exActionsVO: { popup: options.link ? "Y" : "N", clickable: options.link ? "Y" : "N", hint: "multibrowser", exLinksVOList: options.link ? [ { rel: options.linkRel || "popup", href: options.link, args: "", }, ] : [], }, } return await sendNotification(notification) } /** * 헬퍼 함수: 알림 ID 추출 */ export async function extractNotificationId(response: NotificationResponse): Promise { if (response.result === "OK" && response.message) { const match = response.message.match(/uid\s*:\s*([a-f0-9]+)/i) return match ? match[1] : null } return null } /** * 헬퍼 함수: 여러 사용자에게 배치 알림 전송 */ export async function sendBatchNotifications( notifications: Array<{ targetEmails: string[] title: string titleGlobal: string systemName: string link?: string }> ): Promise { const results: NotificationResponse[] = [] for (const notification of notifications) { try { const result = await sendSimpleNotification( notification.targetEmails, notification.title, notification.titleGlobal, notification.systemName, notification.link ) results.push(result) } catch (error) { console.error("Batch notification error:", error) results.push({ result: "ERROR", errorCode: "BATCH_ERROR", message: error instanceof Error ? error.message : "Unknown error", }) } } return results }