daiso 1.0.9 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +88 -9
- package/dist/api/actionsProxy.js +3 -0
- package/dist/api/cgvHandlers.js +15 -3
- package/dist/api/configStatus.js +1 -1
- package/dist/api/dtryxHandlers.js +113 -0
- package/dist/api/emart24Handlers.js +6 -2
- package/dist/api/gs25Handlers.js +22 -19
- package/dist/api/handlers.js +3 -0
- package/dist/api/healthCheckDefinitions.js +56 -8
- package/dist/api/healthCheckShape.js +24 -8
- package/dist/api/healthChecks.js +48 -20
- package/dist/api/operationalAuth.js +32 -0
- package/dist/api/routes/dtryxRoutes.js +22 -0
- package/dist/api/routes/healthRoutes.js +19 -11
- package/dist/api/routes/rateLimitStatsRoutes.js +274 -0
- package/dist/api/routes/registerServiceRoutes.js +37 -0
- package/dist/api/sevenelevenHandlers.js +7 -3
- package/dist/cli/commandOptions.js +24 -0
- package/dist/cli/commands/cinema.js +27 -0
- package/dist/cli/commands/convenience.js +2 -2
- package/dist/cli.js +7 -1
- package/dist/cliHelp.js +3 -337
- package/dist/cliHelpDefinitions.js +369 -0
- package/dist/cliInteractive.js +8 -5
- package/dist/core/errors.js +17 -2
- package/dist/core/registry.js +13 -15
- package/dist/durableObjects/dailyRateLimiter.js +185 -0
- package/dist/durableObjects/rateLimitMetricsStore.js +144 -0
- package/dist/index.js +16 -30
- package/dist/middleware/dailyRateLimit.js +176 -0
- package/dist/pages/openapiFullSpec.js +2 -0
- package/dist/pages/openapiSpecActionParameters.js +429 -0
- package/dist/pages/openapiSpecActions.js +1 -391
- package/dist/pages/openapiSpecComponentsLottecinema.js +2 -2
- package/dist/pages/openapiSpecPathsDtryx.js +148 -0
- package/dist/pages/openapiSpecPathsLotteMart.js +10 -2
- package/dist/pages/promptRetailCinemaText.js +9 -1
- package/dist/services/cgv/client.js +7 -4
- package/dist/services/cgv/errors.js +12 -0
- package/dist/services/cgv/transport.js +19 -2
- package/dist/services/cu/client.js +42 -12
- package/dist/services/cu/index.js +13 -3
- package/dist/services/cu/tools/checkInventory.js +65 -45
- package/dist/services/cu/tools/findNearbyStores.js +4 -3
- package/dist/services/cu/upstreamError.js +15 -0
- package/dist/services/dtryx/api.js +25 -0
- package/dist/services/dtryx/client.js +133 -0
- package/dist/services/dtryx/index.js +24 -0
- package/dist/services/dtryx/location.js +216 -0
- package/dist/services/dtryx/tools/getRemainingSeats.js +117 -0
- package/dist/services/dtryx/tools/listCinemas.js +46 -0
- package/dist/services/dtryx/tools/listNowShowing.js +77 -0
- package/dist/services/dtryx/types.js +4 -0
- package/dist/services/dtryx/validation.js +21 -0
- package/dist/services/emart24/tools/searchProducts.js +37 -19
- package/dist/services/feedback/tools/submitDeveloperRequest.js +15 -1
- package/dist/services/gs25/api.js +2 -2
- package/dist/services/gs25/client.js +16 -123
- package/dist/services/gs25/errors.js +12 -0
- package/dist/services/gs25/index.js +2 -2
- package/dist/services/gs25/productSearch.js +1 -0
- package/dist/services/gs25/storeStockTransport.js +55 -0
- package/dist/services/gs25/tools/checkInventory.js +7 -2
- package/dist/services/gs25/tools/findNearbyStores.js +20 -5
- package/dist/services/lottecinema/client.js +4 -2
- package/dist/services/lottemart/config.js +1 -1
- package/dist/services/lottemart/session.js +29 -160
- package/dist/services/lottemart/socketTransport.js +149 -0
- package/dist/services/oliveyoung/client.js +1 -0
- package/dist/services/places/client.js +29 -8
- package/dist/services/seveneleven/client.js +18 -8
- package/dist/services/seveneleven/index.js +11 -7
- package/dist/services/seveneleven/inventory.js +5 -2
- package/dist/services/seveneleven/productKeyword.js +3 -2
- package/dist/services/seveneleven/tools/checkInventory.js +4 -3
- package/dist/services/seveneleven/tools/getCatalogSnapshot.js +4 -3
- package/dist/services/seveneleven/tools/getSearchPopwords.js +4 -3
- package/dist/services/seveneleven/tools/searchProducts.js +40 -20
- package/dist/services/seveneleven/tools/searchStores.js +4 -3
- package/dist/utils/format.js +10 -3
- package/dist/utils/http.js +38 -10
- package/dist/utils/zyte.js +13 -2
- package/dist/utils/zyteJsonFallback.js +67 -0
- package/openapi.json +51 -3
- package/openapi.yaml +36 -3
- package/package.json +28 -24
package/README.md
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
[](https://mcptoplist.com/server/glama%2Fhmmhmmhm%2Fdaiso-mcp)
|
|
2
|
+
|
|
1
3
|
<div align="center">
|
|
2
4
|
|
|
3
5
|
<img src="./assets/logo.svg" alt="Daiso MCP" width="120" height="120">
|
|
@@ -7,7 +9,9 @@
|
|
|
7
9
|
|
|
8
10
|
# Daiso MCP 및 Skill
|
|
9
11
|
|
|
10
|
-
다이소(제품/매장/재고),
|
|
12
|
+
다이소(제품/매장/재고), 상품 가격 비교, 주변 음식점/카페, 주유소/유가, 개발자 요청 제출, 롯데마트(매장/상품), GS25(매장/상품/재고), 세븐일레븐(상품/매장/재고/인기검색어/카탈로그), CU(매장/재고), 이마트24(매장/상품/재고), 올리브영(매장/재고), 메가박스(지점/영화/시간표/좌석), 롯데시네마(지점/영화/좌석), CGV(극장/영화/시간표), 디트릭스(독립·예술영화관/상영작/잔여 좌석) 조회 기능을 MCP, CLI, Codex Skill로 AI에 연결합니다.
|
|
13
|
+
|
|
14
|
+
디트릭스의 지원 범위와 CLI 예시는 [독립·예술영화관 조회 가이드](docs/dtryx.md)를 참고하세요.
|
|
11
15
|
|
|
12
16
|
<br>
|
|
13
17
|
|
|
@@ -15,7 +19,8 @@
|
|
|
15
19
|
|
|
16
20
|
**ClawHub:** [clawhub.ai/hmmhmmhm/daiso-cli](https://clawhub.ai/hmmhmmhm/daiso-cli)
|
|
17
21
|
|
|
18
|
-
한국 로컬
|
|
22
|
+
한국 로컬 리테일, 생활 정보, 영화관 조회를 MCP, CLI, Codex Skill로 연결하는 도구입니다.
|
|
23
|
+
사용자는 별도 API 키를 준비하지 않고 바로 사용할 수 있습니다.
|
|
19
24
|
|
|
20
25
|
<br>
|
|
21
26
|
|
|
@@ -35,10 +40,15 @@
|
|
|
35
40
|
<td>네이버 지역 검색</td>
|
|
36
41
|
<td>음식점, 카페, 디저트, 주변 장소</td>
|
|
37
42
|
</tr>
|
|
43
|
+
<tr>
|
|
44
|
+
<td>교통</td>
|
|
45
|
+
<td>오피넷</td>
|
|
46
|
+
<td>전국 평균 유가, 최저가 주유소, 위치 기반 주유소, 주유소 상세정보</td>
|
|
47
|
+
</tr>
|
|
38
48
|
<tr>
|
|
39
49
|
<td>비교</td>
|
|
40
50
|
<td>다이소, GS25, 세븐일레븐, 이마트24</td>
|
|
41
|
-
<td
|
|
51
|
+
<td>같은 상품의 판매처별 가격 후보 비교</td>
|
|
42
52
|
</tr>
|
|
43
53
|
<tr>
|
|
44
54
|
<td>운영</td>
|
|
@@ -75,11 +85,19 @@
|
|
|
75
85
|
**[실시간 서비스 상태 보기](https://aka-page.betteruptime.com/)**
|
|
76
86
|
|
|
77
87
|
<!-- WORKERS_INVOCATIONS_CHART:START -->
|
|
78
|
-
<h3>Cloudflare
|
|
88
|
+
<h3>Cloudflare 요청 수 (2026-08-16 ~ 2026-09-14, 30일)</h3>
|
|
79
89
|
|
|
80
|
-
<img src="./assets/analytics/workers-invocations.png?v=2026-
|
|
90
|
+
<img src="./assets/analytics/workers-invocations.png?v=2026-09-14T19:51:30.925Z" alt="Cloudflare 요청 수 그래프 (2026-08-16 ~ 2026-09-14)" width="100%">
|
|
91
|
+
|
|
92
|
+
<sub>기준 워커: <code>daiso-mcp</code> · 마지막 갱신: 2026-09-15 04:51 KST</sub>
|
|
93
|
+
<br><sub>집계: Worker 실행 + 루트 GET 리디렉션 요청 · 사용자 수와 다릅니다.</sub>
|
|
94
|
+
|
|
95
|
+
</div>
|
|
81
96
|
|
|
82
|
-
|
|
97
|
+
> [!IMPORTANT]
|
|
98
|
+
> 최근 공개 서버 사용량이 크게 증가하여 2026년 7월 18일부터 올리브영·CGV·CU·GS25·롯데마트의 검색을 포함한 공개 GET API에 IP당 하루 합산 3,000회(KST 기준)의 호출 제한을 적용합니다. 한도를 초과하는 사용이 필요하다면 Daiso MCP는 오픈 소스이므로 이 저장소를 직접 배포해 이용해 주세요.
|
|
99
|
+
|
|
100
|
+
<div align="center">
|
|
83
101
|
|
|
84
102
|
<!-- WORKERS_INVOCATIONS_CHART:END -->
|
|
85
103
|
|
|
@@ -99,7 +117,7 @@
|
|
|
99
117
|
|
|
100
118
|
## AI 앱에서 MCP 연결하기
|
|
101
119
|
|
|
102
|
-
ChatGPT, Claude, Grok 같은 AI 앱에서 바로 연결해 사용할 수 있습니다.
|
|
120
|
+
ChatGPT, Claude, Home Assistant, Grok 같은 AI 앱에서 바로 연결해 사용할 수 있습니다.
|
|
103
121
|
아래 앱별 가이드에서 먼저 연동한 뒤 검색/재고/영화 조회를 요청하세요.
|
|
104
122
|
|
|
105
123
|
<br>
|
|
@@ -127,6 +145,8 @@ GS25 mcp로 강남 근처 매장과 오감자 재고 알려줘
|
|
|
127
145
|
세븐일레븐 mcp로 인기 검색어와 카탈로그 요약 알려줘
|
|
128
146
|
강남역 근처 카페 찾아줘
|
|
129
147
|
성수동 근처 브런치 음식점 찾아줘
|
|
148
|
+
강남역 근처 제일 싼 주유소 찾아줘
|
|
149
|
+
오늘 전국 평균 휘발유 가격 알려줘
|
|
130
150
|
메가박스 mcp로 강남점 영화와 잔여 좌석 알려줘
|
|
131
151
|
롯데시네마 mcp로 월드타워 근처 지점과 상영 영화 알려줘
|
|
132
152
|
롯데시네마 mcp로 월드타워 잔여 좌석 알려줘
|
|
@@ -137,6 +157,7 @@ CGV mcp로 강남 상영 영화와 시간표 알려줘
|
|
|
137
157
|
|
|
138
158
|
- 가격 비교: `콜라 어디가 싸?` → `compare_products`
|
|
139
159
|
- 주변 장소: `강남역 근처 카페 찾아줘` → `places_search_nearby`
|
|
160
|
+
- 주유소/유가: `강남역 근처 제일 싼 주유소 찾아줘` → `opinet_search_stations_around`
|
|
140
161
|
- 브랜드 명시 재고: `다이소 핫식스 재고 찾아줘` → 먼저 다이소에서 검색 후 결과 없을 때만 대안 제안
|
|
141
162
|
- 편의점 재고: `GS25 강남 오감자 재고` → 상품 후보 확인 후 재고 조회
|
|
142
163
|
- 영화 시간표: `오늘 강남 CGV 시간표` → KST 오늘 날짜로 극장 검색 후 시간표 조회
|
|
@@ -188,6 +209,19 @@ claude mcp add daiso-mcp https://mcp.aka.page --transport http
|
|
|
188
209
|
|
|
189
210
|
<br>
|
|
190
211
|
|
|
212
|
+
### Home Assistant
|
|
213
|
+
|
|
214
|
+
> Home Assistant Core 2026.7.1에서 MCP 도구 40개를 모두 정상적으로 불러오는 것을 확인했습니다.
|
|
215
|
+
|
|
216
|
+
1. Home Assistant에서 **Settings** → **Devices & services**로 이동
|
|
217
|
+
2. **Add Integration**을 선택하고 **Model Context Protocol**을 검색
|
|
218
|
+
3. 서버 URL 입력: `https://mcp.aka.page`
|
|
219
|
+
4. 연동을 마친 뒤 사용할 대화 에이전트가 MCP 도구를 사용하도록 설정
|
|
220
|
+
|
|
221
|
+
참고: [Home Assistant Model Context Protocol 통합 가이드](https://www.home-assistant.io/integrations/mcp)
|
|
222
|
+
|
|
223
|
+
<br>
|
|
224
|
+
|
|
191
225
|
### 
|
|
192
226
|
|
|
193
227
|
> 웹 및 모바일 앱 모두 지원
|
|
@@ -330,9 +364,9 @@ MCP 도구 응답은 각 서비스의 원본 필드를 유지하면서, 클라
|
|
|
330
364
|
- `standard.theaters`: 영화관 목록을 `code`, `name`, `address`, `distanceMeters`, `raw` 형태로 정규화합니다.
|
|
331
365
|
- `standard.places`: 음식점/카페 같은 장소 목록을 `name`, `address`, `raw` 중심으로 정규화합니다.
|
|
332
366
|
|
|
333
|
-
###
|
|
367
|
+
### 통합 상품 가격 비교
|
|
334
368
|
|
|
335
|
-
|
|
369
|
+
여러 리테일/편의점 상품 검색 결과를 묶어 같은 상품의 판매처별 가격 후보를 비교합니다.
|
|
336
370
|
|
|
337
371
|
```bash
|
|
338
372
|
npx daiso compare 콜라 --limit 3 --json
|
|
@@ -434,6 +468,51 @@ curl -H "Authorization: Bearer $HEALTH_CHECK_SECRET" \
|
|
|
434
468
|
|
|
435
469
|
Better Stack 같은 외부 모니터링에서는 `fail`을 장애 알림 기준으로 보고, `degraded`는 느린 외부 API나 응답 품질 저하를 추적하는 경고 신호로 봅니다.
|
|
436
470
|
|
|
471
|
+
### 운영 통계
|
|
472
|
+
|
|
473
|
+
일일 호출 제한으로 차단한 요청의 집계는 인증된 `GET /api/rate-limit/stats`에서 조회합니다. 헬스 체크와 같은 `HEALTH_CHECK_SECRET`을 사용하며, 다음 두 인증 헤더를 모두 지원합니다.
|
|
474
|
+
|
|
475
|
+
```bash
|
|
476
|
+
curl -H "Authorization: Bearer $HEALTH_CHECK_SECRET" \
|
|
477
|
+
"https://mcp.aka.page/api/rate-limit/stats"
|
|
478
|
+
|
|
479
|
+
curl -H "x-health-check-key: $HEALTH_CHECK_SECRET" \
|
|
480
|
+
"https://mcp.aka.page/api/rate-limit/stats?service=cgv"
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
쿼리 필터는 `from`, `to`, `service`입니다. `from`과 `to`는 함께 지정하거나 둘 다 생략해야 하며 날짜 형식은 `YYYY-MM-DD`입니다. `service`에는 `oliveyoung`, `cgv`, `cu`, `gs25`, `lottemart`만 사용할 수 있습니다. 날짜를 생략하면 현재 KST 일자를 포함한 최근 7일을 조회합니다. 조회 가능한 보관 범위는 현재 KST 일자와 그 이전 29일이며, 한 번에 KST 달력 날짜 기준 최대 30일을 요청할 수 있습니다.
|
|
484
|
+
|
|
485
|
+
성공 응답은 전체 합계와 일별·서비스별 차단 요청 수와 고유 차단 주체 수를 제공합니다.
|
|
486
|
+
|
|
487
|
+
```json
|
|
488
|
+
{
|
|
489
|
+
"success": true,
|
|
490
|
+
"data": {
|
|
491
|
+
"totals": {
|
|
492
|
+
"blockedRequests": 3,
|
|
493
|
+
"uniqueIdentities": 2
|
|
494
|
+
},
|
|
495
|
+
"daily": [
|
|
496
|
+
{
|
|
497
|
+
"day": "2026-07-22",
|
|
498
|
+
"blockedRequests": 3,
|
|
499
|
+
"uniqueIdentities": 2
|
|
500
|
+
}
|
|
501
|
+
],
|
|
502
|
+
"services": [
|
|
503
|
+
{
|
|
504
|
+
"day": "2026-07-22",
|
|
505
|
+
"service": "cgv",
|
|
506
|
+
"blockedRequests": 3,
|
|
507
|
+
"uniqueIdentities": 2
|
|
508
|
+
}
|
|
509
|
+
]
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
데이터는 30일 동안 보관하며 집계 응답은 원본 호출 주체나 IP를 노출하지 않습니다. Worker가 생성한 `DAILY_RATE_LIMIT_EXCEEDED` 결정 중 원장 커밋에 성공한 경우만 정확한 집계 범위에 포함됩니다. Cloudflare 또는 네트워크 계층의 429와 클라이언트 전송 결과, 연결 종료 결과는 이 범위에 포함되지 않습니다. 원장 쓰기에 실패하면 요청을 fail-open 처리하고 애플리케이션 429를 반환하지 않습니다. 통계는 이 기능의 배포 시점부터 수집하며 이전 429는 소급 집계하지 않습니다.
|
|
515
|
+
|
|
437
516
|
배포 전 로컬에서 CLI 모드까지 확인할 때는 아래 명령을 사용합니다.
|
|
438
517
|
|
|
439
518
|
```bash
|
package/dist/api/actionsProxy.js
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
* OpenAI Actions용 공통 GET 프록시 정의
|
|
3
3
|
*/
|
|
4
4
|
export const ACTION_QUERY_DEFINITIONS = [
|
|
5
|
+
{ action: 'dtryxListCinemas', summary: '디트릭스 극장 목록', targetPath: '/api/dtryx/cinemas' },
|
|
6
|
+
{ action: 'dtryxListNowShowing', summary: '디트릭스 상영작', targetPath: '/api/dtryx/movies' },
|
|
7
|
+
{ action: 'dtryxGetRemainingSeats', summary: '디트릭스 잔여 좌석', targetPath: '/api/dtryx/seats' },
|
|
5
8
|
{ action: 'daisoSearchProducts', summary: '다이소 제품 검색', targetPath: '/api/daiso/products' },
|
|
6
9
|
{
|
|
7
10
|
action: 'daisoGetProduct',
|
package/dist/api/cgvHandlers.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* CGV GET API 핸들러
|
|
3
3
|
*/
|
|
4
|
-
import { fetchCgvMovies, fetchCgvTheaters, fetchCgvTimetable, toYyyymmdd } from '../services/cgv/client.js';
|
|
4
|
+
import { fetchCgvMovies, fetchCgvTheaters, fetchCgvTimetable, toYyyymmdd, } from '../services/cgv/client.js';
|
|
5
|
+
import { isCgvUpstreamUnavailableError } from '../services/cgv/errors.js';
|
|
5
6
|
import { fetchCgvNearbyTheaters, resolveCgvNearestTheater } from '../services/cgv/location.js';
|
|
6
7
|
import { filterAndSortTimetable } from '../services/cgv/timetable.js';
|
|
7
8
|
import { errorResponse, successResponse } from './response.js';
|
|
@@ -60,6 +61,9 @@ export async function handleCgvFindTheaters(c) {
|
|
|
60
61
|
}, { total: sliced.length, pageSize: limit });
|
|
61
62
|
}
|
|
62
63
|
catch (error) {
|
|
64
|
+
if (isCgvUpstreamUnavailableError(error)) {
|
|
65
|
+
return errorResponse(c, 'CGV_UPSTREAM_UNAVAILABLE', error.message, 503);
|
|
66
|
+
}
|
|
63
67
|
const message = error instanceof Error ? error.message : '알 수 없는 오류가 발생했습니다.';
|
|
64
68
|
return errorResponse(c, 'CGV_THEATER_SEARCH_FAILED', message, 500);
|
|
65
69
|
}
|
|
@@ -77,7 +81,8 @@ export async function handleCgvSearchMovies(c) {
|
|
|
77
81
|
const timeoutMs = parseInt(c.req.query('timeoutMs') || '15000');
|
|
78
82
|
try {
|
|
79
83
|
let resolvedTheater = null;
|
|
80
|
-
if (!theaterCode &&
|
|
84
|
+
if (!theaterCode &&
|
|
85
|
+
(keyword || typeof latitude === 'number' || typeof longitude === 'number')) {
|
|
81
86
|
const resolved = await resolveCgvNearestTheater({
|
|
82
87
|
playDate,
|
|
83
88
|
keyword,
|
|
@@ -114,6 +119,9 @@ export async function handleCgvSearchMovies(c) {
|
|
|
114
119
|
}, { total: movies.length });
|
|
115
120
|
}
|
|
116
121
|
catch (error) {
|
|
122
|
+
if (isCgvUpstreamUnavailableError(error)) {
|
|
123
|
+
return errorResponse(c, 'CGV_UPSTREAM_UNAVAILABLE', error.message, 503);
|
|
124
|
+
}
|
|
117
125
|
const message = error instanceof Error ? error.message : '알 수 없는 오류가 발생했습니다.';
|
|
118
126
|
return errorResponse(c, 'CGV_MOVIE_SEARCH_FAILED', message, 500);
|
|
119
127
|
}
|
|
@@ -133,7 +141,8 @@ export async function handleCgvGetTimetable(c) {
|
|
|
133
141
|
const timeoutMs = parseInt(c.req.query('timeoutMs') || '15000');
|
|
134
142
|
try {
|
|
135
143
|
let resolvedTheater = null;
|
|
136
|
-
if (!theaterCode &&
|
|
144
|
+
if (!theaterCode &&
|
|
145
|
+
(keyword || typeof latitude === 'number' || typeof longitude === 'number')) {
|
|
137
146
|
const resolved = await resolveCgvNearestTheater({
|
|
138
147
|
playDate,
|
|
139
148
|
keyword,
|
|
@@ -178,6 +187,9 @@ export async function handleCgvGetTimetable(c) {
|
|
|
178
187
|
}, { total: filtered.length, pageSize: limit });
|
|
179
188
|
}
|
|
180
189
|
catch (error) {
|
|
190
|
+
if (isCgvUpstreamUnavailableError(error)) {
|
|
191
|
+
return errorResponse(c, 'CGV_UPSTREAM_UNAVAILABLE', error.message, 503);
|
|
192
|
+
}
|
|
181
193
|
const message = error instanceof Error ? error.message : '알 수 없는 오류가 발생했습니다.';
|
|
182
194
|
return errorResponse(c, 'CGV_TIMETABLE_FETCH_FAILED', message, 500);
|
|
183
195
|
}
|
package/dist/api/configStatus.js
CHANGED
|
@@ -9,7 +9,7 @@ export function buildConfigStatus(bindings) {
|
|
|
9
9
|
},
|
|
10
10
|
zyteApiKey: {
|
|
11
11
|
configured: isConfigured(bindings?.ZYTE_API_KEY),
|
|
12
|
-
usedBy: ['oliveyoung', 'gs25', 'lottemart', 'cgv'],
|
|
12
|
+
usedBy: ['oliveyoung', 'gs25', 'cu', 'seveneleven', 'lottemart', 'cgv'],
|
|
13
13
|
},
|
|
14
14
|
naverLocalSearch: {
|
|
15
15
|
configured: isConfigured(bindings?.NAVER_CLIENT_ID) && isConfigured(bindings?.NAVER_CLIENT_SECRET),
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 디트릭스 GET API 핸들러
|
|
3
|
+
*/
|
|
4
|
+
import { resolveDtryxCinemas } from '../services/dtryx/location.js';
|
|
5
|
+
import { DTRYX_INVALID_INPUT, hasInvalidDtryxOptions } from '../services/dtryx/validation.js';
|
|
6
|
+
import { fetchDtryxNowShowing, fetchDtryxPlayDates, fetchDtryxTimetable, toYyyymmdd, } from '../services/dtryx/client.js';
|
|
7
|
+
import { errorResponse, successResponse } from './response.js';
|
|
8
|
+
export async function handleDtryxListCinemas(c) {
|
|
9
|
+
const keyword = c.req.query('keyword') || undefined;
|
|
10
|
+
const region = c.req.query('region') || undefined;
|
|
11
|
+
const brandCode = c.req.query('brandCode') || undefined;
|
|
12
|
+
const limit = Number(c.req.query('limit') ?? '50');
|
|
13
|
+
if (hasInvalidDtryxOptions({ limit })) {
|
|
14
|
+
return errorResponse(c, 'DTRYX_INVALID_INPUT', DTRYX_INVALID_INPUT, 400);
|
|
15
|
+
}
|
|
16
|
+
const cinemas = resolveDtryxCinemas({ keyword, region, brandCode }).slice(0, limit);
|
|
17
|
+
return successResponse(c, {
|
|
18
|
+
filters: {
|
|
19
|
+
keyword: keyword || null,
|
|
20
|
+
region: region || null,
|
|
21
|
+
brandCode: brandCode || null,
|
|
22
|
+
limit,
|
|
23
|
+
},
|
|
24
|
+
count: cinemas.length,
|
|
25
|
+
cinemas,
|
|
26
|
+
}, { total: cinemas.length, pageSize: limit });
|
|
27
|
+
}
|
|
28
|
+
export async function handleDtryxListNowShowing(c) {
|
|
29
|
+
const cinemaCode = c.req.query('cinemaCode') || undefined;
|
|
30
|
+
const keyword = c.req.query('keyword') || undefined;
|
|
31
|
+
const brandCode = c.req.query('brandCode') || undefined;
|
|
32
|
+
const includePlayDates = c.req.query('includePlayDates') === 'true';
|
|
33
|
+
const timeoutMs = Number(c.req.query('timeoutMs') ?? '15000');
|
|
34
|
+
if (hasInvalidDtryxOptions({ timeoutMs })) {
|
|
35
|
+
return errorResponse(c, 'DTRYX_INVALID_INPUT', DTRYX_INVALID_INPUT, 400);
|
|
36
|
+
}
|
|
37
|
+
const cinema = cinemaCode || keyword ? resolveDtryxCinemas({ cinemaCode, keyword, brandCode })[0] : undefined;
|
|
38
|
+
if (!cinema) {
|
|
39
|
+
return errorResponse(c, 'DTRYX_CINEMA_NOT_FOUND', '극장을 찾을 수 없습니다. cinemaCode 또는 keyword 를 확인하세요.', 404);
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
const request = {
|
|
43
|
+
brandCode: cinema.brandCode,
|
|
44
|
+
cinemaCode: cinema.cinemaCode,
|
|
45
|
+
timeout: timeoutMs,
|
|
46
|
+
};
|
|
47
|
+
const [movies, playDates] = await Promise.all([
|
|
48
|
+
fetchDtryxNowShowing(request),
|
|
49
|
+
includePlayDates ? fetchDtryxPlayDates(request) : Promise.resolve([]),
|
|
50
|
+
]);
|
|
51
|
+
return successResponse(c, {
|
|
52
|
+
cinema,
|
|
53
|
+
count: movies.length,
|
|
54
|
+
movies,
|
|
55
|
+
playDates: includePlayDates ? playDates : undefined,
|
|
56
|
+
}, { total: movies.length });
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
const message = error instanceof Error ? error.message : '알 수 없는 오류가 발생했습니다.';
|
|
60
|
+
return errorResponse(c, 'DTRYX_NOW_SHOWING_FAILED', message, 500);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
export async function handleDtryxGetRemainingSeats(c) {
|
|
64
|
+
const playDate = c.req.query('playDate') || toYyyymmdd();
|
|
65
|
+
const cinemaCode = c.req.query('cinemaCode') || undefined;
|
|
66
|
+
const keyword = c.req.query('keyword') || undefined;
|
|
67
|
+
const brandCode = c.req.query('brandCode') || undefined;
|
|
68
|
+
const movieName = c.req.query('movieName') || undefined;
|
|
69
|
+
const region = c.req.query('region') || undefined;
|
|
70
|
+
const limit = Number(c.req.query('limit') ?? '50');
|
|
71
|
+
const timeoutMs = Number(c.req.query('timeoutMs') ?? '15000');
|
|
72
|
+
if (hasInvalidDtryxOptions({ limit, timeoutMs, playDate })) {
|
|
73
|
+
return errorResponse(c, 'DTRYX_INVALID_INPUT', DTRYX_INVALID_INPUT, 400);
|
|
74
|
+
}
|
|
75
|
+
const cinemas = resolveDtryxCinemas({ cinemaCode, keyword, brandCode, region });
|
|
76
|
+
if (cinemas.length === 0) {
|
|
77
|
+
return errorResponse(c, 'DTRYX_CINEMA_NOT_FOUND', '극장을 찾을 수 없습니다. cinemaCode 또는 keyword 를 확인하세요.', 404);
|
|
78
|
+
}
|
|
79
|
+
// Promise.allSettled 로 극장별 실패를 흡수하므로 여기서 예외가 전파되지 않습니다.
|
|
80
|
+
const settled = await Promise.allSettled(cinemas.map((cinema) => fetchDtryxTimetable({
|
|
81
|
+
brandCode: cinema.brandCode,
|
|
82
|
+
cinemaCode: cinema.cinemaCode,
|
|
83
|
+
playDate,
|
|
84
|
+
timeout: timeoutMs,
|
|
85
|
+
})));
|
|
86
|
+
const failedCinemas = cinemas
|
|
87
|
+
.filter((_, index) => settled[index]?.status === 'rejected')
|
|
88
|
+
.map((cinema) => cinema.cinemaName || cinema.cinemaCode);
|
|
89
|
+
if (failedCinemas.length === cinemas.length) {
|
|
90
|
+
return errorResponse(c, 'DTRYX_SEATS_FAILED', `모든 극장 조회에 실패했습니다: ${failedCinemas.join(', ')}`, 503);
|
|
91
|
+
}
|
|
92
|
+
const normalize = (value) => value.replace(/\s+/g, '').toLowerCase();
|
|
93
|
+
const showtimes = settled
|
|
94
|
+
.flatMap((result) => (result.status === 'fulfilled' ? result.value : []))
|
|
95
|
+
.filter((item) => (movieName ? normalize(item.movieName).includes(normalize(movieName)) : true))
|
|
96
|
+
.sort((a, b) => a.startTime === b.startTime
|
|
97
|
+
? a.cinemaName.localeCompare(b.cinemaName)
|
|
98
|
+
: a.startTime.localeCompare(b.startTime))
|
|
99
|
+
.slice(0, limit);
|
|
100
|
+
return successResponse(c, {
|
|
101
|
+
playDate,
|
|
102
|
+
filters: {
|
|
103
|
+
cinemaCode: cinemaCode || null,
|
|
104
|
+
keyword: keyword || null,
|
|
105
|
+
region: region || null,
|
|
106
|
+
movieName: movieName || null,
|
|
107
|
+
},
|
|
108
|
+
searchedCinemaCount: cinemas.length,
|
|
109
|
+
failedCinemas,
|
|
110
|
+
count: showtimes.length,
|
|
111
|
+
showtimes,
|
|
112
|
+
}, { total: showtimes.length, pageSize: limit });
|
|
113
|
+
}
|
|
@@ -15,6 +15,8 @@ export async function handleEmart24FindStores(c) {
|
|
|
15
15
|
const service24h = c.req.query('service24h') === 'true';
|
|
16
16
|
const page = parseInt(c.req.query('page') || '1', 10);
|
|
17
17
|
const limit = parseInt(c.req.query('limit') || '20', 10);
|
|
18
|
+
const parsedTimeoutMs = parseInt(c.req.query('timeoutMs') || '15000', 10);
|
|
19
|
+
const timeoutMs = Number.isFinite(parsedTimeoutMs) && parsedTimeoutMs > 0 ? parsedTimeoutMs : 15000;
|
|
18
20
|
const rawLat = c.req.query('lat');
|
|
19
21
|
const rawLng = c.req.query('lng');
|
|
20
22
|
const parsedLat = rawLat ? parseFloat(rawLat) : undefined;
|
|
@@ -29,7 +31,7 @@ export async function handleEmart24FindStores(c) {
|
|
|
29
31
|
page,
|
|
30
32
|
service24h,
|
|
31
33
|
}, {
|
|
32
|
-
timeout:
|
|
34
|
+
timeout: timeoutMs,
|
|
33
35
|
});
|
|
34
36
|
const stores = result.stores
|
|
35
37
|
.map((store) => {
|
|
@@ -72,6 +74,8 @@ export async function handleEmart24SearchProducts(c) {
|
|
|
72
74
|
const pageSize = parseInt(c.req.query('pageSize') || '10', 10);
|
|
73
75
|
const sortType = c.req.query('sortType') || 'SALE';
|
|
74
76
|
const saleProductYn = c.req.query('saleProductYn') || 'N';
|
|
77
|
+
const parsedTimeoutMs = parseInt(c.req.query('timeoutMs') || '15000', 10);
|
|
78
|
+
const timeoutMs = Number.isFinite(parsedTimeoutMs) && parsedTimeoutMs > 0 ? parsedTimeoutMs : 15000;
|
|
75
79
|
if (keyword.trim().length === 0) {
|
|
76
80
|
return errorResponse(c, 'MISSING_QUERY', '검색어(keyword)를 입력해주세요.');
|
|
77
81
|
}
|
|
@@ -83,7 +87,7 @@ export async function handleEmart24SearchProducts(c) {
|
|
|
83
87
|
sortType,
|
|
84
88
|
saleProductYn,
|
|
85
89
|
}, {
|
|
86
|
-
timeout:
|
|
90
|
+
timeout: timeoutMs,
|
|
87
91
|
});
|
|
88
92
|
return successResponse(c, {
|
|
89
93
|
keyword,
|
package/dist/api/gs25Handlers.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
/* c8 ignore start */
|
|
5
5
|
import { errorResponse, successResponse } from './response.js';
|
|
6
6
|
import { attachDistanceToGs25Stores, fetchGs25SearchProducts, fetchGs25Stores, fetchGs25WebStores, filterGs25StoresByKeyword, geocodeGs25Address, selectGs25StoresForKeyword, sortGs25Stores, } from '../services/gs25/client.js';
|
|
7
|
+
import { isGs25UpstreamUnavailableError } from '../services/gs25/errors.js';
|
|
7
8
|
const GS25_FALLBACK_STORE_LOOKUP_ITEM_CODE = '8801117752804';
|
|
8
9
|
/**
|
|
9
10
|
* GS25 매장 검색 API 핸들러
|
|
@@ -33,6 +34,7 @@ export async function handleGs25FindStores(c) {
|
|
|
33
34
|
geocodeUsed = true;
|
|
34
35
|
}
|
|
35
36
|
}
|
|
37
|
+
let fallbackUsed = false;
|
|
36
38
|
let storeResult = await fetchGs25Stores({
|
|
37
39
|
serviceCode,
|
|
38
40
|
latitude,
|
|
@@ -40,9 +42,15 @@ export async function handleGs25FindStores(c) {
|
|
|
40
42
|
}, {
|
|
41
43
|
timeout: 20000,
|
|
42
44
|
zyteApiKey: c.env?.ZYTE_API_KEY,
|
|
45
|
+
apiKey: c.env?.GS25_API_KEY,
|
|
46
|
+
}).catch(async (error) => {
|
|
47
|
+
if (!isGs25UpstreamUnavailableError(error) || keyword.trim().length === 0)
|
|
48
|
+
throw error;
|
|
49
|
+
const webStoreResult = await fetchGs25WebStores(keyword, { timeout: 20000 });
|
|
50
|
+
fallbackUsed = true;
|
|
51
|
+
return { ...webStoreResult, cacheHit: false };
|
|
43
52
|
});
|
|
44
|
-
|
|
45
|
-
if (storeResult.stores.length === 0 &&
|
|
53
|
+
if (!fallbackUsed && storeResult.stores.length === 0 &&
|
|
46
54
|
typeof latitude === 'number' &&
|
|
47
55
|
typeof longitude === 'number') {
|
|
48
56
|
try {
|
|
@@ -56,6 +64,7 @@ export async function handleGs25FindStores(c) {
|
|
|
56
64
|
}, {
|
|
57
65
|
timeout: 20000,
|
|
58
66
|
zyteApiKey: c.env?.ZYTE_API_KEY,
|
|
67
|
+
apiKey: c.env?.GS25_API_KEY,
|
|
59
68
|
});
|
|
60
69
|
if (fallbackResult.stores.length > 0) {
|
|
61
70
|
storeResult = fallbackResult;
|
|
@@ -66,23 +75,10 @@ export async function handleGs25FindStores(c) {
|
|
|
66
75
|
fallbackUsed = false;
|
|
67
76
|
}
|
|
68
77
|
}
|
|
69
|
-
if (storeResult.stores.length === 0 && keyword.trim().length > 0) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
});
|
|
74
|
-
if (webStoreResult.stores.length > 0) {
|
|
75
|
-
storeResult = {
|
|
76
|
-
totalCount: webStoreResult.totalCount,
|
|
77
|
-
stores: webStoreResult.stores,
|
|
78
|
-
cacheHit: false,
|
|
79
|
-
};
|
|
80
|
-
fallbackUsed = true;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
catch {
|
|
84
|
-
fallbackUsed = false;
|
|
85
|
-
}
|
|
78
|
+
if (!fallbackUsed && storeResult.stores.length === 0 && keyword.trim().length > 0) {
|
|
79
|
+
const webStoreResult = await fetchGs25WebStores(keyword, { timeout: 20000 });
|
|
80
|
+
storeResult = { ...webStoreResult, cacheHit: false };
|
|
81
|
+
fallbackUsed = true;
|
|
86
82
|
}
|
|
87
83
|
const selected = selectGs25StoresForKeyword(storeResult.stores, keyword, {
|
|
88
84
|
relaxWhenEmpty: typeof latitude === 'number' && typeof longitude === 'number',
|
|
@@ -185,6 +181,7 @@ export async function handleGs25CheckInventory(c) {
|
|
|
185
181
|
}, {
|
|
186
182
|
timeout: 20000,
|
|
187
183
|
zyteApiKey: c.env?.ZYTE_API_KEY,
|
|
184
|
+
apiKey: c.env?.GS25_API_KEY,
|
|
188
185
|
});
|
|
189
186
|
const firstAddress = filterGs25StoresByKeyword(baseStores.stores, storeKeyword).find((store) => store.address.trim().length > 0)?.address || '';
|
|
190
187
|
if (firstAddress.length > 0) {
|
|
@@ -216,6 +213,7 @@ export async function handleGs25CheckInventory(c) {
|
|
|
216
213
|
}, {
|
|
217
214
|
timeout: 20000,
|
|
218
215
|
zyteApiKey: c.env?.ZYTE_API_KEY,
|
|
216
|
+
apiKey: c.env?.GS25_API_KEY,
|
|
219
217
|
});
|
|
220
218
|
}
|
|
221
219
|
else {
|
|
@@ -237,6 +235,7 @@ export async function handleGs25CheckInventory(c) {
|
|
|
237
235
|
}, {
|
|
238
236
|
timeout: 20000,
|
|
239
237
|
zyteApiKey: c.env?.ZYTE_API_KEY,
|
|
238
|
+
apiKey: c.env?.GS25_API_KEY,
|
|
240
239
|
});
|
|
241
240
|
}
|
|
242
241
|
else {
|
|
@@ -250,6 +249,7 @@ export async function handleGs25CheckInventory(c) {
|
|
|
250
249
|
}, {
|
|
251
250
|
timeout: 20000,
|
|
252
251
|
zyteApiKey: c.env?.ZYTE_API_KEY,
|
|
252
|
+
apiKey: c.env?.GS25_API_KEY,
|
|
253
253
|
});
|
|
254
254
|
}
|
|
255
255
|
}
|
|
@@ -295,6 +295,9 @@ export async function handleGs25CheckInventory(c) {
|
|
|
295
295
|
});
|
|
296
296
|
}
|
|
297
297
|
catch (error) {
|
|
298
|
+
if (isGs25UpstreamUnavailableError(error)) {
|
|
299
|
+
return errorResponse(c, 'GS25_UPSTREAM_UNAVAILABLE', error.message, 503);
|
|
300
|
+
}
|
|
298
301
|
const message = error instanceof Error ? error.message : '알 수 없는 오류가 발생했습니다.';
|
|
299
302
|
return errorResponse(c, 'GS25_INVENTORY_CHECK_FAILED', message, 500);
|
|
300
303
|
}
|
package/dist/api/handlers.js
CHANGED
|
@@ -229,6 +229,7 @@ export async function handleCuCheckInventory(c) {
|
|
|
229
229
|
searchSort,
|
|
230
230
|
}, {
|
|
231
231
|
timeout: 15000,
|
|
232
|
+
apiKey: c.env?.ZYTE_API_KEY,
|
|
232
233
|
});
|
|
233
234
|
const firstStockItem = stockResult.items.find((item) => item.itemCode.trim().length > 0) || null;
|
|
234
235
|
const hasInputLocation = typeof lat === 'number' && typeof lng === 'number';
|
|
@@ -300,6 +301,8 @@ export async function handleCuCheckInventory(c) {
|
|
|
300
301
|
stores: storeResult.stores.slice(0, storeLimit),
|
|
301
302
|
},
|
|
302
303
|
inventory: {
|
|
304
|
+
available: stockResult.available,
|
|
305
|
+
unavailableReason: stockResult.unavailableReason,
|
|
303
306
|
totalCount: stockResult.totalCount,
|
|
304
307
|
spellModifyYn: stockResult.spellModifyYn,
|
|
305
308
|
items: stockResult.items,
|
|
@@ -6,7 +6,23 @@ export const GS25_CLOUDFRONT_403_PATTERNS = [
|
|
|
6
6
|
'The request could not be satisfied',
|
|
7
7
|
'403 ERROR',
|
|
8
8
|
];
|
|
9
|
+
export const EMART24_UPSTREAM_403_PATTERNS = ['403 Forbidden', '<title>403 Forbidden</title>'];
|
|
10
|
+
export const CU_UPSTREAM_BLOCK_PATTERNS = ['400 Bad Request', 'Request Blocked'];
|
|
11
|
+
export const SEVENELEVEN_UPSTREAM_403_PATTERNS = [
|
|
12
|
+
'403 Forbidden',
|
|
13
|
+
'_Incapsula_Resource',
|
|
14
|
+
'NOINDEX, NOFOLLOW',
|
|
15
|
+
];
|
|
9
16
|
export const HEALTH_CHECKS = [
|
|
17
|
+
{
|
|
18
|
+
id: 'dtryx.movies',
|
|
19
|
+
service: 'dtryx',
|
|
20
|
+
target: 'movies',
|
|
21
|
+
mode: 'deep',
|
|
22
|
+
path: '/api/dtryx/movies?cinemaCode=000067',
|
|
23
|
+
collectionKey: 'movies',
|
|
24
|
+
requiredFields: ['movieCode', 'movieName'],
|
|
25
|
+
},
|
|
10
26
|
{
|
|
11
27
|
id: 'cli.contract',
|
|
12
28
|
service: 'cli',
|
|
@@ -41,6 +57,17 @@ export const HEALTH_CHECKS = [
|
|
|
41
57
|
path: '/api/emart24/products?keyword=%EC%BB%A4%ED%94%BC&pageSize=1',
|
|
42
58
|
collectionKey: 'products',
|
|
43
59
|
requiredFields: ['pluCd', 'goodsName', 'itemName', 'name'],
|
|
60
|
+
degradedFailurePatterns: EMART24_UPSTREAM_403_PATTERNS,
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
id: 'emart24.stores',
|
|
64
|
+
service: 'emart24',
|
|
65
|
+
target: 'stores',
|
|
66
|
+
mode: 'quick',
|
|
67
|
+
path: '/api/emart24/stores?keyword=%EA%B0%95%EB%82%A8&limit=1',
|
|
68
|
+
collectionKey: 'stores',
|
|
69
|
+
requiredFields: ['storeCode', 'storeName', 'name'],
|
|
70
|
+
degradedFailurePatterns: EMART24_UPSTREAM_403_PATTERNS,
|
|
44
71
|
},
|
|
45
72
|
{
|
|
46
73
|
id: 'gs25.products',
|
|
@@ -69,6 +96,26 @@ export const HEALTH_CHECKS = [
|
|
|
69
96
|
path: '/api/seveneleven/products?query=%EC%BB%A4%ED%94%BC&size=1',
|
|
70
97
|
collectionKey: 'products',
|
|
71
98
|
requiredFields: ['itemCode', 'itemName', 'productNo', 'name'],
|
|
99
|
+
degradedFailurePatterns: SEVENELEVEN_UPSTREAM_403_PATTERNS,
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
id: 'seveneleven.stores',
|
|
103
|
+
service: 'seveneleven',
|
|
104
|
+
target: 'stores',
|
|
105
|
+
mode: 'quick',
|
|
106
|
+
path: '/api/seveneleven/stores?keyword=%EA%B0%95%EB%82%A8&limit=1',
|
|
107
|
+
collectionKey: 'stores',
|
|
108
|
+
requiredFields: ['storeCode', 'storeName', 'name'],
|
|
109
|
+
degradedFailurePatterns: SEVENELEVEN_UPSTREAM_403_PATTERNS,
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
id: 'seveneleven.popwords',
|
|
113
|
+
service: 'seveneleven',
|
|
114
|
+
target: 'popwords',
|
|
115
|
+
mode: 'quick',
|
|
116
|
+
path: '/api/seveneleven/popwords?label=home',
|
|
117
|
+
degradedFailurePatterns: SEVENELEVEN_UPSTREAM_403_PATTERNS,
|
|
118
|
+
allowEmpty: true,
|
|
72
119
|
},
|
|
73
120
|
{
|
|
74
121
|
id: 'lottemart.products',
|
|
@@ -87,7 +134,6 @@ export const HEALTH_CHECKS = [
|
|
|
87
134
|
path: '/api/oliveyoung/products?keyword=%EB%A6%BD%EB%B0%A4&size=1',
|
|
88
135
|
collectionKey: 'products',
|
|
89
136
|
requiredFields: ['goodsNumber', 'goodsName', 'productNo', 'productName', 'name'],
|
|
90
|
-
timeoutMs: 5000,
|
|
91
137
|
},
|
|
92
138
|
{
|
|
93
139
|
id: 'megabox.theaters',
|
|
@@ -124,6 +170,7 @@ export const HEALTH_CHECKS = [
|
|
|
124
170
|
path: '/api/cu/inventory?keyword=%EC%BB%A4%ED%94%BC&size=1&storeLimit=0&storeCheck=false',
|
|
125
171
|
collectionKey: 'inventoryItems',
|
|
126
172
|
requiredFields: ['itemCode', 'itemName', 'name'],
|
|
173
|
+
degradedFailurePatterns: CU_UPSTREAM_BLOCK_PATTERNS,
|
|
127
174
|
},
|
|
128
175
|
{
|
|
129
176
|
id: 'emart24.inventory',
|
|
@@ -131,8 +178,9 @@ export const HEALTH_CHECKS = [
|
|
|
131
178
|
target: 'inventory',
|
|
132
179
|
mode: 'deep',
|
|
133
180
|
path: '/api/emart24/inventory?keyword=%EC%BB%A4%ED%94%BC&storeKeyword=%EA%B0%95%EB%82%A8&limit=1',
|
|
134
|
-
collectionKey: '
|
|
135
|
-
requiredFields: ['
|
|
181
|
+
collectionKey: 'stores',
|
|
182
|
+
requiredFields: ['storeCode', 'storeName', 'name'],
|
|
183
|
+
degradedFailurePatterns: EMART24_UPSTREAM_403_PATTERNS,
|
|
136
184
|
},
|
|
137
185
|
{
|
|
138
186
|
id: 'gs25.inventory',
|
|
@@ -140,8 +188,8 @@ export const HEALTH_CHECKS = [
|
|
|
140
188
|
target: 'inventory',
|
|
141
189
|
mode: 'deep',
|
|
142
190
|
path: '/api/gs25/inventory?keyword=%EC%BD%9C%EB%9D%BC&storeKeyword=%EA%B0%95%EB%82%A8&limit=1',
|
|
143
|
-
collectionKey: '
|
|
144
|
-
requiredFields: ['
|
|
191
|
+
collectionKey: 'inventoryStores',
|
|
192
|
+
requiredFields: ['storeCode', 'storeName', 'name'],
|
|
145
193
|
degradedFailurePatterns: [
|
|
146
194
|
'401 Unauthorized',
|
|
147
195
|
'인증키가 제공되지 않음',
|
|
@@ -154,8 +202,9 @@ export const HEALTH_CHECKS = [
|
|
|
154
202
|
target: 'inventory',
|
|
155
203
|
mode: 'deep',
|
|
156
204
|
path: '/api/seveneleven/inventory?keyword=%EC%BB%A4%ED%94%BC&storeKeyword=%EA%B0%95%EB%82%A8&size=1',
|
|
157
|
-
collectionKey: '
|
|
158
|
-
requiredFields: ['
|
|
205
|
+
collectionKey: 'inventoryStores',
|
|
206
|
+
requiredFields: ['storeCode', 'storeName', 'name'],
|
|
207
|
+
degradedFailurePatterns: SEVENELEVEN_UPSTREAM_403_PATTERNS,
|
|
159
208
|
},
|
|
160
209
|
{
|
|
161
210
|
id: 'oliveyoung.inventory',
|
|
@@ -165,6 +214,5 @@ export const HEALTH_CHECKS = [
|
|
|
165
214
|
path: '/api/oliveyoung/inventory?keyword=%EC%84%A0%ED%81%AC%EB%A6%BC&storeKeyword=%EB%AA%85%EB%8F%99&size=1&storeLimit=1&stockCheckLimit=0',
|
|
166
215
|
collectionKey: 'inventoryProducts',
|
|
167
216
|
requiredFields: ['goodsNumber', 'goodsName', 'productNo', 'productName', 'name'],
|
|
168
|
-
timeoutMs: 5000,
|
|
169
217
|
},
|
|
170
218
|
];
|