feat: 서브도메인 와일드카드 라우팅

- Traefik: *.startover.co.kr → crawl-manager (admin 제외)
- startover.co.kr(메인)은 기존 컨테이너 그대로 유지
- admin.startover.co.kr → 관리자
- {서브도메인}.startover.co.kr → DB domain_mappings에서 매핑된 사이트 HTML 서빙
- domainRouter에서 admin 도메인 명시적 제외

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
chpark
2026-03-27 14:42:17 +09:00
parent 2343c1903d
commit 40e3713f99
2 changed files with 23 additions and 8 deletions
+15 -5
View File
@@ -16,11 +16,21 @@ services:
- ./data/app:/app/data
labels:
- traefik.enable=true
- traefik.http.routers.crawl-manager.rule=Host(`admin.startover.co.kr`)
- traefik.http.routers.crawl-manager.entrypoints=websecure,web
- traefik.http.routers.crawl-manager.tls=true
- traefik.http.routers.crawl-manager.tls.certresolver=le
- traefik.http.services.crawl-manager.loadbalancer.server.port=3000
# 관리자 라우터
- traefik.http.routers.crawl-admin.rule=Host(`admin.startover.co.kr`)
- traefik.http.routers.crawl-admin.entrypoints=websecure,web
- traefik.http.routers.crawl-admin.tls=true
- traefik.http.routers.crawl-admin.tls.certresolver=le
- traefik.http.routers.crawl-admin.service=crawl-manager-svc
# 서브도메인 라우터 (*.startover.co.kr → 크롤링 데이터 서빙)
- traefik.http.routers.crawl-sub.rule=HostRegexp(`{subdomain:[a-z0-9-]+}.startover.co.kr`) && !Host(`admin.startover.co.kr`)
- traefik.http.routers.crawl-sub.entrypoints=websecure,web
- traefik.http.routers.crawl-sub.tls=true
- traefik.http.routers.crawl-sub.tls.certresolver=le
- traefik.http.routers.crawl-sub.service=crawl-manager-svc
- traefik.http.routers.crawl-sub.priority=1
# 서비스
- traefik.http.services.crawl-manager-svc.loadbalancer.server.port=3000
crawl-manager-db:
image: postgres:16-alpine
+8 -3
View File
@@ -31,8 +31,15 @@ router.get('/s/:slug', async (req, res) => {
// 도메인 기반 접근 (미들웨어로 사용)
async function domainRouter(req, res, next) {
const host = req.hostname;
// 관리자 도메인이면 무시 (관리자 라우터가 처리)
if (host === 'admin.startover.co.kr' || host === 'localhost') {
return next();
}
// 관리자 경로는 무시
if (req.path.startsWith('/admin') || req.path.startsWith('/api') || req.path.startsWith('/s/')) {
if (req.path.startsWith('/admin') || req.path.startsWith('/api') || req.path.startsWith('/s/') || req.path.startsWith('/login') || req.path.startsWith('/logout')) {
return next();
}
@@ -41,8 +48,6 @@ async function domainRouter(req, res, next) {
return next();
}
const host = req.hostname;
try {
const { rows } = await db.query(`
SELECT dm.site_id, dm.adsense_config_id