summaryrefslogtreecommitdiff
path: root/lib/knox-api/realtime-notification/realtime-notification.ts
diff options
context:
space:
mode:
Diffstat (limited to 'lib/knox-api/realtime-notification/realtime-notification.ts')
-rw-r--r--lib/knox-api/realtime-notification/realtime-notification.ts333
1 files changed, 333 insertions, 0 deletions
diff --git a/lib/knox-api/realtime-notification/realtime-notification.ts b/lib/knox-api/realtime-notification/realtime-notification.ts
new file mode 100644
index 00000000..a26f5b55
--- /dev/null
+++ b/lib/knox-api/realtime-notification/realtime-notification.ts
@@ -0,0 +1,333 @@
+"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<typeof NotificationRequestSchema>
+export type NotificationColor = z.infer<typeof ColorSchema>
+export type NotificationText = z.infer<typeof TextSchema>
+export type NotificationLink = z.infer<typeof LinkSchema>
+export type NotificationVisual = z.infer<typeof VisualSchema>
+export type NotificationActions = z.infer<typeof ActionsSchema>
+
+// 응답 타입
+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<NotificationResponse> {
+ 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<NotificationResponse> {
+ 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<NotificationResponse> {
+ 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<NotificationResponse> {
+ 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<string | null> {
+ 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<NotificationResponse[]> {
+ 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
+}