/** * 테넌트 서브도메인 헬퍼. * 메인 사이트(solution, www, admin 등 예약어) 와 베이스 도메인은 null 을 리턴해서 * TenantGuard 가 체크를 스킵하게 한다. * * 백엔드 SubdomainResolverFilter.extractSubdomain() 와 동일한 규칙을 따라야 함: * - bare localhost / IP / 베이스 도메인 → null (메타) * - {sub}.localhost (dev) → 첫 파트 (예약어 제외) * - {sub}.invyone.com (운영) → 첫 파트 (예약어 제외) * * 백엔드 provisioning 의 RESERVED_SUBDOMAINS 와 같은 값을 유지할 것. */ const RESERVED_MAIN = new Set([ "solution", "www", "admin", "api", "app", "static", "assets", "main", "mail", "blog", "test", "staging", "prod", "console", ]); const IPV4 = /^\d{1,3}(\.\d{1,3}){3}$/; export function extractTenantSubdomain(host: string): string | null { if (!host) return null; const cleanHost = host.split(":")[0].toLowerCase(); if (!cleanHost) return null; if (cleanHost === "localhost") return null; if (IPV4.test(cleanHost)) return null; const parts = cleanHost.split("."); // 2파트 — "{sub}.localhost" 만 허용 (dev). invyone.com 같은 베이스 도메인은 null. if (parts.length === 2) { if (parts[1] !== "localhost") return null; const first = parts[0]; if (!first) return null; if (RESERVED_MAIN.has(first)) return null; return first; } // 3파트 이상 (운영 *.invyone.com 등) — 첫 파트가 서브도메인 if (parts.length < 3) return null; const first = parts[0]; if (!first) return null; if (RESERVED_MAIN.has(first)) return null; return first; }