// @yakcloud/bff-kit — serialize // DTO 마스킹·필터. Prisma-free: 전체 모델 대신 필요한 필드만 가진 구조적 입력 타입을 받는다. // connSecretRef(내부 Secret 이름) 및 시크릿 env 값은 절대 출력에 노출하지 않는다(마스킹 계약). // yakconsole api/serialize.ts 하강. import type { EnvVarDTO } from "@yakcloud/ds-sdk"; // ── 구조적 입력 타입(Repo 결과에서 핸들러가 구성; Prisma 모델도 구조적으로 호환) ── // 날짜는 Repo 가 Date 로 주든 ISO string 으로 주든 그대로 통과시킨다(직렬화는 Response.json 소관). type DateLike = Date | string; export interface DeploymentInput { env: unknown; // Deployment.env(Json) — 배열이 아니면 [] [k: string]: unknown; // 나머지 필드는 그대로 스프레드(구조적) } export interface ServiceInput { id: string; ownerId: string; clusterId: string; name: string; type: string; mode: string; status: string; statusMessage: string | null; size: string; storageGb: number; persist: boolean; connSecretRef?: string | null; // ⛔ 출력에서 제외(마스킹) connInfo: unknown; // 비밀 제외 접속 메타 JSON createdAt: DateLike; updatedAt: DateLike; } export interface PublicServiceExtra { clusterName?: string; bindings?: { app: string; alias: string }[]; } // Deployment.env(Json) → EnvVar[]. 배열이 아니면 빈 배열. export function envOf(dep: DeploymentInput): EnvVarDTO[] { const raw = dep.env; if (!Array.isArray(raw)) return []; return raw as EnvVarDTO[]; } // API 응답용: 시크릿 환경변수의 값을 마스킹(빈 문자열)해 클라이언트로 노출하지 않는다. export function publicDeployment(dep: D): Omit & { env: EnvVarDTO[] } { const env = envOf(dep).map((e) => (e.secret ? { key: e.key, value: "", secret: true } : e)); return { ...dep, env }; } // 외부 서비스 응답 DTO — connSecretRef 미노출, connInfo(host/port/db/bucket)만. // 크리덴셜은 애초에 포털에 저장되지 않으므로 여기서 마스킹할 비밀이 없다(설계 C). export function publicService(svc: ServiceInput, extra?: PublicServiceExtra) { return { id: svc.id, ownerId: svc.ownerId, clusterId: svc.clusterId, name: svc.name, type: svc.type, mode: svc.mode, status: svc.status, statusMessage: svc.statusMessage, size: svc.size, storageGb: svc.storageGb, persist: svc.persist, connInfo: (svc.connInfo as Record | null) ?? null, createdAt: svc.createdAt, updatedAt: svc.updatedAt, ...(extra?.clusterName !== undefined ? { clusterName: extra.clusterName } : {}), ...(extra?.bindings !== undefined ? { bindings: extra.bindings } : {}), }; }