import 'server-only'; import { getSessionAccessToken } from '@/lib/auth/dal'; import { BackendRequestError, backendFetch } from '@/lib/http/backend-fetch'; import type { AdminNotification } from '@/lib/domain/notification'; /** * 관리자 알림 Repository. * * ``` * GET /api/v1/common/ntcn/list 로그인한 사용자의 알림 전체(등록일시 최신순) * ``` * * 관리자 전용(`mngr`)이 아니라 공통 경로다 — 토큰의 주인이 누구인지로 대상이 갈리고, * 인가는 "로그인했으면 통과"라 관리자 토큰으로도 조회된다(SecurityConfig 확인함). * * 지금 적재되는 알림은 「학습자가 1:1문의를 등록했다」 하나다(`CmmBbsServiceImpl`이 등록 시 * 관리자 전원에게 한 건씩 넣는다). * * ⚠️ 읽음 처리(`PUT /{ntcnSn}`)는 붙이지 않았다 — 백엔드 컨트롤러가 경로변수 `ntcnSn`도 * `readYn`도 VO에 담지 않아 어느 행도 갱신되지 않는다(보고함). 고쳐지면 여기에 함수를 더한다. * * 페이징이 없다. 목록 SQL에 LIMIT이 없어 전체가 온다 — 알림이 쌓이면 백엔드에 상한을 * 요청해야 한다. */ const NOTIFICATION_LIST_PATH = '/api/v1/common/ntcn/list'; function isRecord(value: unknown): value is Record { return value !== null && typeof value === 'object'; } function readString(source: Record, key: string): string { const value = source[key]; return typeof value === 'string' ? value : ''; } function toNotification(raw: unknown): AdminNotification | null { if (!isRecord(raw)) { return null; } // `ntcnSn`은 숫자로 올 수도 있다 — 키로만 쓰므로 문자열로 맞춘다. const rawId = raw.ntcnSn; const id = typeof rawId === 'string' ? rawId : typeof rawId === 'number' ? String(rawId) : ''; if (!id) { return null; } return { id, title: readString(raw, 'ntcnSj'), content: readString(raw, 'ntcnCn'), isRead: readString(raw, 'readYn') === 'Y', }; } /** * 로그인한 관리자의 알림을 가져온다. * * **실패해도 던지지 않는다.** 헤더는 모든 화면에 붙어 있어, 알림 조회가 안 된다고 화면 * 전체가 죽으면 안 된다. 못 가져오면 빈 목록으로 둔다. */ export async function fetchNotifications(): Promise { const accessToken = await getSessionAccessToken(); if (!accessToken) { return []; } try { const result = await backendFetch(NOTIFICATION_LIST_PATH, { method: 'GET', accessToken, cache: 'no-store', canHaveNullData: true, }); if (!result.ok) { throw new BackendRequestError(result); } return Array.isArray(result.data) ? result.data .map(toNotification) .filter((item): item is AdminNotification => item !== null) : []; } catch { return []; } }