macbook 에서 리눅스로 이동
This commit is contained in:
31
docs/api-spec.md
Normal file
31
docs/api-spec.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# API 계약(1차)
|
||||
|
||||
## 공개 API
|
||||
- `GET /api/public/route-by-host`
|
||||
- Query: `host`, `path`
|
||||
- Response:
|
||||
- `status`: `ok | not_found`
|
||||
- `page`: block json
|
||||
- `campaignId`, `pageId`, `routeId`
|
||||
- `POST /api/public/lead`
|
||||
- Body: `campaignId`, `pageId`, `routeId`, `payload`, `sourceMeta`
|
||||
|
||||
## 관리자 API
|
||||
- 인증: `POST /api/admin/auth/login` (현재는 역할 기반 데모 토큰 반환)
|
||||
- 캠페인: `GET /api/admin/campaigns`, `POST /api/admin/campaigns`, `PUT /api/admin/campaigns/:id`
|
||||
- 페이지: `GET /api/admin/pages`, `GET /api/admin/pages/:id`, `POST /api/admin/pages`, `PUT /api/admin/pages/:id`
|
||||
- 라우트: `GET /api/admin/pages/:id/routes`, `POST /api/admin/pages/:id/routes`
|
||||
- 조건: `GET /api/admin/pages/:id/conditions`, `POST /api/admin/pages/:id/conditions`, `PATCH /api/admin/pages/:id/conditions/:conditionId`, `DELETE /api/admin/pages/:id/conditions/:conditionId`
|
||||
- 리드: `GET /api/admin/leads`
|
||||
- 사용자: `GET /api/admin/users`, `POST /api/admin/users`, `PUT /api/admin/users/:id/role`
|
||||
|
||||
## 역할/권한(1차)
|
||||
- 헤더: `x-user-role`
|
||||
- 값: `ADMIN`, `LEAD_MANAGER`
|
||||
- `ADMIN`: 관리자 API 전체 사용
|
||||
- `LEAD_MANAGER`: 리드 조회(`/api/admin/leads`)만 사용
|
||||
|
||||
## 공통 에러
|
||||
- 401 `{ error: "unauthorized" }`
|
||||
- 403 `{ error: "forbidden" }`
|
||||
- 422 `{ error: "validation", details: [...] }`
|
||||
137
docs/database.md
Normal file
137
docs/database.md
Normal file
@@ -0,0 +1,137 @@
|
||||
# DB 설계(1차)
|
||||
|
||||
## 핵심 엔티티
|
||||
- Campaign(종목), LandingPage, LandingRoute, RouteCondition, Lead, User
|
||||
- 역할: ADMIN / LEAD_MANAGER
|
||||
|
||||
## 엔티티 관계
|
||||
- Campaign 1:N LandingPage
|
||||
- Campaign 1:N LandingRoute
|
||||
- LandingRoute 1:N RouteCondition
|
||||
- LandingRoute N:1 LandingPage(기본 페이지)
|
||||
- RouteCondition N:1 LandingPage(조건 매칭 페이지)
|
||||
- LandingPage 1:N Lead
|
||||
- Campaign 1:N Lead
|
||||
|
||||
## 권한 정책
|
||||
- ADMIN: 모든 관리자 기능
|
||||
- LEAD_MANAGER: 리드 조회만 가능
|
||||
|
||||
## Prisma 스키마 (SQLite 기준)
|
||||
```prisma
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "sqlite"
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
enum UserRole {
|
||||
ADMIN
|
||||
LEAD_MANAGER
|
||||
}
|
||||
|
||||
model Campaign {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
description String?
|
||||
isActive Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
landingPages LandingPage[]
|
||||
routes LandingRoute[]
|
||||
leads Lead[]
|
||||
}
|
||||
|
||||
model LandingPage {
|
||||
id String @id @default(cuid())
|
||||
campaignId String
|
||||
campaign Campaign @relation(fields: [campaignId], references: [id], onDelete: Cascade)
|
||||
name String
|
||||
slug String?
|
||||
blocks Json
|
||||
isDefault Boolean @default(false)
|
||||
isActive Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
defaultRoutes LandingRoute[] @relation("RouteDefaultPage")
|
||||
conditions RouteCondition[]
|
||||
leads Lead[]
|
||||
|
||||
@@index([campaignId, isActive])
|
||||
}
|
||||
|
||||
model LandingRoute {
|
||||
id String @id @default(cuid())
|
||||
campaignId String
|
||||
campaign Campaign @relation(fields: [campaignId], references: [id], onDelete: Cascade)
|
||||
host String
|
||||
path String
|
||||
defaultPageId String
|
||||
defaultPage LandingPage @relation("RouteDefaultPage", fields: [defaultPageId], references: [id], onDelete: Restrict)
|
||||
isActive Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
conditions RouteCondition[]
|
||||
leads Lead[]
|
||||
|
||||
@@unique([host, path])
|
||||
}
|
||||
|
||||
model RouteCondition {
|
||||
id String @id @default(cuid())
|
||||
routeId String
|
||||
route LandingRoute @relation(fields: [routeId], references: [id], onDelete: Cascade)
|
||||
pageId String
|
||||
page LandingPage @relation(fields: [pageId], references: [id], onDelete: Restrict)
|
||||
label String
|
||||
priority Int @default(0)
|
||||
isActive Boolean @default(true)
|
||||
|
||||
startDate DateTime?
|
||||
endDate DateTime?
|
||||
timezone String @default("Asia/Seoul")
|
||||
weekMask String @default("0000000")
|
||||
startMinute Int?
|
||||
endMinute Int?
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
model Lead {
|
||||
id String @id @default(cuid())
|
||||
campaignId String
|
||||
campaign Campaign @relation(fields: [campaignId], references: [id], onDelete: Cascade)
|
||||
pageId String
|
||||
page LandingPage @relation(fields: [pageId], references: [id], onDelete: Restrict)
|
||||
routeId String?
|
||||
route LandingRoute? @relation(fields: [routeId], references: [id], onDelete: SetNull)
|
||||
payload Json
|
||||
sourceMeta Json?
|
||||
submittedAt DateTime @default(now())
|
||||
|
||||
@@index([campaignId, submittedAt])
|
||||
}
|
||||
|
||||
model User {
|
||||
id String @id @default(cuid())
|
||||
email String @unique
|
||||
name String?
|
||||
password String
|
||||
role UserRole @default(ADMIN)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### 참고
|
||||
- `defaultRoutes`는 `@relation("RouteDefaultPage")`로 라우트 기본 페이지 역참조입니다.
|
||||
- 동일 엔티티에서 기본 라우트를 찾을 때는 `LandingRoute.defaultPageId`가 FK입니다.
|
||||
- PostgreSQL 전환 시 datasource `provider`만 교체하지 않고 nullable/시간대 처리 포함 마이그레이션 검증이 필요하다.
|
||||
29
docs/routing-conditions.md
Normal file
29
docs/routing-conditions.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# 라우팅/조건 매칭(1차: exact)
|
||||
|
||||
## 라우팅 기본값
|
||||
- `host + path` exact 매칭(정확 일치) 사용
|
||||
- 기본 도메인: `aaa.com`
|
||||
- 기본 라우트 path: `/`
|
||||
- 예시: `aaa.com` + `/` , `aaa.com/google`
|
||||
|
||||
## 렌더 우선순위
|
||||
1. `landingRoute` 조회 (`host`, `path`, `isActive`)
|
||||
2. 해당 route의 `RouteCondition` 중 active 조건만 필터
|
||||
3. 요일/시간/날짜 조건 일치 검사
|
||||
4. `priority desc`, `updatedAt desc`로 정렬 후 첫 번째 사용
|
||||
5. 조건 미매칭 시 기본 페이지 사용
|
||||
|
||||
## 호스트 정규화
|
||||
- 요청 host는 소문자 변환, `www.` 제거, 포트(`:4000`, `:3000`) 분리 후 비교
|
||||
- path는 정확 일치(`exact`) 방식
|
||||
|
||||
## 조건 스펙
|
||||
- weekday: `weekMask` 7자리 문자열(일~토: `sun`~`sat`)에서 1이면 매칭
|
||||
- time: `startMinute`~`endMinute` (0~1439)
|
||||
- `start <= end` : 당일 범위
|
||||
- `start > end` : 자정을 넘는 범위(예: 20:00~06:00)
|
||||
- date: `startDate`, `endDate`
|
||||
|
||||
## 블록 정책(빌더)
|
||||
- 블록형 구성 + 드래그 정렬
|
||||
- 카카오 버튼 블록은 `kakaoSyncCode`를 페이지 블록 설정에 저장
|
||||
37
docs/strategy.md
Normal file
37
docs/strategy.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# 랜딩 페이지 SaaS 전략 개요
|
||||
|
||||
작성일: 2026-03-01
|
||||
|
||||
## 제품 방향
|
||||
- 광고 운영자가 랜딩 페이지를 빠르게 생성·수정·배포할 수 있는 SaaS
|
||||
- 핵심 가치: 빠른 제작 + 안정적인 발행 + 팀 운영 + 리드 관리
|
||||
|
||||
## 기술 스택
|
||||
- Nuxt 4 + Nuxt UI + Pinia
|
||||
- Elysia (API)
|
||||
- Prisma + SQLite(개발) + PostgreSQL(운영 전환 예정)
|
||||
- Bun
|
||||
|
||||
## MVP 우선순위
|
||||
1. 사용자/역할 관리
|
||||
2. 캠페인/페이지 CRUD
|
||||
3. 블록형 빌더
|
||||
4. 도메인+경로 라우팅
|
||||
5. 조건 탭(요일/시간) 기반 페이지 분기
|
||||
6. 리드 조회
|
||||
|
||||
## 추후 확장(현재 1차 제외)
|
||||
- 버전관리/롤백
|
||||
- 예약 발행
|
||||
- 내부 분석 대시보드
|
||||
- 결제/구독
|
||||
- 다국어/SEO 고도화
|
||||
|
||||
## 개발 규칙
|
||||
- TypeScript strict
|
||||
- 공식 생성 방식 준수
|
||||
- Nuxt: 공식 nuxi 초기화/디렉토리
|
||||
- Elysia: 공식 앱 초기화/미들웨어 등록
|
||||
- Prisma: `prisma init` + schema + migrate
|
||||
- Bun: 공식 실행 패턴
|
||||
|
||||
31
docs/ui-flow.md
Normal file
31
docs/ui-flow.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# 화면/라우트 설계(1차)
|
||||
|
||||
## 공개 라우트
|
||||
- `/{path?}`: 공개 랜딩 진입점(호스트+경로로 렌더 대상 결정)
|
||||
- `/_lead/success`, `/_lead/error`: 제출 결과 화면
|
||||
- `/admin`: 관리자 홈
|
||||
|
||||
## 관리자 라우트(현재 구현)
|
||||
- `/admin/login`: 역할 선택 로그인(데모 토큰)
|
||||
- `/admin`: 대시보드
|
||||
- `/admin/campaigns`: 캠페인 목록/생성
|
||||
- `/admin/pages`: 페이지 목록/생성
|
||||
- `/admin/pages/[id]/builder`: 페이지 블록 빌더
|
||||
- `/admin/pages/[id]/routes`: 도메인 라우트 관리
|
||||
- `/admin/pages/[id]/conditions`: 요일/시간 조건 관리
|
||||
- `/admin/leads`: 리드 조회
|
||||
- `/admin/builder/[id]`: 과거 호환용 리다이렉트 경로
|
||||
|
||||
## 1차 최소 화면
|
||||
- 로그인
|
||||
- 대시보드
|
||||
- 캠페인 목록/생성
|
||||
- 페이지 목록/생성/빌더 연결
|
||||
- 리드 조회
|
||||
|
||||
## 다음 구현 우선순위
|
||||
- 공개 노출 라우트(이미지/폼/버튼/푸터 블록 렌더)
|
||||
- 블록 기반 랜딩 폼 제출 처리 개선(검증, 중복 제출)
|
||||
- 조건 탭(요일/시간/기간 기반 룰 편집) 품질 개선
|
||||
- 라우트/도메인 매핑 UI 고도화
|
||||
- 사용자/역할 관리(리드관리자 역할 제한)
|
||||
Reference in New Issue
Block a user