feat: add batched recipe import queue
This commit is contained in:
@@ -7,9 +7,10 @@ Instagram 게시물과 YouTube 영상의 텍스트를 레시피로 구조화해
|
||||
- Google Authorization Code/OIDC 로그인, PKCE `S256`, 이메일 allowlist
|
||||
- HttpOnly 자체 인증 cookie와 그룹 멤버 간 recipe 공유
|
||||
- Instagram caption 추출 및 YouTube 설명·자막·timestamp 추출
|
||||
- MiniMax OpenAI 호환 Chat Completions API를 이용한 레시피 구조화
|
||||
- 여러 URL을 영속 대기열에 등록하고 사용자가 요청할 때 서버에서 비동기 처리
|
||||
- 추출한 본문을 최대 3개·10만 자 단위로 묶어 MiniMax Chat Completions API로 레시피 구조화
|
||||
- 영어 등 외국어 원문의 recipe 필드를 자연스러운 한국어로 번역
|
||||
- AI 결과 미리보기/편집 후 저장
|
||||
- AI 결과를 schema로 검증해 자동 저장하고 실패 작업은 대기열에서 재시도
|
||||
- Recipe CRUD, 그룹 멤버 조회와 소유자·원본별 중복 방지
|
||||
- 외부 이미지를 최대 1280px WebP로 변환해 `IMAGE_ROOT`에 저장
|
||||
- 반응형 Recipe 목록/상세 화면과 YouTube timestamp 링크
|
||||
@@ -59,7 +60,7 @@ MONGO_FALLBACK_URI mongodb://172.16.0.7:27017/our_recipe_atlas
|
||||
IMAGE_ROOT ./data/images
|
||||
```
|
||||
|
||||
개발과 운영 모두 `our_recipe_atlas` DB를 사용합니다. 개발 데이터는 `users_dev`, `recipes_dev` 컬렉션으로 분리되며, 로그인 허용 계정은 공통 `allowed_google_emails` 컬렉션을 사용합니다. 프로젝트를 `D:\project\our_recipe_atlas`에서 실행하면 이미지는 `D:\project\our_recipe_atlas\data\images\recipes\...`에 저장됩니다.
|
||||
개발과 운영 모두 `our_recipe_atlas` DB를 사용합니다. 개발 데이터는 `users_dev`, `groups_dev`, `import_jobs_dev`, `recipes_dev` 컬렉션으로 분리되며, 로그인 허용 계정은 공통 `allowed_google_emails` 컬렉션을 사용합니다. 프로젝트를 `D:\project\our_recipe_atlas`에서 실행하면 이미지는 `D:\project\our_recipe_atlas\data\images\recipes\...`에 저장됩니다.
|
||||
|
||||
Linux에서 자동 적용되는 운영 프로필:
|
||||
|
||||
@@ -70,7 +71,7 @@ MONGO_FALLBACK_URI mongodb://172.16.0.7:27017/our_recipe_atlas
|
||||
IMAGE_ROOT /mnt/recipe-ssd/our_recipe_atlas/images
|
||||
```
|
||||
|
||||
운영 데이터는 같은 DB의 `users`, `recipes` 컬렉션에 저장되고 이미지는 `/mnt/recipe-ssd/our_recipe_atlas/images/recipes/...`에 저장됩니다. 이미지의 DB 값은 두 OS 모두 `recipes/<recipe-id>/cover.webp` 형식입니다.
|
||||
운영 데이터는 같은 DB의 `users`, `groups`, `import_jobs`, `recipes` 컬렉션에 저장되고 이미지는 `/mnt/recipe-ssd/our_recipe_atlas/images/recipes/...`에 저장됩니다. 이미지의 DB 값은 두 OS 모두 `recipes/<recipe-id>/cover.webp` 형식입니다.
|
||||
|
||||
Windows에서 만든 `node_modules`는 운영 서버로 복사하지 말고, `sharp` 등 OS별 바이너리가 Linux용으로 설치되도록 운영 서버에서 `npm ci --omit=dev`를 실행해야 합니다.
|
||||
|
||||
@@ -138,7 +139,7 @@ MINIMAX_BASE_URL=https://api.minimax.io/v1
|
||||
MINIMAX_MODEL=MiniMax-M2.7
|
||||
```
|
||||
|
||||
파서는 최대 한 번만 repair 요청을 수행하며, 결과를 Zod schema로 검증한 뒤 미리보기로 반환합니다. 원문에 없는 재료와 수량을 추측하지 않도록 system prompt에 제한을 둡니다.
|
||||
URL별로 본문을 먼저 추출한 뒤 최대 3개·원문 합계 10만 자씩 한 요청에 넣습니다. 배치 전체 또는 일부 결과가 유효하지 않으면 해당 원문만 개별 요청으로 재시도합니다. 각 결과는 Zod schema로 검증하며, 원문에 없는 재료와 수량을 추측하지 않도록 system prompt에 제한을 둡니다.
|
||||
|
||||
### Instagram
|
||||
|
||||
@@ -159,10 +160,11 @@ ID와 비밀번호로 서버에서 자동 로그인하지 않습니다. cookie
|
||||
MongoDB는 `our_recipe_atlas` 하나만 사용하고 collection으로 환경을 분리합니다.
|
||||
|
||||
- 공통: `allowed_google_emails`
|
||||
- 개발: `users_dev`, `groups_dev`, `recipes_dev`
|
||||
- 운영: `users`, `groups`, `recipes`
|
||||
- 개발: `users_dev`, `groups_dev`, `import_jobs_dev`, `recipes_dev`
|
||||
- 운영: `users`, `groups`, `import_jobs`, `recipes`
|
||||
- 각 `users*.googleSub`: unique index
|
||||
- 각 `groups*.memberGoogleSubs`: 조회 index
|
||||
- 각 `import_jobs*`: 사용자·원본별 unique index와 상태 조회 index
|
||||
- 각 `recipes*`: `ownerGoogleSub + source.platform + source.sourceId` unique index
|
||||
- 원본 text와 YouTube transcript를 recipe source에 보존해 재분석에 사용할 수 있습니다.
|
||||
|
||||
@@ -189,7 +191,11 @@ recipes/<recipe-id>/cover.webp
|
||||
|
||||
| Method | Path | 역할 |
|
||||
| --- | --- | --- |
|
||||
| `POST` | `/api/import/preview` | URL 추출 및 AI recipe 미리보기 |
|
||||
| `POST` | `/api/import/jobs` | URL 목록을 사용자 대기열에 등록 |
|
||||
| `GET` | `/api/import/jobs` | 대기·처리·실패 작업 조회 |
|
||||
| `POST` | `/api/import/jobs/process` | 대기 작업의 서버 처리를 요청하고 즉시 `202` 반환 |
|
||||
| `POST` | `/api/import/jobs/:id/retry` | 실패 작업을 대기 상태로 되돌림 |
|
||||
| `POST` | `/api/import/preview` | 이전 클라이언트 호환용 단일 URL 미리보기 |
|
||||
| `GET` | `/api/recipes` | 내 그룹의 recipe 목록 |
|
||||
| `GET` | `/api/recipes/:id` | 내 그룹의 recipe 상세 |
|
||||
| `POST` | `/api/recipes` | 미리보기 확인 후 저장 |
|
||||
@@ -210,13 +216,13 @@ npm test
|
||||
npm run lint
|
||||
```
|
||||
|
||||
테스트 범위에는 URL/ID 판별, AI schema, timestamp 정규화, allowlist, 인증 middleware, 그룹 공유와 소유자 쓰기 권한, CRUD, 중복 source 처리, 이미지 상대경로와 SSRF 차단이 포함됩니다.
|
||||
테스트 범위에는 URL/ID 판별, MiniMax 본문 배치와 AI schema, 대기열 상태 전이, timestamp 정규화, allowlist, 인증 middleware, 그룹 공유와 소유자 쓰기 권한, CRUD, 중복 source 처리, 이미지 상대경로와 SSRF 차단이 포함됩니다.
|
||||
|
||||
실제 계정과 네트워크가 준비된 뒤에는 별도 smoke test로 다음을 확인합니다.
|
||||
|
||||
1. Instagram Reel/Post의 긴 caption 추출
|
||||
2. 실제 YouTube 레시피 영상의 description, transcript, timestamp 추출
|
||||
3. MiniMax 응답 품질과 미리보기 수정/저장
|
||||
3. 여러 본문의 MiniMax 응답 품질과 자동 저장
|
||||
4. 서버 재시작 후 MongoDB recipe와 SSD 이미지 표시
|
||||
|
||||
## 운영 배포 예시
|
||||
|
||||
Reference in New Issue
Block a user