feat(db): clinic_registry 에 서브도메인 컬럼·유니크·예약어 제약 추가
서포터즈 사이트를 공용 도메인(webforai.kr)의 병원별 서브도메인에 두기로 했다. 수백 곳으로 늘 때 이름을 코드가 지으면 "뷰"·"연세"처럼 여러 병원이 쓰는 이름에서 충돌이 나므로, 이 표를 서브도메인의 단일 원본으로 둔다. 컬럼 - subdomain: 공용 도메인 앞에 붙는 라벨 (viewpl → viewpl.webforai.kr) - clinic_slug: 워커 --clinic 값. 빌드 폴더·Vercel 프로젝트 이름에 쓰여 서브도메인과 다를 수 있다 - subdomain_assigned_at: 배정 시각. 색인 전환 전에 주소를 바꾸는 편이 싸므로 판단에 쓴다 제약 - subdomain·clinic_slug 각각 대소문자 무시 유니크 - 형식: 소문자·숫자·붙임표, 2~40자, 붙임표로 시작·끝 금지, 연속 붙임표 금지 - 예약어 70여개 차단. 메일·인증(mx·dmarc·autodiscover)을 병원에 내주면 메일 수신과 도메인 소유 확인이 깨지고, 회수하는 시점에는 이미 색인된 주소가 깨진다 배정: 뷰 viewpl · 원진 wonjin · 오라클 oracle. 오라클은 이 레지스트리가 성형외과 73곳으로 만들어져 등록돼 있지 않아 함께 넣었다. 운영 DB 적용 중 확인한 것: 마이그레이션 이력에 10개 중 3개만 기록돼 있고 나머지는 SQL 편집기로 직접 만든 상태다. 그래서 db push 대신 이 파일만 따로 적용했다. 20260406_clinic_registry.sql 에 있는 verified_by·verified_at·is_active 는 실제 표에 없다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
f26155565c
commit
9d8360d634
65
supabase/migrations/20260914_clinic_subdomain.sql
Normal file
65
supabase/migrations/20260914_clinic_subdomain.sql
Normal file
@ -0,0 +1,65 @@
|
||||
-- ═══════════════════════════════════════════════════════════════
|
||||
-- 서포터즈 사이트 주소: 병원별 서브도메인
|
||||
-- ═══════════════════════════════════════════════════════════════
|
||||
-- 서포터즈 사이트는 공용 도메인(webforai.kr)의 병원별 서브도메인에 둔다.
|
||||
-- 병원마다 도메인을 사면 병원 수만큼 갱신 대상이 늘고, 한 건만 놓쳐도 그 병원 사이트가 멈춘다.
|
||||
-- 근거: docs/business/INFINITH_서포터즈_사이트주소_방식비교_2026-09-14.docx
|
||||
--
|
||||
-- 이 표가 서브도메인의 단일 원본이다. 워커·배포 스크립트가 이름을 매번 짓지 않고 여기서 읽는다.
|
||||
-- 이름을 코드가 지으면 "뷰"·"연세"·"그랜드"처럼 여러 병원이 쓰는 이름에서 충돌이 난다.
|
||||
|
||||
ALTER TABLE clinic_registry
|
||||
ADD COLUMN IF NOT EXISTS subdomain TEXT,
|
||||
-- 워커가 쓰는 병원 id(--clinic). 서브도메인과 다를 수 있어 따로 둔다.
|
||||
ADD COLUMN IF NOT EXISTS clinic_slug TEXT,
|
||||
-- 주소를 언제 붙였는지. 색인 전환 판단에 쓴다.
|
||||
ADD COLUMN IF NOT EXISTS subdomain_assigned_at TIMESTAMPTZ;
|
||||
|
||||
-- 한 서브도메인은 한 병원에만. 대소문자 구분 없이 막는다.
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS clinic_registry_subdomain_key
|
||||
ON clinic_registry (lower(subdomain))
|
||||
WHERE subdomain IS NOT NULL;
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS clinic_registry_clinic_slug_key
|
||||
ON clinic_registry (lower(clinic_slug))
|
||||
WHERE clinic_slug IS NOT NULL;
|
||||
|
||||
-- 형식: 소문자·숫자·붙임표만. 2~40자(1자는 실수일 가능성이 높고 짧은 이름을 병원에 내주게 된다). 붙임표로 시작하거나 끝날 수 없고 연속 붙임표도 막는다.
|
||||
-- 대문자·밑줄·점을 넣으면 DNS 레이블로 쓸 수 없거나 브라우저가 다르게 해석한다.
|
||||
ALTER TABLE clinic_registry
|
||||
DROP CONSTRAINT IF EXISTS clinic_registry_subdomain_format;
|
||||
ALTER TABLE clinic_registry
|
||||
ADD CONSTRAINT clinic_registry_subdomain_format
|
||||
CHECK (subdomain IS NULL OR (subdomain ~ '^[a-z0-9][a-z0-9-]{0,38}[a-z0-9]$' AND subdomain !~ '--'));
|
||||
|
||||
-- 예약어. 서비스가 쓰거나 쓸 수 있는 이름은 병원에 주지 않는다.
|
||||
-- 여기 있는 이름을 병원에 내주면 나중에 메일·API·관리 화면을 붙일 때 주소를 회수해야 한다.
|
||||
-- 회수하면 그 시점에 이미 색인된 주소가 깨진다.
|
||||
ALTER TABLE clinic_registry
|
||||
DROP CONSTRAINT IF EXISTS clinic_registry_subdomain_reserved;
|
||||
ALTER TABLE clinic_registry
|
||||
ADD CONSTRAINT clinic_registry_subdomain_reserved
|
||||
CHECK (subdomain IS NULL OR subdomain NOT IN (
|
||||
-- 웹·서비스 기본
|
||||
'www', 'web', 'app', 'apps', 'api', 'admin', 'dashboard', 'console', 'portal',
|
||||
'static', 'assets', 'cdn', 'img', 'images', 'media', 'files', 'download', 'downloads',
|
||||
-- 메일·인증 (잘못 내주면 메일 수신과 도메인 소유 확인이 깨진다)
|
||||
'mail', 'email', 'smtp', 'imap', 'pop', 'mx', 'ns', 'ns1', 'ns2', 'dns',
|
||||
'autodiscover', 'autoconfig', 'mta-sts', 'dmarc', 'spf', 'dkim',
|
||||
-- 환경·배포
|
||||
'dev', 'test', 'stage', 'staging', 'preview', 'demo', 'sandbox', 'beta', 'alpha', 'local',
|
||||
'status', 'health', 'metrics', 'log', 'logs', 'monitor',
|
||||
-- 제품·조직
|
||||
'infinith', 'webforai', 'supporters', 'clinic', 'clinics', 'hospital', 'report', 'reports',
|
||||
'discovery', 'plan', 'planner', 'blog', 'news', 'newsroom', 'help', 'support', 'docs',
|
||||
'about', 'contact', 'login', 'auth', 'account', 'billing', 'pay', 'payment',
|
||||
-- 오인 방지
|
||||
'en', 'ko', 'ja', 'zh', 'kr', 'go', 'or', 'co', 'ne', 're'
|
||||
));
|
||||
|
||||
COMMENT ON COLUMN clinic_registry.subdomain IS
|
||||
'서포터즈 사이트 주소의 서브도메인 라벨. 공용 도메인 앞에 붙는다(예: viewpl → viewpl.webforai.kr). 소문자·숫자·붙임표만.';
|
||||
COMMENT ON COLUMN clinic_registry.clinic_slug IS
|
||||
'워커 --clinic 에 넘기는 병원 id. 빌드 폴더·Vercel 프로젝트 이름에 쓰인다. 서브도메인과 다를 수 있다.';
|
||||
COMMENT ON COLUMN clinic_registry.subdomain_assigned_at IS
|
||||
'서브도메인을 배정한 시각. 색인 허용 전환 전에 주소를 바꾸는 편이 싸므로 그 판단에 쓴다.';
|
||||
Loading…
Reference in New Issue
Block a user