From 146836f56b3b2c7d9943dcd0fd777f6ad6fef2bd Mon Sep 17 00:00:00 2001 From: gahusb Date: Fri, 12 Jun 2026 01:29:36 +0900 Subject: [PATCH] =?UTF-8?q?fix(portal):=20=ED=86=A0=ED=81=B0=20DEFAULT?= =?UTF-8?q?=C2=B7UNIQUE=20=EC=9D=B8=EB=8D=B1=EC=8A=A4=20=EB=B3=B4=EC=9E=A5?= =?UTF-8?q?=20+=20=EB=A9=94=EC=9D=BC=20=EC=A0=9C=EB=AA=A9=20=EC=9D=B4?= =?UTF-8?q?=EC=8A=A4=EC=BC=80=EC=9D=B4=ED=94=84=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - contact_requests.public_token: 인라인 UNIQUE 제거, 백필 UPDATE 직후 SET DEFAULT + CREATE UNIQUE INDEX IF NOT EXISTS 패턴으로 교체 (라이브 DB 멱등성 보장) - quotes.public_token: ADD COLUMN IF NOT EXISTS + SET DEFAULT + 백필 UPDATE + CREATE UNIQUE INDEX IF NOT EXISTS 4줄 구조로 교체 (인라인 UNIQUE NO-OP 문제 해소) - sendQuoteSentEmail / sendQuoteDecisionEmail subject에서 escapeHtml() 제거 — 메일 제목은 평문, HTML 본문 이스케이프는 유지 Co-Authored-By: Claude Sonnet 4.6 --- lib/request-emails.ts | 4 ++-- supabase/migrations/2026-06-12-client-portal.sql | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/request-emails.ts b/lib/request-emails.ts index 36556d7..6ae6745 100644 --- a/lib/request-emails.ts +++ b/lib/request-emails.ts @@ -37,7 +37,7 @@ export async function sendQuoteSentEmail(opts: { await resend().emails.send({ from: FROM, to: [clientEmail], - subject: `[쟁승메이드] 견적서가 도착했습니다 — ${escapeHtml(quoteTitle)}`, + subject: `[쟁승메이드] 견적서가 도착했습니다 — ${quoteTitle}`, html: `

견적서를 보내드립니다

${escapeHtml(clientName)}님, 요청하신 건의 견적서가 준비되었습니다.

@@ -57,7 +57,7 @@ export async function sendQuoteDecisionEmail(opts: { await resend().emails.send({ from: FROM, to: [ADMIN_EMAIL], - subject: `[쟁승메이드] 견적 ${label} — ${escapeHtml(quoteTitle)}`, + subject: `[쟁승메이드] 견적 ${label} — ${quoteTitle}`, html: `

고객이 견적을 ${label}했습니다

견적: ${escapeHtml(quoteTitle)} / 고객: ${escapeHtml(clientName)}

diff --git a/supabase/migrations/2026-06-12-client-portal.sql b/supabase/migrations/2026-06-12-client-portal.sql index 6f9edc8..ee006c0 100644 --- a/supabase/migrations/2026-06-12-client-portal.sql +++ b/supabase/migrations/2026-06-12-client-portal.sql @@ -1,6 +1,6 @@ -- 2026-06-12 Phase 3: 외주 고객 포털 -- (1) contact_requests 확장 -ALTER TABLE contact_requests ADD COLUMN IF NOT EXISTS public_token text UNIQUE; +ALTER TABLE contact_requests ADD COLUMN IF NOT EXISTS public_token text; ALTER TABLE contact_requests ADD COLUMN IF NOT EXISTS project_type text; ALTER TABLE contact_requests ADD COLUMN IF NOT EXISTS budget text; ALTER TABLE contact_requests ADD COLUMN IF NOT EXISTS timeline text; @@ -9,6 +9,9 @@ ALTER TABLE contact_requests ADD COLUMN IF NOT EXISTS updated_at timestamptz DEF -- 기존 행 토큰 백필 (멱등 — NULL만) UPDATE contact_requests SET public_token = gen_random_uuid()::text WHERE public_token IS NULL; +ALTER TABLE contact_requests ALTER COLUMN public_token SET DEFAULT gen_random_uuid()::text; +CREATE UNIQUE INDEX IF NOT EXISTS idx_contact_requests_public_token_unique ON contact_requests (public_token); + -- 상태 머신 CHECK (레거시 3종 포함 8종) ALTER TABLE contact_requests DROP CONSTRAINT IF EXISTS contact_requests_status_check; ALTER TABLE contact_requests ADD CONSTRAINT contact_requests_status_check @@ -19,8 +22,7 @@ ALTER TABLE quotes ADD COLUMN IF NOT EXISTS contact_request_id uuid REFERENCES c CREATE INDEX IF NOT EXISTS idx_quotes_contact_request ON quotes (contact_request_id); -- (3) quotes.public_token 컬럼 보장 (live DB에 직접 생성된 경우 대비) -ALTER TABLE quotes ADD COLUMN IF NOT EXISTS public_token text UNIQUE; - --- quotes.public_token 기본값 보장 +ALTER TABLE quotes ADD COLUMN IF NOT EXISTS public_token text; ALTER TABLE quotes ALTER COLUMN public_token SET DEFAULT gen_random_uuid()::text; UPDATE quotes SET public_token = gen_random_uuid()::text WHERE public_token IS NULL; +CREATE UNIQUE INDEX IF NOT EXISTS idx_quotes_public_token_unique ON quotes (public_token);