feat(auth-adapter,bff-kit,client-shell): 마지막 추출 배치

@yakcloud/auth-adapter: core 인터페이스(프레임워크 무관, ds-sdk type-only)
  + dev-bypass + next-auth 격리 계층(peer). exports 서브패스(./core·./dev-bypass).
@yakcloud/bff-kit: respond(web 표준)·serialize(Prisma-free)·ratelimit·session(AuthAdapter DI).
  auth-adapter=devDependency(type-only), 죽은 next peer 제거.
@yakcloud/client-shell: ServiceClientShell 셸 크롬(헤더/탭/pane/가드)+패널 슬롯+BackupPanel 스텁.
  (FileBrowser 는 Phase 1)

FIX-FIRST 반영: client-shell tsconfig jsx:react-jsx + @types/react(-dom),
  bff-kit/auth-adapter += @types/node. 7개 패키지 전부 tsc --noEmit 통과.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-25 13:12:34 +09:00
parent e34a325b52
commit 362e3168a7
37 changed files with 2583 additions and 14 deletions

View File

@ -0,0 +1,75 @@
// @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<D extends DeploymentInput>(dep: D): Omit<D, "env"> & { 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<string, unknown> | null) ?? null,
createdAt: svc.createdAt,
updatedAt: svc.updatedAt,
...(extra?.clusterName !== undefined ? { clusterName: extra.clusterName } : {}),
...(extra?.bindings !== undefined ? { bindings: extra.bindings } : {}),
};
}