From f6df890297c834377784317f4e7a62969c5b4a57 Mon Sep 17 00:00:00 2001 From: gahusb Date: Thu, 11 Jun 2026 01:24:07 +0900 Subject: [PATCH] =?UTF-8?q?feat(visibility):=20service=5Fsettings=20?= =?UTF-8?q?=EA=B8=B0=EB=B0=98=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=EC=88=A8?= =?UTF-8?q?=EA=B9=80=20=EA=B0=80=EB=93=9C=20+=20=EB=A0=88=EA=B1=B0?= =?UTF-8?q?=EC=8B=9C=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=EC=8B=9C=EB=93=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- app/api/admin/services/route.ts | 11 ++++--- lib/service-visibility.ts | 29 +++++++++++++++++++ .../2026-06-11-hide-legacy-services.sql | 10 +++++++ 3 files changed, 44 insertions(+), 6 deletions(-) create mode 100644 lib/service-visibility.ts create mode 100644 supabase/migrations/2026-06-11-hide-legacy-services.sql diff --git a/app/api/admin/services/route.ts b/app/api/admin/services/route.ts index ad05a16..6cf8b16 100644 --- a/app/api/admin/services/route.ts +++ b/app/api/admin/services/route.ts @@ -51,10 +51,9 @@ export async function PATCH(request: Request) { } const DEFAULT_SERVICES = [ - { id: 'saju', name: 'AI 사주 분석', description: '사주 입력 및 AI 해석 서비스', is_active: true, order_index: 1 }, - { id: 'lotto', name: '로또 번호 추천', description: '빅데이터 기반 로또 번호 분석', is_active: true, order_index: 2 }, - { id: 'stock', name: '주식 자동매매', description: '텔레그램 연동 자동매매 프로그램', is_active: true, order_index: 3 }, - { id: 'automation', name: '업무 자동화 RPA', description: '반복 업무 자동화 개발', is_active: true, order_index: 4 }, - { id: 'prompt', name: '프롬프트 엔지니어링', description: 'AI 프롬프트 설계 서비스', is_active: true, order_index: 5 }, - { id: 'freelance', name: '외주 개발', description: '맞춤형 소프트웨어 개발', is_active: true, order_index: 6 }, + { id: 'saju', name: 'AI 사주 분석', description: '사주 입력 및 AI 해석 (레거시)', is_active: false, order_index: 101 }, + { id: 'music', name: 'AI 음악 팩', description: '음악 가이드 패키지·샘플·스튜디오', is_active: false, order_index: 102 }, + { id: 'gyeol', name: 'CONTOUR 설문', description: '/gyeol PMF 설문', is_active: false, order_index: 103 }, + { id: 'packages', name: 'SaaS 제품 허브(구)', description: '구 /packages 페이지', is_active: false, order_index: 104 }, + { id: 'lotto', name: '로또 추천', description: '로또 번호 추천 노출', is_active: false, order_index: 105 }, ]; diff --git a/lib/service-visibility.ts b/lib/service-visibility.ts new file mode 100644 index 0000000..3b2d8f2 --- /dev/null +++ b/lib/service-visibility.ts @@ -0,0 +1,29 @@ +import { cookies } from 'next/headers'; +import { createAdminClient } from '@/lib/supabase/admin'; +import { verifyAdminTokenNode } from '@/lib/admin-auth'; + +/** 숨김 가능 서비스 id (service_settings.id와 일치) */ +export type HideableService = 'saju' | 'music' | 'gyeol' | 'packages' | 'lotto'; + +/** + * 서비스 노출 여부. admin_token 세션이면 항상 true. + * service_settings 조회 실패(테이블 미생성 등) 시 안전하게 숨김(false). + */ +export async function isServiceVisible(id: HideableService): Promise { + const cookieStore = await cookies(); + const token = cookieStore.get('admin_token')?.value; + if (token && verifyAdminTokenNode(token)) return true; + + try { + const supabase = createAdminClient(); + const { data, error } = await supabase + .from('service_settings') + .select('is_active') + .eq('id', id) + .maybeSingle(); + if (error || !data) return false; + return data.is_active === true; + } catch { + return false; + } +} diff --git a/supabase/migrations/2026-06-11-hide-legacy-services.sql b/supabase/migrations/2026-06-11-hide-legacy-services.sql new file mode 100644 index 0000000..a759a7b --- /dev/null +++ b/supabase/migrations/2026-06-11-hide-legacy-services.sql @@ -0,0 +1,10 @@ +-- 2026-06-11 리뉴얼: 레거시 서비스 숨김 토글 시드 +-- service_settings: 신규 id 추가 (이미 있으면 무시) — 멱등 +INSERT INTO service_settings (id, name, description, is_active, order_index) +VALUES + ('saju', 'AI 사주 분석', '사주 입력 및 AI 해석 (레거시)', false, 101), + ('music', 'AI 음악 팩', '음악 가이드 패키지·샘플·스튜디오', false, 102), + ('gyeol', 'CONTOUR 설문', '/gyeol PMF 설문', false, 103), + ('packages', 'SaaS 제품 허브(구)', '구 /packages 페이지', false, 104), + ('lotto', '로또 추천', '로또 번호 추천 노출', false, 105) +ON CONFLICT (id) DO NOTHING;